-
Notifications
You must be signed in to change notification settings - Fork 1
/
ipwatcher.c
138 lines (125 loc) · 5.24 KB
/
ipwatcher.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# include <SystemConfiguration/SystemConfiguration.h>
#include <AvailabilityInternal.h>
// MacOS/X Code taken from http://developer.apple.com/technotes/tn/tn1145.html
static OSStatus MoreSCErrorBoolean(Boolean success)
{
OSStatus err = noErr;
if (!success)
{
int scErr = SCError();
if (scErr == kSCStatusOK) scErr = kSCStatusFailed;
err = scErr;
}
return err;
}
static OSStatus MoreSCError(const void *value) {return MoreSCErrorBoolean(value != NULL);}
static OSStatus CFQError(CFTypeRef cf) {return (cf == NULL) ? -1 : noErr;}
static void CFQRelease(CFTypeRef cf) {if (cf != NULL) CFRelease(cf);}
/**
* Create a SCF dynamic store reference and a corresponding CFRunLoop source. If you add the
* run loop source to your run loop then the supplied callback function will be called when local IP
* address list changes.
* @param callback
* @param contextPtr
* @param storeRef
* @param sourceRef
* @return OSStatus
*/
API_AVAILABLE(macos(10.10))
//__attribute__ ((availability))
static OSStatus CreateIPAddressListChangeCallbackSCF(SCDynamicStoreCallBack callback,
void *contextPtr,
SCDynamicStoreRef *storeRef,
CFRunLoopSourceRef *sourceRef)
{
OSStatus err;
SCDynamicStoreContext context = {0, NULL, NULL, NULL, NULL};
SCDynamicStoreRef ref = NULL;
CFStringRef patterns[2] = {NULL, NULL};
CFArrayRef patternList = NULL;
CFRunLoopSourceRef rls = NULL;
assert( callback != NULL);
assert( storeRef != NULL);
assert(*storeRef == NULL);
assert( sourceRef != NULL);
assert(*sourceRef == NULL);
// Create a connection to the dynamic store, then create
// a search pattern that finds all entities.
context.info = contextPtr;
ref = SCDynamicStoreCreate(NULL, CFSTR("AddIPAddressListChangeCallbackSCF"), callback, &context);
err = MoreSCError(ref);
if (err == noErr)
{
// This pattern is "State:/Network/Service/[^/]+/IPv4".
patterns[0] = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetIPSec);
// patterns[0] = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPSec); // kSCEntNetIPv4);
err = MoreSCError(patterns[0]);
if (err == noErr)
{
// This pattern is "State:/Network/Service/[^/]+/IPv6".
patterns[1] = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetIPv4);
// kSCEntNetIPv6);
err = MoreSCError(patterns[1]);
}
}
// Create a pattern list containing just one pattern,
// then tell SCF that we want to watch changes in keys
// that match that pattern list, then create our run loop
// source.
if (err == noErr)
{
patternList = CFArrayCreate(NULL, (const void **) patterns, 2, &kCFTypeArrayCallBacks);
err = CFQError(patternList);
}
if (err == noErr)
{
err = MoreSCErrorBoolean(SCDynamicStoreSetNotificationKeys(ref, NULL, patternList));
}
if (err == noErr)
{
rls = SCDynamicStoreCreateRunLoopSource(NULL, ref, 0);
err = MoreSCError(rls);
}
// Clean up.
CFQRelease(patterns[0]);
CFQRelease(patterns[1]);
CFQRelease(patternList);
if (err != noErr)
{
CFQRelease(ref);
ref = NULL;
}
*storeRef = ref;
*sourceRef = rls;
assert( (err == noErr) == (*storeRef != NULL) );
assert( (err == noErr) == (*sourceRef != NULL) );
return err;
}
static void IPConfigChangedCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void * info)
{
printf("IP Configuration changed, do something!\n");
}
// in real life another thread might tell us to quit by setting this false and then calling CFRunLoopStop() on our CFRunLoop()
volatile bool _threadKeepGoing = true;
int main(int argc, char ** argv)
{
printf("Listening for IP configuration changes...\n");
// only NULL for this trivial example; in real life you could point this to some data you want the IPConfigChangedCallback() to have access to
void * contextPtr = NULL;
SCDynamicStoreRef storeRef = NULL;
CFRunLoopSourceRef sourceRef = NULL;
if (CreateIPAddressListChangeCallbackSCF(IPConfigChangedCallback, contextPtr, &storeRef, &sourceRef) == noErr)
{
CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopDefaultMode);
while(_threadKeepGoing) CFRunLoopRun();
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopDefaultMode);
CFRelease(storeRef);
CFRelease(sourceRef);
}
}