From ef202ce890623c73f7a65873480866cec436d3ca Mon Sep 17 00:00:00 2001 From: Simul Piscator Date: Tue, 14 Nov 2023 00:25:11 +0100 Subject: [PATCH] Implement network hotplug notifier for macOS --- CMakeLists.txt | 5 +- zeroconf/networkhotplugnotifier-macos.mm | 98 ++++++++++++++++++++++++ zeroconf/networkhotplugnotifier.cpp | 8 -- zeroconf/networkhotplugnotifier.h | 1 + 4 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 zeroconf/networkhotplugnotifier-macos.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index 0099b34..5aff6c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,15 +46,19 @@ if(APPLE) endif() set(ZEROCONF_FILES zeroconf/mdnspublisher-dnssd.mm + zeroconf/networkhotplugnotifier-macos.mm ) set(ZEROCONF_LIBS "-lobjc" + "-framework Foundation" "-framework IOKit" "-framework CFNetwork" + "-framework CoreFoundation" ) else() set(ZEROCONF_FILES zeroconf/mdnspublisher-avahi.cpp + zeroconf/networkhotplugnotifier.cpp ) set(ZEROCONF_LIBS avahi-client @@ -85,7 +89,6 @@ add_executable(${PROJECT_NAME} imageformats/pngencoder.cpp zeroconf/mdnspublisher.cpp zeroconf/hotplugnotifier.cpp - zeroconf/networkhotplugnotifier.cpp ${ZEROCONF_FILES} ) if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) diff --git a/zeroconf/networkhotplugnotifier-macos.mm b/zeroconf/networkhotplugnotifier-macos.mm new file mode 100644 index 0000000..352d368 --- /dev/null +++ b/zeroconf/networkhotplugnotifier-macos.mm @@ -0,0 +1,98 @@ +/* +AirSane Imaging Daemon +Copyright (C) 2018-2023 Simul Piscator + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "networkhotplugnotifier.h" + +#import +#import + +#include +#include +#include +#include + +#include + +struct NetworkHotplugNotifier::Private +{ + std::thread mThread; + NetworkHotplugNotifier* mpNotifier; + std::atomic mRunLoop{NULL}; + std::atomic mTerminate{false}; + + Private(NetworkHotplugNotifier* pNotifier) + : mpNotifier(pNotifier) + { + mTerminate = false; + mThread = std::thread([this]() { hotplugThread(); }); + } + + ~Private() + { + CFRunLoopRef runLoop = NULL; + while (!runLoop) + runLoop = mRunLoop; + mTerminate = true; + ::CFRunLoopStop(runLoop); + mThread.join(); + } + + void hotplugThread() + { + mRunLoop = ::CFRunLoopGetCurrent(); + + NSArray *SCMonitoringInterfacePatterns = @[@"State:/Network/Global/IPv[46]"]; + @autoreleasepool { + SCDynamicStoreContext ctx = {0}; + ctx.info = this; + ::SCDynamicStoreRef dsr = ::SCDynamicStoreCreate(NULL, CFSTR("network_interface_detector"), ¬ificationCallback, &ctx); + ::SCDynamicStoreSetNotificationKeys(dsr, NULL, (CFArrayRef)::CFBridgingRetain(SCMonitoringInterfacePatterns)); + ::CFRunLoopAddSource(::CFRunLoopGetCurrent(), ::SCDynamicStoreCreateRunLoopSource(NULL, dsr, 0), kCFRunLoopDefaultMode); + while (!mTerminate && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + } + } + + static void notificationCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void* info) + { +#if 0 + @autoreleasepool { + CFIndex count = CFArrayGetCount(changedKeys); + for (CFIndex i = 0; i < count; ++i) { + CFStringRef key = (CFStringRef)::CFArrayGetValueAtIndex(changedKeys, i); + NSLog(@"Key \"%@\" was changed", key); + CFPropertyListRef ref = ::SCDynamicStoreCopyValue(store, key); + NSDictionary* dict = (NSDictionary*)::CFBridgingRelease(ref); + NSLog(@"Value %@", dict); + } + } +#endif + + auto p = static_cast(info); + p->mpNotifier->onHotplugEvent(addressChange); + } +}; + +NetworkHotplugNotifier::NetworkHotplugNotifier() + : p(new Private(this)) +{} + +NetworkHotplugNotifier::~NetworkHotplugNotifier() +{ + delete p; +} diff --git a/zeroconf/networkhotplugnotifier.cpp b/zeroconf/networkhotplugnotifier.cpp index 4857ec7..caa0d10 100644 --- a/zeroconf/networkhotplugnotifier.cpp +++ b/zeroconf/networkhotplugnotifier.cpp @@ -22,7 +22,6 @@ along with this program. If not, see . #include #include -#if !__APPLE__ #include #include #include @@ -34,7 +33,6 @@ along with this program. If not, see . #include #endif #include -#endif // !__APPLE__ #include @@ -47,7 +45,6 @@ struct NetworkHotplugNotifier::Private Private(NetworkHotplugNotifier* pNotifier) : mpNotifier(pNotifier), mPipeWriteFd(-1), mPipeReadFd(-1) { -#if !__APPLE__ int fds[2]; if (::pipe(fds) < 0) { std::cerr << "Could not create socket pair " @@ -57,23 +54,19 @@ struct NetworkHotplugNotifier::Private mPipeReadFd = fds[0]; mPipeWriteFd = fds[1]; mThread = std::thread([this]() { hotplugThread(); }); -#endif // !__APPLE__ } ~Private() { -#if !__APPLE__ char c = '0'; ::write(mPipeWriteFd, &c, 1); mThread.join(); ::close(mPipeWriteFd); ::close(mPipeReadFd); -#endif // !__APPLE__ } void hotplugThread() { -#if !__APPLE__ int sock = ::socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { std::cerr << "Could not create netlink socket: " @@ -119,7 +112,6 @@ struct NetworkHotplugNotifier::Private } } } -#endif // !__APPLE__ } }; diff --git a/zeroconf/networkhotplugnotifier.h b/zeroconf/networkhotplugnotifier.h index 19a7f67..2d9d0dd 100644 --- a/zeroconf/networkhotplugnotifier.h +++ b/zeroconf/networkhotplugnotifier.h @@ -34,6 +34,7 @@ class NetworkHotplugNotifier other, addressArrived, addressLeft, + addressChange, }; virtual void onHotplugEvent(Event) {}