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