2 * Copyright (c) 2007-2009 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
;
80 CFStringRef configuration_action
;
82 CFRunLoopSourceRef monitorRls
;
84 IONotificationPortRef notifyPort
;
85 io_iterator_t notifyIterator
;
86 CFMutableArrayRef notifyNodes
;
88 // interfaces that we already know about
89 CFMutableSetRef interfaces_known
;
91 // interfaces that should be auto-configured (no user notification)
92 CFMutableArrayRef interfaces_configure
;
94 // interfaces that require user notification
95 CFMutableArrayRef interfaces_prompt
;
97 CFUserNotificationRef userNotification
;
98 CFRunLoopSourceRef userRls
;
101 static CFMutableDictionaryRef notify_to_instance
= NULL
;
105 #pragma mark New interface notification / configuration
109 open_NetworkPrefPane(MyType
*myInstance
)
111 AEDesc aeDesc
= { typeNull
, NULL
};
112 CFArrayRef prefArray
;
114 LSLaunchURLSpec prefSpec
;
117 prefURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
,
118 CFSTR(NETWORK_PREF_APP
),
119 kCFURLPOSIXPathStyle
,
121 prefArray
= CFArrayCreate(NULL
, (const void **)&prefURL
, 1, &kCFTypeArrayCallBacks
);
124 status
= AECreateDesc('ptru',
125 (const void *)NETWORK_PREF_CMD
,
126 strlen(NETWORK_PREF_CMD
),
128 if (status
!= noErr
) {
129 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: AECreateDesc() failed: %d"), status
);
132 prefSpec
.appURL
= NULL
;
133 prefSpec
.itemURLs
= prefArray
;
134 prefSpec
.passThruParams
= &aeDesc
;
135 prefSpec
.launchFlags
= kLSLaunchAsync
| kLSLaunchDontAddToRecents
;
136 prefSpec
.asyncRefCon
= NULL
;
138 status
= LSOpenFromURLSpec(&prefSpec
, NULL
);
139 if (status
!= noErr
) {
140 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: LSOpenFromURLSpec() failed: %d"), status
);
143 CFRelease(prefArray
);
144 if (aeDesc
.descriptorType
!= typeNull
) AEDisposeDesc(&aeDesc
);
150 notify_remove(MyType
*myInstance
, Boolean cancel
)
152 if (myInstance
->interfaces_configure
!= NULL
) {
153 CFRelease(myInstance
->interfaces_configure
);
154 myInstance
->interfaces_configure
= NULL
;
157 if (myInstance
->interfaces_prompt
!= NULL
) {
158 CFRelease(myInstance
->interfaces_prompt
);
159 myInstance
->interfaces_prompt
= NULL
;
162 if (myInstance
->userRls
!= NULL
) {
163 CFRunLoopSourceInvalidate(myInstance
->userRls
);
164 CFRelease(myInstance
->userRls
);
165 myInstance
->userRls
= NULL
;
168 if (myInstance
->userNotification
!= NULL
) {
172 status
= CFUserNotificationCancel(myInstance
->userNotification
);
174 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
175 CFSTR("SCMonitor: CFUserNotificationCancel() failed, status=%d"),
179 CFRelease(myInstance
->userNotification
);
180 myInstance
->userNotification
= NULL
;
188 notify_reply(CFUserNotificationRef userNotification
, CFOptionFlags response_flags
)
190 MyType
*myInstance
= NULL
;
192 // get instance for notification
193 if (notify_to_instance
!= NULL
) {
194 myInstance
= (MyType
*)CFDictionaryGetValue(notify_to_instance
, userNotification
);
195 if (myInstance
!= NULL
) {
196 CFDictionaryRemoveValue(notify_to_instance
, userNotification
);
197 if (CFDictionaryGetCount(notify_to_instance
) == 0) {
198 CFRelease(notify_to_instance
);
199 notify_to_instance
= NULL
;
203 if (myInstance
== NULL
) {
204 SCLOG(NULL
, NULL
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: can't find user notification"));
209 switch (response_flags
& 0x3) {
210 case kCFUserNotificationDefaultResponse
:
211 // user asked to configure interface
212 open_NetworkPrefPane(myInstance
);
219 notify_remove(myInstance
, FALSE
);
225 notify_add(MyType
*myInstance
)
228 CFMutableDictionaryRef dict
= NULL
;
231 CFIndex n
= CFArrayGetCount(myInstance
->interfaces_prompt
);
234 if (myInstance
->userNotification
!= NULL
) {
235 CFMutableArrayRef save
= NULL
;
238 CFRetain(myInstance
->interfaces_prompt
);
239 save
= myInstance
->interfaces_prompt
;
241 notify_remove(myInstance
, TRUE
);
242 myInstance
->interfaces_prompt
= save
;
248 dict
= CFDictionaryCreateMutable(NULL
,
250 &kCFTypeDictionaryKeyCallBacks
,
251 &kCFTypeDictionaryValueCallBacks
);
253 // set localization URL
254 bundle
= CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID
));
255 if (bundle
!= NULL
) {
256 url
= CFBundleCopyBundleURL(bundle
);
260 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
261 (const UInt8
*)"/System/Library/UserEventPlugins/SCMonitor.plugin",
262 strlen("/System/Library/UserEventPlugins/SCMonitor.plugin"),
264 if (bundle
== NULL
) {
265 bundle
= CFBundleCreate(NULL
, url
);
271 CFDictionarySetValue(dict
, kCFUserNotificationLocalizationURLKey
, url
);
274 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: can't find bundle"));
279 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
280 (const UInt8
*)MY_ICON_PATH
,
281 strlen(MY_ICON_PATH
),
284 CFDictionarySetValue(dict
, kCFUserNotificationIconURLKey
, url
);
289 CFDictionarySetValue(dict
,
290 kCFUserNotificationAlertHeaderKey
,
291 (n
== 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N"));
296 SCNetworkInterfaceRef interface
;
300 #define MESSAGE_1 "The \"%@\" network interface has not been set up. To set up this interface, use Network Preferences."
302 format
= CFBundleCopyLocalizedString(bundle
,
306 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, 0);
307 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
308 message
= CFStringCreateWithFormat(NULL
, NULL
, format
, name
);
309 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
313 CFMutableArrayRef message
;
315 message
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
316 CFArrayAppendValue(message
, CFSTR("MESSAGE_SN"));
317 for (i
= 0; i
< n
; i
++) {
318 SCNetworkInterfaceRef interface
;
322 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, i
);
323 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
324 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\r\t%@"), name
);
325 CFArrayAppendValue(message
, str
);
328 CFArrayAppendValue(message
, CFSTR("MESSAGE_EN"));
329 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
334 CFDictionaryAddValue(dict
, kCFUserNotificationDefaultButtonTitleKey
, CFSTR("OPEN_NP"));
335 CFDictionaryAddValue(dict
, kCFUserNotificationAlternateButtonTitleKey
, CFSTR("CANCEL"));
337 // create and post notification
338 myInstance
->userNotification
= CFUserNotificationCreate(NULL
,
340 kCFUserNotificationNoteAlertLevel
,
343 if (myInstance
->userNotification
== NULL
) {
344 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: CFUserNotificationCreate() failed, %d"), error
);
348 // establish callback
349 myInstance
->userRls
= CFUserNotificationCreateRunLoopSource(NULL
,
350 myInstance
->userNotification
,
353 if (myInstance
->userRls
== NULL
) {
354 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("SCMonitor: CFUserNotificationCreateRunLoopSource() failed"));
355 CFRelease(myInstance
->userNotification
);
356 myInstance
->userNotification
= NULL
;
359 CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance
->userRls
, kCFRunLoopDefaultMode
);
361 // add instance for notification
362 if (notify_to_instance
== NULL
) {
363 notify_to_instance
= CFDictionaryCreateMutable(NULL
,
365 &kCFTypeDictionaryKeyCallBacks
,
366 NULL
); // no retain/release/... for values
368 CFDictionarySetValue(notify_to_instance
, myInstance
->userNotification
, myInstance
);
372 if (dict
!= NULL
) CFRelease(dict
);
378 notify_configure(MyType
*myInstance
)
380 AuthorizationRef authorization
= NULL
;
382 CFIndex n
= CFArrayGetCount(myInstance
->interfaces_configure
);
384 SCPreferencesRef prefs
= NULL
;
385 SCNetworkSetRef set
= NULL
;
387 if (geteuid() == 0) {
388 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
390 AuthorizationFlags flags
= kAuthorizationFlagDefaults
;
393 status
= AuthorizationCreate(NULL
,
394 kAuthorizationEmptyEnvironment
,
397 if (status
!= errAuthorizationSuccess
) {
398 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
399 CFSTR("AuthorizationCreate() failed: status = %d"),
404 prefs
= SCPreferencesCreateWithAuthorization(NULL
, CFSTR("SCMonitor"), NULL
, authorization
);
407 set
= SCNetworkSetCopyCurrent(prefs
);
409 set
= SCNetworkSetCreate(prefs
);
415 for (i
= 0; i
< n
; i
++) {
416 SCNetworkInterfaceRef interface
;
418 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_configure
, i
);
419 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
423 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
424 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_NOTICE
, CFSTR("add service for %@"), name
);
428 ok
= SCPreferencesCommitChanges(prefs
);
430 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
431 CFSTR("SCPreferencesCommitChanges() failed: %s"),
432 SCErrorString(SCError()));
436 ok
= SCPreferencesApplyChanges(prefs
);
438 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
439 CFSTR("SCPreferencesApplyChanges() failed: %s"),
440 SCErrorString(SCError()));
456 if (authorization
!= NULL
) {
457 AuthorizationFree(authorization
, kAuthorizationFlagDefaults
);
458 // AuthorizationFree(authorization, kAuthorizationFlagDestroyRights);
459 authorization
= NULL
;
462 CFRelease(myInstance
->interfaces_configure
);
463 myInstance
->interfaces_configure
= NULL
;
473 updateInterfaceList(MyType
*myInstance
)
475 Boolean changed
= FALSE
;
477 CFArrayRef interfaces
;
478 CFMutableSetRef interfaces_old
= NULL
;
480 SCPreferencesRef prefs
;
481 SCNetworkSetRef set
= NULL
;
483 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
488 set
= SCNetworkSetCopyCurrent(prefs
);
490 set
= SCNetworkSetCreate(prefs
);
496 interfaces_old
= CFSetCreateMutableCopy(NULL
, 0, myInstance
->interfaces_known
);
498 interfaces
= SCNetworkInterfaceCopyAll();
499 if (interfaces
!= NULL
) {
500 n
= CFArrayGetCount(interfaces
);
501 for (i
= 0; i
< n
; i
++) {
502 SCNetworkInterfaceRef interface
;
505 interface
= CFArrayGetValueAtIndex(interfaces
, i
);
507 if (_SCNetworkInterfaceIsBuiltin(interface
)) {
508 // skip built-in interfaces
512 // track new vs. old (removed) interfaces
513 CFSetRemoveValue(interfaces_old
, interface
);
514 if (CFSetContainsValue(myInstance
->interfaces_known
, interface
)) {
515 // if we already know about this interface
518 CFSetAddValue(myInstance
->interfaces_known
, interface
);
521 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
525 // this is a *new* interface
527 action
= _SCNetworkInterfaceGetConfigurationAction(interface
);
528 if (action
== NULL
) {
529 // if no per-interface action, use [global] default
530 action
= myInstance
->configuration_action
;
532 if ((action
== NULL
) ||
533 (!CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueNone
) &&
534 !CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigure
))) {
535 action
= kSCNetworkInterfaceConfigurationActionValuePrompt
;
538 if (CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueNone
)) {
540 } else if (CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigure
)) {
541 // configure automatically (without user intervention)
542 if (myInstance
->interfaces_configure
== NULL
) {
543 myInstance
->interfaces_configure
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
545 CFArrayAppendValue(myInstance
->interfaces_configure
, interface
);
548 if (myInstance
->interfaces_prompt
== NULL
) {
549 myInstance
->interfaces_prompt
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
551 CFArrayAppendValue(myInstance
->interfaces_prompt
, interface
);
556 CFRelease(interfaces
);
559 // remove any posted notifications for network interfaces that have been removed
560 n
= CFSetGetCount(interfaces_old
);
562 const void * paths_q
[32];
563 const void ** paths
= paths_q
;
565 if (n
> (CFIndex
)(sizeof(paths_q
) / sizeof(CFTypeRef
)))
566 paths
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
567 CFSetGetValues(interfaces_old
, paths
);
568 for (i
= 0; i
< n
; i
++) {
569 if (myInstance
->interfaces_prompt
!= NULL
) {
572 j
= CFArrayGetCount(myInstance
->interfaces_prompt
);
574 SCNetworkInterfaceRef interface
;
577 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, j
);
578 if (CFEqual(interface
, paths
[i
])) {
579 // if we have previously posted a notification
580 // for this no-longer-present interface
581 CFArrayRemoveValueAtIndex(myInstance
->interfaces_prompt
, j
);
587 CFSetRemoveValue(myInstance
->interfaces_known
, paths
[i
]);
589 if (paths
!= paths_q
) CFAllocatorDeallocate(NULL
, paths
);
595 if (myInstance
->interfaces_configure
!= NULL
) {
596 // if we have network services to configure automatically
597 notify_configure(myInstance
);
600 if (myInstance
->interfaces_prompt
!= NULL
) {
601 // if we have network services that require user intervention
602 // post notification for new interfaces
603 notify_add(myInstance
);
607 if (interfaces_old
!= NULL
) CFRelease(interfaces_old
);
608 if (set
!= NULL
) CFRelease(set
);
615 #pragma mark Watch for new [network] interfaces
619 update_lan(SCDynamicStoreRef store
, CFArrayRef changes
, void * arg
)
621 MyType
*myInstance
= (MyType
*)arg
;
623 updateInterfaceList(myInstance
);
629 watcher_add_lan(MyType
*myInstance
)
631 SCDynamicStoreContext context
= { 0, (void *)myInstance
, NULL
, NULL
, NULL
};
632 CFDictionaryRef dict
;
635 SCDynamicStoreRef store
;
637 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCMonitor"), update_lan
, &context
);
639 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
640 CFSTR("SCMonitor: SCDynamicStoreCreate() failed: %s"),
641 SCErrorString(SCError()));
645 key
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
647 // watch for changes to the list of network interfaces
648 keys
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
649 SCDynamicStoreSetNotificationKeys(store
, NULL
, keys
);
651 myInstance
->monitorRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
652 CFRunLoopAddSource(CFRunLoopGetCurrent(),
653 myInstance
->monitorRls
,
654 kCFRunLoopDefaultMode
);
656 // initialize the list of known interfaces
657 myInstance
->interfaces_known
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
658 dict
= SCDynamicStoreCopyValue(store
, key
);
660 if (isA_CFDictionary(dict
)) {
662 CFArrayRef interfaces
;
665 interfaces
= CFDictionaryGetValue(dict
, kSCPropNetInterfaces
);
666 n
= isA_CFArray(interfaces
) ? CFArrayGetCount(interfaces
) : 0;
667 for (i
= 0; i
< n
; i
++) {
670 bsdName
= CFArrayGetValueAtIndex(interfaces
, i
);
671 if (isA_CFString(bsdName
)) {
672 SCNetworkInterfaceRef interface
;
674 interface
= _SCNetworkInterfaceCreateWithBSDName(NULL
, bsdName
, kIncludeNoVirtualInterfaces
);
675 if (interface
!= NULL
) {
676 CFSetAddValue(myInstance
->interfaces_known
, interface
);
677 CFRelease(interface
);
693 watcher_remove_lan(MyType
*myInstance
)
695 if (myInstance
->monitorRls
!= NULL
) {
696 CFRunLoopSourceInvalidate(myInstance
->monitorRls
);
697 CFRelease(myInstance
->monitorRls
);
698 myInstance
->monitorRls
= NULL
;
701 if (myInstance
->interfaces_known
!= NULL
) {
702 CFRelease(myInstance
->interfaces_known
);
703 myInstance
->interfaces_known
= NULL
;
714 io_registry_entry_t interface
;
716 io_object_t notification
;
721 add_node_watcher(MyType
*myInstance
, io_registry_entry_t node
, io_registry_entry_t interface
);
725 update_node(void *refCon
, io_service_t service
, natural_t messageType
, void *messageArgument
)
728 CFDataRef myData
= (CFDataRef
)refCon
;
732 myNode
= (MyNode
*)CFDataGetBytePtr(myData
);
733 myInstance
= myNode
->myInstance
;
735 switch (messageType
) {
736 case kIOMessageServicePropertyChange
: {
737 Boolean initializing
= FALSE
;
738 SCNetworkInterfaceRef interface
;
741 if (myNode
->interface
== MACH_PORT_NULL
) {
742 // if we are not watching the "Initializing" property
746 val
= IORegistryEntryCreateCFProperty(service
, CFSTR("Initializing"), NULL
, 0);
748 initializing
= (isA_CFBoolean(val
) && CFBooleanGetValue(val
));
751 // if initialization not complete, keep watching
757 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode
->interface
);
758 if (interface
!= NULL
) {
759 CFRelease(interface
);
761 // watch interface (to see when/if it's removed)
762 add_node_watcher(myInstance
, myNode
->interface
, MACH_PORT_NULL
);
767 case kIOMessageServiceIsTerminated
:
774 // remove no-longer-needed notification
775 if (myNode
->interface
!= MACH_PORT_NULL
) {
776 IOObjectRelease(myNode
->interface
);
777 myNode
->interface
= MACH_PORT_NULL
;
779 IOObjectRelease(myNode
->notification
);
780 i
= CFArrayGetFirstIndexOfValue(myInstance
->notifyNodes
,
781 CFRangeMake(0, CFArrayGetCount(myInstance
->notifyNodes
)),
783 if (i
!= kCFNotFound
) {
784 CFArrayRemoveValueAtIndex(myInstance
->notifyNodes
, i
);
785 if (CFArrayGetCount(myInstance
->notifyNodes
) == 0) {
786 CFRelease(myInstance
->notifyNodes
);
787 myInstance
->notifyNodes
= NULL
;
791 updateInterfaceList(myInstance
);
797 add_node_watcher(MyType
*myInstance
, io_registry_entry_t node
, io_registry_entry_t interface
)
800 CFMutableDataRef myData
;
803 // wait for initialization to complete
804 myData
= CFDataCreateMutable(NULL
, sizeof(MyNode
));
805 CFDataSetLength(myData
, sizeof(MyNode
));
806 myNode
= (MyNode
*)CFDataGetBytePtr(myData
);
807 bzero(myNode
, sizeof(MyNode
));
808 if (interface
!= MACH_PORT_NULL
) {
809 IOObjectRetain(interface
);
811 myNode
->interface
= interface
;
812 myNode
->myInstance
= myInstance
;
813 myNode
->notification
= MACH_PORT_NULL
;
815 kr
= IOServiceAddInterestNotification(myInstance
->notifyPort
, // IONotificationPortRef
816 node
, // io_service_t
817 kIOGeneralInterest
, // interestType
818 update_node
, // IOServiceInterestCallback
819 (void *)myData
, // refCon
820 &myNode
->notification
); // notification
821 if (kr
== KERN_SUCCESS
) {
822 if (myInstance
->notifyNodes
== NULL
) {
823 myInstance
->notifyNodes
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
825 CFArrayAppendValue(myInstance
->notifyNodes
, myData
);
827 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
828 CFSTR("add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x"), kr
);
835 add_init_watcher(MyType
*myInstance
, io_registry_entry_t interface
)
838 io_registry_entry_t node
= interface
;
839 CFTypeRef val
= NULL
;
841 while (node
!= MACH_PORT_NULL
) {
842 io_registry_entry_t parent
;
844 val
= IORegistryEntryCreateCFProperty(node
, CFSTR("HiddenPort"), NULL
, 0);
851 val
= IORegistryEntryCreateCFProperty(node
, CFSTR("Initializing"), NULL
, 0);
856 parent
= MACH_PORT_NULL
;
857 kr
= IORegistryEntryGetParentEntry(node
, kIOServicePlane
, &parent
);
859 case kIOReturnSuccess
: // if we have a parent node
860 case kIOReturnNoDevice
: // if we have hit the root node
863 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
, CFSTR("add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x"), kr
);
866 if (node
!= interface
) {
867 IOObjectRelease(node
);
873 if (isA_CFBoolean(val
) && CFBooleanGetValue(val
)) {
874 // watch the "Initializing" node
875 add_node_watcher(myInstance
, node
, interface
);
881 if ((node
!= MACH_PORT_NULL
) && (node
!= interface
)) {
882 IOObjectRelease(node
);
890 update_serial(void *refcon
, io_iterator_t iter
)
892 MyType
*myInstance
= (MyType
*)refcon
;
893 io_registry_entry_t obj
;
895 while ((obj
= IOIteratorNext(iter
)) != MACH_PORT_NULL
) {
896 SCNetworkInterfaceRef interface
;
898 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj
);
899 if (interface
!= NULL
) {
900 CFRelease(interface
);
902 // watch interface (to see when/if it's removed)
903 add_node_watcher(myInstance
, obj
, MACH_PORT_NULL
);
905 // check interface, watch if initializing
906 add_init_watcher(myInstance
, obj
);
909 IOObjectRelease(obj
);
912 updateInterfaceList(myInstance
);
918 watcher_add_serial(MyType
*myInstance
)
922 myInstance
->notifyPort
= IONotificationPortCreate(kIOMasterPortDefault
);
923 if (myInstance
->notifyPort
== NULL
) {
924 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
925 CFSTR("SCMonitor: IONotificationPortCreate failed"));
929 // watch for the introduction of new network serial devices
930 kr
= IOServiceAddMatchingNotification(myInstance
->notifyPort
,
931 kIOFirstMatchNotification
,
932 IOServiceMatching("IOSerialBSDClient"),
934 (void *)myInstance
, // refCon
935 &myInstance
->notifyIterator
); // notification
936 if (kr
!= KERN_SUCCESS
) {
937 SCLOG(NULL
, myInstance
->log_msg
, ASL_LEVEL_ERR
,
938 CFSTR("SCMonitor : IOServiceAddMatchingNotification returned 0x%x"),
943 myInstance
->notifyNodes
= NULL
;
945 // Get the current list of matches and arm the notification for
946 // future interface arrivals.
947 update_serial((void *)myInstance
, myInstance
->notifyIterator
);
950 CFRunLoopAddSource(CFRunLoopGetCurrent(),
951 IONotificationPortGetRunLoopSource(myInstance
->notifyPort
),
952 kCFRunLoopDefaultMode
);
958 watcher_remove_serial(MyType
*myInstance
)
960 if (myInstance
->notifyNodes
!= NULL
) {
962 CFIndex n
= CFArrayGetCount(myInstance
->notifyNodes
);
964 for (i
= 0; i
< n
; i
++) {
968 myData
= CFArrayGetValueAtIndex(myInstance
->notifyNodes
, i
);
969 myNode
= (MyNode
*)CFDataGetBytePtr(myData
);
970 if (myNode
->interface
!= MACH_PORT_NULL
) {
971 IOObjectRelease(myNode
->interface
);
973 IOObjectRelease(myNode
->notification
);
976 CFRelease(myInstance
->notifyNodes
);
977 myInstance
->notifyNodes
= NULL
;
980 if (myInstance
->notifyIterator
!= MACH_PORT_NULL
) {
981 IOObjectRelease(myInstance
->notifyIterator
);
982 myInstance
->notifyIterator
= MACH_PORT_NULL
;
985 if (myInstance
->notifyPort
!= MACH_PORT_NULL
) {
986 IONotificationPortDestroy(myInstance
->notifyPort
);
987 myInstance
->notifyPort
= NULL
;
998 watcher_add(MyType
*myInstance
)
1002 if (myInstance
->log_msg
== NULL
) {
1003 myInstance
->log_msg
= asl_new(ASL_TYPE_MSG
);
1004 asl_set(myInstance
->log_msg
, ASL_KEY_FACILITY
, MY_BUNDLE_ID
);
1007 bundle
= CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID
));
1008 if (bundle
!= NULL
) {
1010 CFDictionaryRef info
;
1012 info
= CFBundleGetInfoDictionary(bundle
);
1013 action
= CFDictionaryGetValue(info
, kSCNetworkInterfaceConfigurationActionKey
);
1014 action
= isA_CFString(action
);
1016 if (action
!= NULL
) {
1017 myInstance
->configuration_action
= action
;
1019 CFBooleanRef user_intervention
;
1021 user_intervention
= CFDictionaryGetValue(info
, CFSTR("User Intervention"));
1022 if (isA_CFBoolean(user_intervention
) && !CFBooleanGetValue(user_intervention
)) {
1023 myInstance
->configuration_action
= kSCNetworkInterfaceConfigurationActionValueConfigure
;
1028 watcher_add_lan(myInstance
);
1029 watcher_add_serial(myInstance
);
1035 watcher_remove(MyType
*myInstance
)
1037 watcher_remove_lan(myInstance
);
1038 watcher_remove_serial(myInstance
);
1040 asl_free(myInstance
->log_msg
);
1041 myInstance
->log_msg
= NULL
;
1047 #pragma mark UserEventAgent stubs
1051 myQueryInterface(void *myInstance
, REFIID iid
, LPVOID
*ppv
)
1053 CFUUIDRef interfaceID
= CFUUIDCreateFromUUIDBytes(NULL
, iid
);
1055 // Test the requested ID against the valid interfaces.
1056 if (CFEqual(interfaceID
, kUserEventAgentInterfaceID
)) {
1057 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
1059 CFRelease(interfaceID
);
1063 if (CFEqual(interfaceID
, IUnknownUUID
)) {
1064 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
1066 CFRelease(interfaceID
);
1070 // Requested interface unknown, bail with error.
1072 CFRelease(interfaceID
);
1073 return E_NOINTERFACE
;
1078 myAddRef(void *myInstance
)
1080 ((MyType
*) myInstance
)->_refCount
++;
1081 return ((MyType
*) myInstance
)->_refCount
;
1086 myRelease(void *myInstance
)
1088 ((MyType
*) myInstance
)->_refCount
--;
1089 if (((MyType
*) myInstance
)->_refCount
== 0) {
1090 CFUUIDRef factoryID
= ((MyType
*) myInstance
)->_factoryID
;
1092 if (factoryID
!= NULL
) {
1093 CFPlugInRemoveInstanceForFactory(factoryID
);
1094 CFRelease(factoryID
);
1096 watcher_remove((MyType
*)myInstance
);
1097 notify_remove((MyType
*)myInstance
, TRUE
);
1103 return ((MyType
*) myInstance
)->_refCount
;
1108 myInstall(void *myInstance
)
1110 watcher_add((MyType
*)myInstance
);
1115 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl
= {
1116 NULL
, // Required padding for COM
1117 myQueryInterface
, // These three are the required COM functions
1120 myInstall
// Interface implementation
1125 UserEventAgentFactory(CFAllocatorRef allocator
, CFUUIDRef typeID
)
1127 MyType
*newOne
= NULL
;
1129 if (CFEqual(typeID
, kUserEventAgentTypeID
)) {
1130 newOne
= (MyType
*)malloc(sizeof(MyType
));
1131 bzero(newOne
, sizeof(*newOne
));
1132 newOne
->_UserEventAgentInterface
= &UserEventAgentInterfaceFtbl
;
1133 newOne
->_factoryID
= (CFUUIDRef
)CFRetain(kUserEventAgentFactoryID
);
1134 CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID
);
1135 newOne
->_refCount
= 1;
1144 main(int argc
, char **argv
)
1146 MyType
*newOne
= (MyType
*)malloc(sizeof(MyType
));
1149 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
1151 bzero(newOne
, sizeof(*newOne
));