"Fossies" - the Fresh Open Source Software Archive

Member "muscle/system/DetectNetworkConfigChangesSession.cpp" (21 Nov 2020, 34535 Bytes) of package /linux/privat/muscle7.62.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "DetectNetworkConfigChangesSession.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 7.61_vs_7.62.

    1 #include "iogateway/SignalMessageIOGateway.h"
    2 
    3 #include "system/DetectNetworkConfigChangesSession.h"
    4 
    5 #ifdef __APPLE__
    6 # include <mach/mach_port.h>
    7 # include <mach/mach_interface.h>
    8 # include <mach/mach_init.h>
    9 # include <TargetConditionals.h>
   10 # if !(TARGET_OS_IPHONE)
   11 #  include <IOKit/pwr_mgt/IOPMLib.h>
   12 #  include <IOKit/IOMessage.h>
   13 # else
   14 #  ifndef MUSCLE_USE_DUMMY_DETECT_NETWORK_CONFIG_CHANGES_SESSION
   15 #   define MUSCLE_USE_DUMMY_DETECT_NETWORK_CONFIG_CHANGES_SESSION
   16 #  endif
   17 # endif
   18 # include <SystemConfiguration/SystemConfiguration.h>
   19 #elif WIN32
   20 # if defined(UNICODE) && !defined(_UNICODE)
   21 #  define _UNICODE 1
   22 # endif
   23 # include <tchar.h>
   24 # if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600) && !defined(MUSCLE_AVOID_NETIOAPI)
   25    // If we're compiling pre-Vista, the NetIOApi isn't available
   26 #  define MUSCLE_AVOID_NETIOAPI
   27 # endif
   28 # ifndef MUSCLE_AVOID_NETIOAPI
   29 #  include <Netioapi.h>
   30 #  include "util/NetworkUtilityFunctions.h"  // for GetNetworkInterfaceInfos()
   31 # endif
   32 # include <Iphlpapi.h>
   33 # define MY_INVALID_HANDLE_VALUE ((::HANDLE)(-1))  // bloody hell...
   34 #endif
   35 
   36 #ifdef __linux__
   37 # include <asm/types.h>
   38 # include <sys/types.h>
   39 # include <sys/socket.h>
   40 # include <net/if.h>
   41 # include <linux/netlink.h>
   42 # include <linux/rtnetlink.h>
   43 #endif
   44 
   45 #include "reflector/ReflectServer.h"
   46 
   47 #if (defined(__APPLE__) && !(TARGET_OS_IPHONE)) || defined(WIN32)
   48 # define USE_SINGLETON_THREAD
   49 # include "system/Thread.h"
   50 # if defined(MUSCLE_SINGLE_THREAD_ONLY)
   51 #  error "Can't compile DetectNetworkConfigChangesSession for MacOS/X or Windos when MUSCLE_SINGLE_THREAD_ONLY is set!"
   52 # endif
   53 #endif
   54 
   55 namespace muscle {
   56 
   57 #if defined(USE_SINGLETON_THREAD)
   58 
   59 class DetectNetworkConfigChangesThread;
   60 
   61 static Mutex _singletonThreadMutex;
   62 static DetectNetworkConfigChangesThread * _singletonThread = NULL;  // demand-allocated
   63 
   64 #ifdef __APPLE__
   65 static OSStatus CreateIPAddressListChangeCallbackSCF(SCDynamicStoreCallBack callback, void *contextPtr, SCDynamicStoreRef * storeRef, CFRunLoopSourceRef *sourceRef, Hashtable<String, String> & keyToInterfaceName);
   66 static void IPConfigChangedCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void * info);
   67 static void SleepCallback(void * refCon, io_service_t /*service*/, natural_t messageType, void * messageArgument);
   68 #endif
   69 
   70 #ifdef WIN32
   71 static LRESULT CALLBACK dnccsWindowHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
   72 static VOID __stdcall AddressCallback(IN PVOID context, IN PMIB_UNICASTIPADDRESS_ROW Address OPTIONAL, IN MIB_NOTIFICATION_TYPE NotificationType);
   73 static VOID __stdcall InterfaceCallback(IN PVOID context, IN PMIB_IPINTERFACE_ROW interfaceRow, IN MIB_NOTIFICATION_TYPE NotificationType);
   74 
   75 #endif
   76 
   77 enum {
   78    DNCCS_MESSAGE_INTERFACES_CHANGED = 1684956003, // 'dncc'
   79    DNCCS_MESSAGE_ABOUT_TO_SLEEP,
   80    DNCCS_MESSAGE_JUST_WOKE_UP
   81 };
   82 
   83 // We'll create just one DetectNetworkConfigChangesThread per process and let all DetectNetworkConfigChangesSession objects
   84 // use it.  That way we limit the number of detect-network-changes-threads created to 1, and relieve the user's code from 
   85 // any pressure to minimize the number of DetectNetworkConfigChangesSessions it creates.
   86 class DetectNetworkConfigChangesThread : public Thread
   87 {
   88 public:
   89    DetectNetworkConfigChangesThread()
   90       : Thread(false)
   91       , _threadKeepGoing(false)
   92       , _isComputerSleeping(false)
   93 #ifdef __APPLE__
   94       , _threadRunLoop(NULL)
   95 #elif WIN32
   96       , _wakeupSignal(MY_INVALID_HANDLE_VALUE)
   97 #endif
   98    {
   99       // empty
  100    }
  101 
  102    virtual ~DetectNetworkConfigChangesThread()
  103    {
  104       MASSERT(_registeredSessions.IsEmpty(), "DetectNetworkConfigChangesThread destroyed while sessions were still registered");
  105    }
  106 
  107    status_t RegisterSession(DetectNetworkConfigChangesSession * s) 
  108    {
  109       const bool startInternalThread = _registeredSessions.IsEmpty();
  110 
  111       status_t ret;
  112       if (_registeredSessions.PutWithDefault(s).IsError(ret)) return ret;
  113 
  114       return startInternalThread ? StartInternalThread() : ret;
  115    }
  116 
  117    status_t UnregisterSession(DetectNetworkConfigChangesSession * s) 
  118    {
  119       status_t ret;
  120       if ((_registeredSessions.Remove(s).IsOK(ret))&&(_registeredSessions.IsEmpty())) ShutdownInternalThread();
  121       return ret;
  122    }
  123 
  124    bool HasRegisteredSessions() const {return _registeredSessions.HasItems();}
  125 
  126    void ThreadSafeSendMessageToSessions(const MessageRef & msg)
  127    {
  128       MutexGuard mg(_singletonThreadMutex);
  129       for (HashtableIterator<DetectNetworkConfigChangesSession *, Void> iter(_registeredSessions); iter.HasData(); iter++) 
  130          iter.GetKey()->ThreadSafeMessageReceivedFromSingletonThread(msg);
  131    }
  132 
  133    void SleepCallback(bool isAboutToSleep)
  134    {
  135       if (isAboutToSleep != _isComputerSleeping)
  136       {
  137          _isComputerSleeping = isAboutToSleep;
  138 
  139          static Message _aboutToSleepMessage(DNCCS_MESSAGE_ABOUT_TO_SLEEP);
  140          static Message _justWokeUpMessage(DNCCS_MESSAGE_JUST_WOKE_UP);
  141          (void) ThreadSafeSendMessageToSessions(MessageRef(isAboutToSleep ? &_aboutToSleepMessage : &_justWokeUpMessage, false));
  142       }       
  143    }
  144 
  145 #ifdef __APPLE__
  146    void SleepCallback(natural_t messageType, long messageArgument)
  147    {
  148       switch(messageType)
  149       {
  150          case kIOMessageCanSystemSleep:
  151             /* Idle sleep is about to kick in. This message will not be sent for forced sleep.
  152                Applications have a chance to prevent sleep by calling IOCancelPowerChange.
  153                Most applications should not prevent idle sleep.
  154 
  155                Power Management waits up to 30 seconds for you to either allow or deny idle
  156                sleep. If you don't acknowledge this power change by calling either
  157                IOAllowPowerChange or IOCancelPowerChange, the system will wait 30
  158                seconds then go to sleep.
  159             */
  160 
  161             //Uncomment to cancel idle sleep
  162             //IOCancelPowerChange(*_rootPortPointer, (long)messageArgument);
  163 
  164             // we will allow idle sleep
  165             IOAllowPowerChange(*_rootPortPointer, (long)messageArgument);
  166           break;
  167     
  168           case kIOMessageSystemWillSleep:
  169              /* The system WILL go to sleep. If you do not call IOAllowPowerChange or
  170                 IOCancelPowerChange to acknowledge this message, sleep will be
  171                 delayed by 30 seconds.
  172 
  173                 NOTE: If you call IOCancelPowerChange to deny sleep it returns
  174                 kIOReturnSuccess, however the system WILL still go to sleep.
  175              */
  176              SleepCallback(true);
  177              IOAllowPowerChange(*_rootPortPointer, (long)messageArgument);
  178           break;
  179 
  180           case kIOMessageSystemWillPowerOn:
  181              //System has started the wake up process...
  182           break;
  183     
  184           case kIOMessageSystemHasPoweredOn:
  185              // System has finished waking up...
  186              SleepCallback(false);
  187           break;
  188 
  189           default:
  190              // empty
  191           break;
  192       }
  193    }
  194 
  195    void IPConfigChanged(SCDynamicStoreRef store, CFArrayRef changedKeys)
  196    {
  197       Hashtable<String, Void> changedInterfaceNames;
  198 
  199       CFIndex c = CFArrayGetCount(changedKeys);
  200       for (CFIndex i=0; i<c; i++)
  201       {
  202          const CFStringRef p = (CFStringRef) CFArrayGetValueAtIndex(changedKeys, i);
  203          const String keyStr(p);
  204          if (keyStr.HasChars())
  205          {
  206             String interfaceName;
  207             if (keyStr.StartsWith("State:/Network/Interface/")) interfaceName = keyStr.Substring(25).Substring(0, "/");
  208             else
  209             {
  210                CFPropertyListRef propList = SCDynamicStoreCopyValue(store, p);
  211                if (propList)
  212                {
  213                   interfaceName = String((const CFStringRef) CFDictionaryGetValue((CFDictionaryRef) propList, CFSTR("InterfaceName")));
  214                   CFRelease(propList);
  215                }
  216             }
  217 
  218             if (interfaceName.HasChars()) (void) _scKeyToInterfaceName.Put(keyStr, interfaceName);
  219                                      else interfaceName = _scKeyToInterfaceName.RemoveWithDefault(keyStr);
  220 
  221             if (interfaceName.HasChars()) (void) changedInterfaceNames.PutWithDefault(interfaceName);
  222          }
  223       }
  224 
  225       SignalInterfacesChanged(changedInterfaceNames);
  226    }
  227 #endif
  228 
  229    void SignalInterfacesChanged(uint32 changedIdx)
  230    {
  231       Hashtable<String, Void> iNames;
  232       Queue<NetworkInterfaceInfo> q;
  233       if (GetNetworkInterfaceInfos(q) == B_NO_ERROR)
  234       {
  235          for (uint32 i=0; i<q.GetNumItems(); i++)
  236          {
  237             const NetworkInterfaceInfo & nii = q[i];
  238             if (nii.GetLocalAddress().GetInterfaceIndex() == changedIdx) iNames.PutWithDefault(nii.GetName());
  239          }
  240       }
  241       SignalInterfacesChanged(iNames);
  242    }
  243 
  244    void SignalInterfacesChanged(const Hashtable<String, Void> & optInterfaceNames)
  245    {
  246       MessageRef msg;  // demand-allocated
  247       if (optInterfaceNames.HasItems())
  248       {
  249          msg = GetMessageFromPool(DNCCS_MESSAGE_INTERFACES_CHANGED);
  250          if (msg()) for (HashtableIterator<String, Void> iter(optInterfaceNames); iter.HasData(); iter++) msg()->AddString("if", iter.GetKey());
  251       }
  252       static Message _msg(DNCCS_MESSAGE_INTERFACES_CHANGED);
  253       ThreadSafeSendMessageToSessions(msg() ? msg : MessageRef(&_msg, false));
  254    }
  255 
  256 protected:
  257    virtual void InternalThreadEntry()
  258    {
  259 # if defined(MUSCLE_USE_DUMMY_DETECT_NETWORK_CONFIG_CHANGES_SESSION)
  260       // empty
  261 # elif defined(__APPLE__)
  262       _threadRunLoop = CFRunLoopGetCurrent();
  263 
  264       // notification port allocated by IORegisterForSystemPower
  265       IONotificationPortRef powerNotifyPortRef = NULL;
  266       io_object_t notifierObject;    // notifier object, used to deregister later
  267       void * refCon = (void *) this; // this parameter is passed to the callback
  268       CFRunLoopSourceRef powerNotifyRunLoopSource = NULL;
  269     
  270       // register to receive system sleep notifications
  271       io_connect_t root_port = IORegisterForSystemPower(refCon, &powerNotifyPortRef, muscle::SleepCallback, &notifierObject);
  272       _rootPortPointer = &root_port;
  273       if (root_port != 0)
  274       {
  275          powerNotifyRunLoopSource = IONotificationPortGetRunLoopSource(powerNotifyPortRef);
  276          CFRunLoopAddSource((CFRunLoopRef)_threadRunLoop, powerNotifyRunLoopSource, kCFRunLoopCommonModes);
  277       }
  278       else LogTime(MUSCLE_LOG_WARNING, "DetectNetworkConfigChangesThread::InternalThreadEntry():  IORegisterForSystemPower() failed [%s]\n", B_ERRNO());
  279 
  280       SCDynamicStoreRef storeRef = NULL;
  281       CFRunLoopSourceRef sourceRef = NULL;
  282       if (CreateIPAddressListChangeCallbackSCF(muscle::IPConfigChangedCallback, this, &storeRef, &sourceRef, _scKeyToInterfaceName) == noErr)
  283       {
  284          CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopDefaultMode);
  285          while(_threadKeepGoing) 
  286          {
  287             CFRunLoopRun();
  288 #ifdef TODO_REWRITE_THIS
  289             while(1)
  290             {
  291                MessageRef msgRef;
  292                int32 numLeft = WaitForNextMessageFromOwner(msgRef, 0);
  293                if (numLeft >= 0)
  294                {
  295                   if (MessageReceivedFromOwner(msgRef, numLeft) != B_NO_ERROR) _threadKeepGoing = false;
  296                }
  297                else break; 
  298             }
  299 #endif
  300          }
  301          CFRunLoopRemoveSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopDefaultMode);
  302          CFRelease(storeRef);
  303          CFRelease(sourceRef);
  304       }
  305       if (powerNotifyRunLoopSource) CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerNotifyRunLoopSource, kCFRunLoopDefaultMode);
  306       if (powerNotifyPortRef) 
  307       {
  308          (void) IODeregisterForSystemPower(&root_port);
  309          (void) IONotificationPortDestroy(powerNotifyPortRef);
  310       }
  311 
  312 # elif defined(WIN32)
  313 #define WINDOW_CLASS_NAME   _T("DetectNetworkConfigChangesThread_HiddenWndClass")
  314 #define WINDOW_MENU_NAME    _T("DetectNetworkConfigChangesThread_MainMenu")
  315 
  316       // Gotta create a hidden window to receive WM_POWERBROADCAST events, lame-o!
  317       // Register the window class for the main window. 
  318       WNDCLASS window_class; memset(&window_class, 0, sizeof(window_class));
  319       window_class.style          = 0;
  320       window_class.lpfnWndProc    = (WNDPROC) dnccsWindowHandler;
  321       window_class.cbClsExtra     = 0;
  322       window_class.cbWndExtra     = 0;
  323       window_class.hInstance      = NULL;
  324       window_class.hIcon          = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
  325       window_class.hCursor        = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
  326       window_class.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
  327       window_class.lpszMenuName   = WINDOW_MENU_NAME; 
  328       window_class.lpszClassName  = WINDOW_CLASS_NAME; 
  329       (void) RegisterClass(&window_class); // Deliberately not checking result, per Chris Guzak at http://msdn.microsoft.com/en-us/library/windows/desktop/ms633586(v=vs.85).aspx
  330      
  331       // This window will never be shown; its only purpose is to allow us to receive WM_POWERBROADCAST events so we can alert the calling code to sleep and wake events
  332       HWND hiddenWindow = CreateWindow(WINDOW_CLASS_NAME, _T(""), WS_OVERLAPPEDWINDOW, -1, -1, 0, 0, (HWND)NULL, (HMENU) NULL, NULL, (LPVOID)NULL); 
  333       if (hiddenWindow) 
  334       {
  335 # if defined(MUSCLE_64_BIT_PLATFORM)
  336          SetWindowLongPtr(hiddenWindow, GWLP_USERDATA, (LONG_PTR) this);
  337 # else
  338          SetWindowLongPtr(hiddenWindow, GWLP_USERDATA, (LONG) this);
  339 # endif
  340       }
  341       else LogTime(MUSCLE_LOG_ERROR, "DetectNetworkConfigChangesThread::InternalThreadEntry():  CreateWindow() failed! [%s]\n", B_ERRNO());
  342    
  343 # ifndef MUSCLE_AVOID_NETIOAPI
  344       HANDLE handle1 = MY_INVALID_HANDLE_VALUE; (void) NotifyUnicastIpAddressChange(AF_UNSPEC, &AddressCallback,   this, FALSE, &handle1);
  345       HANDLE handle2 = MY_INVALID_HANDLE_VALUE; (void) NotifyIpInterfaceChange(     AF_UNSPEC, &InterfaceCallback, this, FALSE, &handle2);
  346 #endif
  347 
  348       OVERLAPPED olap; memset(&olap, 0, sizeof(olap));
  349       olap.hEvent = CreateEvent(NULL, false, false, NULL);
  350       if (olap.hEvent != NULL)
  351       {
  352          while(_threadKeepGoing)
  353          {
  354             ::HANDLE junk;
  355             int nacRet = NotifyAddrChange(&junk, &olap); 
  356             if ((nacRet == NO_ERROR)||(WSAGetLastError() == WSA_IO_PENDING))
  357             {
  358                if (hiddenWindow)
  359                {
  360                   MSG message;
  361                   while(PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&message); 
  362                }
  363 
  364                ::HANDLE events[] = {olap.hEvent, _wakeupSignal};
  365                DWORD waitResult = hiddenWindow ? MsgWaitForMultipleObjects(ARRAYITEMS(events), events, false, INFINITE, QS_ALLINPUT) : WaitForMultipleObjects(ARRAYITEMS(events), events, false, INFINITE);
  366                if (waitResult == WAIT_OBJECT_0)
  367                {
  368                   // Serialized since the NotifyUnicast*Change() callbacks get called from a different thread
  369                   static Message _msg(DNCCS_MESSAGE_INTERFACES_CHANGED);
  370                   ThreadSafeSendMessageToSessions(MessageRef(&_msg, false));
  371                }          
  372                else if ((hiddenWindow)&&(waitResult == DWORD(WAIT_OBJECT_0+ARRAYITEMS(events))))
  373                {
  374                   // Message received from Window-message-handler; go around the loop to process it
  375                }
  376                else 
  377                {
  378                   // Anything else is an error and we should pack it in
  379                   (void) CancelIPChangeNotify(&olap);
  380                   _threadKeepGoing = false;
  381                }
  382             }
  383             else 
  384             {
  385                LogTime(MUSCLE_LOG_ERROR, "DetectNetworkConfigChangesThread:  NotifyAddrChange() failed, code %i (%i) [%s]\n", nacRet, WSAGetLastError(), B_ERRNO());
  386                break;
  387             }
  388          }
  389          CloseHandle(olap.hEvent);
  390       }
  391       else LogTime(MUSCLE_LOG_ERROR, "DetectNetworkConfigChangesThread:  CreateEvent() failed [%s]\n", B_ERRNO());
  392 
  393 # ifndef MUSCLE_AVOID_NETIOAPI
  394       if (handle2 != MY_INVALID_HANDLE_VALUE) CancelMibChangeNotify2(handle2);
  395       if (handle1 != MY_INVALID_HANDLE_VALUE) CancelMibChangeNotify2(handle1);
  396 # endif
  397 
  398       if (hiddenWindow) DestroyWindow(hiddenWindow);
  399       // Deliberately leaving the window_class registered here, since to do otherwise could be thread-unsafe
  400 # else
  401 #  error "DetectNetworkConfigChangesThread:  OS not supported!"
  402 # endif
  403    }
  404 
  405 private:
  406    virtual status_t StartInternalThread()
  407    {
  408       status_t ret;
  409       if (SetupSignalling().IsError(ret)) return ret;
  410 
  411       _threadKeepGoing = true;
  412       if (Thread::StartInternalThread().IsError(ret)) 
  413       {
  414          _threadKeepGoing = false;
  415          CleanupSignalling();
  416       }
  417       return ret;
  418    }
  419 
  420    virtual void ShutdownInternalThread(bool waitForThread = true)
  421    {
  422       _threadKeepGoing = false;
  423 # ifdef __APPLE__
  424       if (_threadRunLoop) CFRunLoopStop((CFRunLoopRef)_threadRunLoop);
  425 # elif WIN32
  426       SetEvent(_wakeupSignal);
  427 # endif
  428 
  429       Thread::ShutdownInternalThread(waitForThread);
  430       CleanupSignalling();
  431    }
  432 
  433    status_t SetupSignalling()
  434    {
  435 #ifdef WIN32
  436       _wakeupSignal = CreateEvent(0, false, false, 0);
  437       if (_wakeupSignal == MY_INVALID_HANDLE_VALUE) return B_ERROR("CreateEvent() failed");
  438 #endif
  439       return B_NO_ERROR;
  440    }
  441 
  442    void CleanupSignalling()
  443    {
  444 #ifdef WIN32
  445       if (_wakeupSignal != MY_INVALID_HANDLE_VALUE)
  446       {
  447          CloseHandle(_wakeupSignal);
  448          _wakeupSignal = MY_INVALID_HANDLE_VALUE;
  449       }
  450 #endif
  451    }
  452 
  453    Hashtable<DetectNetworkConfigChangesSession *, Void> _registeredSessions;
  454 
  455    volatile bool _threadKeepGoing;
  456    bool _isComputerSleeping;
  457 
  458 # ifdef __APPLE__
  459    void * _threadRunLoop; // actually of type CFRunLoopRef but I don't want to include CFRunLoop.h from this header file becaues doing so breaks things
  460    Hashtable<String, String> _scKeyToInterfaceName;
  461    io_connect_t * _rootPortPointer;
  462 # elif _WIN32
  463    ::HANDLE _wakeupSignal;
  464 # endif
  465 };
  466 
  467 # ifdef __APPLE__
  468 // MacOS/X Code taken from http://developer.apple.com/technotes/tn/tn1145.html
  469 static OSStatus MoreSCErrorBoolean(Boolean success)
  470 {
  471    OSStatus err = noErr;
  472    if (!success) 
  473    {
  474       int scErr = SCError();
  475       if (scErr == kSCStatusOK) scErr = kSCStatusFailed;
  476       err = scErr;
  477    }
  478    return err;
  479 }
  480 
  481 static OSStatus MoreSCError(const void *value) {return MoreSCErrorBoolean(value != NULL);}
  482 static OSStatus CFQError(CFTypeRef cf) {return (cf == NULL) ? -1 : noErr;}
  483 static void CFQRelease(CFTypeRef cf) {if (cf != NULL) CFRelease(cf);}
  484 
  485 static void StoreRecordFunc(const void * key, const void * value, void * context)
  486 {
  487    const CFStringRef     keyStr   = (const CFStringRef)     key;
  488    const CFDictionaryRef propList = (const CFDictionaryRef) value;
  489    if ((keyStr)&&(propList))
  490    {
  491       const String k(keyStr);
  492       if (k.StartsWith("State:/Network/Interface/")) 
  493       {
  494          const String interfaceName = k.Substring(25).Substring(0, "/");
  495          (void) ((Hashtable<String, String> *)(context))->Put(k, interfaceName);
  496       }
  497       else
  498       {
  499          const String interfaceName((const CFStringRef) CFDictionaryGetValue((CFDictionaryRef) propList, CFSTR("InterfaceName")));
  500          if (interfaceName.HasChars()) ((Hashtable<String, String> *)(context))->Put(k, interfaceName);
  501       }
  502    }
  503 }
  504 
  505 // Create a SCF dynamic store reference and a corresponding CFRunLoop source.  If you add the
  506 // run loop source to your run loop then the supplied callback function will be called when local IP
  507 // address list changes.
  508 static OSStatus CreateIPAddressListChangeCallbackSCF(SCDynamicStoreCallBack callback, void *contextPtr, SCDynamicStoreRef * storeRef, CFRunLoopSourceRef *sourceRef, Hashtable<String, String> & keyToInterfaceName)
  509 {
  510    OSStatus                err;
  511    SCDynamicStoreContext   context = {0, NULL, NULL, NULL, NULL};
  512    SCDynamicStoreRef       ref = NULL;
  513    CFStringRef             patterns[3] = {NULL, NULL, NULL};
  514    CFArrayRef              patternList = NULL;
  515    CFRunLoopSourceRef      rls = NULL;
  516 
  517    assert(callback   != NULL);
  518    assert( storeRef  != NULL);
  519    assert(*storeRef  == NULL);
  520    assert( sourceRef != NULL);
  521    assert(*sourceRef == NULL);
  522 
  523    // Create a connection to the dynamic store, then create
  524    // a search pattern that finds all entities.
  525    context.info = contextPtr;
  526    ref = SCDynamicStoreCreate(NULL, CFSTR("AddIPAddressListChangeCallbackSCF"), callback, &context);
  527    err = MoreSCError(ref);
  528    if (err == noErr) 
  529    {
  530       // This pattern is "State:/Network/Service/[^/]+/IPv4".
  531       patterns[0] = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);  // FogBugz #6075
  532       err = MoreSCError(patterns[0]);
  533       if (err == noErr)
  534       {
  535          // This pattern is "State:/Network/Service/[^/]+/IPv6".
  536          patterns[1] = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);  // FogBugz #6075
  537          err = MoreSCError(patterns[1]);
  538          if (err == noErr)
  539          {
  540             // This pattern is "State:/Network/Interface/[^/]+/Link"
  541             patterns[2] = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetLink);  // FogBugz #10048
  542             err = MoreSCError(patterns[2]);
  543          }
  544       }
  545    }
  546 
  547    // Tell SCF that we want to watch changes in keys that match that pattern list, then create our run loop source.
  548    if (err == noErr) 
  549    {
  550        patternList = CFArrayCreate(NULL, (const void **) patterns, 3, &kCFTypeArrayCallBacks);
  551        err = CFQError(patternList);
  552    }
  553 
  554    // Query the current values matching our patterns, so we know what interfaces are currently operative
  555    if (err == noErr)
  556    {
  557       CFDictionaryRef curVals = SCDynamicStoreCopyMultiple(ref, NULL, patternList);
  558       if (curVals)
  559       {
  560          CFDictionaryApplyFunction(curVals, StoreRecordFunc, &keyToInterfaceName);
  561          CFRelease(curVals);
  562       }
  563    }
  564 
  565    if (err == noErr) err = MoreSCErrorBoolean(SCDynamicStoreSetNotificationKeys(ref, NULL, patternList));
  566    if (err == noErr) 
  567    {
  568        rls = SCDynamicStoreCreateRunLoopSource(NULL, ref, 0);
  569        err = MoreSCError(rls);
  570    }
  571 
  572    // Clean up.
  573    CFQRelease(patterns[0]);
  574    CFQRelease(patterns[1]);
  575    CFQRelease(patterns[2]);
  576    CFQRelease(patternList);
  577    if (err != noErr) 
  578    {
  579       CFQRelease(ref);
  580       ref = NULL;
  581    }
  582    *storeRef = ref;
  583    *sourceRef = rls;
  584 
  585    assert( (err == noErr) == (*storeRef  != NULL) );
  586    assert( (err == noErr) == (*sourceRef != NULL) );
  587 
  588    return err;
  589 }
  590 
  591 static void SleepCallback(void * refCon, io_service_t /*service*/, natural_t messageType, void * messageArgument) {((DetectNetworkConfigChangesThread *)refCon)->SleepCallback(messageType, (long)messageArgument);}
  592 static void IPConfigChangedCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void * info) {((DetectNetworkConfigChangesThread *) info)->IPConfigChanged(store, changedKeys);}
  593 
  594 # endif  // __APPLE__
  595 
  596 # if defined(WIN32) && !defined(MUSCLE_AVOID_NETIOAPI)
  597 
  598 VOID __stdcall AddressCallback(IN PVOID context, IN PMIB_UNICASTIPADDRESS_ROW Address OPTIONAL, IN MIB_NOTIFICATION_TYPE /*NotificationType*/)
  599 {
  600    if (Address != NULL) ((DetectNetworkConfigChangesThread *)context)->SignalInterfacesChanged(Address->InterfaceIndex);
  601 }
  602 
  603 VOID __stdcall InterfaceCallback(IN PVOID context, IN PMIB_IPINTERFACE_ROW interfaceRow, IN MIB_NOTIFICATION_TYPE /*NotificationType*/)
  604 {
  605    if (interfaceRow != NULL) ((DetectNetworkConfigChangesThread *)context)->SignalInterfacesChanged(interfaceRow->InterfaceIndex);
  606 }
  607 
  608 static LRESULT CALLBACK dnccsWindowHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  609 {
  610    if (message == WM_POWERBROADCAST)
  611    {
  612       switch(wParam)
  613       {
  614          case PBT_APMRESUMEAUTOMATIC: case PBT_APMRESUMESUSPEND: case PBT_APMQUERYSUSPENDFAILED: case PBT_APMRESUMECRITICAL:
  615             ((DetectNetworkConfigChangesThread *)(GetWindowLongPtr(hWnd, GWLP_USERDATA)))->SleepCallback(false);
  616          break;
  617 
  618          case PBT_APMQUERYSUSPEND: case PBT_APMSUSPEND:
  619             ((DetectNetworkConfigChangesThread *)(GetWindowLongPtr(hWnd, GWLP_USERDATA)))->SleepCallback(true);
  620          break;
  621 
  622          default:
  623             // empty
  624          break;
  625       }
  626    }
  627    return DefWindowProc(hWnd, message, wParam, lParam);
  628 }
  629 # endif
  630 
  631 static status_t RegisterWithSingletonThread(DetectNetworkConfigChangesSession * s)
  632 {
  633    MutexGuard mg(_singletonThreadMutex);
  634 
  635    if (_singletonThread == NULL)
  636    {
  637       _singletonThread = newnothrow DetectNetworkConfigChangesThread;
  638       if (_singletonThread == NULL) RETURN_OUT_OF_MEMORY; 
  639    }
  640 
  641    status_t ret;
  642    if ((_singletonThread)&&(_singletonThread->RegisterSession(s).IsError(ret))&&(_singletonThread->HasRegisteredSessions() == false))
  643    {
  644       delete _singletonThread;
  645       _singletonThread = NULL;
  646       return ret;
  647    }
  648 
  649    return ret;
  650 }
  651 
  652 static status_t UnRegisterFromSingletonThread(DetectNetworkConfigChangesSession * s)
  653 {
  654    MutexGuard mg(_singletonThreadMutex);
  655    if (_singletonThread)
  656    {
  657       status_t ret;
  658       if ((_singletonThread)&&(_singletonThread->UnregisterSession(s).IsOK(ret))&&(_singletonThread->HasRegisteredSessions() == false))
  659       {
  660          delete _singletonThread;
  661          _singletonThread = NULL;
  662       }
  663       return ret;
  664    }
  665    else return B_DATA_NOT_FOUND;
  666 }
  667 
  668 #endif
  669 
  670 DetectNetworkConfigChangesSession :: DetectNetworkConfigChangesSession(bool notifyReflectServer)
  671    : _explicitDelayMicros(MUSCLE_TIME_NEVER),
  672      _callbackTime(MUSCLE_TIME_NEVER),
  673      _enabled(true),
  674      _changeAllPending(false),
  675      _notifyReflectServer(notifyReflectServer)
  676 {
  677    // empty
  678 }
  679 
  680 void DetectNetworkConfigChangesSession :: ScheduleSendReport()
  681 {
  682    // We won't actually send the report for a certain number of
  683    // seconds (OS-specific); that way any additional changes the OS
  684    // is making to the network config will have time to be reported
  685    // and we (hopefully) won't end up sending multiple reports in a row.
  686 #ifdef WIN32
  687    const int hysteresisDelaySeconds = 5;  // Windows needs 5, it is lame
  688 #else
  689    const int hysteresisDelaySeconds = 3;  // MacOS/X needs about 3 seconds
  690 #endif
  691    _callbackTime = GetRunTime64() + ((_explicitDelayMicros == MUSCLE_TIME_NEVER) ? SecondsToMicros(hysteresisDelaySeconds) : _explicitDelayMicros);
  692    InvalidatePulseTime();
  693 }
  694 
  695 void DetectNetworkConfigChangesSession :: CallNetworkInterfacesChangedOnAllTargets(const Hashtable<String, Void> & interfaceNames)
  696 {
  697    for (HashtableIterator<const String *, AbstractReflectSessionRef> iter(GetSessions()); iter.HasData(); iter++)
  698    {
  699       INetworkConfigChangesTarget * t = dynamic_cast<INetworkConfigChangesTarget *>(iter.GetValue()());
  700       if (t) t->NetworkInterfacesChanged(interfaceNames);
  701    }
  702 
  703    for (HashtableIterator<IPAddressAndPort, ReflectSessionFactoryRef> iter(GetFactories()); iter.HasData(); iter++)
  704    {
  705       INetworkConfigChangesTarget * t = dynamic_cast<INetworkConfigChangesTarget *>(iter.GetValue()());
  706       if (t) t->NetworkInterfacesChanged(interfaceNames);
  707    }
  708 }
  709 
  710 void DetectNetworkConfigChangesSession :: CallComputerIsAboutToSleepOnAllTargets()
  711 {
  712    if (_notifyReflectServer)
  713    {
  714       ReflectServer * rs = GetOwner();
  715       if (rs) rs->ComputerIsAboutToSleep();
  716    }
  717 
  718    for (HashtableIterator<IPAddressAndPort, ReflectSessionFactoryRef> iter(GetFactories()); iter.HasData(); iter++)
  719    {
  720       INetworkConfigChangesTarget * t = dynamic_cast<INetworkConfigChangesTarget *>(iter.GetValue()());
  721       if (t) t->ComputerIsAboutToSleep();
  722    }
  723 
  724    for (HashtableIterator<const String *, AbstractReflectSessionRef> iter(GetSessions()); iter.HasData(); iter++)
  725    {
  726       INetworkConfigChangesTarget * t = dynamic_cast<INetworkConfigChangesTarget *>(iter.GetValue()());
  727       if (t) t->ComputerIsAboutToSleep();
  728    }
  729 }
  730 
  731 void DetectNetworkConfigChangesSession :: CallComputerJustWokeUpOnAllTargets()
  732 {
  733    for (HashtableIterator<const String *, AbstractReflectSessionRef> iter(GetSessions()); iter.HasData(); iter++)
  734    {
  735       INetworkConfigChangesTarget * t = dynamic_cast<INetworkConfigChangesTarget *>(iter.GetValue()());
  736       if (t) t->ComputerJustWokeUp();
  737    }
  738 
  739    for (HashtableIterator<IPAddressAndPort, ReflectSessionFactoryRef> iter(GetFactories()); iter.HasData(); iter++)
  740    {
  741       INetworkConfigChangesTarget * t = dynamic_cast<INetworkConfigChangesTarget *>(iter.GetValue()());
  742       if (t) t->ComputerJustWokeUp();
  743    }
  744 
  745    if (_notifyReflectServer)
  746    {
  747       ReflectServer * rs = GetOwner();
  748       if (rs) rs->ComputerJustWokeUp();
  749    }
  750 }
  751 
  752 void DetectNetworkConfigChangesSession :: NetworkInterfacesChanged(const Hashtable<String, Void> &)
  753 {
  754    // default implementation is a no-op.
  755 }
  756 
  757 void DetectNetworkConfigChangesSession :: ComputerIsAboutToSleep()
  758 {
  759    // default implementation is a no-op.
  760 }
  761 
  762 void DetectNetworkConfigChangesSession :: ComputerJustWokeUp()
  763 {
  764    // default implementation is a no-op.
  765 }
  766 
  767 void DetectNetworkConfigChangesSession :: Pulse(const PulseArgs & pa)
  768 {
  769    if (pa.GetCallbackTime() >= _callbackTime)
  770    {
  771       _callbackTime = MUSCLE_TIME_NEVER;
  772       if (_enabled) CallNetworkInterfacesChangedOnAllTargets(_changeAllPending ? Hashtable<String, Void>() : _pendingChangedInterfaceNames);
  773       _pendingChangedInterfaceNames.Clear();
  774       _changeAllPending = false;
  775    }
  776    AbstractReflectSession::Pulse(pa);
  777 }
  778 
  779 ConstSocketRef DetectNetworkConfigChangesSession :: CreateDefaultSocket()
  780 {
  781 #ifdef __linux__
  782    struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa));
  783    sa.nl_family = AF_NETLINK;
  784    sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR;
  785 
  786    ConstSocketRef ret = GetConstSocketRefFromPool(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
  787    return ((ret())&&(bind(ret()->GetFileDescriptor(), (struct sockaddr*)&sa, sizeof(sa)) == 0)&&(SetSocketBlockingEnabled(ret, false) == B_NO_ERROR)) ? ret : ConstSocketRef();
  788 #else
  789    return CreateConnectedSocketPair(_notifySocket, _waitSocket).IsOK() ? _waitSocket : ConstSocketRef();
  790 #endif
  791 }
  792 
  793 int32 DetectNetworkConfigChangesSession :: DoInput(AbstractGatewayMessageReceiver & /*r*/, uint32 /*maxBytes*/)
  794 {
  795    const int fd = GetSessionReadSelectSocket().GetFileDescriptor();
  796    if (fd < 0) return -1;
  797 
  798 #ifdef __linux__
  799    bool sendReport = false;
  800    char buf[4096];
  801    struct iovec iov = {buf, sizeof(buf)};
  802    struct sockaddr_nl sa;
  803    struct msghdr msg = {(void *)&sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
  804    int msgLen = recvmsg(fd, &msg, 0);
  805    if (msgLen >= 0)  // FogBugz #9620
  806    {
  807       for (struct nlmsghdr *nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, (unsigned int)msgLen); nh=NLMSG_NEXT(nh, msgLen))
  808       {
  809          /* The end of multipart message. */
  810          if (nh->nlmsg_type == NLMSG_DONE) break;
  811          else
  812          {
  813             switch(nh->nlmsg_type)
  814             {
  815                case RTM_NEWLINK: case RTM_DELLINK:
  816                {
  817                   struct ifinfomsg * iface = (struct ifinfomsg *) NLMSG_DATA(nh);
  818                   int nextLen = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*iface));
  819                   for (struct rtattr * a = IFLA_RTA(iface); RTA_OK(a, nextLen); a = RTA_NEXT(a, nextLen))
  820                      if (a->rta_type == IFLA_IFNAME)
  821                        (void) _pendingChangedInterfaceNames.PutWithDefault((const char *) RTA_DATA(a));
  822                   sendReport = true;
  823                }
  824                break; 
  825 
  826                case RTM_NEWADDR: case RTM_DELADDR:
  827                {
  828                   struct ifaddrmsg * ifa = (struct ifaddrmsg *) NLMSG_DATA(nh);
  829                   struct rtattr *rth = IFA_RTA(ifa);
  830                   int rtl = IFA_PAYLOAD(nh);
  831                   while(rtl && RTA_OK(rth, rtl))
  832                   {
  833                      if (rth->rta_type == IFA_LOCAL)
  834                      {
  835                         char ifName[IFNAMSIZ];
  836                         (void) if_indextoname(ifa->ifa_index, ifName);
  837                         (void) _pendingChangedInterfaceNames.PutWithDefault(ifName);
  838                         sendReport = true;  // FogBugz #17683:  only notify if IFA_LOCAL was specified, to avoid getting spammed about IFA_CACHEINFO
  839                      }
  840                      rth = RTA_NEXT(rth, rtl);
  841                   }
  842                }
  843                break; 
  844 
  845                default:
  846                   // do nothing
  847                break;
  848             }
  849          }
  850       }
  851    }
  852    if (sendReport) ScheduleSendReport();
  853    return msgLen;
  854 #elif defined(USE_SINGLETON_THREAD)
  855    char buf[128]; 
  856    const int32 ret = ReceiveData(_waitSocket, buf, sizeof(buf), false);  // clear any received signalling bytes
  857    Queue<MessageRef> incomingMessages;
  858    {
  859       MutexGuard mg(_messagesFromSingletonThreadMutex);
  860       incomingMessages.SwapContents(_messagesFromSingletonThread);
  861    }
  862 
  863    bool sendReport = false;
  864    MessageRef msg;
  865    while(incomingMessages.RemoveHead(msg).IsOK())
  866    {
  867       switch(msg()->what)
  868       {
  869          case DNCCS_MESSAGE_INTERFACES_CHANGED:
  870             sendReport = true;  // we only need to send one report, even for multiple Messages
  871             if ((msg())&&(_changeAllPending == false))
  872             {
  873                if (msg()->HasName("if", B_STRING_TYPE))
  874                {
  875                   const String * ifName;
  876                   for (int32 i=0; msg()->FindString("if", i, &ifName) == B_NO_ERROR; i++) _pendingChangedInterfaceNames.PutWithDefault(*ifName);
  877                }
  878                else _changeAllPending = true;  // no interfaces specified means "it could be anything"
  879             }
  880          break;
  881 
  882          case DNCCS_MESSAGE_ABOUT_TO_SLEEP:
  883             if (_enabled) CallComputerIsAboutToSleepOnAllTargets();
  884          break;
  885 
  886          case DNCCS_MESSAGE_JUST_WOKE_UP:
  887             if (_enabled) CallComputerJustWokeUpOnAllTargets();
  888          break;
  889       }
  890    }
  891    if (sendReport) ScheduleSendReport();
  892    return ret;
  893 #else
  894    return -1;
  895 #endif
  896 }
  897 
  898 status_t DetectNetworkConfigChangesSession :: AttachedToServer()
  899 {
  900    status_t ret;
  901    if (AbstractReflectSession::AttachedToServer().IsOK(ret))
  902    {
  903 #if defined(USE_SINGLETON_THREAD)
  904       return RegisterWithSingletonThread(this);
  905 #endif
  906    }
  907    return ret;
  908 }
  909 
  910 void DetectNetworkConfigChangesSession :: EndSession()
  911 {
  912 #if defined(USE_SINGLETON_THREAD)
  913    UnRegisterFromSingletonThread(this);  // do this ASAP, otherwise we get the occasional crash on shutdown :(
  914 #endif
  915    AbstractReflectSession::EndSession();
  916 }
  917 
  918 void DetectNetworkConfigChangesSession :: AboutToDetachFromServer()
  919 {
  920 #if defined(USE_SINGLETON_THREAD)
  921    UnRegisterFromSingletonThread(this);
  922 #endif
  923    AbstractReflectSession::AboutToDetachFromServer();
  924 }
  925 
  926 }  // end namespace muscle