2 * Copyright (c) 2007-2018, 2020 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>
34 #include <CoreFoundation/CoreFoundation.h>
36 #define SC_LOG_HANDLE __log_SCMonitor
37 #define SC_LOG_HANDLE_TYPE static
38 #include <SystemConfiguration/SystemConfiguration.h>
39 #include <SystemConfiguration/SCPrivate.h>
41 #include <IOKit/IOKitLib.h>
42 #include <IOKit/IOKitKeysPrivate.h>
43 #include <IOKit/IOMessage.h>
44 #include <CoreServices/CoreServices.h>
45 #include "UserEventAgentInterface.h"
47 #define MY_BUNDLE_ID "com.apple.SystemConfiguration.SCMonitor"
48 #define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns"
50 #define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane"
51 #define NETWORK_PREF_CMD "New Interface"
54 * The following keys/values control the actions taken when a new interface
55 * has been detected and whether we interact with the user.
57 * The keys/values can be provided globally (in SCMonitor.plugin's Info.plist
58 * file) or per-inteface in the IORegistry.
60 * For the "New Interface Detected Action" key we define the following [CFString]
63 * "None" No action, ignore this interface.
64 * "Prompt" Post a "new interface detected" notification to the user.
65 * "Configure" Automatically configure this interface without any
68 * Note: automatic configuration may not be possible if the logged in user
69 * is NOT "root" (eUID==0) or if the authorization right that governs
70 * SCHelper write operations (kSCPreferencesAuthorizationRight_write)
71 * is not currently available.
73 * An [older] "User Intervention" key is also supported. That CFBoolean
74 * key, if present and TRUE, implies "Configure" configuration of the
75 * interface without intervention.
79 UserEventAgentInterfaceStruct
*_UserEventAgentInterface
;
85 CFStringRef configuration_action
;
87 CFRunLoopSourceRef monitorRls
;
89 IONotificationPortRef notifyPort
;
90 io_iterator_t notifyIterator
;
91 CFMutableArrayRef notifyNodes
;
93 // interfaces that we already know about
94 CFMutableSetRef interfaces_known
;
96 // interfaces that should be auto-configured (no user notification)
97 CFMutableArrayRef interfaces_configure
;
99 // interfaces that require user notification
100 CFMutableArrayRef interfaces_prompt
;
102 CFUserNotificationRef userNotification
;
103 CFRunLoopSourceRef userRls
;
105 AuthorizationRef authorization
;
108 static CFMutableDictionaryRef notify_to_instance
= NULL
;
119 __log_SCMonitor(void)
121 static os_log_t log
= NULL
;
124 log
= os_log_create("com.apple.SystemConfiguration", "SCMonitor");
132 #pragma mark Authorization
135 static AuthorizationRef
136 getAuthorization(MyType
*myInstance
)
138 if (myInstance
->authorization
== NULL
) {
139 AuthorizationFlags flags
= kAuthorizationFlagDefaults
;
142 status
= AuthorizationCreate(NULL
,
143 kAuthorizationEmptyEnvironment
,
145 &myInstance
->authorization
);
146 if (status
!= errAuthorizationSuccess
) {
147 SC_log(LOG_ERR
, "AuthorizationCreate() failed: status = %d", (int)status
);
151 return myInstance
->authorization
;
156 hasAuthorization(MyType
*myInstance
)
158 AuthorizationRef authorization
;
159 Boolean isAdmin
= FALSE
;
161 authorization
= getAuthorization(myInstance
);
162 if (authorization
!= NULL
) {
163 AuthorizationFlags flags
= kAuthorizationFlagDefaults
;
164 AuthorizationItem items
[1];
165 AuthorizationRights rights
;
168 items
[0].name
= kSCPreferencesAuthorizationRight_write
;
169 items
[0].value
= NULL
;
170 items
[0].valueLength
= 0;
173 rights
.count
= sizeof(items
) / sizeof(items
[0]);
174 rights
.items
= items
;
176 status
= AuthorizationCopyRights(authorization
,
178 kAuthorizationEmptyEnvironment
,
181 isAdmin
= (status
== errAuthorizationSuccess
);
189 freeAuthorization(MyType
*myInstance
)
191 if (myInstance
->authorization
!= NULL
) {
192 AuthorizationFree(myInstance
->authorization
, kAuthorizationFlagDefaults
);
193 // AuthorizationFree(myInstance->authorization, kAuthorizationFlagDestroyRights);
194 myInstance
->authorization
= NULL
;
202 #pragma mark New interface notification / configuration
206 open_NetworkPrefPane(MyType
*myInstance
)
208 #pragma unused(myInstance)
209 AEDesc aeDesc
= { typeNull
, NULL
};
210 CFArrayRef prefArray
;
212 LSLaunchURLSpec prefSpec
;
215 prefURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
,
216 CFSTR(NETWORK_PREF_APP
),
217 kCFURLPOSIXPathStyle
,
219 prefArray
= CFArrayCreate(NULL
, (const void **)&prefURL
, 1, &kCFTypeArrayCallBacks
);
222 status
= AECreateDesc('ptru',
223 (const void *)NETWORK_PREF_CMD
,
224 strlen(NETWORK_PREF_CMD
),
226 if (status
!= noErr
) {
227 SC_log(LOG_ERR
, "AECreateDesc() failed: %d", (int)status
);
230 prefSpec
.appURL
= NULL
;
231 prefSpec
.itemURLs
= prefArray
;
232 prefSpec
.passThruParams
= &aeDesc
;
233 prefSpec
.launchFlags
= kLSLaunchAsync
| kLSLaunchDontAddToRecents
;
234 prefSpec
.asyncRefCon
= NULL
;
236 status
= LSOpenFromURLSpec(&prefSpec
, NULL
);
237 if (status
!= noErr
) {
238 SC_log(LOG_ERR
, "LSOpenFromURLSpec() failed: %d", (int)status
);
241 CFRelease(prefArray
);
242 if (aeDesc
.descriptorType
!= typeNull
) AEDisposeDesc(&aeDesc
);
248 notify_remove(MyType
*myInstance
, Boolean cancel
)
250 if (myInstance
->interfaces_configure
!= NULL
) {
251 CFRelease(myInstance
->interfaces_configure
);
252 myInstance
->interfaces_configure
= NULL
;
255 if (myInstance
->interfaces_prompt
!= NULL
) {
256 CFRelease(myInstance
->interfaces_prompt
);
257 myInstance
->interfaces_prompt
= NULL
;
260 if (myInstance
->userRls
!= NULL
) {
261 CFRunLoopSourceInvalidate(myInstance
->userRls
);
262 CFRelease(myInstance
->userRls
);
263 myInstance
->userRls
= NULL
;
266 if (myInstance
->userNotification
!= NULL
) {
270 status
= CFUserNotificationCancel(myInstance
->userNotification
);
273 "CFUserNotificationCancel() failed, status=%d",
277 CFRelease(myInstance
->userNotification
);
278 myInstance
->userNotification
= NULL
;
286 notify_reply(CFUserNotificationRef userNotification
, CFOptionFlags response_flags
)
288 MyType
*myInstance
= NULL
;
290 // get instance for notification
291 if (notify_to_instance
!= NULL
) {
292 myInstance
= (MyType
*)CFDictionaryGetValue(notify_to_instance
, userNotification
);
293 if (myInstance
!= NULL
) {
294 CFDictionaryRemoveValue(notify_to_instance
, userNotification
);
295 if (CFDictionaryGetCount(notify_to_instance
) == 0) {
296 CFRelease(notify_to_instance
);
297 notify_to_instance
= NULL
;
301 if (myInstance
== NULL
) {
302 SC_log(LOG_ERR
, "can't find user notification");
307 switch (response_flags
& 0x3) {
308 case kCFUserNotificationDefaultResponse
:
309 // user asked to configure interface
310 open_NetworkPrefPane(myInstance
);
317 notify_remove(myInstance
, FALSE
);
323 notify_add(MyType
*myInstance
)
326 CFMutableDictionaryRef dict
= NULL
;
329 CFIndex n
= CFArrayGetCount(myInstance
->interfaces_prompt
);
332 if (myInstance
->userNotification
!= NULL
) {
333 CFMutableArrayRef save
= NULL
;
336 CFRetain(myInstance
->interfaces_prompt
);
337 save
= myInstance
->interfaces_prompt
;
339 notify_remove(myInstance
, TRUE
);
340 myInstance
->interfaces_prompt
= save
;
346 dict
= CFDictionaryCreateMutable(NULL
,
348 &kCFTypeDictionaryKeyCallBacks
,
349 &kCFTypeDictionaryValueCallBacks
);
351 // set localization URL
352 bundle
= CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID
));
353 if (bundle
!= NULL
) {
354 url
= CFBundleCopyBundleURL(bundle
);
358 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
359 (const UInt8
*)"/System/Library/UserEventPlugins/SCMonitor.plugin",
360 strlen("/System/Library/UserEventPlugins/SCMonitor.plugin"),
362 if (bundle
== NULL
) {
363 bundle
= CFBundleCreate(NULL
, url
);
369 CFDictionarySetValue(dict
, kCFUserNotificationLocalizationURLKey
, url
);
372 SC_log(LOG_ERR
, "can't find bundle");
377 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
378 (const UInt8
*)MY_ICON_PATH
,
379 strlen(MY_ICON_PATH
),
382 CFDictionarySetValue(dict
, kCFUserNotificationIconURLKey
, url
);
387 CFDictionarySetValue(dict
,
388 kCFUserNotificationAlertHeaderKey
,
389 (n
== 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N"));
394 SCNetworkInterfaceRef interface
;
398 #define MESSAGE_1 "The “%@” network interface has not been set up. To set up this interface, use Network Preferences."
400 format
= CFBundleCopyLocalizedString(bundle
,
404 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, 0);
405 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
406 message
= CFStringCreateWithFormat(NULL
, NULL
, format
, name
);
407 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
411 CFMutableArrayRef message
;
413 message
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
414 CFArrayAppendValue(message
, CFSTR("MESSAGE_SN"));
415 for (i
= 0; i
< n
; i
++) {
416 SCNetworkInterfaceRef interface
;
420 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, i
);
421 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
422 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\n\t%@"), name
);
423 CFArrayAppendValue(message
, str
);
426 CFArrayAppendValue(message
, CFSTR("MESSAGE_EN"));
427 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
432 CFDictionaryAddValue(dict
, kCFUserNotificationDefaultButtonTitleKey
, CFSTR("OPEN_NP"));
433 CFDictionaryAddValue(dict
, kCFUserNotificationAlternateButtonTitleKey
, CFSTR("CANCEL"));
435 // create and post notification
436 myInstance
->userNotification
= CFUserNotificationCreate(NULL
,
438 kCFUserNotificationNoteAlertLevel
,
441 if (myInstance
->userNotification
== NULL
) {
442 SC_log(LOG_ERR
, "CFUserNotificationCreate() failed: %d", (int)error
);
446 // establish callback
447 myInstance
->userRls
= CFUserNotificationCreateRunLoopSource(NULL
,
448 myInstance
->userNotification
,
451 if (myInstance
->userRls
== NULL
) {
452 SC_log(LOG_ERR
, "CFUserNotificationCreateRunLoopSource() failed");
453 CFRelease(myInstance
->userNotification
);
454 myInstance
->userNotification
= NULL
;
457 CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance
->userRls
, kCFRunLoopDefaultMode
);
459 // add instance for notification
460 if (notify_to_instance
== NULL
) {
461 notify_to_instance
= CFDictionaryCreateMutable(NULL
,
463 &kCFTypeDictionaryKeyCallBacks
,
464 NULL
); // no retain/release/... for values
466 CFDictionarySetValue(notify_to_instance
, myInstance
->userNotification
, myInstance
);
470 if (dict
!= NULL
) CFRelease(dict
);
476 notify_configure(MyType
*myInstance
)
480 CFIndex n
= CFArrayGetCount(myInstance
->interfaces_configure
);
482 SCPreferencesRef prefs
= NULL
;
483 SCNetworkSetRef set
= NULL
;
485 if (geteuid() == 0) {
486 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
488 AuthorizationRef authorization
;
490 authorization
= getAuthorization(myInstance
);
491 if (authorization
== NULL
) {
495 prefs
= SCPreferencesCreateWithAuthorization(NULL
, CFSTR("SCMonitor"), NULL
, authorization
);
498 locked
= SCPreferencesLock(prefs
, TRUE
);
501 "SCPreferencesLock() failed: %s",
502 SCErrorString(SCError()));
506 set
= SCNetworkSetCopyCurrent(prefs
);
508 // if no "current" set, create new/default ("Automatic") set
509 set
= _SCNetworkSetCreateDefault(prefs
);
513 SC_log(LOG_DEBUG
, "added new \"default\" set");
516 for (i
= 0; i
< n
; i
++) {
517 SCNetworkInterfaceRef interface
;
519 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_configure
, i
);
520 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
524 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
525 SC_log(LOG_NOTICE
, "add/update service for %@", name
);
529 ok
= SCPreferencesCommitChanges(prefs
);
532 "SCPreferencesCommitChanges() failed: %s",
533 SCErrorString(SCError()));
537 ok
= SCPreferencesApplyChanges(prefs
);
540 "SCPreferencesApplyChanges() failed: %s",
541 SCErrorString(SCError()));
553 SCPreferencesUnlock(prefs
);
559 CFRelease(myInstance
->interfaces_configure
);
560 myInstance
->interfaces_configure
= NULL
;
571 CFArrayRef console_sessions
;
573 io_registry_entry_t root
;
574 uid_t uid
= geteuid();
576 root
= IORegistryGetRootEntry(kIOMasterPortDefault
);
577 console_sessions
= IORegistryEntryCreateCFProperty(root
,
578 CFSTR(kIOConsoleUsersKey
),
581 if (console_sessions
!= NULL
) {
584 n
= isA_CFArray(console_sessions
) ? CFArrayGetCount(console_sessions
) : 0;
585 for (CFIndex i
= 0; i
< n
; i
++) {
587 CFDictionaryRef session
;
591 session
= CFArrayGetValueAtIndex(console_sessions
, i
);
592 if (!isA_CFDictionary(session
)) {
597 if (!CFDictionaryGetValueIfPresent(session
,
598 CFSTR(kIOConsoleSessionUIDKey
),
599 (const void **)&val
) ||
600 !isA_CFNumber(val
) ||
601 !CFNumberGetValue(val
, kCFNumberSInt64Type
, (void *)&sessionUID
) ||
602 (uid
!= sessionUID
)) {
607 if (CFDictionaryGetValueIfPresent(session
,
608 CFSTR(kIOConsoleSessionOnConsoleKey
),
609 (const void **)&bVal
) &&
610 isA_CFBoolean(bVal
) &&
611 CFBooleanGetValue(bVal
)) {
612 // if "on console" session
619 CFRelease(console_sessions
);
621 IOObjectRelease(root
);
630 // configure ONLY IF authorized
631 #define kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized CFSTR("Configure-Authorized")
635 updateInterfaceList(MyType
*myInstance
)
637 Boolean changed
= FALSE
;
639 CFArrayRef interfaces
;
640 CFMutableSetRef interfaces_old
= NULL
;
642 SCPreferencesRef prefs
;
643 SCNetworkSetRef set
= NULL
;
649 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
654 set
= SCNetworkSetCopyCurrent(prefs
);
656 // if no "current" set, create new/default ("Automatic") set
657 set
= _SCNetworkSetCreateDefault(prefs
);
663 interfaces_old
= CFSetCreateMutableCopy(NULL
, 0, myInstance
->interfaces_known
);
665 interfaces
= _SCNetworkInterfaceCopyAllWithPreferences(prefs
);
666 if (interfaces
!= NULL
) {
667 n
= CFArrayGetCount(interfaces
);
668 for (i
= 0; i
< n
; i
++) {
669 SCNetworkInterfaceRef interface
;
672 interface
= CFArrayGetValueAtIndex(interfaces
, i
);
674 // track new vs. old (removed) interfaces
675 CFSetRemoveValue(interfaces_old
, interface
);
676 if (CFSetContainsValue(myInstance
->interfaces_known
, interface
)) {
677 // if we already know about this interface
680 CFSetAddValue(myInstance
->interfaces_known
, interface
);
683 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
687 // this is a *new* interface
689 action
= _SCNetworkInterfaceGetConfigurationAction(interface
);
690 if (action
== NULL
) {
691 // if no per-interface action, use [global] default
692 action
= myInstance
->configuration_action
;
694 if ((action
== NULL
) ||
695 (!CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueNone
) &&
696 !CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigure
))) {
697 if (_SCNetworkInterfaceIsBuiltin(interface
)) {
698 // if built-in interface
699 action
= kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized
;
701 action
= kSCNetworkInterfaceConfigurationActionValuePrompt
;
705 if (CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueNone
)) {
707 } else if (CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigure
)) {
708 // configure automatically (without user intervention)
709 if (myInstance
->interfaces_configure
== NULL
) {
710 myInstance
->interfaces_configure
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
712 CFArrayAppendValue(myInstance
->interfaces_configure
, interface
);
713 } else if (hasAuthorization(myInstance
)) {
714 // if we already have the "admin" (kSCPreferencesAuthorizationRight_write)
715 // right, configure automatically (without user intervention)
716 if (myInstance
->interfaces_configure
== NULL
) {
717 myInstance
->interfaces_configure
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
719 CFArrayAppendValue(myInstance
->interfaces_configure
, interface
);
720 } else if (!CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized
)) {
722 if (myInstance
->interfaces_prompt
== NULL
) {
723 myInstance
->interfaces_prompt
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
725 CFArrayAppendValue(myInstance
->interfaces_prompt
, interface
);
730 CFRelease(interfaces
);
733 // remove any posted notifications for network interfaces that have been removed
734 n
= CFSetGetCount(interfaces_old
);
736 const void * paths_q
[32];
737 const void ** paths
= paths_q
;
739 if (n
> (CFIndex
)(sizeof(paths_q
) / sizeof(CFTypeRef
)))
740 paths
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
741 CFSetGetValues(interfaces_old
, paths
);
742 for (i
= 0; i
< n
; i
++) {
743 if (myInstance
->interfaces_prompt
!= NULL
) {
746 j
= CFArrayGetCount(myInstance
->interfaces_prompt
);
748 SCNetworkInterfaceRef interface
;
751 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, j
);
752 if (CFEqual(interface
, paths
[i
])) {
753 // if we have previously posted a notification
754 // for this no-longer-present interface
755 CFArrayRemoveValueAtIndex(myInstance
->interfaces_prompt
, j
);
761 CFSetRemoveValue(myInstance
->interfaces_known
, paths
[i
]);
763 if (paths
!= paths_q
) CFAllocatorDeallocate(NULL
, paths
);
769 if (myInstance
->interfaces_configure
!= NULL
) {
770 // if we have network services to configure automatically
771 notify_configure(myInstance
);
774 if (myInstance
->interfaces_prompt
!= NULL
) {
775 // if we have network services that require user intervention
776 // post notification for new interfaces
777 notify_add(myInstance
);
781 if (interfaces_old
!= NULL
) CFRelease(interfaces_old
);
782 if (set
!= NULL
) CFRelease(set
);
789 #pragma mark Watch for new [network] interfaces
793 update_lan(SCDynamicStoreRef store
, CFArrayRef changes
, void * arg
)
795 #pragma unused(store)
796 #pragma unused(changes)
797 MyType
*myInstance
= (MyType
*)arg
;
799 updateInterfaceList(myInstance
);
805 watcher_add_lan(MyType
*myInstance
)
807 SCDynamicStoreContext context
= { 0, (void *)myInstance
, NULL
, NULL
, NULL
};
810 SCDynamicStoreRef store
;
812 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCMonitor"), update_lan
, &context
);
815 "SCDynamicStoreCreate() failed: %s",
816 SCErrorString(SCError()));
820 key
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
822 // watch for changes to the list of network interfaces
823 keys
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
824 SCDynamicStoreSetNotificationKeys(store
, keys
, NULL
);
826 myInstance
->monitorRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
827 CFRunLoopAddSource(CFRunLoopGetCurrent(),
828 myInstance
->monitorRls
,
829 kCFRunLoopDefaultMode
);
831 // check if we already have the "admin" (kSCPreferencesAuthorizationRight_write)
832 // right. If so, we can automatically configure (without user intervention) any
833 // "new" network interfaces that are present at login (e.g. a USB ethernet
834 // dongle that was plugged in before login).
835 if (!hasAuthorization(myInstance
)) {
836 CFDictionaryRef dict
;
838 // ... and if we don't have the right then we populate the list of
839 // known interfaces with those already named so that we avoid any
840 // login prompts (that the user might have no choice but to dismiss)
841 dict
= SCDynamicStoreCopyValue(store
, key
);
843 if (isA_CFDictionary(dict
)) {
845 CFArrayRef interfaces
;
848 interfaces
= CFDictionaryGetValue(dict
, kSCPropNetInterfaces
);
849 n
= isA_CFArray(interfaces
) ? CFArrayGetCount(interfaces
) : 0;
850 for (i
= 0; i
< n
; i
++) {
853 bsdName
= CFArrayGetValueAtIndex(interfaces
, i
);
854 if (isA_CFString(bsdName
)) {
855 SCNetworkInterfaceRef interface
;
857 interface
= _SCNetworkInterfaceCreateWithBSDName(NULL
, bsdName
, kIncludeNoVirtualInterfaces
);
858 if (interface
!= NULL
) {
859 CFSetAddValue(myInstance
->interfaces_known
, interface
);
860 CFRelease(interface
);
877 watcher_remove_lan(MyType
*myInstance
)
879 if (myInstance
->monitorRls
!= NULL
) {
880 CFRunLoopSourceInvalidate(myInstance
->monitorRls
);
881 CFRelease(myInstance
->monitorRls
);
882 myInstance
->monitorRls
= NULL
;
893 io_registry_entry_t interface
;
894 io_registry_entry_t interface_node
;
896 io_object_t notification
;
901 add_node_watcher(MyType
*myInstance
, io_registry_entry_t node
, io_registry_entry_t interface
);
905 update_node(void *refCon
, io_service_t service
, natural_t messageType
, void *messageArgument
)
907 #pragma unused(messageArgument)
909 CFDataRef myData
= (CFDataRef
)refCon
;
913 /* ALIGN: CF aligns to at least >8 bytes */
914 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
915 myInstance
= myNode
->myInstance
;
917 switch (messageType
) {
918 case kIOMessageServicePropertyChange
: {
919 Boolean initializing
= FALSE
;
920 SCNetworkInterfaceRef interface
;
923 if (myNode
->interface
== MACH_PORT_NULL
) {
924 // if we are not watching the "Initializing" property
928 val
= IORegistryEntryCreateCFProperty(service
, kSCNetworkInterfaceInitializingKey
, NULL
, 0);
930 initializing
= (isA_CFBoolean(val
) && CFBooleanGetValue(val
));
933 // if initialization not complete, keep watching
939 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode
->interface
);
940 if (interface
!= NULL
) {
941 CFRelease(interface
);
943 // watch interface (to see when/if it's removed)
944 add_node_watcher(myInstance
, myNode
->interface
, MACH_PORT_NULL
);
949 case kIOMessageServiceIsTerminated
:
956 // remove no-longer-needed notification
957 if (myNode
->interface
!= myNode
->interface_node
) {
958 IOObjectRelease(myNode
->interface_node
);
960 if (myNode
->interface
!= MACH_PORT_NULL
) {
961 IOObjectRelease(myNode
->interface
);
963 IOObjectRelease(myNode
->notification
);
964 i
= CFArrayGetFirstIndexOfValue(myInstance
->notifyNodes
,
965 CFRangeMake(0, CFArrayGetCount(myInstance
->notifyNodes
)),
967 if (i
!= kCFNotFound
) {
968 CFArrayRemoveValueAtIndex(myInstance
->notifyNodes
, i
);
969 if (CFArrayGetCount(myInstance
->notifyNodes
) == 0) {
970 CFRelease(myInstance
->notifyNodes
);
971 myInstance
->notifyNodes
= NULL
;
975 updateInterfaceList(myInstance
);
981 add_node_watcher(MyType
*myInstance
, io_registry_entry_t node
, io_registry_entry_t interface
)
984 CFMutableDataRef myData
;
987 // wait for initialization to complete
988 myData
= CFDataCreateMutable(NULL
, sizeof(MyNode
));
989 CFDataSetLength(myData
, sizeof(MyNode
));
991 /* ALIGN: CF aligns to at least >8 bytes */
992 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
994 memset(myNode
, 0, sizeof(MyNode
));
995 myNode
->interface
= interface
;
996 if (myNode
->interface
!= MACH_PORT_NULL
) {
997 IOObjectRetain(myNode
->interface
);
999 myNode
->interface_node
= (interface
== MACH_PORT_NULL
) ? node
: interface
;
1000 if (myNode
->interface
!= myNode
->interface_node
) {
1001 IOObjectRetain(myNode
->interface_node
);
1003 myNode
->myInstance
= myInstance
;
1004 myNode
->notification
= MACH_PORT_NULL
;
1006 kr
= IOServiceAddInterestNotification(myInstance
->notifyPort
, // IONotificationPortRef
1007 node
, // io_service_t
1008 kIOGeneralInterest
, // interestType
1009 update_node
, // IOServiceInterestCallback
1010 (void *)myData
, // refCon
1011 &myNode
->notification
); // notification
1012 if (kr
== KERN_SUCCESS
) {
1013 if (myInstance
->notifyNodes
== NULL
) {
1014 myInstance
->notifyNodes
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1016 CFArrayAppendValue(myInstance
->notifyNodes
, myData
);
1019 "add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x",
1027 add_init_watcher(MyType
*myInstance
, io_registry_entry_t interface
)
1030 io_registry_entry_t node
= interface
;
1031 CFTypeRef val
= NULL
;
1033 while (node
!= MACH_PORT_NULL
) {
1034 io_registry_entry_t parent
;
1036 val
= IORegistryEntryCreateCFProperty(node
, kSCNetworkInterfaceHiddenPortKey
, NULL
, 0);
1043 val
= IORegistryEntryCreateCFProperty(node
, kSCNetworkInterfaceInitializingKey
, NULL
, 0);
1048 parent
= MACH_PORT_NULL
;
1049 kr
= IORegistryEntryGetParentEntry(node
, kIOServicePlane
, &parent
);
1051 case kIOReturnSuccess
: // if we have a parent node
1052 case kIOReturnNoDevice
: // if we have hit the root node
1055 SC_log(LOG_ERR
, "add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x", kr
);
1058 if (node
!= interface
) {
1059 IOObjectRelease(node
);
1065 if (isA_CFBoolean(val
) && CFBooleanGetValue(val
)) {
1066 // watch the "Initializing" node
1067 add_node_watcher(myInstance
, node
, interface
);
1073 if ((node
!= MACH_PORT_NULL
) && (node
!= interface
)) {
1074 IOObjectRelease(node
);
1082 update_serial(void *refcon
, io_iterator_t iter
)
1084 MyType
*myInstance
= (MyType
*)refcon
;
1085 io_registry_entry_t obj
;
1087 while ((obj
= IOIteratorNext(iter
)) != MACH_PORT_NULL
) {
1088 SCNetworkInterfaceRef interface
;
1090 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj
);
1091 if (interface
!= NULL
) {
1092 CFRelease(interface
);
1094 // watch interface (to see when/if it's removed)
1095 add_node_watcher(myInstance
, obj
, MACH_PORT_NULL
);
1097 // check interface, watch if initializing
1098 add_init_watcher(myInstance
, obj
);
1101 IOObjectRelease(obj
);
1109 update_serial_nodes(void *refcon
, io_iterator_t iter
)
1111 MyType
*myInstance
= (MyType
*)refcon
;
1113 update_serial(refcon
, iter
);
1114 updateInterfaceList(myInstance
);
1119 watcher_add_serial(MyType
*myInstance
)
1123 myInstance
->notifyPort
= IONotificationPortCreate(kIOMasterPortDefault
);
1124 if (myInstance
->notifyPort
== NULL
) {
1125 SC_log(LOG_ERR
, "IONotificationPortCreate failed");
1129 // watch for the introduction of new network serial devices
1130 kr
= IOServiceAddMatchingNotification(myInstance
->notifyPort
,
1131 kIOFirstMatchNotification
,
1132 IOServiceMatching("IOSerialBSDClient"),
1133 &update_serial_nodes
,
1134 (void *)myInstance
, // refCon
1135 &myInstance
->notifyIterator
); // notification
1136 if (kr
!= KERN_SUCCESS
) {
1137 SC_log(LOG_ERR
, "SCMonitor : IOServiceAddMatchingNotification returned 0x%x", kr
);
1141 myInstance
->notifyNodes
= NULL
;
1143 // Get the current list of matches and arm the notification for
1144 // future interface arrivals.
1145 update_serial((void *)myInstance
, myInstance
->notifyIterator
);
1147 if (myInstance
->notifyNodes
!= NULL
) {
1148 // if we have any serial nodes, check if we already have the
1149 // "admin" (kSCPreferencesAuthorizationRight_write) right. If
1150 // so, we can automatically configure (without user intervention)
1151 // any "new" network interfaces that are present at login (e.g. a
1152 // USB modem that was plugged in before login).
1154 if (!hasAuthorization(myInstance
)) {
1156 CFIndex n
= CFArrayGetCount(myInstance
->notifyNodes
);
1158 // ... and if we don't have the right then we populate the list of
1159 // known interfaces with those already named so that we avoid any
1160 // login prompts (that the user might have no choice but to dismiss)
1162 for (i
= 0; i
< n
; i
++) {
1163 SCNetworkInterfaceRef interface
;
1167 myData
= CFArrayGetValueAtIndex(myInstance
->notifyNodes
, i
);
1169 /* ALIGN: CF aligns to at least >8 bytes */
1170 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
1172 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode
->interface_node
);
1173 if (interface
!= NULL
) {
1174 CFSetAddValue(myInstance
->interfaces_known
, interface
);
1175 CFRelease(interface
);
1181 // and keep watching
1182 CFRunLoopAddSource(CFRunLoopGetCurrent(),
1183 IONotificationPortGetRunLoopSource(myInstance
->notifyPort
),
1184 kCFRunLoopDefaultMode
);
1190 watcher_remove_serial(MyType
*myInstance
)
1192 if (myInstance
->notifyNodes
!= NULL
) {
1194 CFIndex n
= CFArrayGetCount(myInstance
->notifyNodes
);
1196 for (i
= 0; i
< n
; i
++) {
1200 myData
= CFArrayGetValueAtIndex(myInstance
->notifyNodes
, i
);
1202 /* ALIGN: CF aligns to at least >8 bytes */
1203 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
1205 if (myNode
->interface
!= myNode
->interface_node
) {
1206 IOObjectRelease(myNode
->interface_node
);
1208 if (myNode
->interface
!= MACH_PORT_NULL
) {
1209 IOObjectRelease(myNode
->interface
);
1211 IOObjectRelease(myNode
->notification
);
1214 CFRelease(myInstance
->notifyNodes
);
1215 myInstance
->notifyNodes
= NULL
;
1218 if (myInstance
->notifyIterator
!= MACH_PORT_NULL
) {
1219 IOObjectRelease(myInstance
->notifyIterator
);
1220 myInstance
->notifyIterator
= MACH_PORT_NULL
;
1223 if (myInstance
->notifyPort
!= MACH_PORT_NULL
) {
1224 IONotificationPortDestroy(myInstance
->notifyPort
);
1225 myInstance
->notifyPort
= NULL
;
1236 watcher_add(MyType
*myInstance
)
1240 bundle
= CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID
));
1241 if (bundle
!= NULL
) {
1244 CFDictionaryRef info
;
1246 info
= CFBundleGetInfoDictionary(bundle
);
1248 bVal
= CFDictionaryGetValue(info
, CFSTR("Debug"));
1249 bVal
= isA_CFBoolean(bVal
);
1251 myInstance
->debug
= CFBooleanGetValue(bVal
);
1254 action
= CFDictionaryGetValue(info
, kSCNetworkInterfaceConfigurationActionKey
);
1255 action
= isA_CFString(action
);
1256 if (action
!= NULL
) {
1257 myInstance
->configuration_action
= action
;
1259 CFBooleanRef user_intervention
;
1261 user_intervention
= CFDictionaryGetValue(info
, CFSTR("User Intervention"));
1262 if (isA_CFBoolean(user_intervention
) && !CFBooleanGetValue(user_intervention
)) {
1263 myInstance
->configuration_action
= kSCNetworkInterfaceConfigurationActionValueConfigure
;
1268 // initialize the list of known interfaces
1269 myInstance
->interfaces_known
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
1271 // add LAN interfaces
1272 watcher_add_lan(myInstance
);
1274 // add SERIAL interfaces
1275 watcher_add_serial(myInstance
);
1277 // auto-configure (as needed)
1278 updateInterfaceList(myInstance
);
1285 watcher_remove(MyType
*myInstance
)
1287 watcher_remove_lan(myInstance
);
1288 watcher_remove_serial(myInstance
);
1290 if (myInstance
->interfaces_known
!= NULL
) {
1291 CFRelease(myInstance
->interfaces_known
);
1292 myInstance
->interfaces_known
= NULL
;
1300 #pragma mark UserEventAgent stubs
1304 myQueryInterface(void *myInstance
, REFIID iid
, LPVOID
*ppv
)
1306 CFUUIDRef interfaceID
= CFUUIDCreateFromUUIDBytes(NULL
, iid
);
1308 // Test the requested ID against the valid interfaces.
1309 if (CFEqual(interfaceID
, kUserEventAgentInterfaceID
)) {
1310 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
1312 CFRelease(interfaceID
);
1316 if (CFEqual(interfaceID
, IUnknownUUID
)) {
1317 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
1319 CFRelease(interfaceID
);
1323 // Requested interface unknown, bail with error.
1325 CFRelease(interfaceID
);
1326 return E_NOINTERFACE
;
1331 myAddRef(void *myInstance
)
1333 ((MyType
*) myInstance
)->_refCount
++;
1334 return ((MyType
*) myInstance
)->_refCount
;
1339 myRelease(void *myInstance
)
1341 ((MyType
*) myInstance
)->_refCount
--;
1342 if (((MyType
*) myInstance
)->_refCount
== 0) {
1343 CFUUIDRef factoryID
= ((MyType
*) myInstance
)->_factoryID
;
1345 if (factoryID
!= NULL
) {
1346 CFPlugInRemoveInstanceForFactory(factoryID
);
1347 CFRelease(factoryID
);
1349 watcher_remove((MyType
*)myInstance
);
1350 notify_remove((MyType
*)myInstance
, TRUE
);
1351 freeAuthorization((MyType
*)myInstance
);
1357 return ((MyType
*) myInstance
)->_refCount
;
1362 myInstall(void *myInstance
)
1364 watcher_add((MyType
*)myInstance
);
1369 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl
= {
1370 NULL
, // Required padding for COM
1371 myQueryInterface
, // These three are the required COM functions
1374 myInstall
// Interface implementation
1379 UserEventAgentFactory(CFAllocatorRef allocator
, CFUUIDRef typeID
)
1381 #pragma unused(allocator)
1382 MyType
*newOne
= NULL
;
1384 if (CFEqual(typeID
, kUserEventAgentTypeID
)) {
1385 newOne
= (MyType
*)malloc(sizeof(MyType
));
1386 memset(newOne
, 0, sizeof(*newOne
));
1387 newOne
->_UserEventAgentInterface
= &UserEventAgentInterfaceFtbl
;
1388 newOne
->_factoryID
= (CFUUIDRef
)CFRetain(kUserEventAgentFactoryID
);
1389 CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID
);
1390 newOne
->_refCount
= 1;
1399 main(int argc
, char **argv
)
1401 MyType
*newOne
= (MyType
*)malloc(sizeof(MyType
));
1403 _sc_log
= kSCLogDestinationFile
;
1404 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
1406 memset(newOne
, 0, sizeof(*newOne
));