2 * Copyright (c) 2007-2017 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 #include <SystemConfiguration/SystemConfiguration.h>
38 #include <SystemConfiguration/SCPrivate.h>
40 #include <IOKit/IOKitLib.h>
41 #include <IOKit/IOKitKeysPrivate.h>
42 #include <IOKit/IOMessage.h>
43 #include <ApplicationServices/ApplicationServices.h>
44 #include "UserEventAgentInterface.h"
46 #define MY_BUNDLE_ID "com.apple.SystemConfiguration.SCMonitor"
47 #define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns"
49 #define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane"
50 #define NETWORK_PREF_CMD "New Interface"
53 * The following keys/values control the actions taken when a new interface
54 * has been detected and whether we interact with the user.
56 * The keys/values can be provided globally (in SCMonitor.plugin's Info.plist
57 * file) or per-inteface in the IORegistry.
59 * For the "New Interface Detected Action" key we define the following [CFString]
62 * "None" No action, ignore this interface.
63 * "Prompt" Post a "new interface detected" notification to the user.
64 * "Configure" Automatically configure this interface without any
67 * Note: automatic configuration may not be possible if the logged in user
68 * is NOT "root" (eUID==0) or if the authorization right that governs
69 * SCHelper write operations (kSCPreferencesAuthorizationRight_write)
70 * is not currently available.
72 * An [older] "User Intervention" key is also supported. That CFBoolean
73 * key, if present and TRUE, implies "Configure" configuration of the
74 * interface without intervention.
78 UserEventAgentInterfaceStruct
*_UserEventAgentInterface
;
84 CFStringRef configuration_action
;
86 CFRunLoopSourceRef monitorRls
;
88 IONotificationPortRef notifyPort
;
89 io_iterator_t notifyIterator
;
90 CFMutableArrayRef notifyNodes
;
92 // interfaces that we already know about
93 CFMutableSetRef interfaces_known
;
95 // interfaces that should be auto-configured (no user notification)
96 CFMutableArrayRef interfaces_configure
;
98 // interfaces that require user notification
99 CFMutableArrayRef interfaces_prompt
;
101 CFUserNotificationRef userNotification
;
102 CFRunLoopSourceRef userRls
;
104 AuthorizationRef authorization
;
107 static CFMutableDictionaryRef notify_to_instance
= NULL
;
120 static os_log_t log
= NULL
;
123 log
= os_log_create("com.apple.SystemConfiguration", "SCMonitor");
131 #pragma mark Authorization
134 static AuthorizationRef
135 getAuthorization(MyType
*myInstance
)
137 if (myInstance
->authorization
== NULL
) {
138 AuthorizationFlags flags
= kAuthorizationFlagDefaults
;
141 status
= AuthorizationCreate(NULL
,
142 kAuthorizationEmptyEnvironment
,
144 &myInstance
->authorization
);
145 if (status
!= errAuthorizationSuccess
) {
146 SC_log(LOG_ERR
, "AuthorizationCreate() failed: status = %d", (int)status
);
150 return myInstance
->authorization
;
155 hasAuthorization(MyType
*myInstance
)
157 AuthorizationRef authorization
;
158 Boolean isAdmin
= FALSE
;
160 authorization
= getAuthorization(myInstance
);
161 if (authorization
!= NULL
) {
162 AuthorizationFlags flags
= kAuthorizationFlagDefaults
;
163 AuthorizationItem items
[1];
164 AuthorizationRights rights
;
167 items
[0].name
= kSCPreferencesAuthorizationRight_write
;
168 items
[0].value
= NULL
;
169 items
[0].valueLength
= 0;
172 rights
.count
= sizeof(items
) / sizeof(items
[0]);
173 rights
.items
= items
;
175 status
= AuthorizationCopyRights(authorization
,
177 kAuthorizationEmptyEnvironment
,
180 isAdmin
= (status
== errAuthorizationSuccess
);
188 freeAuthorization(MyType
*myInstance
)
190 if (myInstance
->authorization
!= NULL
) {
191 AuthorizationFree(myInstance
->authorization
, kAuthorizationFlagDefaults
);
192 // AuthorizationFree(myInstance->authorization, kAuthorizationFlagDestroyRights);
193 myInstance
->authorization
= NULL
;
201 #pragma mark New interface notification / configuration
205 open_NetworkPrefPane(MyType
*myInstance
)
207 AEDesc aeDesc
= { typeNull
, NULL
};
208 CFArrayRef prefArray
;
210 LSLaunchURLSpec prefSpec
;
213 prefURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
,
214 CFSTR(NETWORK_PREF_APP
),
215 kCFURLPOSIXPathStyle
,
217 prefArray
= CFArrayCreate(NULL
, (const void **)&prefURL
, 1, &kCFTypeArrayCallBacks
);
220 status
= AECreateDesc('ptru',
221 (const void *)NETWORK_PREF_CMD
,
222 strlen(NETWORK_PREF_CMD
),
224 if (status
!= noErr
) {
225 SC_log(LOG_ERR
, "AECreateDesc() failed: %d", (int)status
);
228 prefSpec
.appURL
= NULL
;
229 prefSpec
.itemURLs
= prefArray
;
230 prefSpec
.passThruParams
= &aeDesc
;
231 prefSpec
.launchFlags
= kLSLaunchAsync
| kLSLaunchDontAddToRecents
;
232 prefSpec
.asyncRefCon
= NULL
;
234 status
= LSOpenFromURLSpec(&prefSpec
, NULL
);
235 if (status
!= noErr
) {
236 SC_log(LOG_ERR
, "LSOpenFromURLSpec() failed: %d", (int)status
);
239 CFRelease(prefArray
);
240 if (aeDesc
.descriptorType
!= typeNull
) AEDisposeDesc(&aeDesc
);
246 notify_remove(MyType
*myInstance
, Boolean cancel
)
248 if (myInstance
->interfaces_configure
!= NULL
) {
249 CFRelease(myInstance
->interfaces_configure
);
250 myInstance
->interfaces_configure
= NULL
;
253 if (myInstance
->interfaces_prompt
!= NULL
) {
254 CFRelease(myInstance
->interfaces_prompt
);
255 myInstance
->interfaces_prompt
= NULL
;
258 if (myInstance
->userRls
!= NULL
) {
259 CFRunLoopSourceInvalidate(myInstance
->userRls
);
260 CFRelease(myInstance
->userRls
);
261 myInstance
->userRls
= NULL
;
264 if (myInstance
->userNotification
!= NULL
) {
268 status
= CFUserNotificationCancel(myInstance
->userNotification
);
271 "CFUserNotificationCancel() failed, status=%d",
275 CFRelease(myInstance
->userNotification
);
276 myInstance
->userNotification
= NULL
;
284 notify_reply(CFUserNotificationRef userNotification
, CFOptionFlags response_flags
)
286 MyType
*myInstance
= NULL
;
288 // get instance for notification
289 if (notify_to_instance
!= NULL
) {
290 myInstance
= (MyType
*)CFDictionaryGetValue(notify_to_instance
, userNotification
);
291 if (myInstance
!= NULL
) {
292 CFDictionaryRemoveValue(notify_to_instance
, userNotification
);
293 if (CFDictionaryGetCount(notify_to_instance
) == 0) {
294 CFRelease(notify_to_instance
);
295 notify_to_instance
= NULL
;
299 if (myInstance
== NULL
) {
300 SC_log(LOG_ERR
, "can't find user notification");
305 switch (response_flags
& 0x3) {
306 case kCFUserNotificationDefaultResponse
:
307 // user asked to configure interface
308 open_NetworkPrefPane(myInstance
);
315 notify_remove(myInstance
, FALSE
);
321 notify_add(MyType
*myInstance
)
324 CFMutableDictionaryRef dict
= NULL
;
327 CFIndex n
= CFArrayGetCount(myInstance
->interfaces_prompt
);
330 if (myInstance
->userNotification
!= NULL
) {
331 CFMutableArrayRef save
= NULL
;
334 CFRetain(myInstance
->interfaces_prompt
);
335 save
= myInstance
->interfaces_prompt
;
337 notify_remove(myInstance
, TRUE
);
338 myInstance
->interfaces_prompt
= save
;
344 dict
= CFDictionaryCreateMutable(NULL
,
346 &kCFTypeDictionaryKeyCallBacks
,
347 &kCFTypeDictionaryValueCallBacks
);
349 // set localization URL
350 bundle
= CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID
));
351 if (bundle
!= NULL
) {
352 url
= CFBundleCopyBundleURL(bundle
);
356 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
357 (const UInt8
*)"/System/Library/UserEventPlugins/SCMonitor.plugin",
358 strlen("/System/Library/UserEventPlugins/SCMonitor.plugin"),
360 if (bundle
== NULL
) {
361 bundle
= CFBundleCreate(NULL
, url
);
367 CFDictionarySetValue(dict
, kCFUserNotificationLocalizationURLKey
, url
);
370 SC_log(LOG_ERR
, "can't find bundle");
375 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
376 (const UInt8
*)MY_ICON_PATH
,
377 strlen(MY_ICON_PATH
),
380 CFDictionarySetValue(dict
, kCFUserNotificationIconURLKey
, url
);
385 CFDictionarySetValue(dict
,
386 kCFUserNotificationAlertHeaderKey
,
387 (n
== 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N"));
392 SCNetworkInterfaceRef interface
;
396 #define MESSAGE_1 "The \"%@\" network interface has not been set up. To set up this interface, use Network Preferences."
398 format
= CFBundleCopyLocalizedString(bundle
,
402 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, 0);
403 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
404 message
= CFStringCreateWithFormat(NULL
, NULL
, format
, name
);
405 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
409 CFMutableArrayRef message
;
411 message
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
412 CFArrayAppendValue(message
, CFSTR("MESSAGE_SN"));
413 for (i
= 0; i
< n
; i
++) {
414 SCNetworkInterfaceRef interface
;
418 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, i
);
419 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
420 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\r\t%@"), name
);
421 CFArrayAppendValue(message
, str
);
424 CFArrayAppendValue(message
, CFSTR("MESSAGE_EN"));
425 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
430 CFDictionaryAddValue(dict
, kCFUserNotificationDefaultButtonTitleKey
, CFSTR("OPEN_NP"));
431 CFDictionaryAddValue(dict
, kCFUserNotificationAlternateButtonTitleKey
, CFSTR("CANCEL"));
433 // create and post notification
434 myInstance
->userNotification
= CFUserNotificationCreate(NULL
,
436 kCFUserNotificationNoteAlertLevel
,
439 if (myInstance
->userNotification
== NULL
) {
440 SC_log(LOG_ERR
, "CFUserNotificationCreate() failed: %d", (int)error
);
444 // establish callback
445 myInstance
->userRls
= CFUserNotificationCreateRunLoopSource(NULL
,
446 myInstance
->userNotification
,
449 if (myInstance
->userRls
== NULL
) {
450 SC_log(LOG_ERR
, "CFUserNotificationCreateRunLoopSource() failed");
451 CFRelease(myInstance
->userNotification
);
452 myInstance
->userNotification
= NULL
;
455 CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance
->userRls
, kCFRunLoopDefaultMode
);
457 // add instance for notification
458 if (notify_to_instance
== NULL
) {
459 notify_to_instance
= CFDictionaryCreateMutable(NULL
,
461 &kCFTypeDictionaryKeyCallBacks
,
462 NULL
); // no retain/release/... for values
464 CFDictionarySetValue(notify_to_instance
, myInstance
->userNotification
, myInstance
);
468 if (dict
!= NULL
) CFRelease(dict
);
474 notify_configure(MyType
*myInstance
)
477 CFIndex n
= CFArrayGetCount(myInstance
->interfaces_configure
);
479 SCPreferencesRef prefs
= NULL
;
480 SCNetworkSetRef set
= NULL
;
482 if (geteuid() == 0) {
483 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
485 AuthorizationRef authorization
;
487 authorization
= getAuthorization(myInstance
);
488 if (authorization
== NULL
) {
492 prefs
= SCPreferencesCreateWithAuthorization(NULL
, CFSTR("SCMonitor"), NULL
, authorization
);
495 set
= SCNetworkSetCopyCurrent(prefs
);
497 // if no "current" set, create new/default ("Automatic") set
498 set
= _SCNetworkSetCreateDefault(prefs
);
502 SC_log(LOG_DEBUG
, "added new \"default\" set");
505 for (i
= 0; i
< n
; i
++) {
506 SCNetworkInterfaceRef interface
;
508 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_configure
, i
);
509 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
513 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
514 SC_log(LOG_NOTICE
, "add/update service for %@", name
);
518 ok
= SCPreferencesCommitChanges(prefs
);
521 "SCPreferencesCommitChanges() failed: %s",
522 SCErrorString(SCError()));
526 ok
= SCPreferencesApplyChanges(prefs
);
529 "SCPreferencesApplyChanges() failed: %s",
530 SCErrorString(SCError()));
546 CFRelease(myInstance
->interfaces_configure
);
547 myInstance
->interfaces_configure
= NULL
;
558 CFArrayRef console_sessions
;
560 io_registry_entry_t root
;
561 uid_t uid
= geteuid();
563 root
= IORegistryGetRootEntry(kIOMasterPortDefault
);
564 console_sessions
= IORegistryEntryCreateCFProperty(root
,
565 CFSTR(kIOConsoleUsersKey
),
568 if (console_sessions
!= NULL
) {
571 n
= isA_CFArray(console_sessions
) ? CFArrayGetCount(console_sessions
) : 0;
572 for (CFIndex i
= 0; i
< n
; i
++) {
574 CFDictionaryRef session
;
578 session
= CFArrayGetValueAtIndex(console_sessions
, i
);
579 if (!isA_CFDictionary(session
)) {
584 if (!CFDictionaryGetValueIfPresent(session
,
585 CFSTR(kIOConsoleSessionUIDKey
),
586 (const void **)&val
) ||
587 !isA_CFNumber(val
) ||
588 !CFNumberGetValue(val
, kCFNumberSInt64Type
, (void *)&sessionUID
) ||
589 (uid
!= sessionUID
)) {
594 if (CFDictionaryGetValueIfPresent(session
,
595 CFSTR(kIOConsoleSessionOnConsoleKey
),
596 (const void **)&bVal
) &&
597 isA_CFBoolean(bVal
) &&
598 CFBooleanGetValue(bVal
)) {
599 // if "on console" session
606 CFRelease(console_sessions
);
608 IOObjectRelease(root
);
617 // configure ONLY IF authorized
618 #define kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized CFSTR("Configure-Authorized")
622 updateInterfaceList(MyType
*myInstance
)
624 Boolean changed
= FALSE
;
626 CFArrayRef interfaces
;
627 CFMutableSetRef interfaces_old
= NULL
;
629 SCPreferencesRef prefs
;
630 SCNetworkSetRef set
= NULL
;
636 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
641 set
= SCNetworkSetCopyCurrent(prefs
);
643 // if no "current" set, create new/default ("Automatic") set
644 set
= _SCNetworkSetCreateDefault(prefs
);
650 interfaces_old
= CFSetCreateMutableCopy(NULL
, 0, myInstance
->interfaces_known
);
652 interfaces
= _SCNetworkInterfaceCopyAllWithPreferences(prefs
);
653 if (interfaces
!= NULL
) {
654 n
= CFArrayGetCount(interfaces
);
655 for (i
= 0; i
< n
; i
++) {
656 SCNetworkInterfaceRef interface
;
659 interface
= CFArrayGetValueAtIndex(interfaces
, i
);
661 // track new vs. old (removed) interfaces
662 CFSetRemoveValue(interfaces_old
, interface
);
663 if (CFSetContainsValue(myInstance
->interfaces_known
, interface
)) {
664 // if we already know about this interface
667 CFSetAddValue(myInstance
->interfaces_known
, interface
);
670 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
674 // this is a *new* interface
676 action
= _SCNetworkInterfaceGetConfigurationAction(interface
);
677 if (action
== NULL
) {
678 // if no per-interface action, use [global] default
679 action
= myInstance
->configuration_action
;
681 if ((action
== NULL
) ||
682 (!CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueNone
) &&
683 !CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigure
))) {
684 if (_SCNetworkInterfaceIsBuiltin(interface
)) {
685 // if built-in interface
686 action
= kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized
;
688 action
= kSCNetworkInterfaceConfigurationActionValuePrompt
;
692 if (CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueNone
)) {
694 } else if (CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigure
)) {
695 // configure automatically (without user intervention)
696 if (myInstance
->interfaces_configure
== NULL
) {
697 myInstance
->interfaces_configure
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
699 CFArrayAppendValue(myInstance
->interfaces_configure
, interface
);
700 } else if (hasAuthorization(myInstance
)) {
701 // if we already have the "admin" (kSCPreferencesAuthorizationRight_write)
702 // right, configure automatically (without user intervention)
703 if (myInstance
->interfaces_configure
== NULL
) {
704 myInstance
->interfaces_configure
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
706 CFArrayAppendValue(myInstance
->interfaces_configure
, interface
);
707 } else if (!CFEqual(action
, kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized
)) {
709 if (myInstance
->interfaces_prompt
== NULL
) {
710 myInstance
->interfaces_prompt
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
712 CFArrayAppendValue(myInstance
->interfaces_prompt
, interface
);
717 CFRelease(interfaces
);
720 // remove any posted notifications for network interfaces that have been removed
721 n
= CFSetGetCount(interfaces_old
);
723 const void * paths_q
[32];
724 const void ** paths
= paths_q
;
726 if (n
> (CFIndex
)(sizeof(paths_q
) / sizeof(CFTypeRef
)))
727 paths
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
728 CFSetGetValues(interfaces_old
, paths
);
729 for (i
= 0; i
< n
; i
++) {
730 if (myInstance
->interfaces_prompt
!= NULL
) {
733 j
= CFArrayGetCount(myInstance
->interfaces_prompt
);
735 SCNetworkInterfaceRef interface
;
738 interface
= CFArrayGetValueAtIndex(myInstance
->interfaces_prompt
, j
);
739 if (CFEqual(interface
, paths
[i
])) {
740 // if we have previously posted a notification
741 // for this no-longer-present interface
742 CFArrayRemoveValueAtIndex(myInstance
->interfaces_prompt
, j
);
748 CFSetRemoveValue(myInstance
->interfaces_known
, paths
[i
]);
750 if (paths
!= paths_q
) CFAllocatorDeallocate(NULL
, paths
);
756 if (myInstance
->interfaces_configure
!= NULL
) {
757 // if we have network services to configure automatically
758 notify_configure(myInstance
);
761 if (myInstance
->interfaces_prompt
!= NULL
) {
762 // if we have network services that require user intervention
763 // post notification for new interfaces
764 notify_add(myInstance
);
768 if (interfaces_old
!= NULL
) CFRelease(interfaces_old
);
769 if (set
!= NULL
) CFRelease(set
);
776 #pragma mark Watch for new [network] interfaces
780 update_lan(SCDynamicStoreRef store
, CFArrayRef changes
, void * arg
)
782 MyType
*myInstance
= (MyType
*)arg
;
784 updateInterfaceList(myInstance
);
790 watcher_add_lan(MyType
*myInstance
)
792 SCDynamicStoreContext context
= { 0, (void *)myInstance
, NULL
, NULL
, NULL
};
795 SCDynamicStoreRef store
;
797 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCMonitor"), update_lan
, &context
);
800 "SCDynamicStoreCreate() failed: %s",
801 SCErrorString(SCError()));
805 key
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
807 // watch for changes to the list of network interfaces
808 keys
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
809 SCDynamicStoreSetNotificationKeys(store
, NULL
, keys
);
811 myInstance
->monitorRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
812 CFRunLoopAddSource(CFRunLoopGetCurrent(),
813 myInstance
->monitorRls
,
814 kCFRunLoopDefaultMode
);
816 // check if we already have the "admin" (kSCPreferencesAuthorizationRight_write)
817 // right. If so, we can automatically configure (without user intervention) any
818 // "new" network interfaces that are present at login (e.g. a USB ethernet
819 // dongle that was plugged in before login).
820 if (!hasAuthorization(myInstance
)) {
821 CFDictionaryRef dict
;
823 // ... and if we don't have the right then we populate the list of
824 // known interfaces with those already named so that we avoid any
825 // login prompts (that the user might have no choice but to dismiss)
826 dict
= SCDynamicStoreCopyValue(store
, key
);
828 if (isA_CFDictionary(dict
)) {
830 CFArrayRef interfaces
;
833 interfaces
= CFDictionaryGetValue(dict
, kSCPropNetInterfaces
);
834 n
= isA_CFArray(interfaces
) ? CFArrayGetCount(interfaces
) : 0;
835 for (i
= 0; i
< n
; i
++) {
838 bsdName
= CFArrayGetValueAtIndex(interfaces
, i
);
839 if (isA_CFString(bsdName
)) {
840 SCNetworkInterfaceRef interface
;
842 interface
= _SCNetworkInterfaceCreateWithBSDName(NULL
, bsdName
, kIncludeNoVirtualInterfaces
);
843 if (interface
!= NULL
) {
844 CFSetAddValue(myInstance
->interfaces_known
, interface
);
845 CFRelease(interface
);
862 watcher_remove_lan(MyType
*myInstance
)
864 if (myInstance
->monitorRls
!= NULL
) {
865 CFRunLoopSourceInvalidate(myInstance
->monitorRls
);
866 CFRelease(myInstance
->monitorRls
);
867 myInstance
->monitorRls
= NULL
;
878 io_registry_entry_t interface
;
879 io_registry_entry_t interface_node
;
881 io_object_t notification
;
886 add_node_watcher(MyType
*myInstance
, io_registry_entry_t node
, io_registry_entry_t interface
);
890 update_node(void *refCon
, io_service_t service
, natural_t messageType
, void *messageArgument
)
893 CFDataRef myData
= (CFDataRef
)refCon
;
897 /* ALIGN: CF aligns to at least >8 bytes */
898 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
899 myInstance
= myNode
->myInstance
;
901 switch (messageType
) {
902 case kIOMessageServicePropertyChange
: {
903 Boolean initializing
= FALSE
;
904 SCNetworkInterfaceRef interface
;
907 if (myNode
->interface
== MACH_PORT_NULL
) {
908 // if we are not watching the "Initializing" property
912 val
= IORegistryEntryCreateCFProperty(service
, CFSTR("Initializing"), NULL
, 0);
914 initializing
= (isA_CFBoolean(val
) && CFBooleanGetValue(val
));
917 // if initialization not complete, keep watching
923 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode
->interface
);
924 if (interface
!= NULL
) {
925 CFRelease(interface
);
927 // watch interface (to see when/if it's removed)
928 add_node_watcher(myInstance
, myNode
->interface
, MACH_PORT_NULL
);
933 case kIOMessageServiceIsTerminated
:
940 // remove no-longer-needed notification
941 if (myNode
->interface
!= myNode
->interface_node
) {
942 IOObjectRelease(myNode
->interface_node
);
944 if (myNode
->interface
!= MACH_PORT_NULL
) {
945 IOObjectRelease(myNode
->interface
);
947 IOObjectRelease(myNode
->notification
);
948 i
= CFArrayGetFirstIndexOfValue(myInstance
->notifyNodes
,
949 CFRangeMake(0, CFArrayGetCount(myInstance
->notifyNodes
)),
951 if (i
!= kCFNotFound
) {
952 CFArrayRemoveValueAtIndex(myInstance
->notifyNodes
, i
);
953 if (CFArrayGetCount(myInstance
->notifyNodes
) == 0) {
954 CFRelease(myInstance
->notifyNodes
);
955 myInstance
->notifyNodes
= NULL
;
959 updateInterfaceList(myInstance
);
965 add_node_watcher(MyType
*myInstance
, io_registry_entry_t node
, io_registry_entry_t interface
)
968 CFMutableDataRef myData
;
971 // wait for initialization to complete
972 myData
= CFDataCreateMutable(NULL
, sizeof(MyNode
));
973 CFDataSetLength(myData
, sizeof(MyNode
));
975 /* ALIGN: CF aligns to at least >8 bytes */
976 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
978 bzero(myNode
, sizeof(MyNode
));
979 myNode
->interface
= interface
;
980 if (myNode
->interface
!= MACH_PORT_NULL
) {
981 IOObjectRetain(myNode
->interface
);
983 myNode
->interface_node
= (interface
== MACH_PORT_NULL
) ? node
: interface
;
984 if (myNode
->interface
!= myNode
->interface_node
) {
985 IOObjectRetain(myNode
->interface_node
);
987 myNode
->myInstance
= myInstance
;
988 myNode
->notification
= MACH_PORT_NULL
;
990 kr
= IOServiceAddInterestNotification(myInstance
->notifyPort
, // IONotificationPortRef
991 node
, // io_service_t
992 kIOGeneralInterest
, // interestType
993 update_node
, // IOServiceInterestCallback
994 (void *)myData
, // refCon
995 &myNode
->notification
); // notification
996 if (kr
== KERN_SUCCESS
) {
997 if (myInstance
->notifyNodes
== NULL
) {
998 myInstance
->notifyNodes
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1000 CFArrayAppendValue(myInstance
->notifyNodes
, myData
);
1003 "add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x",
1011 add_init_watcher(MyType
*myInstance
, io_registry_entry_t interface
)
1014 io_registry_entry_t node
= interface
;
1015 CFTypeRef val
= NULL
;
1017 while (node
!= MACH_PORT_NULL
) {
1018 io_registry_entry_t parent
;
1020 val
= IORegistryEntryCreateCFProperty(node
, kSCNetworkInterfaceHiddenPortKey
, NULL
, 0);
1027 val
= IORegistryEntryCreateCFProperty(node
, kSCNetworkInterfaceInitializingKey
, NULL
, 0);
1032 parent
= MACH_PORT_NULL
;
1033 kr
= IORegistryEntryGetParentEntry(node
, kIOServicePlane
, &parent
);
1035 case kIOReturnSuccess
: // if we have a parent node
1036 case kIOReturnNoDevice
: // if we have hit the root node
1039 SC_log(LOG_ERR
, "add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x", kr
);
1042 if (node
!= interface
) {
1043 IOObjectRelease(node
);
1049 if (isA_CFBoolean(val
) && CFBooleanGetValue(val
)) {
1050 // watch the "Initializing" node
1051 add_node_watcher(myInstance
, node
, interface
);
1057 if ((node
!= MACH_PORT_NULL
) && (node
!= interface
)) {
1058 IOObjectRelease(node
);
1066 update_serial(void *refcon
, io_iterator_t iter
)
1068 MyType
*myInstance
= (MyType
*)refcon
;
1069 io_registry_entry_t obj
;
1071 while ((obj
= IOIteratorNext(iter
)) != MACH_PORT_NULL
) {
1072 SCNetworkInterfaceRef interface
;
1074 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj
);
1075 if (interface
!= NULL
) {
1076 CFRelease(interface
);
1078 // watch interface (to see when/if it's removed)
1079 add_node_watcher(myInstance
, obj
, MACH_PORT_NULL
);
1081 // check interface, watch if initializing
1082 add_init_watcher(myInstance
, obj
);
1085 IOObjectRelease(obj
);
1093 update_serial_nodes(void *refcon
, io_iterator_t iter
)
1095 MyType
*myInstance
= (MyType
*)refcon
;
1097 update_serial(refcon
, iter
);
1098 updateInterfaceList(myInstance
);
1103 watcher_add_serial(MyType
*myInstance
)
1107 myInstance
->notifyPort
= IONotificationPortCreate(kIOMasterPortDefault
);
1108 if (myInstance
->notifyPort
== NULL
) {
1109 SC_log(LOG_ERR
, "IONotificationPortCreate failed");
1113 // watch for the introduction of new network serial devices
1114 kr
= IOServiceAddMatchingNotification(myInstance
->notifyPort
,
1115 kIOFirstMatchNotification
,
1116 IOServiceMatching("IOSerialBSDClient"),
1117 &update_serial_nodes
,
1118 (void *)myInstance
, // refCon
1119 &myInstance
->notifyIterator
); // notification
1120 if (kr
!= KERN_SUCCESS
) {
1121 SC_log(LOG_ERR
, "SCMonitor : IOServiceAddMatchingNotification returned 0x%x", kr
);
1125 myInstance
->notifyNodes
= NULL
;
1127 // Get the current list of matches and arm the notification for
1128 // future interface arrivals.
1129 update_serial((void *)myInstance
, myInstance
->notifyIterator
);
1131 if (myInstance
->notifyNodes
!= NULL
) {
1132 // if we have any serial nodes, check if we already have the
1133 // "admin" (kSCPreferencesAuthorizationRight_write) right. If
1134 // so, we can automatically configure (without user intervention)
1135 // any "new" network interfaces that are present at login (e.g. a
1136 // USB modem that was plugged in before login).
1138 if (!hasAuthorization(myInstance
)) {
1140 CFIndex n
= CFArrayGetCount(myInstance
->notifyNodes
);
1142 // ... and if we don't have the right then we populate the list of
1143 // known interfaces with those already named so that we avoid any
1144 // login prompts (that the user might have no choice but to dismiss)
1146 for (i
= 0; i
< n
; i
++) {
1147 SCNetworkInterfaceRef interface
;
1151 myData
= CFArrayGetValueAtIndex(myInstance
->notifyNodes
, i
);
1153 /* ALIGN: CF aligns to at least >8 bytes */
1154 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
1156 interface
= _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode
->interface_node
);
1157 if (interface
!= NULL
) {
1158 CFSetAddValue(myInstance
->interfaces_known
, interface
);
1159 CFRelease(interface
);
1165 // and keep watching
1166 CFRunLoopAddSource(CFRunLoopGetCurrent(),
1167 IONotificationPortGetRunLoopSource(myInstance
->notifyPort
),
1168 kCFRunLoopDefaultMode
);
1174 watcher_remove_serial(MyType
*myInstance
)
1176 if (myInstance
->notifyNodes
!= NULL
) {
1178 CFIndex n
= CFArrayGetCount(myInstance
->notifyNodes
);
1180 for (i
= 0; i
< n
; i
++) {
1184 myData
= CFArrayGetValueAtIndex(myInstance
->notifyNodes
, i
);
1186 /* ALIGN: CF aligns to at least >8 bytes */
1187 myNode
= (MyNode
*)(void *)CFDataGetBytePtr(myData
);
1189 if (myNode
->interface
!= myNode
->interface_node
) {
1190 IOObjectRelease(myNode
->interface_node
);
1192 if (myNode
->interface
!= MACH_PORT_NULL
) {
1193 IOObjectRelease(myNode
->interface
);
1195 IOObjectRelease(myNode
->notification
);
1198 CFRelease(myInstance
->notifyNodes
);
1199 myInstance
->notifyNodes
= NULL
;
1202 if (myInstance
->notifyIterator
!= MACH_PORT_NULL
) {
1203 IOObjectRelease(myInstance
->notifyIterator
);
1204 myInstance
->notifyIterator
= MACH_PORT_NULL
;
1207 if (myInstance
->notifyPort
!= MACH_PORT_NULL
) {
1208 IONotificationPortDestroy(myInstance
->notifyPort
);
1209 myInstance
->notifyPort
= NULL
;
1220 watcher_add(MyType
*myInstance
)
1224 bundle
= CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID
));
1225 if (bundle
!= NULL
) {
1228 CFDictionaryRef info
;
1230 info
= CFBundleGetInfoDictionary(bundle
);
1232 bVal
= CFDictionaryGetValue(info
, CFSTR("Debug"));
1233 bVal
= isA_CFBoolean(bVal
);
1235 myInstance
->debug
= CFBooleanGetValue(bVal
);
1238 action
= CFDictionaryGetValue(info
, kSCNetworkInterfaceConfigurationActionKey
);
1239 action
= isA_CFString(action
);
1240 if (action
!= NULL
) {
1241 myInstance
->configuration_action
= action
;
1243 CFBooleanRef user_intervention
;
1245 user_intervention
= CFDictionaryGetValue(info
, CFSTR("User Intervention"));
1246 if (isA_CFBoolean(user_intervention
) && !CFBooleanGetValue(user_intervention
)) {
1247 myInstance
->configuration_action
= kSCNetworkInterfaceConfigurationActionValueConfigure
;
1252 // initialize the list of known interfaces
1253 myInstance
->interfaces_known
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
1255 // add LAN interfaces
1256 watcher_add_lan(myInstance
);
1258 // add SERIAL interfaces
1259 watcher_add_serial(myInstance
);
1261 // auto-configure (as needed)
1262 updateInterfaceList(myInstance
);
1269 watcher_remove(MyType
*myInstance
)
1271 watcher_remove_lan(myInstance
);
1272 watcher_remove_serial(myInstance
);
1274 if (myInstance
->interfaces_known
!= NULL
) {
1275 CFRelease(myInstance
->interfaces_known
);
1276 myInstance
->interfaces_known
= NULL
;
1284 #pragma mark UserEventAgent stubs
1288 myQueryInterface(void *myInstance
, REFIID iid
, LPVOID
*ppv
)
1290 CFUUIDRef interfaceID
= CFUUIDCreateFromUUIDBytes(NULL
, iid
);
1292 // Test the requested ID against the valid interfaces.
1293 if (CFEqual(interfaceID
, kUserEventAgentInterfaceID
)) {
1294 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
1296 CFRelease(interfaceID
);
1300 if (CFEqual(interfaceID
, IUnknownUUID
)) {
1301 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
1303 CFRelease(interfaceID
);
1307 // Requested interface unknown, bail with error.
1309 CFRelease(interfaceID
);
1310 return E_NOINTERFACE
;
1315 myAddRef(void *myInstance
)
1317 ((MyType
*) myInstance
)->_refCount
++;
1318 return ((MyType
*) myInstance
)->_refCount
;
1323 myRelease(void *myInstance
)
1325 ((MyType
*) myInstance
)->_refCount
--;
1326 if (((MyType
*) myInstance
)->_refCount
== 0) {
1327 CFUUIDRef factoryID
= ((MyType
*) myInstance
)->_factoryID
;
1329 if (factoryID
!= NULL
) {
1330 CFPlugInRemoveInstanceForFactory(factoryID
);
1331 CFRelease(factoryID
);
1333 watcher_remove((MyType
*)myInstance
);
1334 notify_remove((MyType
*)myInstance
, TRUE
);
1335 freeAuthorization((MyType
*)myInstance
);
1341 return ((MyType
*) myInstance
)->_refCount
;
1346 myInstall(void *myInstance
)
1348 watcher_add((MyType
*)myInstance
);
1353 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl
= {
1354 NULL
, // Required padding for COM
1355 myQueryInterface
, // These three are the required COM functions
1358 myInstall
// Interface implementation
1363 UserEventAgentFactory(CFAllocatorRef allocator
, CFUUIDRef typeID
)
1365 MyType
*newOne
= NULL
;
1367 if (CFEqual(typeID
, kUserEventAgentTypeID
)) {
1368 newOne
= (MyType
*)malloc(sizeof(MyType
));
1369 bzero(newOne
, sizeof(*newOne
));
1370 newOne
->_UserEventAgentInterface
= &UserEventAgentInterfaceFtbl
;
1371 newOne
->_factoryID
= (CFUUIDRef
)CFRetain(kUserEventAgentFactoryID
);
1372 CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID
);
1373 newOne
->_refCount
= 1;
1382 main(int argc
, char **argv
)
1384 MyType
*newOne
= (MyType
*)malloc(sizeof(MyType
));
1387 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
1389 bzero(newOne
, sizeof(*newOne
));