]> git.saurik.com Git - apple/configd.git/blob - SCMonitor/monitor.c
configd-596.12.tar.gz
[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 (kSCPreferencesAuthorizationRight_write)
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 = kSCPreferencesAuthorizationRight_write;
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 CFStringRef key;
703 CFArrayRef keys;
704 SCDynamicStoreRef store;
705
706 store = SCDynamicStoreCreate(NULL, CFSTR("SCMonitor"), update_lan, &context);
707 if (store == NULL) {
708 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
709 CFSTR("SCMonitor: SCDynamicStoreCreate() failed: %s"),
710 SCErrorString(SCError()));
711 return;
712 }
713
714 key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
715
716 // watch for changes to the list of network interfaces
717 keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
718 SCDynamicStoreSetNotificationKeys(store, NULL, keys);
719 CFRelease(keys);
720 myInstance->monitorRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
721 CFRunLoopAddSource(CFRunLoopGetCurrent(),
722 myInstance->monitorRls,
723 kCFRunLoopDefaultMode);
724
725 // check if we already have the "admin" (kSCPreferencesAuthorizationRight_write)
726 // right. If so, we can automatically configure (without user intervention) any
727 // "new" network interfaces that are present at login (e.g. a USB ethernet
728 // dongle that was plugged in before login).
729 if (!hasAuthorization(myInstance)) {
730 CFDictionaryRef dict;
731
732 // ... and if we don't have the right then we populate the list of
733 // known interfaces with those already named so that we avoid any
734 // login prompts (that the user might have no choice but to dismiss)
735 dict = SCDynamicStoreCopyValue(store, key);
736 if (dict != NULL) {
737 if (isA_CFDictionary(dict)) {
738 CFIndex i;
739 CFArrayRef interfaces;
740 CFIndex n;
741
742 interfaces = CFDictionaryGetValue(dict, kSCPropNetInterfaces);
743 n = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0;
744 for (i = 0; i < n; i++) {
745 CFStringRef bsdName;
746
747 bsdName = CFArrayGetValueAtIndex(interfaces, i);
748 if (isA_CFString(bsdName)) {
749 SCNetworkInterfaceRef interface;
750
751 interface = _SCNetworkInterfaceCreateWithBSDName(NULL, bsdName, kIncludeNoVirtualInterfaces);
752 if (interface != NULL) {
753 CFSetAddValue(myInstance->interfaces_known, interface);
754 CFRelease(interface);
755 }
756 }
757 }
758 }
759
760 CFRelease(dict);
761 }
762 }
763
764 CFRelease(key);
765 CFRelease(store);
766 return;
767 }
768
769
770 static void
771 watcher_remove_lan(MyType *myInstance)
772 {
773 if (myInstance->monitorRls != NULL) {
774 CFRunLoopSourceInvalidate(myInstance->monitorRls);
775 CFRelease(myInstance->monitorRls);
776 myInstance->monitorRls = NULL;
777 }
778
779 return;
780 }
781
782
783 #pragma mark -
784
785
786 typedef struct {
787 io_registry_entry_t interface;
788 io_registry_entry_t interface_node;
789 MyType *myInstance;
790 io_object_t notification;
791 } MyNode;
792
793
794 static void
795 add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface);
796
797
798 static void
799 update_node(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
800 {
801 CFIndex i;
802 CFDataRef myData = (CFDataRef)refCon;
803 MyType *myInstance;
804 MyNode *myNode;
805
806 /* ALIGN: CF aligns to at least >8 bytes */
807 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
808 myInstance = myNode->myInstance;
809
810 switch (messageType) {
811 case kIOMessageServicePropertyChange : {
812 Boolean initializing = FALSE;
813 SCNetworkInterfaceRef interface;
814 CFTypeRef val;
815
816 if (myNode->interface == MACH_PORT_NULL) {
817 // if we are not watching the "Initializing" property
818 return;
819 }
820
821 val = IORegistryEntryCreateCFProperty(service, CFSTR("Initializing"), NULL, 0);
822 if (val != NULL) {
823 initializing = (isA_CFBoolean(val) && CFBooleanGetValue(val));
824 CFRelease(val);
825 if (initializing) {
826 // if initialization not complete, keep watching
827 return;
828 }
829 }
830
831 // node is ready
832 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode->interface);
833 if (interface != NULL) {
834 CFRelease(interface);
835
836 // watch interface (to see when/if it's removed)
837 add_node_watcher(myInstance, myNode->interface, MACH_PORT_NULL);
838 }
839 break;
840 }
841
842 case kIOMessageServiceIsTerminated :
843 break;
844
845 default :
846 return;
847 }
848
849 // remove no-longer-needed notification
850 if (myNode->interface != myNode->interface_node) {
851 IOObjectRelease(myNode->interface_node);
852 }
853 if (myNode->interface != MACH_PORT_NULL) {
854 IOObjectRelease(myNode->interface);
855 }
856 IOObjectRelease(myNode->notification);
857 i = CFArrayGetFirstIndexOfValue(myInstance->notifyNodes,
858 CFRangeMake(0, CFArrayGetCount(myInstance->notifyNodes)),
859 myData);
860 if (i != kCFNotFound) {
861 CFArrayRemoveValueAtIndex(myInstance->notifyNodes, i);
862 if (CFArrayGetCount(myInstance->notifyNodes) == 0) {
863 CFRelease(myInstance->notifyNodes);
864 myInstance->notifyNodes = NULL;
865 }
866 }
867
868 updateInterfaceList(myInstance);
869 return;
870 }
871
872
873 static void
874 add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface)
875 {
876 kern_return_t kr;
877 CFMutableDataRef myData;
878 MyNode *myNode;
879
880 // wait for initialization to complete
881 myData = CFDataCreateMutable(NULL, sizeof(MyNode));
882 CFDataSetLength(myData, sizeof(MyNode));
883
884 /* ALIGN: CF aligns to at least >8 bytes */
885 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
886
887 bzero(myNode, sizeof(MyNode));
888 myNode->interface = interface;
889 if (myNode->interface != MACH_PORT_NULL) {
890 IOObjectRetain(myNode->interface);
891 }
892 myNode->interface_node = (interface == MACH_PORT_NULL) ? node : interface;
893 if (myNode->interface != myNode->interface_node) {
894 IOObjectRetain(myNode->interface_node);
895 }
896 myNode->myInstance = myInstance;
897 myNode->notification = MACH_PORT_NULL;
898
899 kr = IOServiceAddInterestNotification(myInstance->notifyPort, // IONotificationPortRef
900 node, // io_service_t
901 kIOGeneralInterest, // interestType
902 update_node, // IOServiceInterestCallback
903 (void *)myData, // refCon
904 &myNode->notification); // notification
905 if (kr == KERN_SUCCESS) {
906 if (myInstance->notifyNodes == NULL) {
907 myInstance->notifyNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
908 }
909 CFArrayAppendValue(myInstance->notifyNodes, myData);
910 } else {
911 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
912 CFSTR("add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x"), kr);
913 }
914 CFRelease(myData);
915 }
916
917
918 static void
919 add_init_watcher(MyType *myInstance, io_registry_entry_t interface)
920 {
921 kern_return_t kr;
922 io_registry_entry_t node = interface;
923 CFTypeRef val = NULL;
924
925 while (node != MACH_PORT_NULL) {
926 io_registry_entry_t parent;
927
928 val = IORegistryEntryCreateCFProperty(node, kSCNetworkInterfaceHiddenPortKey, NULL, 0);
929 if (val != NULL) {
930 CFRelease(val);
931 val = NULL;
932 break;
933 }
934
935 val = IORegistryEntryCreateCFProperty(node, kSCNetworkInterfaceInitializingKey, NULL, 0);
936 if (val != NULL) {
937 break;
938 }
939
940 parent = MACH_PORT_NULL;
941 kr = IORegistryEntryGetParentEntry(node, kIOServicePlane, &parent);
942 switch (kr) {
943 case kIOReturnSuccess : // if we have a parent node
944 case kIOReturnNoDevice : // if we have hit the root node
945 break;
946 default :
947 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR, CFSTR("add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x"), kr);
948 break;
949 }
950 if (node != interface) {
951 IOObjectRelease(node);
952 }
953 node = parent;
954 }
955
956 if (val != NULL) {
957 if (isA_CFBoolean(val) && CFBooleanGetValue(val)) {
958 // watch the "Initializing" node
959 add_node_watcher(myInstance, node, interface);
960 }
961
962 CFRelease(val);
963 }
964
965 if ((node != MACH_PORT_NULL) && (node != interface)) {
966 IOObjectRelease(node);
967 }
968
969 return;
970 }
971
972
973 static void
974 update_serial(void *refcon, io_iterator_t iter)
975 {
976 MyType *myInstance = (MyType *)refcon;
977 io_registry_entry_t obj;
978
979 while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) {
980 SCNetworkInterfaceRef interface;
981
982 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj);
983 if (interface != NULL) {
984 CFRelease(interface);
985
986 // watch interface (to see when/if it's removed)
987 add_node_watcher(myInstance, obj, MACH_PORT_NULL);
988 } else {
989 // check interface, watch if initializing
990 add_init_watcher(myInstance, obj);
991 }
992
993 IOObjectRelease(obj);
994 }
995
996 return;
997 }
998
999
1000 static void
1001 update_serial_nodes(void *refcon, io_iterator_t iter)
1002 {
1003 MyType *myInstance = (MyType *)refcon;
1004
1005 update_serial(refcon, iter);
1006 updateInterfaceList(myInstance);
1007 }
1008
1009
1010 static void
1011 watcher_add_serial(MyType *myInstance)
1012 {
1013 kern_return_t kr;
1014
1015 myInstance->notifyPort = IONotificationPortCreate(kIOMasterPortDefault);
1016 if (myInstance->notifyPort == NULL) {
1017 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
1018 CFSTR("SCMonitor: IONotificationPortCreate failed"));
1019 return;
1020 }
1021
1022 // watch for the introduction of new network serial devices
1023 kr = IOServiceAddMatchingNotification(myInstance->notifyPort,
1024 kIOFirstMatchNotification,
1025 IOServiceMatching("IOSerialBSDClient"),
1026 &update_serial_nodes,
1027 (void *)myInstance, // refCon
1028 &myInstance->notifyIterator); // notification
1029 if (kr != KERN_SUCCESS) {
1030 SCLOG(NULL, myInstance->log_msg, ASL_LEVEL_ERR,
1031 CFSTR("SCMonitor : IOServiceAddMatchingNotification returned 0x%x"),
1032 kr);
1033 return;
1034 }
1035
1036 myInstance->notifyNodes = NULL;
1037
1038 // Get the current list of matches and arm the notification for
1039 // future interface arrivals.
1040 update_serial((void *)myInstance, myInstance->notifyIterator);
1041
1042 if (myInstance->notifyNodes != NULL) {
1043 // if we have any serial nodes, check if we already have the
1044 // "admin" (kSCPreferencesAuthorizationRight_write) right. If
1045 // so, we can automatically configure (without user intervention)
1046 // any "new" network interfaces that are present at login (e.g. a
1047 // USB modem that was plugged in before login).
1048
1049 if (!hasAuthorization(myInstance)) {
1050 CFIndex i;
1051 CFIndex n = CFArrayGetCount(myInstance->notifyNodes);
1052
1053 // ... and if we don't have the right then we populate the list of
1054 // known interfaces with those already named so that we avoid any
1055 // login prompts (that the user might have no choice but to dismiss)
1056
1057 for (i = 0; i < n; i++) {
1058 SCNetworkInterfaceRef interface;
1059 CFDataRef myData;
1060 MyNode *myNode;
1061
1062 myData = CFArrayGetValueAtIndex(myInstance->notifyNodes, i);
1063
1064 /* ALIGN: CF aligns to at least >8 bytes */
1065 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
1066
1067 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode->interface_node);
1068 if (interface != NULL) {
1069 CFSetAddValue(myInstance->interfaces_known, interface);
1070 CFRelease(interface);
1071 }
1072 }
1073 }
1074 }
1075
1076 // and keep watching
1077 CFRunLoopAddSource(CFRunLoopGetCurrent(),
1078 IONotificationPortGetRunLoopSource(myInstance->notifyPort),
1079 kCFRunLoopDefaultMode);
1080 return;
1081 }
1082
1083
1084 static void
1085 watcher_remove_serial(MyType *myInstance)
1086 {
1087 if (myInstance->notifyNodes != NULL) {
1088 CFIndex i;
1089 CFIndex n = CFArrayGetCount(myInstance->notifyNodes);
1090
1091 for (i = 0; i < n; i++) {
1092 CFDataRef myData;
1093 MyNode *myNode;
1094
1095 myData = CFArrayGetValueAtIndex(myInstance->notifyNodes, i);
1096
1097 /* ALIGN: CF aligns to at least >8 bytes */
1098 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
1099
1100 if (myNode->interface != myNode->interface_node) {
1101 IOObjectRelease(myNode->interface_node);
1102 }
1103 if (myNode->interface != MACH_PORT_NULL) {
1104 IOObjectRelease(myNode->interface);
1105 }
1106 IOObjectRelease(myNode->notification);
1107 }
1108
1109 CFRelease(myInstance->notifyNodes);
1110 myInstance->notifyNodes = NULL;
1111 }
1112
1113 if (myInstance->notifyIterator != MACH_PORT_NULL) {
1114 IOObjectRelease(myInstance->notifyIterator);
1115 myInstance->notifyIterator = MACH_PORT_NULL;
1116 }
1117
1118 if (myInstance->notifyPort != MACH_PORT_NULL) {
1119 IONotificationPortDestroy(myInstance->notifyPort);
1120 myInstance->notifyPort = NULL;
1121 }
1122
1123 return;
1124 }
1125
1126
1127 #pragma mark -
1128
1129
1130 static void
1131 watcher_add(MyType *myInstance)
1132 {
1133 CFBundleRef bundle;
1134
1135 if (myInstance->log_msg == NULL) {
1136 myInstance->log_msg = asl_new(ASL_TYPE_MSG);
1137 asl_set(myInstance->log_msg, ASL_KEY_FACILITY, MY_BUNDLE_ID);
1138 }
1139
1140 bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID));
1141 if (bundle != NULL) {
1142 CFStringRef action;
1143 CFBooleanRef bVal;
1144 CFDictionaryRef info;
1145
1146 info = CFBundleGetInfoDictionary(bundle);
1147
1148 bVal = CFDictionaryGetValue(info, CFSTR("Debug"));
1149 bVal = isA_CFBoolean(bVal);
1150 if (bVal != NULL) {
1151 myInstance->debug = CFBooleanGetValue(bVal);
1152 }
1153
1154 action = CFDictionaryGetValue(info, kSCNetworkInterfaceConfigurationActionKey);
1155 action = isA_CFString(action);
1156 if (action != NULL) {
1157 myInstance->configuration_action = action;
1158 } else {
1159 CFBooleanRef user_intervention;
1160
1161 user_intervention = CFDictionaryGetValue(info, CFSTR("User Intervention"));
1162 if (isA_CFBoolean(user_intervention) && !CFBooleanGetValue(user_intervention)) {
1163 myInstance->configuration_action = kSCNetworkInterfaceConfigurationActionValueConfigure;
1164 }
1165 }
1166 }
1167
1168 // initialize the list of known interfaces
1169 myInstance->interfaces_known = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
1170
1171 // add LAN interfaces
1172 watcher_add_lan(myInstance);
1173
1174 // add SERIAL interfaces
1175 watcher_add_serial(myInstance);
1176
1177 // auto-configure (as needed)
1178 updateInterfaceList(myInstance);
1179
1180 return;
1181 }
1182
1183
1184 static void
1185 watcher_remove(MyType *myInstance)
1186 {
1187 watcher_remove_lan(myInstance);
1188 watcher_remove_serial(myInstance);
1189
1190 if (myInstance->interfaces_known != NULL) {
1191 CFRelease(myInstance->interfaces_known);
1192 myInstance->interfaces_known = NULL;
1193 }
1194
1195 asl_free(myInstance->log_msg);
1196 myInstance->log_msg = NULL;
1197 return;
1198 }
1199
1200
1201 #pragma mark -
1202 #pragma mark UserEventAgent stubs
1203
1204
1205 static HRESULT
1206 myQueryInterface(void *myInstance, REFIID iid, LPVOID *ppv)
1207 {
1208 CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid);
1209
1210 // Test the requested ID against the valid interfaces.
1211 if (CFEqual(interfaceID, kUserEventAgentInterfaceID)) {
1212 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
1213 *ppv = myInstance;
1214 CFRelease(interfaceID);
1215 return S_OK;
1216 }
1217
1218 if (CFEqual(interfaceID, IUnknownUUID)) {
1219 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
1220 *ppv = myInstance;
1221 CFRelease(interfaceID);
1222 return S_OK;
1223 }
1224
1225 // Requested interface unknown, bail with error.
1226 *ppv = NULL;
1227 CFRelease(interfaceID);
1228 return E_NOINTERFACE;
1229 }
1230
1231
1232 static ULONG
1233 myAddRef(void *myInstance)
1234 {
1235 ((MyType *) myInstance)->_refCount++;
1236 return ((MyType *) myInstance)->_refCount;
1237 }
1238
1239
1240 static ULONG
1241 myRelease(void *myInstance)
1242 {
1243 ((MyType *) myInstance)->_refCount--;
1244 if (((MyType *) myInstance)->_refCount == 0) {
1245 CFUUIDRef factoryID = ((MyType *) myInstance)->_factoryID;
1246
1247 if (factoryID != NULL) {
1248 CFPlugInRemoveInstanceForFactory(factoryID);
1249 CFRelease(factoryID);
1250
1251 watcher_remove((MyType *)myInstance);
1252 notify_remove((MyType *)myInstance, TRUE);
1253 freeAuthorization((MyType *)myInstance);
1254 }
1255 free(myInstance);
1256 return 0;
1257 }
1258
1259 return ((MyType *) myInstance)->_refCount;
1260 }
1261
1262
1263 static void
1264 myInstall(void *myInstance)
1265 {
1266 watcher_add((MyType *)myInstance);
1267 return;
1268 }
1269
1270
1271 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = {
1272 NULL, // Required padding for COM
1273 myQueryInterface, // These three are the required COM functions
1274 myAddRef,
1275 myRelease,
1276 myInstall // Interface implementation
1277 };
1278
1279
1280 void *
1281 UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
1282 {
1283 MyType *newOne = NULL;
1284
1285 if (CFEqual(typeID, kUserEventAgentTypeID)) {
1286 newOne = (MyType *)malloc(sizeof(MyType));
1287 bzero(newOne, sizeof(*newOne));
1288 newOne->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl;
1289 newOne->_factoryID = (CFUUIDRef)CFRetain(kUserEventAgentFactoryID);
1290 CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID);
1291 newOne->_refCount = 1;
1292 }
1293
1294 return newOne;
1295 }
1296
1297
1298 #ifdef MAIN
1299 int
1300 main(int argc, char **argv)
1301 {
1302 MyType *newOne = (MyType *)malloc(sizeof(MyType));
1303
1304 _sc_log = FALSE;
1305 _sc_verbose = (argc > 1) ? TRUE : FALSE;
1306
1307 bzero(newOne, sizeof(*newOne));
1308 myInstall(newOne);
1309 CFRunLoopRun();
1310 exit(0);
1311 return (0);
1312 }
1313 #endif // MAIN