source: trunk/PrefsPane/proxyApp/NetworkConfigurationListener.m @ 1046

Revision 1017, 12.4 KB checked in by speck, 10 months ago (diff)

fix indents.

Line 
1/* Copyright (C) 2009 Peter Speck
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <stdio.h>
18#include <sys/types.h>
19#include <sys/sysctl.h>
20#include <time.h>
21#import <SystemConfiguration/SCPreferences.h>
22#import <SystemConfiguration/SCNetworkConfiguration.h>
23
24static CFStringRef bundleIdentifier = CFSTR("org.glimmerblocker.proxy.networkConfigurationListener");
25
26/*
27static void FailedAssert(const char *msg)
28{
29    printf("Failed Assert: %s\n", msg);
30    exit(1);
31}
32#undef NSAssert
33#define NSAssert(x, msg) if (!(x)) FailedAssert(msg)
34*/
35
36static NSMutableString* mstr;
37static CFMutableStringRef cfmstr;
38static int needComma = 0;
39static int indent = 0;
40
41static void printIndent()
42{
43    for (int i = 0; i < indent; i++)
44        CFStringAppend(cfmstr, CFSTR("   "));
45}
46
47static void enterDict()
48{
49    CFStringAppend(cfmstr, CFSTR("{"));
50    needComma <<= 1;
51    indent++;
52}
53
54static void printItemIndent()
55{
56    if (needComma & 1)
57        CFStringAppend(cfmstr, CFSTR(",\n"));
58    else
59        CFStringAppend(cfmstr, CFSTR("\n")); // after '{'
60    needComma |= 1;
61    printIndent();
62}
63
64static void leaveDict()
65{
66    int hasItems = needComma & 1;
67    if (hasItems)
68        CFStringAppend(cfmstr, CFSTR("\n"));
69    indent--;
70    needComma >>= 1;
71    if (hasItems)
72        printIndent();
73    CFStringAppend(cfmstr, CFSTR("}"));
74}
75
76static void appendValue(const CFTypeRef value)
77{
78    if (!value) {
79        CFStringAppend(cfmstr, CFSTR("null"));
80        return;
81    }
82    CFTypeID type = CFGetTypeID(value);
83    if (type == CFStringGetTypeID()) {
84        CFStringAppend(cfmstr, CFSTR("'"));
85        CFStringRef s = (CFStringRef)value;
86        CFIndex len = CFStringGetLength(s);
87        for (CFIndex i = 0; i < len; i++) {
88            UniChar ch = CFStringGetCharacterAtIndex(s, i);
89            if (ch == '\'') {
90                [mstr appendString:@"\\'"];
91                continue;
92            }
93            if (ch == '\\') {
94                [mstr appendString:@"\\\\"];
95                continue;
96            }
97            if (ch < 32) {
98                [mstr appendFormat:@"\\x%02x", (int)ch];
99                continue;
100            }
101            CFStringAppendCharacters(cfmstr, &ch, 1);
102        }
103        CFStringAppend(cfmstr, CFSTR("'"));
104        return;
105    }
106    if (type == CFArrayGetTypeID()) {
107        CFStringAppend(cfmstr, CFSTR("["));
108        CFArrayRef a = (CFArrayRef)value;
109        CFIndex num = CFArrayGetCount(a);
110        for (CFIndex i = 0; i < num; i++) {
111            if (i > 0)
112                CFStringAppend(cfmstr, CFSTR(", "));
113            appendValue(CFArrayGetValueAtIndex(a, i));
114        }
115        CFStringAppend(cfmstr, CFSTR("]"));
116        return;
117    }
118    if (type == CFNumberGetTypeID()) {
119        [mstr appendString:[(NSNumber*)value stringValue]];
120        return;
121    }
122    if (type == CFBooleanGetTypeID()) {
123        NSNumber* n = (NSNumber*)value;
124        NSString* s = [n intValue] ? @"true" : @"false";
125        [mstr appendString:s];
126        return;
127    }
128    if (type == CFDataGetTypeID()) {
129        //CFStringAppend(cfmstr, CFSTR("/* CFData */"));
130        CFStringAppend(cfmstr, CFSTR("'0x"));
131        CFDataRef a = (CFDataRef)value;
132        CFIndex len = CFDataGetLength(a);
133        for (CFIndex i = 0; i < len; i++)
134            [mstr appendFormat:@"%02x", 255 & *(CFDataGetBytePtr(a) + i)];
135        CFStringAppend(cfmstr, CFSTR("'"));
136        return;
137    }
138    CFStringAppend(cfmstr, CFSTR("null"));
139    CFStringRef s = CFCopyTypeIDDescription(type);
140    printf("MSG: unhandled value type = %ld = %s\n", type, [(NSString*)s UTF8String]);
141    CFRelease(s);
142}
143
144static void printLocationName()
145{
146    CFStringRef appName = CFSTR("GlimmerBlocker");
147    SCPreferencesRef prefsR = SCPreferencesCreate(NULL, appName, NULL);
148    if (!prefsR) {
149        printf("MSG: SCPreferencesCreate returned NULL.");
150        return;
151    }
152    SCNetworkSetRef networkSetR = SCNetworkSetCopyCurrent(prefsR);
153    if (!networkSetR) {
154        printf("MSG: SCNetworkSetCopyCurrent returned NULL.");
155    } else {
156        CFStringRef name = SCNetworkSetGetName(networkSetR);
157        appendValue(name);
158        CFRelease(networkSetR);
159    }
160    CFRelease(prefsR);
161}
162
163
164static void appendInterfaceData(SCDynamicStoreRef store, CFStringRef interfaceName, CFStringRef format,
165                                CFStringRef dictName, ...)
166{
167    CFStringRef path = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, format, interfaceName);
168    CFPropertyListRef propList = SCDynamicStoreCopyValue(store, path);
169    CFRelease(path);
170    path = NULL;
171    if (!propList) {
172        //printf("MSG: SCDynamicStoreCopyValue(%s) returned NULL.\n", [(NSString*)path UTF8String]);
173        return;
174    }
175    printItemIndent();
176    CFStringAppend(cfmstr, dictName);
177    CFStringAppend(cfmstr, CFSTR(": "));
178    enterDict();
179    va_list arglist;
180    va_start(arglist, dictName);
181    CFStringRef attrName;
182    while ((attrName = va_arg(arglist, CFStringRef)) != NULL) {
183        CFStringRef jsName = va_arg(arglist, CFStringRef);
184        const CFTypeRef value = CFDictionaryGetValue(propList, attrName);
185        if (!value)
186            continue;
187        printItemIndent();
188        CFStringAppend(cfmstr, jsName);
189        CFStringAppend(cfmstr, CFSTR(": "));
190        appendValue(value);
191    }
192    leaveDict();
193    CFRelease(propList);
194    propList = NULL;
195    va_end(arglist);
196}
197
198static void appendInterfaceLinkStaus(SCDynamicStoreRef store, CFStringRef interfaceName)
199{
200    CFStringRef path = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("State:/Network/Interface/%@/Link"), interfaceName);
201    CFPropertyListRef propList = SCDynamicStoreCopyValue(store, path);
202    CFRelease(path);
203    path = NULL;
204    if (!propList)
205        return;
206    CFTypeRef value = CFDictionaryGetValue(propList, CFSTR("Active"));
207    if (value) {
208        CFTypeID type = CFGetTypeID(value);
209        if (type == CFBooleanGetTypeID()) {
210            NSNumber* n = (NSNumber*)value;
211            if (![n intValue])
212                value = NULL; // don't print false, omit it to make json more terse
213        }
214    }
215    if (value) {
216        printItemIndent();
217        CFStringAppend(cfmstr, CFSTR("link: "));
218        appendValue(value);
219    }
220    CFRelease(propList);
221    propList = NULL;
222}
223
224static void printOneInterface(SCDynamicStoreRef store, CFStringRef interfaceName, int pass)
225{
226    BOOL isLocalhost = CFStringGetLength(interfaceName) >= 3
227        && CFStringGetCharacterAtIndex(interfaceName, 0) == 'l'
228        && CFStringGetCharacterAtIndex(interfaceName, 1) == 'o'
229        && CFStringGetCharacterAtIndex(interfaceName, 2) <= '9';
230    if (isLocalhost != (pass == 2)) {
231        // print localhost interfaces at the bottom, as they're just too boring to be at the top.
232        return;
233    }
234    printItemIndent();
235    UniChar ch = CFStringGetCharacterAtIndex(interfaceName, 0);
236    if (ch >= 'a' && ch <= 'z')
237        CFStringAppend(cfmstr, interfaceName);
238    else
239        appendValue(interfaceName);
240    CFStringAppend(cfmstr, CFSTR(": "));
241    enterDict();
242    appendInterfaceLinkStaus(store, interfaceName);
243    appendInterfaceData(store, interfaceName, CFSTR("State:/Network/Interface/%@/AirPort"),
244                        CFSTR("AirPort"),
245                        CFSTR("Power Status"), CFSTR("power"),
246                        CFSTR("SSID_STR"), CFSTR("ssid"),
247                        CFSTR("SSID"), CFSTR("ssid_hex"),
248                        NULL);
249    appendInterfaceData(store, interfaceName, CFSTR("State:/Network/Interface/%@/IPv4"),
250                        CFSTR("IPv4"),
251                        CFSTR("Addresses"), CFSTR("addr"),
252                        CFSTR("SubnetMasks"), CFSTR("mask"),
253                        CFSTR("BroadcastAddresses"), CFSTR("broadcast"),
254                        NULL);
255    appendInterfaceData(store, interfaceName, CFSTR("State:/Network/Interface/%@/IPv6"),
256                        CFSTR("IPv6"),
257                        CFSTR("Addresses"), CFSTR("addr"),
258                        CFSTR("Flags"), CFSTR("flags"),
259                        CFSTR("PrefixLength"), CFSTR("prefixlen"),
260                        NULL);
261    //  en1: { ssid:'Funny', ipv4: [ "127.0.0.1" ], ipv4mask: [ "255.255.255.0" ], ipv6: ["fe80::1"] }
262    leaveDict();
263}
264
265static void printInterfaces()
266{
267    // http://developer.apple.com/documentation/Networking/Conceptual/SystemConfigFrameworks/SC_UnderstandSchema/SC_UnderstandSchema.html#//apple_ref/doc/uid/TP40001065-CH203-CHDFGAEF
268    SCDynamicStoreContext context = { 0, NULL, NULL, NULL, NULL };
269    CFPropertyListRef propList = NULL;
270    SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, bundleIdentifier, nil, &context);
271    if (!store) {
272        printf("MSG: SCDynamicStoreCreate returned NULL.");
273        goto release;
274    }
275    propList = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Interface"));
276    if (!propList) {
277        printf("MSG: SCDynamicStoreCopyValue(State:/Network/Interface) returned NULL.");
278        goto release;
279    }
280    CFArrayRef interfaceList = CFDictionaryGetValue(propList, CFSTR("Interfaces"));
281    if (!interfaceList) {
282        printf("MSG: CFDictionaryGetValue(State:/Network/Interface@Interfaces) returned NULL.");
283        goto release;
284    }
285    CFIndex numInterfaces = CFArrayGetCount(interfaceList);
286    //printf("DEBUG: got %d interfaces\n", numInterfaces);
287    enterDict();
288    for (int pass = 1; pass <= 2; pass++) {
289        for (CFIndex interfaceIdx = 0; interfaceIdx < numInterfaces; interfaceIdx++) {
290            CFStringRef interfaceName = CFArrayGetValueAtIndex(interfaceList, interfaceIdx);
291            if (!interfaceName) {
292                printf("DEBUG: interface[%ld] has no name?\n", interfaceIdx);
293                continue;
294            }
295            //printf("DEBUG: interface = %s\n", [(NSString*)interfaceName UTF8String]);
296            printOneInterface(store, interfaceName, pass);
297        }
298    }
299    leaveDict();
300release:
301    if (store)
302        CFRelease(store);
303    if (propList)
304        CFRelease(propList);
305}
306
307static void appendSoftNewline()
308{
309    if (![mstr hasSuffix:@"\n"])
310        [mstr appendString:@"\n"];
311}
312
313static void printState(BOOL forDisplay)
314{
315    // reset globals:
316    needComma = 0;
317    indent = 0;
318    if (!mstr) {
319        mstr = [[NSMutableString  stringWithCapacity:200] retain];
320        cfmstr = (CFMutableStringRef)mstr;
321    }
322    [mstr deleteCharactersInRange:NSMakeRange(0, [mstr length])];
323    //
324    if (forDisplay) {
325        [mstr appendString:@"gb.network = "];
326        enterDict();
327    }
328    printItemIndent();
329    [mstr appendString:@"location: "];
330    printLocationName();
331    //
332    printItemIndent();
333    [mstr appendString:@"interfaces: "];
334    printInterfaces();
335    //
336    if (forDisplay)
337        leaveDict();
338    //
339    appendSoftNewline();
340    if (!forDisplay) {
341        time_t t = time(NULL);
342        [mstr appendFormat:@"DEBUG: %s", ctime(&t)];
343    }
344    //
345    fputs([mstr UTF8String], stdout);
346    fflush(stdout);
347}
348
349static void scCallback(SCDynamicStoreRef dynStore, CFArrayRef changedKeys, void *info)
350{
351    CFIndex num = CFArrayGetCount(changedKeys);
352    for (CFIndex i = 0; i < num; i++)
353        printf("DEBUG: State changed[%ld]: %s\n", i, [(NSString*)CFArrayGetValueAtIndex(changedKeys, i) UTF8String]);
354    // sometimes the callback is a wee bit faster than AirPort goes offline when selecting
355    // "Turn AirPort Off" from the menubar,
356    // so waiting a bit is sometimes required.
357    sleep(1);
358    printState(NO);
359}
360
361static void sourceCallBack(void *info)
362{
363    // dummy for CFRunLoop so it doesn't exit.  Never called.
364}
365
366static void addListener()
367{
368    SCDynamicStoreContext context = {
369        .version            = 0,
370        .info               = NULL,
371        .retain             = NULL,
372        .release            = NULL,
373        .copyDescription    = NULL
374    };
375    SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, bundleIdentifier, scCallback, &context);
376    CFRunLoopSourceRef runLoopSrc = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
377    CFRunLoopAddSource([[NSRunLoop currentRunLoop] getCFRunLoop], runLoopSrc, kCFRunLoopCommonModes);
378    CFRelease(runLoopSrc);
379    //
380    CFMutableArrayRef fixedKeys = CFArrayCreateMutable(NULL, 0, NULL);
381    //
382    CFStringRef locationKey = SCDynamicStoreKeyCreateLocation(NULL);
383    //NSLog(@"SCDynamicStoreKeyCreateLocation: %@", key);
384    CFArrayAppendValue(fixedKeys, locationKey);
385    CFRelease(locationKey);
386    CFArrayAppendValue(fixedKeys, @"State:/Network/Interface");
387    //
388    CFMutableArrayRef regexpKeys = CFArrayCreateMutable(NULL, 0, NULL);
389    CFArrayAppendValue(regexpKeys, @"State:/Network/Interface/.*");
390    //
391    SCDynamicStoreSetNotificationKeys(store, fixedKeys, regexpKeys);
392    CFRelease(fixedKeys);
393    CFRelease(regexpKeys);
394}
395
396int main (int argc, const char * argv[])
397{
398    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
399    if (argc < 2 || strcmp(argv[1], "-continous")) {
400        printState(YES);
401        exit(0);
402    }
403    // Create a a sourceContext to be used by our source that makes
404    // sure the CFRunLoop doesn't exit right away
405    CFRunLoopSourceContext sourceContext;
406    sourceContext.version = 0;
407    sourceContext.info = NULL;
408    sourceContext.retain = NULL;
409    sourceContext.release = NULL;
410    sourceContext.copyDescription = NULL;
411    sourceContext.equal = NULL;
412    sourceContext.hash = NULL;
413    sourceContext.schedule = NULL;
414    sourceContext.cancel = NULL;
415    sourceContext.perform = &sourceCallBack;
416    //
417    CFRunLoopSourceRef sourceRef = CFRunLoopSourceCreate(NULL, 0, &sourceContext);
418    CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
419    addListener();
420    CFRelease(sourceRef);
421    printState(NO);
422    CFRunLoopRun();
423    printf("main() done.\n");
424    [pool release];
425    return 0;
426}
427
Note: See TracBrowser for help on using the repository browser.