2 * Copyright (c) 2007-2011 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 require authorization if the logged
65 * in user is NOT "root" (eUID==0) or if the "system.preferences"
66 * administrator right is not currently available.
68 * An [older] "User Intervention" key is also supported. That CFBoolean
69 * key, if present and TRUE, implies "Configure" configuration of the
70 * interface without intervention.
74 UserEventAgentInterfaceStruct
*_UserEventAgentInterface
;
82 CFStringRef configuration_action
;
84 CFRunLoopSourceRef monitorRls
;
86 IONotificationPortRef notifyPort
;
87 io_iterator_t notifyIterator
;
88 CFMutableArrayRef notifyNodes
;
90 // interfaces that we already know about
91 CFMutableSetRef interfaces_known
;
93 // interfaces that should be auto-configured (no user notification)
94 CFMutableArrayRef interfaces_configure
;
96 // interfaces that require user notification
97 CFMutableArrayRef interfaces_prompt
;
99 CFUserNotificationRef userNotification
;
100 CFRunLoopSourceRef userRls
;
102 AuthorizationRef authorization
;
105 static CFMutableDictionaryRef notify_to_instance
= NULL
;
109 #pragma mark Authorization
112 static AuthorizationRef
113 getAuthorization(MyType
*myInstance
)
115 if (myInstance
->authorization
== NULL
) {
116 AuthorizationFlags flags
= kAuthorizationFlagDefaults
;
119 status
= AuthorizationCreate(NULL
,
120 kAuthorizationEmptyEnvironment
,
122 &myInstance
->authorization
);
123 if (status
!= errAuthorizationSuccess
) {
124 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
125 CFSTR("AuthorizationCreate() failed: status = %d"),
130 return myInstance
->authorization
;
135 hasAuthorization(MyType
*myInstance
)
137 AuthorizationRef authorization
;
138 Boolean isAdmin
= FALSE
;
140 authorization
= getAuthorization(myInstance
);
141 if (authorization
!= NULL
) {
142 AuthorizationFlags flags
= kAuthorizationFlagDefaults
;
143 AuthorizationItem items
[1];
144 AuthorizationRights rights
;
147 items
[0].name
= "system.preferences";
148 items
[0].value
= NULL
;
149 items
[0].valueLength
= 0;
152 rights
.count
= sizeof(items
) / sizeof(items
[0]);
153 rights
.items
= items
;
155 status
= AuthorizationCopyRights(authorization
,
157 kAuthorizationEmptyEnvironment
,
160 isAdmin
= (status
== errAuthorizationSuccess
);
168 freeAuthorization(MyType
*myInstance
)
170 if (myInstance
->authorization
!= NULL
) {
171 AuthorizationFree(myInstance
->authorization
, kAuthorizationFlagDefaults
);
172 // AuthorizationFree(myInstance->authorization, kAuthorizationFlagDestroyRights);
173 myInstance
->authorization
= NULL
;
181 #pragma mark New interface notification / configuration
185 open_NetworkPrefPane(MyType
*myInstance
)
187 AEDesc aeDesc
= { typeNull
, NULL
};
188 CFArrayRef prefArray
;
190 LSLaunchURLSpec prefSpec
;
193 prefURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
,
194 CFSTR(NETWORK_PREF_APP
),
195 kCFURLPOSIXPathStyle
,
197 prefArray
= CFArrayCreate(NULL
, (const void **)&prefURL
, 1, &kCFTypeArrayCallBacks
);
200 status
= AECreateDesc('ptru',
201 (const void *)NETWORK_PREF_CMD
,
202 strlen(NETWORK_PREF_CMD
),
204 if (status
!= noErr
) {
205 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: AECreateDesc() failed: %d"), status
);
208 prefSpec
.appURL
= NULL
;
209 prefSpec
.itemURLs
= prefArray
;
210 prefSpec
.passThruParams
= &aeDesc
;
211 prefSpec
.launchFlags
= kLSLaunchAsync
| kLSLaunchDontAddToRecents
;
212 prefSpec
.asyncRefCon
= NULL
;
214 status
= LSOpenFromURLSpec(&prefSpec
, NULL
);
215 if (status
!= noErr
) {
216 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: LSOpenFromURLSpec() failed: %d"), status
);
219 CFRelease(prefArray
);
220 if (aeDesc
.descriptorType
!= typeNull
) AEDisposeDesc(&aeDesc
);
226 notify_remove(MyType
*myInstance
, Boolean cancel
)
228 if (myInstance
->interfaces_configure
!= NULL
) {
229 CFRelease(myInstance
->interfaces_configure
);
230 myInstance
->interfaces_configure
= NULL
;
233 if (myInstance
->interfaces_prompt
!= NULL
) {
234 CFRelease(myInstance
->interfaces_prompt
);
235 myInstance
->interfaces_prompt
= NULL
;
238 if (myInstance
->userRls
!= NULL
) {
239 CFRunLoopSourceInvalidate(myInstance
->userRls
);
240 CFRelease(myInstance
->userRls
);
241 myInstance
->userRls
= NULL
;
244 if (myInstance
->userNotification
!= NULL
) {
248 status
= CFUserNotificationCancel(myInstance
->userNotification
);
250 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
251 CFSTR("SCMonitor: CFUserNotificationCancel() failed, status=%d"),
255 CFRelease(myInstance
->userNotification
);
256 myInstance
->userNotification
= NULL
;
264 notify_reply(CFUserNotificationRef userNotification
, CFOptionFlags response_flags
)
266 MyType
*myInstance
= NULL
;
268 // get instance for notification
269 if (notify_to_instance
!= NULL
) {
270 myInstance
= (MyType
*)CFDictionaryGetValue(notify_to_instance
, userNotification
);
271 if (myInstance
!= NULL
) {
272 CFDictionaryRemoveValue(notify_to_instance
, userNotification
);
273 if (CFDictionaryGetCount(notify_to_instance
) == 0) {
274 CFRelease(notify_to_instance
);
275 notify_to_instance
= NULL
;
279 if (myInstance
== NULL
) {
280 SCLOG(NULL
, NULL
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: can't find user notification"));
285 switch (response_flags
& 0x3) {
286 case kCFUserNotificationDefaultResponse
:
287 // user asked to configure interface
288 open_NetworkPrefPane(myInstance
);
295 notify_remove(myInstance
, FALSE
);
301 notify_add(MyType
*myInstance
)
304 CFMutableDictionaryRef dict
= NULL
;
307 CFIndex n
= CFArrayGetCount(myInstance
->interfaces_prompt
);
310 if (myInstance
->userNotification
!= NULL
) {
311 CFMutableArrayRef save
= NULL
;
314 CFRetain(myInstance
->interfaces_prompt
);
315 save
= myInstance
->interfaces_prompt
;
317 notify_remove(myInstance
, TRUE
);
318 myInstance
->interfaces_prompt
= save
;
324 dict
= CFDictionaryCreateMutable(NULL
,
326 &kCFTypeDictionaryKeyCallBacks
,
327 &kCFTypeDictionaryValueCallBacks
);
329 // set localization URL
330 bundle
= CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID
));
331 if (bundle
!= NULL
) {
332 url
= CFBundleCopyBundleURL(bundle
);
336 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
337 (const UInt8
*)"/System/Library/UserEventPlugins/SCMonitor.plugin",
338 strlen("/System/Library/UserEventPlugins/SCMonitor.plugin"),
340 if (bundle
== NULL
) {
341 bundle
= CFBundleCreate(NULL
, url
);
347 CFDictionarySetValue(dict
, kCFUserNotificationLocalizationURLKey
, url
);
350 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: can't find bundle"));
355 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
356 (const UInt8
*)MY_ICON_PATH
,
357 strlen(MY_ICON_PATH
),
360 CFDictionarySetValue(dict
, kCFUserNotificationIconURLKey
, url
);
365 CFDictionarySetValue(dict
,
366 kCFUserNotificationAlertHeaderKey
,
367 (n
== 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N"));
372 SCNetworkInterfaceRef interface
;
376 #define MESSAGE_1 "The \"%@\" network interface has not been set up. To set up this interface, use Network Preferences."
378 format
= CFBundleCopyLocalizedString(bundle
,
382 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, 0);
383 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
384 message
= CFStringCreateWithFormat(NULL
, NULL
, format
, name
);
385 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
389 CFMutableArrayRef message
;
391 message
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
392 CFArrayAppendValue(message
, CFSTR("MESSAGE_SN"));
393 for (i
= 0; i
< n
; i
++) {
394 SCNetworkInterfaceRef interface
;
398 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, i
);
399 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
400 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\r\t%@"), name
);
401 CFArrayAppendValue(message
, str
);
404 CFArrayAppendValue(message
, CFSTR("MESSAGE_EN"));
405 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
410 CFDictionaryAddValue(dict
, kCFUserNotificationDefaultButtonTitleKey
, CFSTR("OPEN_NP"));
411 CFDictionaryAddValue(dict
, kCFUserNotificationAlternateButtonTitleKey
, CFSTR("CANCEL"));
413 // create and post notification
414 myInstance
->userNotification
= CFUserNotificationCreate(NULL
,
416 kCFUserNotificationNoteAlertLevel
,
419 if (myInstance
->userNotification
== NULL
) {
420 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: CFUserNotificationCreate() failed, %d"), error
);
424 // establish callback
425 myInstance
->userRls
= CFUserNotificationCreateRunLoopSource(NULL
,
426 myInstance
->userNotification
,
429 if (myInstance
->userRls
== NULL
) {
430 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: CFUserNotificationCreateRunLoopSource() failed"));
431 CFRelease(myInstance
->userNotification
);
432 myInstance
->userNotification
= NULL
;
435 CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance
->userRls
, kCFRunLoopDefaultMode
);
437 // add instance for notification
438 if (notify_to_instance
== NULL
) {
439 notify_to_instance
= CFDictionaryCreateMutable(NULL
,
441 &kCFTypeDictionaryKeyCallBacks
,
442 NULL
); // no retain/release/... for values
444 CFDictionarySetValue(notify_to_instance
, myInstance
->userNotification
, myInstance
);
448 if (dict
!= NULL
) CFRelease(dict
);
454 notify_configure(MyType
*myInstance
)
457 CFIndex n
= CFArrayGetCount(myInstance
->interfaces_configure
);
459 SCPreferencesRef prefs
= NULL
;
460 SCNetworkSetRef set
= NULL
;
462 if (geteuid() == 0) {
463 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
465 AuthorizationRef authorization
;
467 authorization
= getAuthorization(myInstance
);
468 if (authorization
== NULL
) {
472 prefs
= SCPreferencesCreateWithAuthorization(NULL
, CFSTR("SCMonitor"), NULL
, authorization
);
475 set
= SCNetworkSetCopyCurrent(prefs
);
477 set
= SCNetworkSetCreate(prefs
);
483 for (i
= 0; i
< n
; i
++) {
484 SCNetworkInterfaceRef interface
;
486 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_configure
, i
);
487 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
491 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
492 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_NOTICE
, CFSTR("add service for %@"), name
);
496 ok
= SCPreferencesCommitChanges(prefs
);
498 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
499 CFSTR("SCPreferencesCommitChanges() failed: %s"),
500 SCErrorString(SCError()));
504 ok
= SCPreferencesApplyChanges(prefs
);
506 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
507 CFSTR("SCPreferencesApplyChanges() failed: %s"),
508 SCErrorString(SCError()));
524 CFRelease(myInstance
->interfaces_configure
);
525 myInstance
->interfaces_configure
= NULL
;
535 updateInterfaceList(MyType
*myInstance
)
537 Boolean changed
= FALSE
;
539 CFArrayRef interfaces
;
540 CFMutableSetRef interfaces_old
= NULL
;
542 SCPreferencesRef prefs
;
543 SCNetworkSetRef set
= NULL
;
545 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
550 set
= SCNetworkSetCopyCurrent(prefs
);
552 set
= SCNetworkSetCreate(prefs
);
558 interfaces_old
= CFSetCreateMutableCopy(NULL
, 0, myInstance
->interfaces_known
);
560 interfaces
= _SCNetworkInterfaceCopyAllWithPreferences(prefs
);
561 if (interfaces
!= NULL
) {
562 n
= CFArrayGetCount(interfaces
);
563 for (i
= 0; i
< n
; i
++) {
564 SCNetworkInterfaceRef interface
;
567 interface
= CFArrayGetValueAtIndex(interfaces
, i
);
569 if (_SCNetworkInterfaceIsBuiltin(interface
)) {
570 // skip built-in interfaces
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 action
= kSCNetworkInterfaceConfigurationActionValuePrompt
;
600 if (CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueNone
)) {
602 } else if (CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigure
)) {
603 // configure automatically (without user intervention)
604 if (myInstance
->interfaces_configure
== NULL
) {
605 myInstance
->interfaces_configure
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
607 CFArrayAppendValue(myInstance
->interfaces_configure
, interface
);
608 } else if (hasAuthorization(myInstance
)) {
609 // if we already have the "admin" (system.preferences) right, configure automatically (without user intervention)
610 if (myInstance
->interfaces_configure
== NULL
) {
611 myInstance
->interfaces_configure
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
613 CFArrayAppendValue(myInstance
->interfaces_configure
, interface
);
616 if (myInstance
->interfaces_prompt
== NULL
) {
617 myInstance
->interfaces_prompt
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
619 CFArrayAppendValue(myInstance
->interfaces_prompt
, interface
);
624 CFRelease(interfaces
);
627 // remove any posted notifications for network interfaces that have been removed
628 n
= CFSetGetCount(interfaces_old
);
630 const void * paths_q
[32];
631 const void ** paths
= paths_q
;
633 if (n
> (CFIndex
)(sizeof(paths_q
) / sizeof(CFTypeRef
)))
634 paths
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
635 CFSetGetValues(interfaces_old
, paths
);
636 for (i
= 0; i
< n
; i
++) {
637 if (myInstance
->interfaces_prompt
!= NULL
) {
640 j
= CFArrayGetCount(myInstance
->interfaces_prompt
);
642 SCNetworkInterfaceRef interface
;
645 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, j
);
646 if (CFEqual(interface
, paths
[i
])) {
647 // if we have previously posted a notification
648 // for this no-longer-present interface
649 CFArrayRemoveValueAtIndex(myInstance
->interfaces_prompt
, j
);
655 CFSetRemoveValue(myInstance
->interfaces_known
, paths
[i
]);
657 if (paths
!= paths_q
) CFAllocatorDeallocate(NULL
, paths
);
663 if (myInstance
->interfaces_configure
!= NULL
) {
664 // if we have network services to configure automatically
665 notify_configure(myInstance
);
668 if (myInstance
->interfaces_prompt
!= NULL
) {
669 // if we have network services that require user intervention
670 // post notification for new interfaces
671 notify_add(myInstance
);
675 if (interfaces_old
!= NULL
) CFRelease(interfaces_old
);
676 if (set
!= NULL
) CFRelease(set
);
683 #pragma mark Watch for new [network] interfaces
687 update_lan(SCDynamicStoreRef store
, CFArrayRef changes
, void * arg
)
689 MyType
*myInstance
= (MyType
*)arg
;
691 updateInterfaceList(myInstance
);
697 watcher_add_lan(MyType
*myInstance
)
699 SCDynamicStoreContext context
= { 0, (void *)myInstance
, NULL
, NULL
, NULL
};
700 CFDictionaryRef dict
;
703 SCDynamicStoreRef store
;
705 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCMonitor"), update_lan
, &context
);
707 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
708 CFSTR("SCMonitor: SCDynamicStoreCreate() failed: %s"),
709 SCErrorString(SCError()));
713 key
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
715 // watch for changes to the list of network interfaces
716 keys
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
717 SCDynamicStoreSetNotificationKeys(store
, NULL
, keys
);
719 myInstance
->monitorRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
720 CFRunLoopAddSource(CFRunLoopGetCurrent(),
721 myInstance
->monitorRls
,
722 kCFRunLoopDefaultMode
);
724 // initialize the list of known interfaces
725 myInstance
->interfaces_known
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
726 dict
= SCDynamicStoreCopyValue(store
, key
);
728 if (isA_CFDictionary(dict
)) {
730 CFArrayRef interfaces
;
733 interfaces
= CFDictionaryGetValue(dict
, kSCPropNetInterfaces
);
734 n
= isA_CFArray(interfaces
) ? CFArrayGetCount(interfaces
) : 0;
735 for (i
= 0; i
< n
; i
++) {
738 bsdName
= CFArrayGetValueAtIndex(interfaces
, i
);
739 if (isA_CFString(bsdName
)) {
740 SCNetworkInterfaceRef interface
;
742 interface
= _SCNetworkInterfaceCreateWithBSDName(NULL
, bsdName
, kIncludeNoVirtualInterfaces
);
743 if (interface
!= NULL
) {
744 CFSetAddValue(myInstance
->interfaces_known
, interface
);
745 CFRelease(interface
);
761 watcher_remove_lan(MyType
*myInstance
)
763 if (myInstance
->monitorRls
!= NULL
) {
764 CFRunLoopSourceInvalidate(myInstance
->monitorRls
);
765 CFRelease(myInstance
->monitorRls
);
766 myInstance
->monitorRls
= NULL
;
769 if (myInstance
->interfaces_known
!= NULL
) {
770 CFRelease(myInstance
->interfaces_known
);
771 myInstance
->interfaces_known
= NULL
;
782 io_registry_entry_t interface
;
784 io_object_t notification
;
789 add_node_watcher(MyType
*myInstance
, io_registry_entry_t node
, io_registry_entry_t interface
);
793 update_node(void *refCon
, io_service_t service
, natural_t messageType
, void *messageArgument
)
796 CFDataRef myData
= (CFDataRef
)refCon
;
800 myNode
= (MyNode
*)CFDataGetBytePtr(myData
);
801 myInstance
= myNode
->myInstance
;
803 switch (messageType
) {
804 case kIOMessageServicePropertyChange
: {
805 Boolean initializing
= FALSE
;
806 SCNetworkInterfaceRef interface
;
809 if (myNode
->interface
== MACH_PORT_NULL
) {
810 // if we are not watching the "Initializing" property
814 val
= IORegistryEntryCreateCFProperty(service
, CFSTR("Initializing"), NULL
, 0);
816 initializing
= (isA_CFBoolean(val
) && CFBooleanGetValue(val
));
819 // if initialization not complete, keep watching
825 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode
->interface
);
826 if (interface
!= NULL
) {
827 CFRelease(interface
);
829 // watch interface (to see when/if it's removed)
830 add_node_watcher(myInstance
, myNode
->interface
, MACH_PORT_NULL
);
835 case kIOMessageServiceIsTerminated
:
842 // remove no-longer-needed notification
843 if (myNode
->interface
!= MACH_PORT_NULL
) {
844 IOObjectRelease(myNode
->interface
);
845 myNode
->interface
= MACH_PORT_NULL
;
847 IOObjectRelease(myNode
->notification
);
848 i
= CFArrayGetFirstIndexOfValue(myInstance
->notifyNodes
,
849 CFRangeMake(0, CFArrayGetCount(myInstance
->notifyNodes
)),
851 if (i
!= kCFNotFound
) {
852 CFArrayRemoveValueAtIndex(myInstance
->notifyNodes
, i
);
853 if (CFArrayGetCount(myInstance
->notifyNodes
) == 0) {
854 CFRelease(myInstance
->notifyNodes
);
855 myInstance
->notifyNodes
= NULL
;
859 updateInterfaceList(myInstance
);
865 add_node_watcher(MyType
*myInstance
, io_registry_entry_t node
, io_registry_entry_t interface
)
868 CFMutableDataRef myData
;
871 // wait for initialization to complete
872 myData
= CFDataCreateMutable(NULL
, sizeof(MyNode
));
873 CFDataSetLength(myData
, sizeof(MyNode
));
874 myNode
= (MyNode
*)CFDataGetBytePtr(myData
);
875 bzero(myNode
, sizeof(MyNode
));
876 if (interface
!= MACH_PORT_NULL
) {
877 IOObjectRetain(interface
);
879 myNode
->interface
= interface
;
880 myNode
->myInstance
= myInstance
;
881 myNode
->notification
= MACH_PORT_NULL
;
883 kr
= IOServiceAddInterestNotification(myInstance
->notifyPort
, // IONotificationPortRef
884 node
, // io_service_t
885 kIOGeneralInterest
, // interestType
886 update_node
, // IOServiceInterestCallback
887 (void *)myData
, // refCon
888 &myNode
->notification
); // notification
889 if (kr
== KERN_SUCCESS
) {
890 if (myInstance
->notifyNodes
== NULL
) {
891 myInstance
->notifyNodes
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
893 CFArrayAppendValue(myInstance
->notifyNodes
, myData
);
895 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
896 CFSTR("add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x"), kr
);
903 add_init_watcher(MyType
*myInstance
, io_registry_entry_t interface
)
906 io_registry_entry_t node
= interface
;
907 CFTypeRef val
= NULL
;
909 while (node
!= MACH_PORT_NULL
) {
910 io_registry_entry_t parent
;
912 val
= IORegistryEntryCreateCFProperty(node
, kSCNetworkInterfaceHiddenPortKey
, NULL
, 0);
919 val
= IORegistryEntryCreateCFProperty(node
, kSCNetworkInterfaceInitializingKey
, NULL
, 0);
924 parent
= MACH_PORT_NULL
;
925 kr
= IORegistryEntryGetParentEntry(node
, kIOServicePlane
, &parent
);
927 case kIOReturnSuccess
: // if we have a parent node
928 case kIOReturnNoDevice
: // if we have hit the root node
931 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x"), kr
);
934 if (node
!= interface
) {
935 IOObjectRelease(node
);
941 if (isA_CFBoolean(val
) && CFBooleanGetValue(val
)) {
942 // watch the "Initializing" node
943 add_node_watcher(myInstance
, node
, interface
);
949 if ((node
!= MACH_PORT_NULL
) && (node
!= interface
)) {
950 IOObjectRelease(node
);
958 update_serial(void *refcon
, io_iterator_t iter
)
960 MyType
*myInstance
= (MyType
*)refcon
;
961 io_registry_entry_t obj
;
963 while ((obj
= IOIteratorNext(iter
)) != MACH_PORT_NULL
) {
964 SCNetworkInterfaceRef interface
;
966 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj
);
967 if (interface
!= NULL
) {
968 CFRelease(interface
);
970 // watch interface (to see when/if it's removed)
971 add_node_watcher(myInstance
, obj
, MACH_PORT_NULL
);
973 // check interface, watch if initializing
974 add_init_watcher(myInstance
, obj
);
977 IOObjectRelease(obj
);
980 updateInterfaceList(myInstance
);
986 watcher_add_serial(MyType
*myInstance
)
990 myInstance
->notifyPort
= IONotificationPortCreate(kIOMasterPortDefault
);
991 if (myInstance
->notifyPort
== NULL
) {
992 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
993 CFSTR("SCMonitor: IONotificationPortCreate failed"));
997 // watch for the introduction of new network serial devices
998 kr
= IOServiceAddMatchingNotification(myInstance
->notifyPort
,
999 kIOFirstMatchNotification
,
1000 IOServiceMatching("IOSerialBSDClient"),
1002 (void *)myInstance
, // refCon
1003 &myInstance
->notifyIterator
); // notification
1004 if (kr
!= KERN_SUCCESS
) {
1005 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
1006 CFSTR("SCMonitor : IOServiceAddMatchingNotification returned 0x%x"),
1011 myInstance
->notifyNodes
= NULL
;
1013 // Get the current list of matches and arm the notification for
1014 // future interface arrivals.
1015 update_serial((void *)myInstance
, myInstance
->notifyIterator
);
1017 // and keep watching
1018 CFRunLoopAddSource(CFRunLoopGetCurrent(),
1019 IONotificationPortGetRunLoopSource(myInstance
->notifyPort
),
1020 kCFRunLoopDefaultMode
);
1026 watcher_remove_serial(MyType
*myInstance
)
1028 if (myInstance
->notifyNodes
!= NULL
) {
1030 CFIndex n
= CFArrayGetCount(myInstance
->notifyNodes
);
1032 for (i
= 0; i
< n
; i
++) {
1036 myData
= CFArrayGetValueAtIndex(myInstance
->notifyNodes
, i
);
1037 myNode
= (MyNode
*)CFDataGetBytePtr(myData
);
1038 if (myNode
->interface
!= MACH_PORT_NULL
) {
1039 IOObjectRelease(myNode
->interface
);
1041 IOObjectRelease(myNode
->notification
);
1044 CFRelease(myInstance
->notifyNodes
);
1045 myInstance
->notifyNodes
= NULL
;
1048 if (myInstance
->notifyIterator
!= MACH_PORT_NULL
) {
1049 IOObjectRelease(myInstance
->notifyIterator
);
1050 myInstance
->notifyIterator
= MACH_PORT_NULL
;
1053 if (myInstance
->notifyPort
!= MACH_PORT_NULL
) {
1054 IONotificationPortDestroy(myInstance
->notifyPort
);
1055 myInstance
->notifyPort
= NULL
;
1066 watcher_add(MyType
*myInstance
)
1070 if (myInstance
->log_msg
== NULL
) {
1071 myInstance
->log_msg
= asl_new(ASL_TYPE_MSG
);
1072 asl_set(myInstance
->log_msg
, ASL_KEY_FACILITY
, MY_BUNDLE_ID
);
1075 bundle
= CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID
));
1076 if (bundle
!= NULL
) {
1079 CFDictionaryRef info
;
1081 info
= CFBundleGetInfoDictionary(bundle
);
1083 bVal
= CFDictionaryGetValue(info
, CFSTR("Debug"));
1084 bVal
= isA_CFBoolean(bVal
);
1086 myInstance
->debug
= CFBooleanGetValue(bVal
);
1089 action
= CFDictionaryGetValue(info
, kSCNetworkInterfaceConfigurationActionKey
);
1090 action
= isA_CFString(action
);
1091 if (action
!= NULL
) {
1092 myInstance
->configuration_action
= action
;
1094 CFBooleanRef user_intervention
;
1096 user_intervention
= CFDictionaryGetValue(info
, CFSTR("User Intervention"));
1097 if (isA_CFBoolean(user_intervention
) && !CFBooleanGetValue(user_intervention
)) {
1098 myInstance
->configuration_action
= kSCNetworkInterfaceConfigurationActionValueConfigure
;
1103 watcher_add_lan(myInstance
);
1104 watcher_add_serial(myInstance
);
1110 watcher_remove(MyType
*myInstance
)
1112 watcher_remove_lan(myInstance
);
1113 watcher_remove_serial(myInstance
);
1115 asl_free(myInstance
->log_msg
);
1116 myInstance
->log_msg
= NULL
;
1122 #pragma mark UserEventAgent stubs
1126 myQueryInterface(void *myInstance
, REFIID iid
, LPVOID
*ppv
)
1128 CFUUIDRef interfaceID
= CFUUIDCreateFromUUIDBytes(NULL
, iid
);
1130 // Test the requested ID against the valid interfaces.
1131 if (CFEqual(interfaceID
, kUserEventAgentInterfaceID
)) {
1132 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
1134 CFRelease(interfaceID
);
1138 if (CFEqual(interfaceID
, IUnknownUUID
)) {
1139 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
1141 CFRelease(interfaceID
);
1145 // Requested interface unknown, bail with error.
1147 CFRelease(interfaceID
);
1148 return E_NOINTERFACE
;
1153 myAddRef(void *myInstance
)
1155 ((MyType
*) myInstance
)->_refCount
++;
1156 return ((MyType
*) myInstance
)->_refCount
;
1161 myRelease(void *myInstance
)
1163 ((MyType
*) myInstance
)->_refCount
--;
1164 if (((MyType
*) myInstance
)->_refCount
== 0) {
1165 CFUUIDRef factoryID
= ((MyType
*) myInstance
)->_factoryID
;
1167 if (factoryID
!= NULL
) {
1168 CFPlugInRemoveInstanceForFactory(factoryID
);
1169 CFRelease(factoryID
);
1171 watcher_remove((MyType
*)myInstance
);
1172 notify_remove((MyType
*)myInstance
, TRUE
);
1173 freeAuthorization((MyType
*)myInstance
);
1179 return ((MyType
*) myInstance
)->_refCount
;
1184 myInstall(void *myInstance
)
1186 watcher_add((MyType
*)myInstance
);
1191 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl
= {
1192 NULL
, // Required padding for COM
1193 myQueryInterface
, // These three are the required COM functions
1196 myInstall
// Interface implementation
1201 UserEventAgentFactory(CFAllocatorRef allocator
, CFUUIDRef typeID
)
1203 MyType
*newOne
= NULL
;
1205 if (CFEqual(typeID
, kUserEventAgentTypeID
)) {
1206 newOne
= (MyType
*)malloc(sizeof(MyType
));
1207 bzero(newOne
, sizeof(*newOne
));
1208 newOne
->_UserEventAgentInterface
= &UserEventAgentInterfaceFtbl
;
1209 newOne
->_factoryID
= (CFUUIDRef
)CFRetain(kUserEventAgentFactoryID
);
1210 CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID
);
1211 newOne
->_refCount
= 1;
1220 main(int argc
, char **argv
)
1222 MyType
*newOne
= (MyType
*)malloc(sizeof(MyType
));
1225 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
1227 bzero(newOne
, sizeof(*newOne
));