+
+#pragma mark -
+#pragma mark Interface monitoring (e.g. watch for "detach")
+
+
+typedef struct WatchedInfo *WatchedInfoRef;
+
+typedef void (*InterfaceUpdateCallBack) (
+ CFDataRef watched,
+ natural_t messageType,
+ void *messageArgument
+);
+
+typedef struct {
+ SCNetworkInterfaceRef interface;
+ io_service_t interface_node;
+ io_object_t notification;
+ InterfaceUpdateCallBack callback;
+} WatchedInfo;
+
+static void
+watcherRelease(CFDataRef watched);
+
+static void
+updateWatchedInterface(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
+{
+#pragma unused(service)
+#pragma unused(messageArgument)
+ switch (messageType) {
+ case kIOMessageServiceIsTerminated : { // if [locked] interface yanked
+ CFDataRef watched = (CFDataRef)refCon;
+ WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
+
+ CFRetain(watched);
+ watchedInfo->callback(watched, messageType, messageArgument);
+ watcherRelease(watched);
+ CFRelease(watched);
+ break;
+ }
+
+ default :
+ return;
+ }
+
+ return;
+}
+
+static CFDataRef
+watcherCreate(SCNetworkInterfaceRef interface, InterfaceUpdateCallBack callback)
+{
+ uint64_t entryID;
+ io_service_t interface_node;
+ kern_return_t kr;
+ CFDictionaryRef matching;
+ CFMutableDataRef watched;
+ WatchedInfo *watchedInfo;
+
+ // get the IORegistry node
+ entryID = _SCNetworkInterfaceGetIORegistryEntryID(interface);
+ matching = IORegistryEntryIDMatching(entryID);
+ interface_node = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
+ if (interface_node == MACH_PORT_NULL) {
+ // interface no longer present
+ return NULL;
+ }
+
+ // create [locked] interface watcher
+ watched = CFDataCreateMutable(NULL, sizeof(WatchedInfo));
+ CFDataSetLength(watched, sizeof(WatchedInfo));
+ watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
+ bzero(watchedInfo, sizeof(*watchedInfo));
+
+ // retain interface
+ watchedInfo->interface = CFRetain(interface);
+
+ // ... and the interface node
+ watchedInfo->interface_node = interface_node;
+
+ // ... and set the callback
+ watchedInfo->callback = callback;
+
+ kr = IOServiceAddInterestNotification(S_notify, // IONotificationPortRef
+ watchedInfo->interface_node, // io_service_t
+ kIOGeneralInterest, // interestType
+ updateWatchedInterface, // IOServiceInterestCallback
+ (void *)watched, // refCon
+ &watchedInfo->notification); // notification
+ if (kr != KERN_SUCCESS) {
+ SC_log(LOG_ERR,
+ "IOServiceAddInterestNotification() failed, kr = 0x%x",
+ kr);
+ watcherRelease(watched);
+ CFRelease(watched);
+ return NULL;
+ }
+
+ return watched;
+}
+
+static void
+watcherRelease(CFDataRef watched)
+{
+ WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
+
+ // release watcher
+ if (watchedInfo->notification != IO_OBJECT_NULL) {
+ IOObjectRelease(watchedInfo->notification);
+ watchedInfo->notification = IO_OBJECT_NULL;
+ }
+
+ // release interface node
+ if (watchedInfo->interface_node != IO_OBJECT_NULL) {
+ IOObjectRelease(watchedInfo->interface_node);
+ watchedInfo->interface_node = IO_OBJECT_NULL;
+ }
+
+ // release interface
+ if (watchedInfo->interface != NULL) {
+ CFRelease(watchedInfo->interface);
+ watchedInfo->interface = NULL;
+ }
+
+ return;
+}
+
+
+#pragma mark -
+#pragma mark Locked device support [macOS]
+
+