From 9a594722435172ffc3af0d2f5fb6aba2f47669a2 Mon Sep 17 00:00:00 2001 From: Eran Date: Thu, 5 Sep 2024 12:52:12 +0300 Subject: [PATCH] add Linux code to adapter-watcher --- third-party/rsutils/src/adapter-watcher.cpp | 143 ++++++++++++++++++-- 1 file changed, 129 insertions(+), 14 deletions(-) diff --git a/third-party/rsutils/src/adapter-watcher.cpp b/third-party/rsutils/src/adapter-watcher.cpp index bb336886d05..3c488e66b63 100644 --- a/third-party/rsutils/src/adapter-watcher.cpp +++ b/third-party/rsutils/src/adapter-watcher.cpp @@ -1,22 +1,29 @@ // License: Apache 2.0. See LICENSE file in root directory. // Copyright(c) 2024 Intel Corporation. All Rights Reserved. -//#define _WINSOCKAPI_ // stops windows.h including winsock.h - #include #include #include #include -#ifdef WIN32 +#if ! defined( __APPLE__ ) && ! defined( __ANDROID__ ) +#ifdef _WIN32 #include -//#include #include #pragma comment( lib, "iphlpapi.lib" ) #else + +#include +#include +#include +#include +#include +#include + #endif +#endif // ! __APPLE__ && ! __ANDROID__ namespace rsutils { @@ -24,33 +31,35 @@ namespace os { namespace detail { +#if ! defined( __APPLE__ ) && ! defined( __ANDROID__ ) class adapter_watcher_singleton { using public_signal = rsutils::public_signal< adapter_watcher_singleton >; -#ifdef WIN32 +#ifdef _WIN32 HANDLE _done; - std::thread _th; #else + int _socket; + int _done; #endif + std::thread _th; public: public_signal callbacks; adapter_watcher_singleton() -#ifdef WIN32 +#ifdef _WIN32 : _done( CreateEvent( nullptr /*security attrs*/, TRUE /*manual reset*/, FALSE /*init unset*/, nullptr /*no name*/ ) ) , _th( - [this]() + [this] { // We're going to wait on the following handles: HANDLE handles[2]; DWORD const n_handles = sizeof( handles ) / sizeof( handles[0] ); handles[0] = _done; // https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-notifyaddrchange - OVERLAPPED overlap = {0}; - //overlap.hEvent = WSACreateEvent(); + OVERLAPPED overlap = { 0 }; overlap.hEvent = CreateEvent( nullptr /*security attrs*/, FALSE /*auto reset*/, FALSE /*init unset*/, @@ -69,7 +78,6 @@ class adapter_watcher_singleton == WaitForMultipleObjects( n_handles, handles, FALSE /*wait-for-all*/, INFINITE ) ) break; // we're done LOG_DEBUG( "network adapter changes detected!" ); - //WSAResetEvent( handles[1] ); callbacks.raise(); } @@ -78,9 +86,80 @@ class adapter_watcher_singleton CancelIPChangeNotify( &overlap ); } ) #else - // https://manpages.ubuntu.com/manpages/oracular/en/man7/netlink.7.html // https://stackoverflow.com/questions/27008067/how-to-get-notified-about-network-interface-changes-with-netlist-and-rtmgrp-link - On Linux, it is done opening and reading on a special kind of socket.Documentation here.Some nice examples here. + : _socket( open_netlink() ) + , _done( eventfd( 0, 0 ) ) + , _th( + [this] + { + // We're going to wait on the following handles: + LOG_DEBUG( "starting network adapter watcher" ); + + int const timeout = -1; // negative value = infinite + int const nfds = 2; + pollfd fds[nfds]; + memset( fds, 0, sizeof( fds ) ); + fds[0].fd = _done; + fds[0].events = POLLIN; + fds[1].fd = _socket; + fds[1].events = POLLIN; + + while( true ) + { + fds[0].revents = 0; + fds[1].revents = 0; + auto rv = poll( fds, nfds, timeout ); + if( rv <= 0 ) + { + LOG_ERROR( "poll failed: " << rv ); + break; + } + + if( fds[0].revents != 0 ) + break; // we're done + + LOG_DEBUG( "network adapter changes detected!" ); + + char buf[4096]; + struct sockaddr_nl snl; + iovec iov = { buf, sizeof buf }; + msghdr msg = { (void *)&snl, sizeof snl, &iov, 1, NULL, 0, 0 }; + int status = recvmsg( _socket, &msg, 0 ); + if( status < 0 ) + { + // Socket non-blocking so bail out once we have read everything + if( errno == EWOULDBLOCK || errno == EAGAIN ) + continue; + + // Anything else is an error + LOG_DEBUG( "read_netlink: Error recvmsg: " << status ); + break; + } + if( status == 0 ) + LOG_DEBUG( "read_netlink: EOF" ); + + // We need to handle more than one message per recvmsg + for( auto h = (struct nlmsghdr *)buf; NLMSG_OK( h, (unsigned int)status ); + h = NLMSG_NEXT( h, status ) ) + { + // Finish reading + if( h->nlmsg_type == NLMSG_DONE ) + continue; + + // Message is some kind of error + if( h->nlmsg_type == NLMSG_ERROR ) + { + LOG_DEBUG( "read_netlink: got NLMSG_ERROR" ); + continue; + } + } + + // Call our clients once for all the messages... outside the loop + callbacks.raise(); + } + + LOG_DEBUG( "exiting network adapter watcher" ); + } ) #endif { LOG_DEBUG( "network adapter watcher singleton is up" ); @@ -88,7 +167,7 @@ class adapter_watcher_singleton ~adapter_watcher_singleton() { -#ifdef WIN32 +#ifdef _WIN32 if( _th.joinable() ) { SetEvent( _done ); @@ -96,19 +175,55 @@ class adapter_watcher_singleton } CloseHandle( _done ); #else + if( _th.joinable() ) + { + if( write( _done, &_done, sizeof( &_done ) ) != sizeof( &_done ) ) + /* to avoid compiler warning about not using return value */; + _th.join(); + } + close( _done ); #endif } + +#ifndef _WIN32 + static int open_netlink() + { + // NETLINK_ROUTE + // Receives routing and link updates and may be used to modify the routing tables + // (both IPv4 and IPv6), IP addresses, link parameters, neighbor setups, queueing + // disciplines, traffic classes, and packet classifiers. + // https://manpages.ubuntu.com/manpages/oracular/en/man7/netlink.7.html + int sock = socket( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE ); + if( sock < 0 ) + return sock; + + struct sockaddr_nl addr; + memset( (void *)&addr, 0, sizeof( addr ) ); + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; + if( bind( sock, (struct sockaddr *)&addr, sizeof( addr ) ) < 0 ) + return -1; + + return sock; + } +#endif }; + static rsutils::shared_ptr_singleton< detail::adapter_watcher_singleton > the_adapter_watcher; +#endif // ! __APPLE__ && ! __ANDROID__ + } // namespace detail adapter_watcher::adapter_watcher( callback && cb ) +#if ! defined( __APPLE__ ) && ! defined( __ANDROID__ ) : _singleton( detail::the_adapter_watcher.instance() ) // keep it alive , _subscription( _singleton->callbacks.subscribe( std::move( cb ) ) ) +#endif { // As long as someone keeps a pointer to an adapter_watcher, the singleton will be kept alive and it will watch for // changes; as soon as all instances disappear, the singleton will disappear and the watch should stop.