]> git.saurik.com Git - apple/configd.git/blob - SCMonitor/monitor.c
6ff25d623aaa8db9a1f17b7f4eee3ad7e4823887
[apple/configd.git] / SCMonitor / monitor.c
1 /*
2 * Copyright (c) 2007 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * October 24, 2007 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <CoreFoundation/CoreFoundation.h>
35 #include <SystemConfiguration/SystemConfiguration.h>
36 #include <SystemConfiguration/SCPrivate.h>
37 #include <ApplicationServices/ApplicationServices.h>
38 #include "UserEventAgentInterface.h"
39
40 #define MY_BUNDLE_ID CFSTR("com.apple.SystemConfiguration.SCMonitor")
41 #define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns"
42
43 #define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane"
44 #define NETWORK_PREF_CMD "New Interface"
45
46 typedef struct {
47 UserEventAgentInterfaceStruct *_UserEventAgentInterface;
48 CFUUIDRef _factoryID;
49 UInt32 _refCount;
50
51 CFRunLoopSourceRef monitorRls;
52
53 CFMutableSetRef knownInterfaces;
54
55 CFMutableArrayRef userInterfaces;
56 CFUserNotificationRef userNotification;
57 CFRunLoopSourceRef userRls;
58 } MyType;
59
60 static CFMutableDictionaryRef notify_to_instance = NULL;
61
62
63 #pragma mark -
64 #pragma mark Watch for new [network] interfaces
65
66
67 static void
68 open_NetworkPrefPane(void)
69 {
70 AEDesc aeDesc = { typeNull, NULL };
71 CFArrayRef prefArray;
72 CFURLRef prefURL;
73 LSLaunchURLSpec prefSpec;
74 OSStatus status;
75
76 prefURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
77 CFSTR(NETWORK_PREF_APP),
78 kCFURLPOSIXPathStyle,
79 FALSE);
80 prefArray = CFArrayCreate(NULL, (const void **)&prefURL, 1, &kCFTypeArrayCallBacks);
81 CFRelease(prefURL);
82
83 status = AECreateDesc('ptru',
84 (const void *)NETWORK_PREF_CMD,
85 strlen(NETWORK_PREF_CMD),
86 &aeDesc);
87 if (status != noErr) {
88 SCLog(TRUE, LOG_ERR, CFSTR("SCMonitor: AECreateDesc() failed: %d"), status);
89 }
90
91 prefSpec.appURL = NULL;
92 prefSpec.itemURLs = prefArray;
93 prefSpec.passThruParams = &aeDesc;
94 prefSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents;
95 prefSpec.asyncRefCon = NULL;
96
97 status = LSOpenFromURLSpec(&prefSpec, NULL);
98 if (status != noErr) {
99 SCLog(TRUE, LOG_ERR, CFSTR("SCMonitor: LSOpenFromURLSpec() failed: %d"), status);
100 }
101
102 CFRelease(prefArray);
103 if (aeDesc.descriptorType != typeNull) AEDisposeDesc(&aeDesc);
104 return;
105 }
106
107
108 static void
109 notify_remove(MyType *myInstance, Boolean cancel)
110 {
111 if (myInstance->userInterfaces != NULL) {
112 CFRelease(myInstance->userInterfaces);
113 myInstance->userInterfaces = NULL;
114 }
115
116 if (myInstance->userRls != NULL) {
117 CFRunLoopSourceInvalidate(myInstance->userRls);
118 CFRelease(myInstance->userRls);
119 myInstance->userRls = NULL;
120 }
121
122 if (myInstance->userNotification != NULL) {
123 if (cancel) {
124 SInt32 status;
125
126 status = CFUserNotificationCancel(myInstance->userNotification);
127 if (status != 0) {
128 SCLog(TRUE, LOG_ERR,
129 CFSTR("SCMonitor: CFUserNotificationCancel() failed, status=%d"),
130 status);
131 }
132 }
133 CFRelease(myInstance->userNotification);
134 myInstance->userNotification = NULL;
135 }
136
137 return;
138 }
139
140
141 static void
142 notify_reply(CFUserNotificationRef userNotification, CFOptionFlags response_flags)
143 {
144 MyType *myInstance = NULL;
145
146 // get instance for notification
147 if (notify_to_instance != NULL) {
148 myInstance = (MyType *)CFDictionaryGetValue(notify_to_instance, userNotification);
149 if (myInstance != NULL) {
150 CFDictionaryRemoveValue(notify_to_instance, userNotification);
151 if (CFDictionaryGetCount(notify_to_instance) == 0) {
152 CFRelease(notify_to_instance);
153 notify_to_instance = NULL;
154 }
155 }
156 }
157 if (myInstance == NULL) {
158 SCLog(TRUE, LOG_ERR, CFSTR("SCMonitor: can't find user notification"));
159 return;
160 }
161
162 // process response
163 switch (response_flags & 0x3) {
164 case kCFUserNotificationDefaultResponse:
165 // user asked to configure interface
166 open_NetworkPrefPane();
167 break;
168 default:
169 // user cancelled
170 break;
171 }
172
173 notify_remove(myInstance, FALSE);
174 return;
175 }
176
177 static void
178 notify_add(MyType *myInstance)
179 {
180 CFBundleRef bundle;
181 CFMutableDictionaryRef dict = NULL;
182 SInt32 error = 0;
183 CFIndex i;
184 CFMutableArrayRef message;
185 CFIndex n = CFArrayGetCount(myInstance->userInterfaces);
186 CFURLRef url = NULL;
187
188 if (myInstance->userNotification != NULL) {
189 CFMutableArrayRef save = NULL;
190
191 if (n > 0) {
192 CFRetain(myInstance->userInterfaces);
193 save = myInstance->userInterfaces;
194 }
195 notify_remove(myInstance, TRUE);
196 myInstance->userInterfaces = save;
197 if (n == 0) {
198 return;
199 }
200 }
201
202 dict = CFDictionaryCreateMutable(NULL,
203 0,
204 &kCFTypeDictionaryKeyCallBacks,
205 &kCFTypeDictionaryValueCallBacks);
206
207 // set localization URL
208 bundle = CFBundleGetBundleWithIdentifier(MY_BUNDLE_ID);
209 if (bundle != NULL) {
210 url = CFBundleCopyBundleURL(bundle);
211 }
212 if (url != NULL) {
213 // set URL
214 CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, url);
215 CFRelease(url);
216 } else {
217 SCLog(TRUE, LOG_NOTICE, CFSTR("SCMonitor: can't find bundle"));
218 goto done;
219 }
220
221 // set icon URL
222 url = CFURLCreateFromFileSystemRepresentation(NULL,
223 (const UInt8 *)MY_ICON_PATH,
224 strlen(MY_ICON_PATH),
225 FALSE);
226 if (url != NULL) {
227 CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, url);
228 CFRelease(url);
229 }
230
231 // header
232 CFDictionarySetValue(dict,
233 kCFUserNotificationAlertHeaderKey,
234 (n == 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N"));
235
236 // message
237 message = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
238 CFArrayAppendValue(message,
239 (n == 1) ? CFSTR("MESSAGE_S1") : CFSTR("MESSAGE_SN"));
240 for (i = 0; i < n; i++) {
241 SCNetworkInterfaceRef interface;
242 CFStringRef name;
243
244 interface = CFArrayGetValueAtIndex(myInstance->userInterfaces, i);
245 name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
246 if (n == 1) {
247 CFArrayAppendValue(message, name);
248 } else {
249 CFStringRef str;
250
251 str = CFStringCreateWithFormat(NULL, NULL, CFSTR("\r\t%@"), name);
252 CFArrayAppendValue(message, str);
253 CFRelease(str);
254 }
255 }
256 CFArrayAppendValue(message,
257 (n == 1) ? CFSTR("MESSAGE_E1") : CFSTR("MESSAGE_EN"));
258 CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message);
259 CFRelease(message);
260
261 // button titles
262 CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFSTR("OPEN_NP"));
263 CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFSTR("CANCEL"));
264
265 // create and post notification
266 myInstance->userNotification = CFUserNotificationCreate(NULL,
267 0,
268 kCFUserNotificationNoteAlertLevel,
269 &error,
270 dict);
271 if (myInstance->userNotification == NULL) {
272 SCLog(TRUE, LOG_ERR, CFSTR("SCMonitor: CFUserNotificationCreate() failed, %d"), error);
273 goto done;
274 }
275
276 // establish callback
277 myInstance->userRls = CFUserNotificationCreateRunLoopSource(NULL,
278 myInstance->userNotification,
279 notify_reply,
280 0);
281 if (myInstance->userRls == NULL) {
282 SCLog(TRUE, LOG_ERR, CFSTR("SCMonitor: CFUserNotificationCreateRunLoopSource() failed"));
283 CFRelease(myInstance->userNotification);
284 myInstance->userNotification = NULL;
285 goto done;
286 }
287 CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance->userRls, kCFRunLoopDefaultMode);
288
289 // add instance for notification
290 if (notify_to_instance == NULL) {
291 notify_to_instance = CFDictionaryCreateMutable(NULL,
292 0,
293 &kCFTypeDictionaryKeyCallBacks,
294 NULL); // no retain/release/... for values
295 }
296 CFDictionarySetValue(notify_to_instance, myInstance->userNotification, myInstance);
297
298 done :
299
300 if (dict != NULL) CFRelease(dict);
301 return;
302 }
303
304
305 static void
306 updateInterfaceList(SCDynamicStoreRef store, CFArrayRef changes, void * arg)
307 {
308 CFIndex i;
309 CFArrayRef interfaces;
310 MyType *myInstance = (MyType *)arg;
311 CFIndex n;
312 SCPreferencesRef prefs;
313 CFMutableSetRef previouslyKnown = NULL;
314 SCNetworkSetRef set = NULL;
315
316 prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL);
317 if (prefs == NULL) {
318 return;
319 }
320
321 set = SCNetworkSetCopyCurrent(prefs);
322 if (set == NULL) {
323 set = SCNetworkSetCreate(prefs);
324 if (set == NULL) {
325 goto done;
326 }
327 }
328
329 previouslyKnown = CFSetCreateMutableCopy(NULL, 0, myInstance->knownInterfaces);
330
331 interfaces = SCNetworkInterfaceCopyAll();
332 if (interfaces != NULL) {
333
334 n = CFArrayGetCount(interfaces);
335 for (i = 0; i < n; i++) {
336 CFStringRef bsdName;
337 SCNetworkInterfaceRef interface;
338 Boolean ok;
339
340 interface = CFArrayGetValueAtIndex(interfaces, i);
341 bsdName = SCNetworkInterfaceGetBSDName(interface);
342 if (bsdName == NULL) {
343 // if no BSD name
344 continue;
345 }
346
347 CFSetRemoveValue(previouslyKnown, bsdName);
348
349 if (CFSetContainsValue(myInstance->knownInterfaces, bsdName)) {
350 // if known interface
351 continue;
352 }
353
354 CFSetAddValue(myInstance->knownInterfaces, bsdName);
355
356 ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface);
357 if (ok) {
358 // this is a *new* interface
359 if (myInstance->userInterfaces == NULL) {
360 myInstance->userInterfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
361 }
362 CFArrayAppendValue(myInstance->userInterfaces, interface);
363 }
364 }
365
366 CFRelease(interfaces);
367 }
368
369 n = CFSetGetCount(previouslyKnown);
370 if (n > 0) {
371 const void * names_q[32];
372 const void ** names = names_q;
373
374 if (n > (CFIndex)(sizeof(names_q) / sizeof(CFTypeRef)))
375 names = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
376 CFSetGetValues(previouslyKnown, names);
377 for (i = 0; i < n; i++) {
378 if (myInstance->userInterfaces != NULL) {
379 CFIndex j;
380
381 j = CFArrayGetCount(myInstance->userInterfaces);
382 while (--j >= 0) {
383 CFStringRef bsdName;
384 SCNetworkInterfaceRef interface;
385
386 interface = CFArrayGetValueAtIndex(myInstance->userInterfaces, j);
387 bsdName = SCNetworkInterfaceGetBSDName(interface);
388 if (CFEqual(bsdName, names[i])) {
389 // if we have previously posted a notification
390 // for this no-longer-present interface
391 CFArrayRemoveValueAtIndex(myInstance->userInterfaces, j);
392 }
393 }
394 }
395
396 CFSetRemoveValue(myInstance->knownInterfaces, names[i]);
397 }
398 if (names != names_q) CFAllocatorDeallocate(NULL, names);
399 }
400
401 done :
402
403 // post notification
404 if (myInstance->userInterfaces != NULL) {
405 notify_add(myInstance);
406 }
407
408 if (set != NULL) CFRelease(set);
409 CFRelease(prefs);
410 return;
411 }
412
413
414 static void
415 watcher_remove(MyType *myInstance)
416 {
417 if (myInstance->monitorRls != NULL) {
418 CFRunLoopSourceInvalidate(myInstance->monitorRls);
419 CFRelease(myInstance->monitorRls);
420 myInstance->monitorRls = NULL;
421 }
422
423 if (myInstance->knownInterfaces != NULL) {
424 CFRelease(myInstance->knownInterfaces);
425 myInstance->knownInterfaces = NULL;
426 }
427
428 return;
429 }
430
431
432 static void
433 watcher_add(MyType *myInstance)
434 {
435 SCDynamicStoreContext context = { 0, (void *)myInstance, NULL, NULL, NULL };
436 CFDictionaryRef dict;
437 CFStringRef key;
438 CFArrayRef keys;
439 SCDynamicStoreRef store;
440
441 store = SCDynamicStoreCreate(NULL, CFSTR("SCMonitor"), updateInterfaceList, &context);
442 if (store == NULL) {
443 SCLog(TRUE, LOG_ERR,
444 CFSTR("SCMonitor: SCDynamicStoreCreate() failed: %s"),
445 SCErrorString(SCError()));
446 return;
447 }
448
449 key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
450
451 // watch for changes to the list of network interfaces
452 keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
453 SCDynamicStoreSetNotificationKeys(store, NULL, keys);
454 CFRelease(keys);
455 myInstance->monitorRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
456 CFRunLoopAddSource(CFRunLoopGetCurrent(),
457 myInstance->monitorRls,
458 kCFRunLoopDefaultMode);
459
460 // initialize the list of known interfaces
461 myInstance->knownInterfaces = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
462 dict = SCDynamicStoreCopyValue(store, key);
463 if (dict != NULL) {
464 if (isA_CFDictionary(dict)) {
465 CFIndex i;
466 CFArrayRef interfaces;
467 CFIndex n;
468
469 interfaces = CFDictionaryGetValue(dict, kSCPropNetInterfaces);
470 n = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0;
471 for (i = 0; i < n; i++) {
472 CFStringRef bsdName;
473
474 bsdName = CFArrayGetValueAtIndex(interfaces, i);
475 if (isA_CFString(bsdName)) {
476 CFSetAddValue(myInstance->knownInterfaces, bsdName);
477 }
478 }
479 }
480
481 CFRelease(dict);
482 }
483
484 CFRelease(key);
485 CFRelease(store);
486 return;
487 }
488
489
490 #pragma mark -
491 #pragma mark UserEventAgent stubs
492
493
494 static HRESULT
495 myQueryInterface(void *myInstance, REFIID iid, LPVOID *ppv)
496 {
497 CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid);
498
499 // Test the requested ID against the valid interfaces.
500 if (CFEqual(interfaceID, kUserEventAgentInterfaceID)) {
501 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
502 *ppv = myInstance;
503 CFRelease(interfaceID);
504 return S_OK;
505 }
506
507 if (CFEqual(interfaceID, IUnknownUUID)) {
508 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
509 *ppv = myInstance;
510 CFRelease(interfaceID);
511 return S_OK;
512 }
513
514 // Requested interface unknown, bail with error.
515 *ppv = NULL;
516 CFRelease(interfaceID);
517 return E_NOINTERFACE;
518 }
519
520
521 static ULONG
522 myAddRef(void *myInstance)
523 {
524 ((MyType *) myInstance)->_refCount++;
525 return ((MyType *) myInstance)->_refCount;
526 }
527
528
529 static ULONG
530 myRelease(void *myInstance)
531 {
532 ((MyType *) myInstance)->_refCount--;
533 if (((MyType *) myInstance)->_refCount == 0) {
534 CFUUIDRef factoryID = ((MyType *) myInstance)->_factoryID;
535
536 if (factoryID != NULL) {
537 CFPlugInRemoveInstanceForFactory(factoryID);
538 CFRelease(factoryID);
539
540 watcher_remove((MyType *)myInstance);
541 notify_remove((MyType *)myInstance, TRUE);
542 }
543 free(myInstance);
544 return 0;
545 }
546
547 return ((MyType *) myInstance)->_refCount;
548 }
549
550
551 static void
552 myInstall(void *myInstance)
553 {
554 watcher_add((MyType *)myInstance);
555 return;
556 }
557
558
559 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = {
560 NULL, // Required padding for COM
561 myQueryInterface, // These three are the required COM functions
562 myAddRef,
563 myRelease,
564 myInstall // Interface implementation
565 };
566
567
568 void *
569 UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
570 {
571 MyType *newOne = NULL;
572
573 if (CFEqual(typeID, kUserEventAgentTypeID)) {
574 newOne = (MyType *)malloc(sizeof(MyType));
575 bzero(newOne, sizeof(*newOne));
576 newOne->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl;
577 newOne->_factoryID = (CFUUIDRef)CFRetain(kUserEventAgentFactoryID);
578 CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID);
579 newOne->_refCount = 1;
580 }
581
582 return newOne;
583 }
584
585
586 #ifdef MAIN
587 int
588 main(int argc, char **argv)
589 {
590 MyType *newOne = (MyType *)malloc(sizeof(MyType));
591
592 _sc_log = FALSE;
593 _sc_verbose = (argc > 1) ? TRUE : FALSE;
594
595 bzero(newOne, sizeof(*newOne));
596 myInstall(newOne);
597 CFRunLoopRun();
598 exit(0);
599 return (0);
600 }
601 #endif // MAIN