2 * Copyright (c) 2007-2014 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * October 24, 2007 Allan Nathanson <ajn@apple.com>
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"
43 #define MY_BUNDLE_ID "com.apple.SystemConfiguration.SCMonitor"
44 #define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns"
46 #define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane"
47 #define NETWORK_PREF_CMD "New Interface"
50 * The following keys/values control the actions taken when a new interface
51 * has been detected and whether we interact with the user.
53 * The keys/values can be provided globally (in SCMonitor.plugin's Info.plist
54 * file) or per-inteface in the IORegistry.
56 * For the "New Interface Detected Action" key we define the following [CFString]
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
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.
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.
75 UserEventAgentInterfaceStruct
*_UserEventAgentInterface
;
83 CFStringRef configuration_action
;
85 CFRunLoopSourceRef monitorRls
;
87 IONotificationPortRef notifyPort
;
88 io_iterator_t notifyIterator
;
89 CFMutableArrayRef notifyNodes
;
91 // interfaces that we already know about
92 CFMutableSetRef interfaces_known
;
94 // interfaces that should be auto-configured (no user notification)
95 CFMutableArrayRef interfaces_configure
;
97 // interfaces that require user notification
98 CFMutableArrayRef interfaces_prompt
;
100 CFUserNotificationRef userNotification
;
101 CFRunLoopSourceRef userRls
;
103 AuthorizationRef authorization
;
106 static CFMutableDictionaryRef notify_to_instance
= NULL
;
110 #pragma mark Authorization
113 static AuthorizationRef
114 getAuthorization(MyType
*myInstance
)
116 if (myInstance
->authorization
== NULL
) {
117 AuthorizationFlags flags
= kAuthorizationFlagDefaults
;
120 status
= AuthorizationCreate(NULL
,
121 kAuthorizationEmptyEnvironment
,
123 &myInstance
->authorization
);
124 if (status
!= errAuthorizationSuccess
) {
125 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
126 CFSTR("AuthorizationCreate() failed: status = %d"),
131 return myInstance
->authorization
;
136 hasAuthorization(MyType
*myInstance
)
138 AuthorizationRef authorization
;
139 Boolean isAdmin
= FALSE
;
141 authorization
= getAuthorization(myInstance
);
142 if (authorization
!= NULL
) {
143 AuthorizationFlags flags
= kAuthorizationFlagDefaults
;
144 AuthorizationItem items
[1];
145 AuthorizationRights rights
;
148 items
[0].name
= kSCPreferencesAuthorizationRight_write
;
149 items
[0].value
= NULL
;
150 items
[0].valueLength
= 0;
153 rights
.count
= sizeof(items
) / sizeof(items
[0]);
154 rights
.items
= items
;
156 status
= AuthorizationCopyRights(authorization
,
158 kAuthorizationEmptyEnvironment
,
161 isAdmin
= (status
== errAuthorizationSuccess
);
169 freeAuthorization(MyType
*myInstance
)
171 if (myInstance
->authorization
!= NULL
) {
172 AuthorizationFree(myInstance
->authorization
, kAuthorizationFlagDefaults
);
173 // AuthorizationFree(myInstance->authorization, kAuthorizationFlagDestroyRights);
174 myInstance
->authorization
= NULL
;
182 #pragma mark New interface notification / configuration
186 open_NetworkPrefPane(MyType
*myInstance
)
188 AEDesc aeDesc
= { typeNull
, NULL
};
189 CFArrayRef prefArray
;
191 LSLaunchURLSpec prefSpec
;
194 prefURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
,
195 CFSTR(NETWORK_PREF_APP
),
196 kCFURLPOSIXPathStyle
,
198 prefArray
= CFArrayCreate(NULL
, (const void **)&prefURL
, 1, &kCFTypeArrayCallBacks
);
201 status
= AECreateDesc('ptru',
202 (const void *)NETWORK_PREF_CMD
,
203 strlen(NETWORK_PREF_CMD
),
205 if (status
!= noErr
) {
206 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: AECreateDesc() failed: %d"), (int)status
);
209 prefSpec
.appURL
= NULL
;
210 prefSpec
.itemURLs
= prefArray
;
211 prefSpec
.passThruParams
= &aeDesc
;
212 prefSpec
.launchFlags
= kLSLaunchAsync
| kLSLaunchDontAddToRecents
;
213 prefSpec
.asyncRefCon
= NULL
;
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
);
220 CFRelease(prefArray
);
221 if (aeDesc
.descriptorType
!= typeNull
) AEDisposeDesc(&aeDesc
);
227 notify_remove(MyType
*myInstance
, Boolean cancel
)
229 if (myInstance
->interfaces_configure
!= NULL
) {
230 CFRelease(myInstance
->interfaces_configure
);
231 myInstance
->interfaces_configure
= NULL
;
234 if (myInstance
->interfaces_prompt
!= NULL
) {
235 CFRelease(myInstance
->interfaces_prompt
);
236 myInstance
->interfaces_prompt
= NULL
;
239 if (myInstance
->userRls
!= NULL
) {
240 CFRunLoopSourceInvalidate(myInstance
->userRls
);
241 CFRelease(myInstance
->userRls
);
242 myInstance
->userRls
= NULL
;
245 if (myInstance
->userNotification
!= NULL
) {
249 status
= CFUserNotificationCancel(myInstance
->userNotification
);
251 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
252 CFSTR("SCMonitor: CFUserNotificationCancel() failed, status=%d"),
256 CFRelease(myInstance
->userNotification
);
257 myInstance
->userNotification
= NULL
;
265 notify_reply(CFUserNotificationRef userNotification
, CFOptionFlags response_flags
)
267 MyType
*myInstance
= NULL
;
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
;
280 if (myInstance
== NULL
) {
281 SCLOG(NULL
, NULL
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: can't find user notification"));
286 switch (response_flags
& 0x3) {
287 case kCFUserNotificationDefaultResponse
:
288 // user asked to configure interface
289 open_NetworkPrefPane(myInstance
);
296 notify_remove(myInstance
, FALSE
);
302 notify_add(MyType
*myInstance
)
305 CFMutableDictionaryRef dict
= NULL
;
308 CFIndex n
= CFArrayGetCount(myInstance
->interfaces_prompt
);
311 if (myInstance
->userNotification
!= NULL
) {
312 CFMutableArrayRef save
= NULL
;
315 CFRetain(myInstance
->interfaces_prompt
);
316 save
= myInstance
->interfaces_prompt
;
318 notify_remove(myInstance
, TRUE
);
319 myInstance
->interfaces_prompt
= save
;
325 dict
= CFDictionaryCreateMutable(NULL
,
327 &kCFTypeDictionaryKeyCallBacks
,
328 &kCFTypeDictionaryValueCallBacks
);
330 // set localization URL
331 bundle
= CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID
));
332 if (bundle
!= NULL
) {
333 url
= CFBundleCopyBundleURL(bundle
);
337 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
338 (const UInt8
*)"/System/Library/UserEventPlugins/SCMonitor.plugin",
339 strlen("/System/Library/UserEventPlugins/SCMonitor.plugin"),
341 if (bundle
== NULL
) {
342 bundle
= CFBundleCreate(NULL
, url
);
348 CFDictionarySetValue(dict
, kCFUserNotificationLocalizationURLKey
, url
);
351 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: can't find bundle"));
356 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
357 (const UInt8
*)MY_ICON_PATH
,
358 strlen(MY_ICON_PATH
),
361 CFDictionarySetValue(dict
, kCFUserNotificationIconURLKey
, url
);
366 CFDictionarySetValue(dict
,
367 kCFUserNotificationAlertHeaderKey
,
368 (n
== 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N"));
373 SCNetworkInterfaceRef interface
;
377 #define MESSAGE_1 "The \"%@\" network interface has not been set up. To set up this interface, use Network Preferences."
379 format
= CFBundleCopyLocalizedString(bundle
,
383 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, 0);
384 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
385 message
= CFStringCreateWithFormat(NULL
, NULL
, format
, name
);
386 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
390 CFMutableArrayRef message
;
392 message
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
393 CFArrayAppendValue(message
, CFSTR("MESSAGE_SN"));
394 for (i
= 0; i
< n
; i
++) {
395 SCNetworkInterfaceRef interface
;
399 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, i
);
400 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
401 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\r\t%@"), name
);
402 CFArrayAppendValue(message
, str
);
405 CFArrayAppendValue(message
, CFSTR("MESSAGE_EN"));
406 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
411 CFDictionaryAddValue(dict
, kCFUserNotificationDefaultButtonTitleKey
, CFSTR("OPEN_NP"));
412 CFDictionaryAddValue(dict
, kCFUserNotificationAlternateButtonTitleKey
, CFSTR("CANCEL"));
414 // create and post notification
415 myInstance
->userNotification
= CFUserNotificationCreate(NULL
,
417 kCFUserNotificationNoteAlertLevel
,
420 if (myInstance
->userNotification
== NULL
) {
421 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: CFUserNotificationCreate() failed, %d"), (int)error
);
425 // establish callback
426 myInstance
->userRls
= CFUserNotificationCreateRunLoopSource(NULL
,
427 myInstance
->userNotification
,
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
;
436 CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance
->userRls
, kCFRunLoopDefaultMode
);
438 // add instance for notification
439 if (notify_to_instance
== NULL
) {
440 notify_to_instance
= CFDictionaryCreateMutable(NULL
,
442 &kCFTypeDictionaryKeyCallBacks
,
443 NULL
); // no retain/release/... for values
445 CFDictionarySetValue(notify_to_instance
, myInstance
->userNotification
, myInstance
);
449 if (dict
!= NULL
) CFRelease(dict
);
455 notify_configure(MyType
*myInstance
)
458 CFIndex n
= CFArrayGetCount(myInstance
->interfaces_configure
);
460 SCPreferencesRef prefs
= NULL
;
461 SCNetworkSetRef set
= NULL
;
463 if (geteuid() == 0) {
464 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
466 AuthorizationRef authorization
;
468 authorization
= getAuthorization(myInstance
);
469 if (authorization
== NULL
) {
473 prefs
= SCPreferencesCreateWithAuthorization(NULL
, CFSTR("SCMonitor"), NULL
, authorization
);
476 set
= SCNetworkSetCopyCurrent(prefs
);
478 set
= SCNetworkSetCreate(prefs
);
484 for (i
= 0; i
< n
; i
++) {
485 SCNetworkInterfaceRef interface
;
487 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_configure
, i
);
488 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
492 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
493 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_NOTICE
, CFSTR("add/update service for %@"), name
);
497 ok
= SCPreferencesCommitChanges(prefs
);
499 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
500 CFSTR("SCPreferencesCommitChanges() failed: %s"),
501 SCErrorString(SCError()));
505 ok
= SCPreferencesApplyChanges(prefs
);
507 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
508 CFSTR("SCPreferencesApplyChanges() failed: %s"),
509 SCErrorString(SCError()));
525 CFRelease(myInstance
->interfaces_configure
);
526 myInstance
->interfaces_configure
= NULL
;
535 // configure ONLY IF authorized
536 #define kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized CFSTR("Configure-Authorized")
540 updateInterfaceList(MyType
*myInstance
)
542 Boolean changed
= FALSE
;
544 CFArrayRef interfaces
;
545 CFMutableSetRef interfaces_old
= NULL
;
547 SCPreferencesRef prefs
;
548 SCNetworkSetRef set
= NULL
;
550 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
555 set
= SCNetworkSetCopyCurrent(prefs
);
557 set
= SCNetworkSetCreate(prefs
);
563 interfaces_old
= CFSetCreateMutableCopy(NULL
, 0, myInstance
->interfaces_known
);
565 interfaces
= _SCNetworkInterfaceCopyAllWithPreferences(prefs
);
566 if (interfaces
!= NULL
) {
567 n
= CFArrayGetCount(interfaces
);
568 for (i
= 0; i
< n
; i
++) {
569 SCNetworkInterfaceRef interface
;
572 interface
= CFArrayGetValueAtIndex(interfaces
, i
);
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
580 CFSetAddValue(myInstance
->interfaces_known
, interface
);
583 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
587 // this is a *new* interface
589 action
= _SCNetworkInterfaceGetConfigurationAction(interface
);
590 if (action
== NULL
) {
591 // if no per-interface action, use [global] default
592 action
= myInstance
->configuration_action
;
594 if ((action
== NULL
) ||
595 (!CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueNone
) &&
596 !CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigure
))) {
597 if (_SCNetworkInterfaceIsBuiltin(interface
)) {
598 // if built-in interface
599 action
= kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized
;
601 action
= kSCNetworkInterfaceConfigurationActionValuePrompt
;
605 if (CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueNone
)) {
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
);
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
);
619 CFArrayAppendValue(myInstance
->interfaces_configure
, interface
);
620 } else if (!CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized
)) {
622 if (myInstance
->interfaces_prompt
== NULL
) {
623 myInstance
->interfaces_prompt
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
625 CFArrayAppendValue(myInstance
->interfaces_prompt
, interface
);
630 CFRelease(interfaces
);
633 // remove any posted notifications for network interfaces that have been removed
634 n
= CFSetGetCount(interfaces_old
);
636 const void * paths_q
[32];
637 const void ** paths
= paths_q
;
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
) {
646 j
= CFArrayGetCount(myInstance
->interfaces_prompt
);
648 SCNetworkInterfaceRef interface
;
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
);
661 CFSetRemoveValue(myInstance
->interfaces_known
, paths
[i
]);
663 if (paths
!= paths_q
) CFAllocatorDeallocate(NULL
, paths
);
669 if (myInstance
->interfaces_configure
!= NULL
) {
670 // if we have network services to configure automatically
671 notify_configure(myInstance
);
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
);
681 if (interfaces_old
!= NULL
) CFRelease(interfaces_old
);
682 if (set
!= NULL
) CFRelease(set
);
689 #pragma mark Watch for new [network] interfaces
693 update_lan(SCDynamicStoreRef store
, CFArrayRef changes
, void * arg
)
695 MyType
*myInstance
= (MyType
*)arg
;
697 updateInterfaceList(myInstance
);
703 watcher_add_lan(MyType
*myInstance
)
705 SCDynamicStoreContext context
= { 0, (void *)myInstance
, NULL
, NULL
, NULL
};
708 SCDynamicStoreRef store
;
710 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCMonitor"), update_lan
, &context
);
712 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
713 CFSTR("SCMonitor: SCDynamicStoreCreate() failed: %s"),
714 SCErrorString(SCError()));
718 key
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
720 // watch for changes to the list of network interfaces
721 keys
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
722 SCDynamicStoreSetNotificationKeys(store
, NULL
, keys
);
724 myInstance
->monitorRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
725 CFRunLoopAddSource(CFRunLoopGetCurrent(),
726 myInstance
->monitorRls
,
727 kCFRunLoopDefaultMode
);
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
;
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
);
741 if (isA_CFDictionary(dict
)) {
743 CFArrayRef interfaces
;
746 interfaces
= CFDictionaryGetValue(dict
, kSCPropNetInterfaces
);
747 n
= isA_CFArray(interfaces
) ? CFArrayGetCount(interfaces
) : 0;
748 for (i
= 0; i
< n
; i
++) {
751 bsdName
= CFArrayGetValueAtIndex(interfaces
, i
);
752 if (isA_CFString(bsdName
)) {
753 SCNetworkInterfaceRef interface
;
755 interface
= _SCNetworkInterfaceCreateWithBSDName(NULL
, bsdName
, kIncludeNoVirtualInterfaces
);
756 if (interface
!= NULL
) {
757 CFSetAddValue(myInstance
->interfaces_known
, interface
);
758 CFRelease(interface
);
775 watcher_remove_lan(MyType
*myInstance
)
777 if (myInstance
->monitorRls
!= NULL
) {
778 CFRunLoopSourceInvalidate(myInstance
->monitorRls
);
779 CFRelease(myInstance
->monitorRls
);
780 myInstance
->monitorRls
= NULL
;
791 io_registry_entry_t interface
;
792 io_registry_entry_t interface_node
;
794 io_object_t notification
;
799 add_node_watcher(MyType
*myInstance
, io_registry_entry_t node
, io_registry_entry_t interface
);
803 update_node(void *refCon
, io_service_t service
, natural_t messageType
, void *messageArgument
)
806 CFDataRef myData
= (CFDataRef
)refCon
;
810 /* ALIGN: CF aligns to at least >8 bytes */
811 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
812 myInstance
= myNode
->myInstance
;
814 switch (messageType
) {
815 case kIOMessageServicePropertyChange
: {
816 Boolean initializing
= FALSE
;
817 SCNetworkInterfaceRef interface
;
820 if (myNode
->interface
== MACH_PORT_NULL
) {
821 // if we are not watching the "Initializing" property
825 val
= IORegistryEntryCreateCFProperty(service
, CFSTR("Initializing"), NULL
, 0);
827 initializing
= (isA_CFBoolean(val
) && CFBooleanGetValue(val
));
830 // if initialization not complete, keep watching
836 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode
->interface
);
837 if (interface
!= NULL
) {
838 CFRelease(interface
);
840 // watch interface (to see when/if it's removed)
841 add_node_watcher(myInstance
, myNode
->interface
, MACH_PORT_NULL
);
846 case kIOMessageServiceIsTerminated
:
853 // remove no-longer-needed notification
854 if (myNode
->interface
!= myNode
->interface_node
) {
855 IOObjectRelease(myNode
->interface_node
);
857 if (myNode
->interface
!= MACH_PORT_NULL
) {
858 IOObjectRelease(myNode
->interface
);
860 IOObjectRelease(myNode
->notification
);
861 i
= CFArrayGetFirstIndexOfValue(myInstance
->notifyNodes
,
862 CFRangeMake(0, CFArrayGetCount(myInstance
->notifyNodes
)),
864 if (i
!= kCFNotFound
) {
865 CFArrayRemoveValueAtIndex(myInstance
->notifyNodes
, i
);
866 if (CFArrayGetCount(myInstance
->notifyNodes
) == 0) {
867 CFRelease(myInstance
->notifyNodes
);
868 myInstance
->notifyNodes
= NULL
;
872 updateInterfaceList(myInstance
);
878 add_node_watcher(MyType
*myInstance
, io_registry_entry_t node
, io_registry_entry_t interface
)
881 CFMutableDataRef myData
;
884 // wait for initialization to complete
885 myData
= CFDataCreateMutable(NULL
, sizeof(MyNode
));
886 CFDataSetLength(myData
, sizeof(MyNode
));
888 /* ALIGN: CF aligns to at least >8 bytes */
889 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
891 bzero(myNode
, sizeof(MyNode
));
892 myNode
->interface
= interface
;
893 if (myNode
->interface
!= MACH_PORT_NULL
) {
894 IOObjectRetain(myNode
->interface
);
896 myNode
->interface_node
= (interface
== MACH_PORT_NULL
) ? node
: interface
;
897 if (myNode
->interface
!= myNode
->interface_node
) {
898 IOObjectRetain(myNode
->interface_node
);
900 myNode
->myInstance
= myInstance
;
901 myNode
->notification
= MACH_PORT_NULL
;
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
);
913 CFArrayAppendValue(myInstance
->notifyNodes
, myData
);
915 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
916 CFSTR("add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x"), kr
);
923 add_init_watcher(MyType
*myInstance
, io_registry_entry_t interface
)
926 io_registry_entry_t node
= interface
;
927 CFTypeRef val
= NULL
;
929 while (node
!= MACH_PORT_NULL
) {
930 io_registry_entry_t parent
;
932 val
= IORegistryEntryCreateCFProperty(node
, kSCNetworkInterfaceHiddenPortKey
, NULL
, 0);
939 val
= IORegistryEntryCreateCFProperty(node
, kSCNetworkInterfaceInitializingKey
, NULL
, 0);
944 parent
= MACH_PORT_NULL
;
945 kr
= IORegistryEntryGetParentEntry(node
, kIOServicePlane
, &parent
);
947 case kIOReturnSuccess
: // if we have a parent node
948 case kIOReturnNoDevice
: // if we have hit the root node
951 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x"), kr
);
954 if (node
!= interface
) {
955 IOObjectRelease(node
);
961 if (isA_CFBoolean(val
) && CFBooleanGetValue(val
)) {
962 // watch the "Initializing" node
963 add_node_watcher(myInstance
, node
, interface
);
969 if ((node
!= MACH_PORT_NULL
) && (node
!= interface
)) {
970 IOObjectRelease(node
);
978 update_serial(void *refcon
, io_iterator_t iter
)
980 MyType
*myInstance
= (MyType
*)refcon
;
981 io_registry_entry_t obj
;
983 while ((obj
= IOIteratorNext(iter
)) != MACH_PORT_NULL
) {
984 SCNetworkInterfaceRef interface
;
986 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj
);
987 if (interface
!= NULL
) {
988 CFRelease(interface
);
990 // watch interface (to see when/if it's removed)
991 add_node_watcher(myInstance
, obj
, MACH_PORT_NULL
);
993 // check interface, watch if initializing
994 add_init_watcher(myInstance
, obj
);
997 IOObjectRelease(obj
);
1005 update_serial_nodes(void *refcon
, io_iterator_t iter
)
1007 MyType
*myInstance
= (MyType
*)refcon
;
1009 update_serial(refcon
, iter
);
1010 updateInterfaceList(myInstance
);
1015 watcher_add_serial(MyType
*myInstance
)
1019 myInstance
->notifyPort
= IONotificationPortCreate(kIOMasterPortDefault
);
1020 if (myInstance
->notifyPort
== NULL
) {
1021 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
1022 CFSTR("SCMonitor: IONotificationPortCreate failed"));
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"),
1040 myInstance
->notifyNodes
= NULL
;
1042 // Get the current list of matches and arm the notification for
1043 // future interface arrivals.
1044 update_serial((void *)myInstance
, myInstance
->notifyIterator
);
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).
1053 if (!hasAuthorization(myInstance
)) {
1055 CFIndex n
= CFArrayGetCount(myInstance
->notifyNodes
);
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)
1061 for (i
= 0; i
< n
; i
++) {
1062 SCNetworkInterfaceRef interface
;
1066 myData
= CFArrayGetValueAtIndex(myInstance
->notifyNodes
, i
);
1068 /* ALIGN: CF aligns to at least >8 bytes */
1069 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
1071 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode
->interface_node
);
1072 if (interface
!= NULL
) {
1073 CFSetAddValue(myInstance
->interfaces_known
, interface
);
1074 CFRelease(interface
);
1080 // and keep watching
1081 CFRunLoopAddSource(CFRunLoopGetCurrent(),
1082 IONotificationPortGetRunLoopSource(myInstance
->notifyPort
),
1083 kCFRunLoopDefaultMode
);
1089 watcher_remove_serial(MyType
*myInstance
)
1091 if (myInstance
->notifyNodes
!= NULL
) {
1093 CFIndex n
= CFArrayGetCount(myInstance
->notifyNodes
);
1095 for (i
= 0; i
< n
; i
++) {
1099 myData
= CFArrayGetValueAtIndex(myInstance
->notifyNodes
, i
);
1101 /* ALIGN: CF aligns to at least >8 bytes */
1102 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
1104 if (myNode
->interface
!= myNode
->interface_node
) {
1105 IOObjectRelease(myNode
->interface_node
);
1107 if (myNode
->interface
!= MACH_PORT_NULL
) {
1108 IOObjectRelease(myNode
->interface
);
1110 IOObjectRelease(myNode
->notification
);
1113 CFRelease(myInstance
->notifyNodes
);
1114 myInstance
->notifyNodes
= NULL
;
1117 if (myInstance
->notifyIterator
!= MACH_PORT_NULL
) {
1118 IOObjectRelease(myInstance
->notifyIterator
);
1119 myInstance
->notifyIterator
= MACH_PORT_NULL
;
1122 if (myInstance
->notifyPort
!= MACH_PORT_NULL
) {
1123 IONotificationPortDestroy(myInstance
->notifyPort
);
1124 myInstance
->notifyPort
= NULL
;
1135 watcher_add(MyType
*myInstance
)
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
);
1144 bundle
= CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID
));
1145 if (bundle
!= NULL
) {
1148 CFDictionaryRef info
;
1150 info
= CFBundleGetInfoDictionary(bundle
);
1152 bVal
= CFDictionaryGetValue(info
, CFSTR("Debug"));
1153 bVal
= isA_CFBoolean(bVal
);
1155 myInstance
->debug
= CFBooleanGetValue(bVal
);
1158 action
= CFDictionaryGetValue(info
, kSCNetworkInterfaceConfigurationActionKey
);
1159 action
= isA_CFString(action
);
1160 if (action
!= NULL
) {
1161 myInstance
->configuration_action
= action
;
1163 CFBooleanRef user_intervention
;
1165 user_intervention
= CFDictionaryGetValue(info
, CFSTR("User Intervention"));
1166 if (isA_CFBoolean(user_intervention
) && !CFBooleanGetValue(user_intervention
)) {
1167 myInstance
->configuration_action
= kSCNetworkInterfaceConfigurationActionValueConfigure
;
1172 // initialize the list of known interfaces
1173 myInstance
->interfaces_known
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
1175 // add LAN interfaces
1176 watcher_add_lan(myInstance
);
1178 // add SERIAL interfaces
1179 watcher_add_serial(myInstance
);
1181 // auto-configure (as needed)
1182 updateInterfaceList(myInstance
);
1189 watcher_remove(MyType
*myInstance
)
1191 watcher_remove_lan(myInstance
);
1192 watcher_remove_serial(myInstance
);
1194 if (myInstance
->interfaces_known
!= NULL
) {
1195 CFRelease(myInstance
->interfaces_known
);
1196 myInstance
->interfaces_known
= NULL
;
1199 asl_release(myInstance
->log_msg
);
1200 myInstance
->log_msg
= NULL
;
1206 #pragma mark UserEventAgent stubs
1210 myQueryInterface(void *myInstance
, REFIID iid
, LPVOID
*ppv
)
1212 CFUUIDRef interfaceID
= CFUUIDCreateFromUUIDBytes(NULL
, iid
);
1214 // Test the requested ID against the valid interfaces.
1215 if (CFEqual(interfaceID
, kUserEventAgentInterfaceID
)) {
1216 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
1218 CFRelease(interfaceID
);
1222 if (CFEqual(interfaceID
, IUnknownUUID
)) {
1223 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
1225 CFRelease(interfaceID
);
1229 // Requested interface unknown, bail with error.
1231 CFRelease(interfaceID
);
1232 return E_NOINTERFACE
;
1237 myAddRef(void *myInstance
)
1239 ((MyType
*) myInstance
)->_refCount
++;
1240 return ((MyType
*) myInstance
)->_refCount
;
1245 myRelease(void *myInstance
)
1247 ((MyType
*) myInstance
)->_refCount
--;
1248 if (((MyType
*) myInstance
)->_refCount
== 0) {
1249 CFUUIDRef factoryID
= ((MyType
*) myInstance
)->_factoryID
;
1251 if (factoryID
!= NULL
) {
1252 CFPlugInRemoveInstanceForFactory(factoryID
);
1253 CFRelease(factoryID
);
1255 watcher_remove((MyType
*)myInstance
);
1256 notify_remove((MyType
*)myInstance
, TRUE
);
1257 freeAuthorization((MyType
*)myInstance
);
1263 return ((MyType
*) myInstance
)->_refCount
;
1268 myInstall(void *myInstance
)
1270 watcher_add((MyType
*)myInstance
);
1275 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl
= {
1276 NULL
, // Required padding for COM
1277 myQueryInterface
, // These three are the required COM functions
1280 myInstall
// Interface implementation
1285 UserEventAgentFactory(CFAllocatorRef allocator
, CFUUIDRef typeID
)
1287 MyType
*newOne
= NULL
;
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;
1304 main(int argc
, char **argv
)
1306 MyType
*newOne
= (MyType
*)malloc(sizeof(MyType
));
1309 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
1311 bzero(newOne
, sizeof(*newOne
));