]> git.saurik.com Git - apple/configd.git/blob - SCMonitor/monitor.c
ca340286e9556bb48d89906566fdcc15de42ed67
[apple/configd.git] / SCMonitor / monitor.c
1 /*
2 * Copyright (c) 2007-2012 Apple 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 <asl.h>
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <SystemConfiguration/SystemConfiguration.h>
37 #include <SystemConfiguration/SCPrivate.h>
38 #include <IOKit/IOKitLib.h>
39 #include <IOKit/IOMessage.h>
40 #include <ApplicationServices/ApplicationServices.h>
41 #include "UserEventAgentInterface.h"
42
43 #define MY_BUNDLE_ID "com.apple.SystemConfiguration.SCMonitor"
44 #define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns"
45
46 #define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane"
47 #define NETWORK_PREF_CMD "New Interface"
48
49 /*
50 * The following keys/values control the actions taken when a new interface
51 * has been detected and whether we interact with the user.
52 *
53 * The keys/values can be provided globally (in SCMonitor.plugin's Info.plist
54 * file) or per-inteface in the IORegistry.
55 *
56 * For the "New Interface Detected Action" key we define the following [CFString]
57 * values :
58 *
59 * "None" No action, ignore this interface.
60 * "Prompt" Post a "new interface detected" notification to the user.
61 * "Configure" Automatically configure this interface without any
62 * intervention.
63 *
64 * Note: automatic configuration may not be possible if the logged in user
65 * is NOT "root" (eUID==0) or if the authorization right that governs
66 * SCHelper write operations (kSCPreferencesWriteAuthorizationRight)
67 * is not currently available.
68 *
69 * An [older] "User Intervention" key is also supported. That CFBoolean
70 * key, if present and TRUE, implies "Configure" configuration of the
71 * interface without intervention.
72 */
73
74 typedef struct {
75 UserEventAgentInterfaceStruct *_UserEventAgentInterface;
76 CFUUIDRef _factoryID;
77 UInt32 _refCount;
78
79 Boolean debug;
80
81 aslmsg log_msg;
82
83 CFStringRef configuration_action;
84
85 CFRunLoopSourceRef monitorRls;
86
87 IONotificationPortRef notifyPort;
88 io_iterator_t notifyIterator;
89 CFMutableArrayRef notifyNodes;
90
91 // interfaces that we already know about
92 CFMutableSetRef interfaces_known;
93
94 // interfaces that should be auto-configured (no user notification)
95 CFMutableArrayRef interfaces_configure;
96
97 // interfaces that require user notification
98 CFMutableArrayRef interfaces_prompt;
99
100 CFUserNotificationRef userNotification;
101 CFRunLoopSourceRef userRls;
102
103 AuthorizationRef authorization;
104 } MyType;
105
106 static CFMutableDictionaryRef notify_to_instance = NULL;
107
108
109 #pragma mark -
110 #pragma mark Authorization
111
112
113 static AuthorizationRef
114 getAuthorization(MyType *myInstance)
115 {
116 if (myInstance->authorization == NULL) {
117 AuthorizationFlags flags = kAuthorizationFlagDefaults;
118 OSStatus status;
119
120 status = AuthorizationCreate(NULL,
121 kAuthorizationEmptyEnvironment,
122 flags,
123 &myInstance->authorization);
124 if (status != errAuthorizationSuccess) {
125 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
126 CFSTR("AuthorizationCreate() failed: status = %d"),
127 status);
128 }
129 }
130
131 return myInstance->authorization;
132 }
133
134
135 static Boolean
136 hasAuthorization(MyType *myInstance)
137 {
138 AuthorizationRef authorization;
139 Boolean isAdmin = FALSE;
140
141 authorization = getAuthorization(myInstance);
142 if (authorization != NULL) {
143 AuthorizationFlags flags = kAuthorizationFlagDefaults;
144 AuthorizationItem items[1];
145 AuthorizationRights rights;
146 OSStatus status;
147
148 items[0].name = kSCPreferencesWriteAuthorizationRight;
149 items[0].value = NULL;
150 items[0].valueLength = 0;
151 items[0].flags = 0;
152
153 rights.count = sizeof(items) / sizeof(items[0]);
154 rights.items = items;
155
156 status = AuthorizationCopyRights(authorization,
157 &rights,
158 kAuthorizationEmptyEnvironment,
159 flags,
160 NULL);
161 isAdmin = (status == errAuthorizationSuccess);
162 }
163
164 return isAdmin;
165 }
166
167
168 static void
169 freeAuthorization(MyType *myInstance)
170 {
171 if (myInstance->authorization != NULL) {
172 AuthorizationFree(myInstance->authorization, kAuthorizationFlagDefaults);
173 // AuthorizationFree(myInstance->authorization, kAuthorizationFlagDestroyRights);
174 myInstance->authorization = NULL;
175 }
176
177 return;
178 }
179
180
181 #pragma mark -
182 #pragma mark New interface notification / configuration
183
184
185 static void
186 open_NetworkPrefPane(MyType *myInstance)
187 {
188 AEDesc aeDesc = { typeNull, NULL };
189 CFArrayRef prefArray;
190 CFURLRef prefURL;
191 LSLaunchURLSpec prefSpec;
192 OSStatus status;
193
194 prefURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
195 CFSTR(NETWORK_PREF_APP),
196 kCFURLPOSIXPathStyle,
197 FALSE);
198 prefArray = CFArrayCreate(NULL, (const void **)&prefURL, 1, &kCFTypeArrayCallBacks);
199 CFRelease(prefURL);
200
201 status = AECreateDesc('ptru',
202 (const void *)NETWORK_PREF_CMD,
203 strlen(NETWORK_PREF_CMD),
204 &aeDesc);
205 if (status != noErr) {
206 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: AECreateDesc() failed: %d"), status);
207 }
208
209 prefSpec.appURL = NULL;
210 prefSpec.itemURLs = prefArray;
211 prefSpec.passThruParams = &aeDesc;
212 prefSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents;
213 prefSpec.asyncRefCon = NULL;
214
215 status = LSOpenFromURLSpec(&prefSpec, NULL);
216 if (status != noErr) {
217 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: LSOpenFromURLSpec() failed: %d"), status);
218 }
219
220 CFRelease(prefArray);
221 if (aeDesc.descriptorType != typeNull) AEDisposeDesc(&aeDesc);
222 return;
223 }
224
225
226 static void
227 notify_remove(MyType *myInstance, Boolean cancel)
228 {
229 if (myInstance->interfaces_configure != NULL) {
230 CFRelease(myInstance->interfaces_configure);
231 myInstance->interfaces_configure = NULL;
232 }
233
234 if (myInstance->interfaces_prompt != NULL) {
235 CFRelease(myInstance->interfaces_prompt);
236 myInstance->interfaces_prompt = NULL;
237 }
238
239 if (myInstance->userRls != NULL) {
240 CFRunLoopSourceInvalidate(myInstance->userRls);
241 CFRelease(myInstance->userRls);
242 myInstance->userRls = NULL;
243 }
244
245 if (myInstance->userNotification != NULL) {
246 if (cancel) {
247 SInt32 status;
248
249 status = CFUserNotificationCancel(myInstance->userNotification);
250 if (status != 0) {
251 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
252 CFSTR("SCMonitor: CFUserNotificationCancel() failed, status=%d"),
253 status);
254 }
255 }
256 CFRelease(myInstance->userNotification);
257 myInstance->userNotification = NULL;
258 }
259
260 return;
261 }
262
263
264 static void
265 notify_reply(CFUserNotificationRef userNotification, CFOptionFlags response_flags)
266 {
267 MyType *myInstance = NULL;
268
269 // get instance for notification
270 if (notify_to_instance != NULL) {
271 myInstance = (MyType *)CFDictionaryGetValue(notify_to_instance, userNotification);
272 if (myInstance != NULL) {
273 CFDictionaryRemoveValue(notify_to_instance, userNotification);
274 if (CFDictionaryGetCount(notify_to_instance) == 0) {
275 CFRelease(notify_to_instance);
276 notify_to_instance = NULL;
277 }
278 }
279 }
280 if (myInstance == NULL) {
281 SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("SCMonitor: can't find user notification"));
282 return;
283 }
284
285 // process response
286 switch (response_flags & 0x3) {
287 case kCFUserNotificationDefaultResponse:
288 // user asked to configure interface
289 open_NetworkPrefPane(myInstance);
290 break;
291 default:
292 // user cancelled
293 break;
294 }
295
296 notify_remove(myInstance, FALSE);
297 return;
298 }
299
300
301 static void
302 notify_add(MyType *myInstance)
303 {
304 CFBundleRef bundle;
305 CFMutableDictionaryRef dict = NULL;
306 SInt32 error = 0;
307 CFIndex i;
308 CFIndex n = CFArrayGetCount(myInstance->interfaces_prompt);
309 CFURLRef url = NULL;
310
311 if (myInstance->userNotification != NULL) {
312 CFMutableArrayRef save = NULL;
313
314 if (n > 0) {
315 CFRetain(myInstance->interfaces_prompt);
316 save = myInstance->interfaces_prompt;
317 }
318 notify_remove(myInstance, TRUE);
319 myInstance->interfaces_prompt = save;
320 if (n == 0) {
321 return;
322 }
323 }
324
325 dict = CFDictionaryCreateMutable(NULL,
326 0,
327 &kCFTypeDictionaryKeyCallBacks,
328 &kCFTypeDictionaryValueCallBacks);
329
330 // set localization URL
331 bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID));
332 if (bundle != NULL) {
333 url = CFBundleCopyBundleURL(bundle);
334 }
335 #ifdef MAIN
336 if (url == NULL) {
337 url = CFURLCreateFromFileSystemRepresentation(NULL,
338 (const UInt8 *)"/System/Library/UserEventPlugins/SCMonitor.plugin",
339 strlen("/System/Library/UserEventPlugins/SCMonitor.plugin"),
340 FALSE);
341 if (bundle == NULL) {
342 bundle = CFBundleCreate(NULL, url);
343 }
344 }
345 #endif // MAIN
346 if (url != NULL) {
347 // set URL
348 CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, url);
349 CFRelease(url);
350 } else {
351 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: can't find bundle"));
352 goto done;
353 }
354
355 // set icon URL
356 url = CFURLCreateFromFileSystemRepresentation(NULL,
357 (const UInt8 *)MY_ICON_PATH,
358 strlen(MY_ICON_PATH),
359 FALSE);
360 if (url != NULL) {
361 CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, url);
362 CFRelease(url);
363 }
364
365 // header
366 CFDictionarySetValue(dict,
367 kCFUserNotificationAlertHeaderKey,
368 (n == 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N"));
369
370 // message
371 if (n == 1) {
372 CFStringRef format;
373 SCNetworkInterfaceRef interface;
374 CFStringRef message;
375 CFStringRef name;
376
377 #define MESSAGE_1 "The \"%@\" network interface has not been set up. To set up this interface, use Network Preferences."
378
379 format = CFBundleCopyLocalizedString(bundle,
380 CFSTR("MESSAGE_1"),
381 CFSTR(MESSAGE_1),
382 NULL);
383 interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, 0);
384 name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
385 message = CFStringCreateWithFormat(NULL, NULL, format, name);
386 CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message);
387 CFRelease(message);
388 CFRelease(format);
389 } else {
390 CFMutableArrayRef message;
391
392 message = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
393 CFArrayAppendValue(message, CFSTR("MESSAGE_SN"));
394 for (i = 0; i < n; i++) {
395 SCNetworkInterfaceRef interface;
396 CFStringRef name;
397 CFStringRef str;
398
399 interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, i);
400 name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
401 str = CFStringCreateWithFormat(NULL, NULL, CFSTR("\r\t%@"), name);
402 CFArrayAppendValue(message, str);
403 CFRelease(str);
404 }
405 CFArrayAppendValue(message, CFSTR("MESSAGE_EN"));
406 CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message);
407 CFRelease(message);
408 }
409
410 // button titles
411 CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFSTR("OPEN_NP"));
412 CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFSTR("CANCEL"));
413
414 // create and post notification
415 myInstance->userNotification = CFUserNotificationCreate(NULL,
416 0,
417 kCFUserNotificationNoteAlertLevel,
418 &error,
419 dict);
420 if (myInstance->userNotification == NULL) {
421 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: CFUserNotificationCreate() failed, %d"), error);
422 goto done;
423 }
424
425 // establish callback
426 myInstance->userRls = CFUserNotificationCreateRunLoopSource(NULL,
427 myInstance->userNotification,
428 notify_reply,
429 0);
430 if (myInstance->userRls == NULL) {
431 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("SCMonitor: CFUserNotificationCreateRunLoopSource() failed"));
432 CFRelease(myInstance->userNotification);
433 myInstance->userNotification = NULL;
434 goto done;
435 }
436 CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance->userRls, kCFRunLoopDefaultMode);
437
438 // add instance for notification
439 if (notify_to_instance == NULL) {
440 notify_to_instance = CFDictionaryCreateMutable(NULL,
441 0,
442 &kCFTypeDictionaryKeyCallBacks,
443 NULL); // no retain/release/... for values
444 }
445 CFDictionarySetValue(notify_to_instance, myInstance->userNotification, myInstance);
446
447 done :
448
449 if (dict != NULL) CFRelease(dict);
450 return;
451 }
452
453
454 static void
455 notify_configure(MyType *myInstance)
456 {
457 CFIndex i;
458 CFIndex n = CFArrayGetCount(myInstance->interfaces_configure);
459 Boolean ok;
460 SCPreferencesRef prefs = NULL;
461 SCNetworkSetRef set = NULL;
462
463 if (geteuid() == 0) {
464 prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL);
465 } else {
466 AuthorizationRef authorization;
467
468 authorization = getAuthorization(myInstance);
469 if (authorization == NULL) {
470 return;
471 }
472
473 prefs = SCPreferencesCreateWithAuthorization(NULL, CFSTR("SCMonitor"), NULL, authorization);
474 }
475
476 set = SCNetworkSetCopyCurrent(prefs);
477 if (set == NULL) {
478 set = SCNetworkSetCreate(prefs);
479 if (set == NULL) {
480 goto done;
481 }
482 }
483
484 for (i = 0; i < n; i++) {
485 SCNetworkInterfaceRef interface;
486
487 interface = CFArrayGetValueAtIndex(myInstance->interfaces_configure, i);
488 ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface);
489 if (ok) {
490 CFStringRef name;
491
492 name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
493 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_NOTICE, CFSTR("add service for %@"), name);
494 }
495 }
496
497 ok = SCPreferencesCommitChanges(prefs);
498 if (!ok) {
499 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
500 CFSTR("SCPreferencesCommitChanges() failed: %s"),
501 SCErrorString(SCError()));
502 goto done;
503 }
504
505 ok = SCPreferencesApplyChanges(prefs);
506 if (!ok) {
507 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
508 CFSTR("SCPreferencesApplyChanges() failed: %s"),
509 SCErrorString(SCError()));
510 goto done;
511 }
512
513 done :
514
515 if (set != NULL) {
516 CFRelease(set);
517 set = NULL;
518 }
519
520 if (prefs != NULL) {
521 CFRelease(prefs);
522 prefs = NULL;
523 }
524
525 CFRelease(myInstance->interfaces_configure);
526 myInstance->interfaces_configure = NULL;
527
528 return;
529 }
530
531
532 #pragma mark -
533
534
535 static void
536 updateInterfaceList(MyType *myInstance)
537 {
538 Boolean changed = FALSE;
539 CFIndex i;
540 CFArrayRef interfaces;
541 CFMutableSetRef interfaces_old = NULL;
542 CFIndex n;
543 SCPreferencesRef prefs;
544 SCNetworkSetRef set = NULL;
545
546 prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL);
547 if (prefs == NULL) {
548 return;
549 }
550
551 set = SCNetworkSetCopyCurrent(prefs);
552 if (set == NULL) {
553 set = SCNetworkSetCreate(prefs);
554 if (set == NULL) {
555 goto done;
556 }
557 }
558
559 interfaces_old = CFSetCreateMutableCopy(NULL, 0, myInstance->interfaces_known);
560
561 interfaces = _SCNetworkInterfaceCopyAllWithPreferences(prefs);
562 if (interfaces != NULL) {
563 n = CFArrayGetCount(interfaces);
564 for (i = 0; i < n; i++) {
565 SCNetworkInterfaceRef interface;
566 Boolean ok;
567
568 interface = CFArrayGetValueAtIndex(interfaces, i);
569
570 if (_SCNetworkInterfaceIsBuiltin(interface)) {
571 // skip built-in interfaces
572 continue;
573 }
574
575 // track new vs. old (removed) interfaces
576 CFSetRemoveValue(interfaces_old, interface);
577 if (CFSetContainsValue(myInstance->interfaces_known, interface)) {
578 // if we already know about this interface
579 continue;
580 }
581 CFSetAddValue(myInstance->interfaces_known, interface);
582 changed = TRUE;
583
584 ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface);
585 if (ok) {
586 CFStringRef action;
587
588 // this is a *new* interface
589
590 action = _SCNetworkInterfaceGetConfigurationAction(interface);
591 if (action == NULL) {
592 // if no per-interface action, use [global] default
593 action = myInstance->configuration_action;
594 }
595 if ((action == NULL) ||
596 (!CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone) &&
597 !CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigure))) {
598 action = kSCNetworkInterfaceConfigurationActionValuePrompt;
599 }
600
601 if (CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone)) {
602 continue;
603 } else if (CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigure)) {
604 // configure automatically (without user intervention)
605 if (myInstance->interfaces_configure == NULL) {
606 myInstance->interfaces_configure = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
607 }
608 CFArrayAppendValue(myInstance->interfaces_configure, interface);
609 } else if (hasAuthorization(myInstance)) {
610 // if we already have the "admin" (kSCPreferencesWriteAuthorizationRight)
611 // right, configure automatically (without user intervention)
612 if (myInstance->interfaces_configure == NULL) {
613 myInstance->interfaces_configure = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
614 }
615 CFArrayAppendValue(myInstance->interfaces_configure, interface);
616 } else {
617 // notify user
618 if (myInstance->interfaces_prompt == NULL) {
619 myInstance->interfaces_prompt = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
620 }
621 CFArrayAppendValue(myInstance->interfaces_prompt, interface);
622 }
623 }
624 }
625
626 CFRelease(interfaces);
627 }
628
629 // remove any posted notifications for network interfaces that have been removed
630 n = CFSetGetCount(interfaces_old);
631 if (n > 0) {
632 const void * paths_q[32];
633 const void ** paths = paths_q;
634
635 if (n > (CFIndex)(sizeof(paths_q) / sizeof(CFTypeRef)))
636 paths = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
637 CFSetGetValues(interfaces_old, paths);
638 for (i = 0; i < n; i++) {
639 if (myInstance->interfaces_prompt != NULL) {
640 CFIndex j;
641
642 j = CFArrayGetCount(myInstance->interfaces_prompt);
643 while (j > 0) {
644 SCNetworkInterfaceRef interface;
645
646 j--;
647 interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, j);
648 if (CFEqual(interface, paths[i])) {
649 // if we have previously posted a notification
650 // for this no-longer-present interface
651 CFArrayRemoveValueAtIndex(myInstance->interfaces_prompt, j);
652 changed = TRUE;
653 }
654 }
655 }
656
657 CFSetRemoveValue(myInstance->interfaces_known, paths[i]);
658 }
659 if (paths != paths_q) CFAllocatorDeallocate(NULL, paths);
660 }
661
662 done :
663
664 if (changed) {
665 if (myInstance->interfaces_configure != NULL) {
666 // if we have network services to configure automatically
667 notify_configure(myInstance);
668 }
669
670 if (myInstance->interfaces_prompt != NULL) {
671 // if we have network services that require user intervention
672 // post notification for new interfaces
673 notify_add(myInstance);
674 }
675 }
676
677 if (interfaces_old != NULL) CFRelease(interfaces_old);
678 if (set != NULL) CFRelease(set);
679 CFRelease(prefs);
680 return;
681 }
682
683
684 #pragma mark -
685 #pragma mark Watch for new [network] interfaces
686
687
688 static void
689 update_lan(SCDynamicStoreRef store, CFArrayRef changes, void * arg)
690 {
691 MyType *myInstance = (MyType *)arg;
692
693 updateInterfaceList(myInstance);
694 return;
695 }
696
697
698 static void
699 watcher_add_lan(MyType *myInstance)
700 {
701 SCDynamicStoreContext context = { 0, (void *)myInstance, NULL, NULL, NULL };
702 CFDictionaryRef dict;
703 CFStringRef key;
704 CFArrayRef keys;
705 SCDynamicStoreRef store;
706
707 store = SCDynamicStoreCreate(NULL, CFSTR("SCMonitor"), update_lan, &context);
708 if (store == NULL) {
709 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
710 CFSTR("SCMonitor: SCDynamicStoreCreate() failed: %s"),
711 SCErrorString(SCError()));
712 return;
713 }
714
715 key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
716
717 // watch for changes to the list of network interfaces
718 keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
719 SCDynamicStoreSetNotificationKeys(store, NULL, keys);
720 CFRelease(keys);
721 myInstance->monitorRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
722 CFRunLoopAddSource(CFRunLoopGetCurrent(),
723 myInstance->monitorRls,
724 kCFRunLoopDefaultMode);
725
726 // initialize the list of known interfaces
727 myInstance->interfaces_known = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
728 dict = SCDynamicStoreCopyValue(store, key);
729 if (dict != NULL) {
730 if (isA_CFDictionary(dict)) {
731 CFIndex i;
732 CFArrayRef interfaces;
733 CFIndex n;
734
735 interfaces = CFDictionaryGetValue(dict, kSCPropNetInterfaces);
736 n = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0;
737 for (i = 0; i < n; i++) {
738 CFStringRef bsdName;
739
740 bsdName = CFArrayGetValueAtIndex(interfaces, i);
741 if (isA_CFString(bsdName)) {
742 SCNetworkInterfaceRef interface;
743
744 interface = _SCNetworkInterfaceCreateWithBSDName(NULL, bsdName, kIncludeNoVirtualInterfaces);
745 if (interface != NULL) {
746 CFSetAddValue(myInstance->interfaces_known, interface);
747 CFRelease(interface);
748 }
749 }
750 }
751 }
752
753 CFRelease(dict);
754 }
755
756 CFRelease(key);
757 CFRelease(store);
758 return;
759 }
760
761
762 static void
763 watcher_remove_lan(MyType *myInstance)
764 {
765 if (myInstance->monitorRls != NULL) {
766 CFRunLoopSourceInvalidate(myInstance->monitorRls);
767 CFRelease(myInstance->monitorRls);
768 myInstance->monitorRls = NULL;
769 }
770
771 if (myInstance->interfaces_known != NULL) {
772 CFRelease(myInstance->interfaces_known);
773 myInstance->interfaces_known = NULL;
774 }
775
776 return;
777 }
778
779
780 #pragma mark -
781
782
783 typedef struct {
784 io_registry_entry_t interface;
785 MyType *myInstance;
786 io_object_t notification;
787 } MyNode;
788
789
790 static void
791 add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface);
792
793
794 static void
795 update_node(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
796 {
797 CFIndex i;
798 CFDataRef myData = (CFDataRef)refCon;
799 MyType *myInstance;
800 MyNode *myNode;
801
802 /* ALIGN: CF aligns to at least >8 bytes */
803 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
804 myInstance = myNode->myInstance;
805
806 switch (messageType) {
807 case kIOMessageServicePropertyChange : {
808 Boolean initializing = FALSE;
809 SCNetworkInterfaceRef interface;
810 CFTypeRef val;
811
812 if (myNode->interface == MACH_PORT_NULL) {
813 // if we are not watching the "Initializing" property
814 return;
815 }
816
817 val = IORegistryEntryCreateCFProperty(service, CFSTR("Initializing"), NULL, 0);
818 if (val != NULL) {
819 initializing = (isA_CFBoolean(val) && CFBooleanGetValue(val));
820 CFRelease(val);
821 if (initializing) {
822 // if initialization not complete, keep watching
823 return;
824 }
825 }
826
827 // node is ready
828 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode->interface);
829 if (interface != NULL) {
830 CFRelease(interface);
831
832 // watch interface (to see when/if it's removed)
833 add_node_watcher(myInstance, myNode->interface, MACH_PORT_NULL);
834 }
835 break;
836 }
837
838 case kIOMessageServiceIsTerminated :
839 break;
840
841 default :
842 return;
843 }
844
845 // remove no-longer-needed notification
846 if (myNode->interface != MACH_PORT_NULL) {
847 IOObjectRelease(myNode->interface);
848 myNode->interface = MACH_PORT_NULL;
849 }
850 IOObjectRelease(myNode->notification);
851 i = CFArrayGetFirstIndexOfValue(myInstance->notifyNodes,
852 CFRangeMake(0, CFArrayGetCount(myInstance->notifyNodes)),
853 myData);
854 if (i != kCFNotFound) {
855 CFArrayRemoveValueAtIndex(myInstance->notifyNodes, i);
856 if (CFArrayGetCount(myInstance->notifyNodes) == 0) {
857 CFRelease(myInstance->notifyNodes);
858 myInstance->notifyNodes = NULL;
859 }
860 }
861
862 updateInterfaceList(myInstance);
863 return;
864 }
865
866
867 static void
868 add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface)
869 {
870 kern_return_t kr;
871 CFMutableDataRef myData;
872 MyNode *myNode;
873
874 // wait for initialization to complete
875 myData = CFDataCreateMutable(NULL, sizeof(MyNode));
876 CFDataSetLength(myData, sizeof(MyNode));
877
878 /* ALIGN: CF aligns to at least >8 bytes */
879 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
880
881 bzero(myNode, sizeof(MyNode));
882 if (interface != MACH_PORT_NULL) {
883 IOObjectRetain(interface);
884 }
885 myNode->interface = interface;
886 myNode->myInstance = myInstance;
887 myNode->notification = MACH_PORT_NULL;
888
889 kr = IOServiceAddInterestNotification(myInstance->notifyPort, // IONotificationPortRef
890 node, // io_service_t
891 kIOGeneralInterest, // interestType
892 update_node, // IOServiceInterestCallback
893 (void *)myData, // refCon
894 &myNode->notification); // notification
895 if (kr == KERN_SUCCESS) {
896 if (myInstance->notifyNodes == NULL) {
897 myInstance->notifyNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
898 }
899 CFArrayAppendValue(myInstance->notifyNodes, myData);
900 } else {
901 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
902 CFSTR("add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x"), kr);
903 }
904 CFRelease(myData);
905 }
906
907
908 static void
909 add_init_watcher(MyType *myInstance, io_registry_entry_t interface)
910 {
911 kern_return_t kr;
912 io_registry_entry_t node = interface;
913 CFTypeRef val = NULL;
914
915 while (node != MACH_PORT_NULL) {
916 io_registry_entry_t parent;
917
918 val = IORegistryEntryCreateCFProperty(node, kSCNetworkInterfaceHiddenPortKey, NULL, 0);
919 if (val != NULL) {
920 CFRelease(val);
921 val = NULL;
922 break;
923 }
924
925 val = IORegistryEntryCreateCFProperty(node, kSCNetworkInterfaceInitializingKey, NULL, 0);
926 if (val != NULL) {
927 break;
928 }
929
930 parent = MACH_PORT_NULL;
931 kr = IORegistryEntryGetParentEntry(node, kIOServicePlane, &parent);
932 switch (kr) {
933 case kIOReturnSuccess : // if we have a parent node
934 case kIOReturnNoDevice : // if we have hit the root node
935 break;
936 default :
937 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x"), kr);
938 break;
939 }
940 if (node != interface) {
941 IOObjectRelease(node);
942 }
943 node = parent;
944 }
945
946 if (val != NULL) {
947 if (isA_CFBoolean(val) && CFBooleanGetValue(val)) {
948 // watch the "Initializing" node
949 add_node_watcher(myInstance, node, interface);
950 }
951
952 CFRelease(val);
953 }
954
955 if ((node != MACH_PORT_NULL) && (node != interface)) {
956 IOObjectRelease(node);
957 }
958
959 return;
960 }
961
962
963 static void
964 update_serial(void *refcon, io_iterator_t iter)
965 {
966 MyType *myInstance = (MyType *)refcon;
967 io_registry_entry_t obj;
968
969 while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) {
970 SCNetworkInterfaceRef interface;
971
972 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj);
973 if (interface != NULL) {
974 CFRelease(interface);
975
976 // watch interface (to see when/if it's removed)
977 add_node_watcher(myInstance, obj, MACH_PORT_NULL);
978 } else {
979 // check interface, watch if initializing
980 add_init_watcher(myInstance, obj);
981 }
982
983 IOObjectRelease(obj);
984 }
985
986 updateInterfaceList(myInstance);
987 return;
988 }
989
990
991 static void
992 watcher_add_serial(MyType *myInstance)
993 {
994 kern_return_t kr;
995
996 myInstance->notifyPort = IONotificationPortCreate(kIOMasterPortDefault);
997 if (myInstance->notifyPort == NULL) {
998 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
999 CFSTR("SCMonitor: IONotificationPortCreate failed"));
1000 return;
1001 }
1002
1003 // watch for the introduction of new network serial devices
1004 kr = IOServiceAddMatchingNotification(myInstance->notifyPort,
1005 kIOFirstMatchNotification,
1006 IOServiceMatching("IOSerialBSDClient"),
1007 &update_serial,
1008 (void *)myInstance, // refCon
1009 &myInstance->notifyIterator); // notification
1010 if (kr != KERN_SUCCESS) {
1011 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
1012 CFSTR("SCMonitor : IOServiceAddMatchingNotification returned 0x%x"),
1013 kr);
1014 return;
1015 }
1016
1017 myInstance->notifyNodes = NULL;
1018
1019 // Get the current list of matches and arm the notification for
1020 // future interface arrivals.
1021 update_serial((void *)myInstance, myInstance->notifyIterator);
1022
1023 // and keep watching
1024 CFRunLoopAddSource(CFRunLoopGetCurrent(),
1025 IONotificationPortGetRunLoopSource(myInstance->notifyPort),
1026 kCFRunLoopDefaultMode);
1027 return;
1028 }
1029
1030
1031 static void
1032 watcher_remove_serial(MyType *myInstance)
1033 {
1034 if (myInstance->notifyNodes != NULL) {
1035 CFIndex i;
1036 CFIndex n = CFArrayGetCount(myInstance->notifyNodes);
1037
1038 for (i = 0; i < n; i++) {
1039 CFDataRef myData;
1040 MyNode *myNode;
1041
1042 myData = CFArrayGetValueAtIndex(myInstance->notifyNodes, i);
1043
1044 /* ALIGN: CF aligns to at least >8 bytes */
1045 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
1046
1047 if (myNode->interface != MACH_PORT_NULL) {
1048 IOObjectRelease(myNode->interface);
1049 }
1050 IOObjectRelease(myNode->notification);
1051 }
1052
1053 CFRelease(myInstance->notifyNodes);
1054 myInstance->notifyNodes = NULL;
1055 }
1056
1057 if (myInstance->notifyIterator != MACH_PORT_NULL) {
1058 IOObjectRelease(myInstance->notifyIterator);
1059 myInstance->notifyIterator = MACH_PORT_NULL;
1060 }
1061
1062 if (myInstance->notifyPort != MACH_PORT_NULL) {
1063 IONotificationPortDestroy(myInstance->notifyPort);
1064 myInstance->notifyPort = NULL;
1065 }
1066
1067 return;
1068 }
1069
1070
1071 #pragma mark -
1072
1073
1074 static void
1075 watcher_add(MyType *myInstance)
1076 {
1077 CFBundleRef bundle;
1078
1079 if (myInstance->log_msg == NULL) {
1080 myInstance->log_msg = asl_new(ASL_TYPE_MSG);
1081 asl_set(myInstance->log_msg, ASL_KEY_FACILITY, MY_BUNDLE_ID);
1082 }
1083
1084 bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID));
1085 if (bundle != NULL) {
1086 CFStringRef action;
1087 CFBooleanRef bVal;
1088 CFDictionaryRef info;
1089
1090 info = CFBundleGetInfoDictionary(bundle);
1091
1092 bVal = CFDictionaryGetValue(info, CFSTR("Debug"));
1093 bVal = isA_CFBoolean(bVal);
1094 if (bVal != NULL) {
1095 myInstance->debug = CFBooleanGetValue(bVal);
1096 }
1097
1098 action = CFDictionaryGetValue(info, kSCNetworkInterfaceConfigurationActionKey);
1099 action = isA_CFString(action);
1100 if (action != NULL) {
1101 myInstance->configuration_action = action;
1102 } else {
1103 CFBooleanRef user_intervention;
1104
1105 user_intervention = CFDictionaryGetValue(info, CFSTR("User Intervention"));
1106 if (isA_CFBoolean(user_intervention) && !CFBooleanGetValue(user_intervention)) {
1107 myInstance->configuration_action = kSCNetworkInterfaceConfigurationActionValueConfigure;
1108 }
1109 }
1110 }
1111
1112 watcher_add_lan(myInstance);
1113 watcher_add_serial(myInstance);
1114 return;
1115 }
1116
1117
1118 static void
1119 watcher_remove(MyType *myInstance)
1120 {
1121 watcher_remove_lan(myInstance);
1122 watcher_remove_serial(myInstance);
1123
1124 asl_free(myInstance->log_msg);
1125 myInstance->log_msg = NULL;
1126 return;
1127 }
1128
1129
1130 #pragma mark -
1131 #pragma mark UserEventAgent stubs
1132
1133
1134 static HRESULT
1135 myQueryInterface(void *myInstance, REFIID iid, LPVOID *ppv)
1136 {
1137 CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid);
1138
1139 // Test the requested ID against the valid interfaces.
1140 if (CFEqual(interfaceID, kUserEventAgentInterfaceID)) {
1141 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
1142 *ppv = myInstance;
1143 CFRelease(interfaceID);
1144 return S_OK;
1145 }
1146
1147 if (CFEqual(interfaceID, IUnknownUUID)) {
1148 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
1149 *ppv = myInstance;
1150 CFRelease(interfaceID);
1151 return S_OK;
1152 }
1153
1154 // Requested interface unknown, bail with error.
1155 *ppv = NULL;
1156 CFRelease(interfaceID);
1157 return E_NOINTERFACE;
1158 }
1159
1160
1161 static ULONG
1162 myAddRef(void *myInstance)
1163 {
1164 ((MyType *) myInstance)->_refCount++;
1165 return ((MyType *) myInstance)->_refCount;
1166 }
1167
1168
1169 static ULONG
1170 myRelease(void *myInstance)
1171 {
1172 ((MyType *) myInstance)->_refCount--;
1173 if (((MyType *) myInstance)->_refCount == 0) {
1174 CFUUIDRef factoryID = ((MyType *) myInstance)->_factoryID;
1175
1176 if (factoryID != NULL) {
1177 CFPlugInRemoveInstanceForFactory(factoryID);
1178 CFRelease(factoryID);
1179
1180 watcher_remove((MyType *)myInstance);
1181 notify_remove((MyType *)myInstance, TRUE);
1182 freeAuthorization((MyType *)myInstance);
1183 }
1184 free(myInstance);
1185 return 0;
1186 }
1187
1188 return ((MyType *) myInstance)->_refCount;
1189 }
1190
1191
1192 static void
1193 myInstall(void *myInstance)
1194 {
1195 watcher_add((MyType *)myInstance);
1196 return;
1197 }
1198
1199
1200 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = {
1201 NULL, // Required padding for COM
1202 myQueryInterface, // These three are the required COM functions
1203 myAddRef,
1204 myRelease,
1205 myInstall // Interface implementation
1206 };
1207
1208
1209 void *
1210 UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
1211 {
1212 MyType *newOne = NULL;
1213
1214 if (CFEqual(typeID, kUserEventAgentTypeID)) {
1215 newOne = (MyType *)malloc(sizeof(MyType));
1216 bzero(newOne, sizeof(*newOne));
1217 newOne->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl;
1218 newOne->_factoryID = (CFUUIDRef)CFRetain(kUserEventAgentFactoryID);
1219 CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID);
1220 newOne->_refCount = 1;
1221 }
1222
1223 return newOne;
1224 }
1225
1226
1227 #ifdef MAIN
1228 int
1229 main(int argc, char **argv)
1230 {
1231 MyType *newOne = (MyType *)malloc(sizeof(MyType));
1232
1233 _sc_log = FALSE;
1234 _sc_verbose = (argc > 1) ? TRUE : FALSE;
1235
1236 bzero(newOne, sizeof(*newOne));
1237 myInstall(newOne);
1238 CFRunLoopRun();
1239 exit(0);
1240 return (0);
1241 }
1242 #endif // MAIN