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