2 * Copyright (c) 2007 Apple Computer, 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>
35 #include <SystemConfiguration/SystemConfiguration.h>
36 #include <SystemConfiguration/SCPrivate.h>
37 #include <ApplicationServices/ApplicationServices.h>
38 #include "UserEventAgentInterface.h"
40 #define MY_BUNDLE_ID CFSTR("com.apple.SystemConfiguration.SCMonitor")
41 #define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns"
43 #define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane"
44 #define NETWORK_PREF_CMD "New Interface"
47 UserEventAgentInterfaceStruct
*_UserEventAgentInterface
;
51 Boolean no_user_intervention
;
53 CFRunLoopSourceRef monitorRls
;
55 CFMutableSetRef knownInterfaces
;
57 CFMutableArrayRef userInterfaces
;
58 CFUserNotificationRef userNotification
;
59 CFRunLoopSourceRef userRls
;
62 static CFMutableDictionaryRef notify_to_instance
= NULL
;
66 #pragma mark Watch for new [network] interfaces
70 open_NetworkPrefPane(void)
72 AEDesc aeDesc
= { typeNull
, NULL
};
75 LSLaunchURLSpec prefSpec
;
78 prefURL
= CFURLCreateWithFileSystemPath(kCFAllocatorDefault
,
79 CFSTR(NETWORK_PREF_APP
),
82 prefArray
= CFArrayCreate(NULL
, (const void **)&prefURL
, 1, &kCFTypeArrayCallBacks
);
85 status
= AECreateDesc('ptru',
86 (const void *)NETWORK_PREF_CMD
,
87 strlen(NETWORK_PREF_CMD
),
89 if (status
!= noErr
) {
90 SCLog(TRUE
, LOG_ERR
, CFSTR("SCMonitor: AECreateDesc() failed: %d"), status
);
93 prefSpec
.appURL
= NULL
;
94 prefSpec
.itemURLs
= prefArray
;
95 prefSpec
.passThruParams
= &aeDesc
;
96 prefSpec
.launchFlags
= kLSLaunchAsync
| kLSLaunchDontAddToRecents
;
97 prefSpec
.asyncRefCon
= NULL
;
99 status
= LSOpenFromURLSpec(&prefSpec
, NULL
);
100 if (status
!= noErr
) {
101 SCLog(TRUE
, LOG_ERR
, CFSTR("SCMonitor: LSOpenFromURLSpec() failed: %d"), status
);
104 CFRelease(prefArray
);
105 if (aeDesc
.descriptorType
!= typeNull
) AEDisposeDesc(&aeDesc
);
111 notify_remove(MyType
*myInstance
, Boolean cancel
)
113 if (myInstance
->userInterfaces
!= NULL
) {
114 CFRelease(myInstance
->userInterfaces
);
115 myInstance
->userInterfaces
= NULL
;
118 if (myInstance
->userRls
!= NULL
) {
119 CFRunLoopSourceInvalidate(myInstance
->userRls
);
120 CFRelease(myInstance
->userRls
);
121 myInstance
->userRls
= NULL
;
124 if (myInstance
->userNotification
!= NULL
) {
128 status
= CFUserNotificationCancel(myInstance
->userNotification
);
131 CFSTR("SCMonitor: CFUserNotificationCancel() failed, status=%d"),
135 CFRelease(myInstance
->userNotification
);
136 myInstance
->userNotification
= NULL
;
144 notify_reply(CFUserNotificationRef userNotification
, CFOptionFlags response_flags
)
146 MyType
*myInstance
= NULL
;
148 // get instance for notification
149 if (notify_to_instance
!= NULL
) {
150 myInstance
= (MyType
*)CFDictionaryGetValue(notify_to_instance
, userNotification
);
151 if (myInstance
!= NULL
) {
152 CFDictionaryRemoveValue(notify_to_instance
, userNotification
);
153 if (CFDictionaryGetCount(notify_to_instance
) == 0) {
154 CFRelease(notify_to_instance
);
155 notify_to_instance
= NULL
;
159 if (myInstance
== NULL
) {
160 SCLog(TRUE
, LOG_ERR
, CFSTR("SCMonitor: can't find user notification"));
165 switch (response_flags
& 0x3) {
166 case kCFUserNotificationDefaultResponse
:
167 // user asked to configure interface
168 open_NetworkPrefPane();
175 notify_remove(myInstance
, FALSE
);
181 notify_add(MyType
*myInstance
)
184 CFMutableDictionaryRef dict
= NULL
;
187 CFMutableArrayRef message
;
188 CFIndex n
= CFArrayGetCount(myInstance
->userInterfaces
);
191 if (myInstance
->userNotification
!= NULL
) {
192 CFMutableArrayRef save
= NULL
;
195 CFRetain(myInstance
->userInterfaces
);
196 save
= myInstance
->userInterfaces
;
198 notify_remove(myInstance
, TRUE
);
199 myInstance
->userInterfaces
= save
;
205 dict
= CFDictionaryCreateMutable(NULL
,
207 &kCFTypeDictionaryKeyCallBacks
,
208 &kCFTypeDictionaryValueCallBacks
);
210 // set localization URL
211 bundle
= CFBundleGetBundleWithIdentifier(MY_BUNDLE_ID
);
212 if (bundle
!= NULL
) {
213 url
= CFBundleCopyBundleURL(bundle
);
217 CFDictionarySetValue(dict
, kCFUserNotificationLocalizationURLKey
, url
);
220 SCLog(TRUE
, LOG_NOTICE
, CFSTR("SCMonitor: can't find bundle"));
225 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
226 (const UInt8
*)MY_ICON_PATH
,
227 strlen(MY_ICON_PATH
),
230 CFDictionarySetValue(dict
, kCFUserNotificationIconURLKey
, url
);
235 CFDictionarySetValue(dict
,
236 kCFUserNotificationAlertHeaderKey
,
237 (n
== 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N"));
240 message
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
241 CFArrayAppendValue(message
,
242 (n
== 1) ? CFSTR("MESSAGE_S1") : CFSTR("MESSAGE_SN"));
243 for (i
= 0; i
< n
; i
++) {
244 SCNetworkInterfaceRef interface
;
247 interface
= CFArrayGetValueAtIndex(myInstance
->userInterfaces
, i
);
248 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
250 CFArrayAppendValue(message
, name
);
254 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\r\t%@"), name
);
255 CFArrayAppendValue(message
, str
);
259 CFArrayAppendValue(message
,
260 (n
== 1) ? CFSTR("MESSAGE_E1") : CFSTR("MESSAGE_EN"));
261 CFDictionarySetValue(dict
, kCFUserNotificationAlertMessageKey
, message
);
265 CFDictionaryAddValue(dict
, kCFUserNotificationDefaultButtonTitleKey
, CFSTR("OPEN_NP"));
266 CFDictionaryAddValue(dict
, kCFUserNotificationAlternateButtonTitleKey
, CFSTR("CANCEL"));
268 // create and post notification
269 myInstance
->userNotification
= CFUserNotificationCreate(NULL
,
271 kCFUserNotificationNoteAlertLevel
,
274 if (myInstance
->userNotification
== NULL
) {
275 SCLog(TRUE
, LOG_ERR
, CFSTR("SCMonitor: CFUserNotificationCreate() failed, %d"), error
);
279 // establish callback
280 myInstance
->userRls
= CFUserNotificationCreateRunLoopSource(NULL
,
281 myInstance
->userNotification
,
284 if (myInstance
->userRls
== NULL
) {
285 SCLog(TRUE
, LOG_ERR
, CFSTR("SCMonitor: CFUserNotificationCreateRunLoopSource() failed"));
286 CFRelease(myInstance
->userNotification
);
287 myInstance
->userNotification
= NULL
;
290 CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance
->userRls
, kCFRunLoopDefaultMode
);
292 // add instance for notification
293 if (notify_to_instance
== NULL
) {
294 notify_to_instance
= CFDictionaryCreateMutable(NULL
,
296 &kCFTypeDictionaryKeyCallBacks
,
297 NULL
); // no retain/release/... for values
299 CFDictionarySetValue(notify_to_instance
, myInstance
->userNotification
, myInstance
);
303 if (dict
!= NULL
) CFRelease(dict
);
309 notify_configure(MyType
*myInstance
)
311 AuthorizationRef authorization
= NULL
;
315 SCPreferencesRef prefs
= NULL
;
316 SCNetworkSetRef set
= NULL
;
318 if (geteuid() == 0) {
319 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
321 AuthorizationFlags flags
= kAuthorizationFlagDefaults
;
324 status
= AuthorizationCreate(NULL
,
325 kAuthorizationEmptyEnvironment
,
328 if (status
!= errAuthorizationSuccess
) {
330 CFSTR("AuthorizationCreate() failed: status = %d\n"),
335 prefs
= SCPreferencesCreateWithAuthorization(NULL
, CFSTR("SCMonitor"), NULL
, authorization
);
338 set
= SCNetworkSetCopyCurrent(prefs
);
340 set
= SCNetworkSetCreate(prefs
);
346 n
= CFArrayGetCount(myInstance
->userInterfaces
);
347 for (i
= 0; i
< n
; i
++) {
348 SCNetworkInterfaceRef interface
;
350 interface
= CFArrayGetValueAtIndex(myInstance
->userInterfaces
, i
);
351 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
355 name
= SCNetworkInterfaceGetLocalizedDisplayName(interface
);
356 SCLog(TRUE
, LOG_NOTICE
, CFSTR("add service for %@"), name
);
360 ok
= SCPreferencesCommitChanges(prefs
);
363 CFSTR("SCPreferencesCommitChanges() failed: %s\n"),
364 SCErrorString(SCError()));
368 ok
= SCPreferencesApplyChanges(prefs
);
371 CFSTR("SCPreferencesApplyChanges() failed: %s\n"),
372 SCErrorString(SCError()));
388 if (authorization
!= NULL
) {
389 AuthorizationFree(authorization
, kAuthorizationFlagDefaults
);
390 // AuthorizationFree(authorization, kAuthorizationFlagDestroyRights);
391 authorization
= NULL
;
394 CFRelease(myInstance
->userInterfaces
);
395 myInstance
->userInterfaces
= NULL
;
402 updateInterfaceList(SCDynamicStoreRef store
, CFArrayRef changes
, void * arg
)
405 CFArrayRef interfaces
;
406 MyType
*myInstance
= (MyType
*)arg
;
408 SCPreferencesRef prefs
;
409 CFMutableSetRef previouslyKnown
= NULL
;
410 SCNetworkSetRef set
= NULL
;
412 prefs
= SCPreferencesCreate(NULL
, CFSTR("SCMonitor"), NULL
);
417 set
= SCNetworkSetCopyCurrent(prefs
);
419 set
= SCNetworkSetCreate(prefs
);
425 previouslyKnown
= CFSetCreateMutableCopy(NULL
, 0, myInstance
->knownInterfaces
);
427 interfaces
= SCNetworkInterfaceCopyAll();
428 if (interfaces
!= NULL
) {
430 n
= CFArrayGetCount(interfaces
);
431 for (i
= 0; i
< n
; i
++) {
433 SCNetworkInterfaceRef interface
;
436 interface
= CFArrayGetValueAtIndex(interfaces
, i
);
437 bsdName
= SCNetworkInterfaceGetBSDName(interface
);
438 if (bsdName
== NULL
) {
443 CFSetRemoveValue(previouslyKnown
, bsdName
);
445 if (CFSetContainsValue(myInstance
->knownInterfaces
, bsdName
)) {
446 // if known interface
450 CFSetAddValue(myInstance
->knownInterfaces
, bsdName
);
452 ok
= SCNetworkSetEstablishDefaultInterfaceConfiguration(set
, interface
);
454 // this is a *new* interface
455 if (myInstance
->userInterfaces
== NULL
) {
456 myInstance
->userInterfaces
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
458 CFArrayAppendValue(myInstance
->userInterfaces
, interface
);
462 CFRelease(interfaces
);
465 n
= CFSetGetCount(previouslyKnown
);
467 const void * names_q
[32];
468 const void ** names
= names_q
;
470 if (n
> (CFIndex
)(sizeof(names_q
) / sizeof(CFTypeRef
)))
471 names
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
472 CFSetGetValues(previouslyKnown
, names
);
473 for (i
= 0; i
< n
; i
++) {
474 if (myInstance
->userInterfaces
!= NULL
) {
477 j
= CFArrayGetCount(myInstance
->userInterfaces
);
480 SCNetworkInterfaceRef interface
;
482 interface
= CFArrayGetValueAtIndex(myInstance
->userInterfaces
, j
);
483 bsdName
= SCNetworkInterfaceGetBSDName(interface
);
484 if (CFEqual(bsdName
, names
[i
])) {
485 // if we have previously posted a notification
486 // for this no-longer-present interface
487 CFArrayRemoveValueAtIndex(myInstance
->userInterfaces
, j
);
492 CFSetRemoveValue(myInstance
->knownInterfaces
, names
[i
]);
494 if (names
!= names_q
) CFAllocatorDeallocate(NULL
, names
);
499 if (myInstance
->userInterfaces
!= NULL
) {
500 if (myInstance
->no_user_intervention
) {
501 // add network services for new interfaces
502 notify_configure(myInstance
);
505 notify_add(myInstance
);
509 if (set
!= NULL
) CFRelease(set
);
516 watcher_remove(MyType
*myInstance
)
518 if (myInstance
->monitorRls
!= NULL
) {
519 CFRunLoopSourceInvalidate(myInstance
->monitorRls
);
520 CFRelease(myInstance
->monitorRls
);
521 myInstance
->monitorRls
= NULL
;
524 if (myInstance
->knownInterfaces
!= NULL
) {
525 CFRelease(myInstance
->knownInterfaces
);
526 myInstance
->knownInterfaces
= NULL
;
534 watcher_add(MyType
*myInstance
)
537 SCDynamicStoreContext context
= { 0, (void *)myInstance
, NULL
, NULL
, NULL
};
538 CFDictionaryRef dict
;
541 SCDynamicStoreRef store
;
543 bundle
= CFBundleGetBundleWithIdentifier(MY_BUNDLE_ID
);
544 if (bundle
!= NULL
) {
545 CFDictionaryRef info
;
546 CFBooleanRef user_intervention
;
548 info
= CFBundleGetInfoDictionary(bundle
);
549 user_intervention
= CFDictionaryGetValue(info
, CFSTR("User Intervention"));
550 if (isA_CFBoolean(user_intervention
)) {
551 myInstance
->no_user_intervention
= !CFBooleanGetValue(user_intervention
);
555 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCMonitor"), updateInterfaceList
, &context
);
558 CFSTR("SCMonitor: SCDynamicStoreCreate() failed: %s"),
559 SCErrorString(SCError()));
563 key
= SCDynamicStoreKeyCreateNetworkInterface(NULL
, kSCDynamicStoreDomainState
);
565 // watch for changes to the list of network interfaces
566 keys
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
567 SCDynamicStoreSetNotificationKeys(store
, NULL
, keys
);
569 myInstance
->monitorRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
570 CFRunLoopAddSource(CFRunLoopGetCurrent(),
571 myInstance
->monitorRls
,
572 kCFRunLoopDefaultMode
);
574 // initialize the list of known interfaces
575 myInstance
->knownInterfaces
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
576 dict
= SCDynamicStoreCopyValue(store
, key
);
578 if (isA_CFDictionary(dict
)) {
580 CFArrayRef interfaces
;
583 interfaces
= CFDictionaryGetValue(dict
, kSCPropNetInterfaces
);
584 n
= isA_CFArray(interfaces
) ? CFArrayGetCount(interfaces
) : 0;
585 for (i
= 0; i
< n
; i
++) {
588 bsdName
= CFArrayGetValueAtIndex(interfaces
, i
);
589 if (isA_CFString(bsdName
)) {
590 CFSetAddValue(myInstance
->knownInterfaces
, bsdName
);
605 #pragma mark UserEventAgent stubs
609 myQueryInterface(void *myInstance
, REFIID iid
, LPVOID
*ppv
)
611 CFUUIDRef interfaceID
= CFUUIDCreateFromUUIDBytes(NULL
, iid
);
613 // Test the requested ID against the valid interfaces.
614 if (CFEqual(interfaceID
, kUserEventAgentInterfaceID
)) {
615 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
617 CFRelease(interfaceID
);
621 if (CFEqual(interfaceID
, IUnknownUUID
)) {
622 ((MyType
*) myInstance
)->_UserEventAgentInterface
->AddRef(myInstance
);
624 CFRelease(interfaceID
);
628 // Requested interface unknown, bail with error.
630 CFRelease(interfaceID
);
631 return E_NOINTERFACE
;
636 myAddRef(void *myInstance
)
638 ((MyType
*) myInstance
)->_refCount
++;
639 return ((MyType
*) myInstance
)->_refCount
;
644 myRelease(void *myInstance
)
646 ((MyType
*) myInstance
)->_refCount
--;
647 if (((MyType
*) myInstance
)->_refCount
== 0) {
648 CFUUIDRef factoryID
= ((MyType
*) myInstance
)->_factoryID
;
650 if (factoryID
!= NULL
) {
651 CFPlugInRemoveInstanceForFactory(factoryID
);
652 CFRelease(factoryID
);
654 watcher_remove((MyType
*)myInstance
);
655 notify_remove((MyType
*)myInstance
, TRUE
);
661 return ((MyType
*) myInstance
)->_refCount
;
666 myInstall(void *myInstance
)
668 watcher_add((MyType
*)myInstance
);
673 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl
= {
674 NULL
, // Required padding for COM
675 myQueryInterface
, // These three are the required COM functions
678 myInstall
// Interface implementation
683 UserEventAgentFactory(CFAllocatorRef allocator
, CFUUIDRef typeID
)
685 MyType
*newOne
= NULL
;
687 if (CFEqual(typeID
, kUserEventAgentTypeID
)) {
688 newOne
= (MyType
*)malloc(sizeof(MyType
));
689 bzero(newOne
, sizeof(*newOne
));
690 newOne
->_UserEventAgentInterface
= &UserEventAgentInterfaceFtbl
;
691 newOne
->_factoryID
= (CFUUIDRef
)CFRetain(kUserEventAgentFactoryID
);
692 CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID
);
693 newOne
->_refCount
= 1;
702 main(int argc
, char **argv
)
704 MyType
*newOne
= (MyType
*)malloc(sizeof(MyType
));
707 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
709 bzero(newOne
, sizeof(*newOne
));