Skip to content

Commit

Permalink
Implement network hotplug notifier for macOS
Browse files Browse the repository at this point in the history
  • Loading branch information
Simul Piscator authored and Simul Piscator committed Nov 13, 2023
1 parent 6f6e91c commit ef202ce
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 9 deletions.
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
98 changes: 98 additions & 0 deletions zeroconf/networkhotplugnotifier-macos.mm
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#include "networkhotplugnotifier.h"

#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>

#include <iostream>
#include <vector>
#include <thread>
#include <atomic>

#include <string.h>

struct NetworkHotplugNotifier::Private
{
std::thread mThread;
NetworkHotplugNotifier* mpNotifier;
std::atomic<CFRunLoopRef> mRunLoop{NULL};
std::atomic<bool> 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"), &notificationCallback, &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<Private*>(info);
p->mpNotifier->onHotplugEvent(addressChange);
}
};

NetworkHotplugNotifier::NetworkHotplugNotifier()
: p(new Private(this))
{}

NetworkHotplugNotifier::~NetworkHotplugNotifier()
{
delete p;
}
8 changes: 0 additions & 8 deletions zeroconf/networkhotplugnotifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <vector>
#include <thread>

#if !__APPLE__
#include <poll.h>
#include <unistd.h>
#include <netinet/in.h>
Expand All @@ -34,7 +33,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <linux/rtnetlink.h>
#endif
#include <net/if.h>
#endif // !__APPLE__

#include <string.h>

Expand All @@ -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 "
Expand All @@ -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: "
Expand Down Expand Up @@ -119,7 +112,6 @@ struct NetworkHotplugNotifier::Private
}
}
}
#endif // !__APPLE__
}
};

Expand Down
1 change: 1 addition & 0 deletions zeroconf/networkhotplugnotifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class NetworkHotplugNotifier
other,
addressArrived,
addressLeft,
addressChange,
};
virtual void onHotplugEvent(Event) {}

Expand Down

0 comments on commit ef202ce

Please sign in to comment.