2 * Copyright (c) 2000-2007 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 * April 2, 2004 Allan Nathanson <ajn@apple.com>
28 * - use SCPreference notification APIs
30 * June 24, 2001 Allan Nathanson <ajn@apple.com>
31 * - update to public SystemConfiguration.framework APIs
33 * November 10, 2000 Allan Nathanson <ajn@apple.com>
39 #include <sys/types.h>
44 #include <SystemConfiguration/SystemConfiguration.h>
45 #include <SystemConfiguration/SCPrivate.h>
46 #include <SystemConfiguration/SCValidation.h>
52 static SCPreferencesRef prefs
= NULL
;
53 static SCDynamicStoreRef store
= NULL
;
55 /* preferences "initialization" globals */
56 static CFStringRef initKey
= NULL
;
57 static CFRunLoopSourceRef initRls
= NULL
;
59 /* SCDynamicStore (Setup:) */
60 static CFMutableDictionaryRef currentPrefs
; /* current prefs */
61 static CFMutableDictionaryRef newPrefs
; /* new prefs */
62 static CFMutableArrayRef unchangedPrefsKeys
; /* new prefs keys which match current */
63 static CFMutableArrayRef removedPrefsKeys
; /* old prefs keys to be removed */
65 static Boolean _verbose
= FALSE
;
69 establishNewPreferences()
73 int sc_status
= kSCStatusFailed
;
74 SCNetworkSetRef set
= NULL
;
75 CFStringRef setName
= NULL
;
78 ok
= SCPreferencesLock(prefs
, TRUE
);
83 sc_status
= SCError();
84 if (sc_status
== kSCStatusStale
) {
85 (void) SCPreferencesSynchronize(prefs
);
88 CFSTR("Could not acquire network configuration lock: %s"),
89 SCErrorString(sc_status
));
94 set
= SCNetworkSetCreate(prefs
);
97 sc_status
= SCError();
101 bundle
= _SC_CFBundleGet();
102 if (bundle
!= NULL
) {
103 setName
= CFBundleCopyLocalizedString(bundle
,
104 CFSTR("DEFAULT_SET_NAME"),
109 ok
= SCNetworkSetSetName(set
, (setName
!= NULL
) ? setName
: CFSTR("Automatic"));
111 sc_status
= SCError();
115 ok
= SCNetworkSetSetCurrent(set
);
117 sc_status
= SCError();
121 ok
= SCNetworkSetEstablishDefaultConfiguration(set
);
123 sc_status
= SCError();
130 ok
= SCPreferencesCommitChanges(prefs
);
132 SCLog(TRUE
, LOG_NOTICE
, CFSTR("New network configuration saved"));
134 sc_status
= SCError();
135 if (sc_status
== EROFS
) {
136 /* a read-only fileysstem is OK */
141 /* apply (committed or temporary/read-only) changes */
142 (void) SCPreferencesApplyChanges(prefs
);
143 } else if (set
!= NULL
) {
144 (void) SCNetworkSetRemove(set
);
149 CFSTR("Could not establish network configuration: %s"),
150 SCErrorString(sc_status
));
153 (void)SCPreferencesUnlock(prefs
);
154 if (setName
!= NULL
) CFRelease(setName
);
155 if (set
!= NULL
) CFRelease(set
);
163 CFDictionaryRef dict
;
164 Boolean quiet
= FALSE
;
167 dict
= SCDynamicStoreCopyValue(store
, initKey
);
169 if (isA_CFDictionary(dict
) &&
170 (CFDictionaryContainsKey(dict
, CFSTR("*QUIET*")) ||
171 CFDictionaryContainsKey(dict
, CFSTR("*TIMEOUT*")))) {
184 if ((initKey
== NULL
) && (initRls
== NULL
)) {
188 (void) SCDynamicStoreSetNotificationKeys(store
, NULL
, NULL
);
190 CFRunLoopSourceInvalidate(initRls
);
207 initKey
= SCDynamicStoreKeyCreate(NULL
,
208 CFSTR("%@" "InterfaceNamer"),
209 kSCDynamicStoreDomainPlugin
);
211 initRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
212 CFRunLoopAddSource(CFRunLoopGetCurrent(), initRls
, kCFRunLoopDefaultMode
);
214 keys
= CFArrayCreate(NULL
, (const void **)&initKey
, 1, &kCFTypeArrayCallBacks
);
215 ok
= SCDynamicStoreSetNotificationKeys(store
, keys
, NULL
);
218 SCPrint(TRUE
, stderr
,
219 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s\n"), SCErrorString(SCError()));
227 watchQuietCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
231 establishNewPreferences();
239 updateCache(const void *key
, const void *value
, void *context
)
241 CFStringRef configKey
= (CFStringRef
)key
;
242 CFPropertyListRef configData
= (CFPropertyListRef
)value
;
243 CFPropertyListRef cacheData
;
246 cacheData
= CFDictionaryGetValue(currentPrefs
, configKey
);
249 if (CFEqual(cacheData
, configData
)) {
251 * if the old & new property list values have
252 * not changed then we don't need to update
255 CFArrayAppendValue(unchangedPrefsKeys
, configKey
);
259 /* in any case, this key should not be removed */
260 i
= CFArrayGetFirstIndexOfValue(removedPrefsKeys
,
261 CFRangeMake(0, CFArrayGetCount(removedPrefsKeys
)),
263 if (i
!= kCFNotFound
) {
264 CFArrayRemoveValueAtIndex(removedPrefsKeys
, i
);
272 flatten(SCPreferencesRef prefs
,
274 CFDictionaryRef base
)
276 CFDictionaryRef subset
;
278 CFMutableDictionaryRef myDict
;
285 if (!CFDictionaryGetValueIfPresent(base
, kSCResvLink
, (const void **)&link
)) {
286 /* if this dictionary is not linked */
289 /* if __LINK__ key is present */
290 subset
= SCPreferencesPathGetValue(prefs
, link
);
292 /* if error with link */
294 CFSTR("SCPreferencesPathGetValue(,%@,) failed: %s"),
296 SCErrorString(SCError()));
301 if (CFDictionaryContainsKey(subset
, kSCResvInactive
)) {
302 /* if __INACTIVE__ key is present */
306 myKey
= CFStringCreateWithFormat(NULL
,
309 kSCDynamicStoreDomainSetup
,
312 myDict
= (CFMutableDictionaryRef
)CFDictionaryGetValue(newPrefs
, myKey
);
314 myDict
= CFDictionaryCreateMutableCopy(NULL
,
316 (CFDictionaryRef
)myDict
);
318 myDict
= CFDictionaryCreateMutable(NULL
,
320 &kCFTypeDictionaryKeyCallBacks
,
321 &kCFTypeDictionaryValueCallBacks
);
324 nKeys
= CFDictionaryGetCount(subset
);
326 keys
= CFAllocatorAllocate(NULL
, nKeys
* sizeof(CFStringRef
) , 0);
327 vals
= CFAllocatorAllocate(NULL
, nKeys
* sizeof(CFPropertyListRef
), 0);
328 CFDictionaryGetKeysAndValues(subset
, keys
, vals
);
329 for (i
= 0; i
< nKeys
; i
++) {
330 if (CFGetTypeID((CFTypeRef
)vals
[i
]) != CFDictionaryGetTypeID()) {
331 /* add this key/value to the current dictionary */
332 CFDictionarySetValue(myDict
, keys
[i
], vals
[i
]);
336 /* flatten [sub]dictionaries */
337 subKey
= CFStringCreateWithFormat(NULL
,
341 CFEqual(key
, CFSTR("/")) ? "" : "/",
343 flatten(prefs
, subKey
, vals
[i
]);
347 CFAllocatorDeallocate(NULL
, keys
);
348 CFAllocatorDeallocate(NULL
, vals
);
351 if (CFDictionaryGetCount(myDict
) > 0) {
352 /* add this dictionary to the new preferences */
353 CFDictionarySetValue(newPrefs
, myKey
, myDict
);
364 updateSCDynamicStore(SCPreferencesRef prefs
)
366 CFStringRef current
= NULL
;
367 CFDateRef date
= NULL
;
368 CFMutableDictionaryRef dict
= NULL
;
369 CFDictionaryRef global
= NULL
;
374 CFMutableArrayRef patterns
;
375 CFDictionaryRef set
= NULL
;
378 * initialize old preferences, new preferences, an array
379 * of keys which have not changed, and an array of keys
380 * to be removed (cleaned up).
383 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
384 pattern
= CFStringCreateWithFormat(NULL
,
387 kSCDynamicStoreDomainSetup
);
388 CFArrayAppendValue(patterns
, pattern
);
389 dict
= (CFMutableDictionaryRef
)SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
393 currentPrefs
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
396 currentPrefs
= CFDictionaryCreateMutable(NULL
,
398 &kCFTypeDictionaryKeyCallBacks
,
399 &kCFTypeDictionaryValueCallBacks
);
402 unchangedPrefsKeys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
404 i
= CFDictionaryGetCount(currentPrefs
);
406 const void **currentKeys
;
409 currentKeys
= CFAllocatorAllocate(NULL
, i
* sizeof(CFStringRef
), 0);
410 CFDictionaryGetKeysAndValues(currentPrefs
, currentKeys
, NULL
);
411 array
= CFArrayCreate(NULL
, currentKeys
, i
, &kCFTypeArrayCallBacks
);
412 removedPrefsKeys
= CFArrayCreateMutableCopy(NULL
, 0, array
);
414 CFAllocatorDeallocate(NULL
, currentKeys
);
416 removedPrefsKeys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
420 * The "newPrefs" dictionary will contain the new / updated
421 * configuration which will be written to the configuration cache.
423 newPrefs
= CFDictionaryCreateMutable(NULL
,
425 &kCFTypeDictionaryKeyCallBacks
,
426 &kCFTypeDictionaryValueCallBacks
);
429 * create status dictionary associated with current configuration
430 * information including:
431 * - current set "name" to cache
432 * - time stamp indicating when the cache preferences were
435 dict
= CFDictionaryCreateMutable(NULL
,
437 &kCFTypeDictionaryKeyCallBacks
,
438 &kCFTypeDictionaryValueCallBacks
);
439 date
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
444 keys
= SCPreferencesCopyKeyList(prefs
);
445 if ((keys
== NULL
) || (CFArrayGetCount(keys
) == 0)) {
446 SCLog(TRUE
, LOG_NOTICE
, CFSTR("updateConfiguration(): no preferences."));
451 * get "global" system preferences
453 global
= SCPreferencesGetValue(prefs
, kSCPrefSystem
);
455 /* if no global preferences are defined */
459 if (!isA_CFDictionary(global
)) {
461 CFSTR("updateConfiguration(): %@ is not a dictionary."),
466 /* flatten property list */
467 flatten(prefs
, CFSTR("/"), global
);
472 * get current set name
474 current
= SCPreferencesGetValue(prefs
, kSCPrefCurrentSet
);
476 /* if current set not defined */
480 if (!isA_CFString(current
)) {
482 CFSTR("updateConfiguration(): %@ is not a string."),
490 set
= SCPreferencesPathGetValue(prefs
, current
);
492 /* if error with path */
494 CFSTR("%@ value (%@) not valid"),
500 if (!isA_CFDictionary(set
)) {
502 CFSTR("updateConfiguration(): %@ is not a dictionary."),
507 /* flatten property list */
508 flatten(prefs
, CFSTR("/"), set
);
510 CFDictionarySetValue(dict
, kSCDynamicStorePropSetupCurrentSet
, current
);
514 /* add last updated time stamp */
515 CFDictionarySetValue(dict
, kSCDynamicStorePropSetupLastUpdated
, date
);
518 CFDictionarySetValue(newPrefs
, kSCDynamicStoreDomainSetup
, dict
);
520 /* compare current and new preferences */
521 CFDictionaryApplyFunction(newPrefs
, updateCache
, NULL
);
523 /* remove those keys which have not changed from the update */
524 n
= CFArrayGetCount(unchangedPrefsKeys
);
525 for (i
= 0; i
< n
; i
++) {
528 key
= CFArrayGetValueAtIndex(unchangedPrefsKeys
, i
);
529 CFDictionaryRemoveValue(newPrefs
, key
);
532 /* Update the dynamic store */
533 if (!SCDynamicStoreSetMultiple(store
, newPrefs
, removedPrefsKeys
, NULL
)) {
535 CFSTR("SCDynamicStoreSetMultiple() failed: %s"),
536 SCErrorString(SCError()));
539 CFRelease(currentPrefs
);
541 CFRelease(unchangedPrefsKeys
);
542 CFRelease(removedPrefsKeys
);
543 if (dict
) CFRelease(dict
);
544 if (date
) CFRelease(date
);
545 if (keys
) CFRelease(keys
);
551 updateConfiguration(SCPreferencesRef prefs
,
552 SCPreferencesNotification notificationType
,
557 if ((notificationType
& kSCPreferencesNotificationCommit
) == kSCPreferencesNotificationCommit
) {
558 SCNetworkSetRef current
;
560 current
= SCNetworkSetCopyCurrent(prefs
);
561 if (current
!= NULL
) {
562 /* network configuration available, disable template creation */
568 if ((notificationType
& kSCPreferencesNotificationApply
) != kSCPreferencesNotificationApply
) {
572 SCLog(_verbose
, LOG_DEBUG
, CFSTR("updating configuration"));
574 /* update SCDynamicStore (Setup:) */
575 updateSCDynamicStore(prefs
);
577 /* finished with current prefs, wait for changes */
578 SCPreferencesSynchronize(prefs
);
586 stop_PreferencesMonitor(CFRunLoopSourceRef stopRls
)
593 if (!SCPreferencesUnscheduleFromRunLoop(prefs
,
594 CFRunLoopGetCurrent(),
595 kCFRunLoopDefaultMode
)) {
597 CFSTR("SCPreferencesUnscheduleFromRunLoop() failed: %s"),
598 SCErrorString(SCError()));
609 CFRunLoopSourceSignal(stopRls
);
616 prime_PreferencesMonitor()
618 SCLog(_verbose
, LOG_DEBUG
, CFSTR("prime() called"));
620 /* load the initial configuration from the database */
621 updateConfiguration(prefs
, kSCPreferencesNotificationApply
, (void *)store
);
629 load_PreferencesMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
631 Boolean initPrefs
= TRUE
;
637 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
638 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
640 /* open a SCDynamicStore session to allow cache updates */
641 store
= SCDynamicStoreCreate(NULL
,
642 CFSTR("PreferencesMonitor.bundle"),
647 CFSTR("SCDynamicStoreCreate() failed: %s"),
648 SCErrorString(SCError()));
652 /* open a SCPreferences session */
653 prefs
= SCPreferencesCreate(NULL
, CFSTR("PreferencesMonitor.bundle"), NULL
);
655 SCNetworkSetRef current
;
657 current
= SCNetworkSetCopyCurrent(prefs
);
658 if (current
!= NULL
) {
659 /* network configuration available, disable template creation */
665 CFSTR("SCPreferencesCreate() failed: %s"),
666 SCErrorString(SCError()));
671 * register for change notifications.
673 if (!SCPreferencesSetCallback(prefs
, updateConfiguration
, NULL
)) {
675 CFSTR("SCPreferencesSetCallBack() failed: %s"),
676 SCErrorString(SCError()));
680 if (!SCPreferencesScheduleWithRunLoop(prefs
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
)) {
682 CFSTR("SCPreferencesScheduleWithRunLoop() failed: %s"),
683 SCErrorString(SCError()));
688 * if no preferences, initialize with a template (now or
689 * when IOKit has quiesced).
693 watchQuietCallback(store
, NULL
, NULL
);
701 if (store
!= NULL
) CFRelease(store
);
702 if (prefs
!= NULL
) CFRelease(prefs
);
710 main(int argc
, char **argv
)
713 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
715 load_PreferencesMonitor(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
716 prime_PreferencesMonitor();