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