2 * Copyright (c) 2000-2008, 2010, 2012-2014 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>
38 #include <TargetConditionals.h>
40 #include <sys/types.h>
45 #include <SystemConfiguration/SystemConfiguration.h>
46 #include <SystemConfiguration/SCPrivate.h>
47 #include <SystemConfiguration/SCValidation.h>
53 static SCPreferencesRef prefs
= NULL
;
54 static SCDynamicStoreRef store
= NULL
;
56 /* preferences "initialization" globals */
57 static CFStringRef initKey
= NULL
;
58 static CFRunLoopSourceRef initRls
= NULL
;
60 /* SCDynamicStore (Setup:) */
61 static CFMutableDictionaryRef currentPrefs
; /* current prefs */
62 static CFMutableDictionaryRef newPrefs
; /* new prefs */
63 static CFMutableArrayRef unchangedPrefsKeys
; /* new prefs keys which match current */
64 static CFMutableArrayRef removedPrefsKeys
; /* old prefs keys to be removed */
66 static Boolean rofs
= FALSE
;
67 static Boolean _verbose
= FALSE
;
71 establishNewPreferences()
74 SCNetworkSetRef current
= NULL
;
75 CFStringRef new_model
;
77 int sc_status
= kSCStatusFailed
;
78 SCNetworkSetRef set
= NULL
;
79 CFStringRef setName
= NULL
;
80 Boolean updated
= FALSE
;
83 ok
= SCPreferencesLock(prefs
, TRUE
);
88 sc_status
= SCError();
89 if (sc_status
== kSCStatusStale
) {
90 SCPreferencesSynchronize(prefs
);
93 CFSTR("Could not acquire network configuration lock: %s"),
94 SCErrorString(sc_status
));
99 /* Ensure that the preferences has the new model */
100 new_model
= _SC_hw_model(FALSE
);
102 /* Need to regenerate the new configuration for new model */
103 if (new_model
!= NULL
) {
104 CFStringRef old_model
;
106 old_model
= SCPreferencesGetValue(prefs
, MODEL
);
107 if ((old_model
!= NULL
) && !_SC_CFEqual(old_model
, new_model
)) {
112 keys
= SCPreferencesCopyKeyList(prefs
);
113 count
= (keys
!= NULL
) ? CFArrayGetCount(keys
) : 0;
115 for (index
= 0; index
< count
; index
++) {
116 CFStringRef existing_key
;
118 existing_key
= CFArrayGetValueAtIndex(keys
, index
);
119 if (isA_CFString(existing_key
) != NULL
) {
121 CFPropertyListRef value
;
123 /* If it already contains a Model
124 or if it already contains a MODEL:KEY key skip it*/
125 if (CFEqual(existing_key
, MODEL
)
126 || CFStringFind(existing_key
, CFSTR(":"), 0).location
131 value
= SCPreferencesGetValue(prefs
, existing_key
);
133 /* Create a new key as OLD_MODEL:OLD_KEY */
134 new_key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@:%@"),
135 old_model
, existing_key
);
136 SCPreferencesSetValue(prefs
, new_key
, value
);
137 if (!CFEqual(existing_key
, kSCPrefSystem
)) {
138 /* preserve existing host names */
139 SCPreferencesRemoveValue(prefs
, existing_key
);
149 /* Set the new model */
150 SCPreferencesSetValue(prefs
, MODEL
, new_model
);
153 current
= SCNetworkSetCopyCurrent(prefs
);
154 if (current
!= NULL
) {
159 set
= SCNetworkSetCreate(prefs
);
162 sc_status
= SCError();
166 bundle
= _SC_CFBundleGet();
167 if (bundle
!= NULL
) {
168 setName
= CFBundleCopyLocalizedString(bundle
,
169 CFSTR("DEFAULT_SET_NAME"),
174 ok
= SCNetworkSetSetName(set
, (setName
!= NULL
) ? setName
: CFSTR("Automatic"));
176 sc_status
= SCError();
180 ok
= SCNetworkSetSetCurrent(set
);
182 sc_status
= SCError();
187 ok
= SCNetworkSetEstablishDefaultConfiguration(set
);
189 sc_status
= SCError();
196 ok
= SCPreferencesCommitChanges(prefs
);
198 SCLog(TRUE
, LOG_NOTICE
, CFSTR("New network configuration saved"));
201 sc_status
= SCError();
202 if (sc_status
== EROFS
) {
203 /* a read-only fileysstem is OK */
206 /* ... but we don't want to synchronize */
211 /* apply (committed or temporary/read-only) changes */
212 (void) SCPreferencesApplyChanges(prefs
);
213 } else if ((current
== NULL
) && (set
!= NULL
)) {
214 (void) SCNetworkSetRemove(set
);
219 CFSTR("Could not establish network configuration: %s"),
220 SCErrorString(sc_status
));
223 (void)SCPreferencesUnlock(prefs
);
224 if (setName
!= NULL
) CFRelease(setName
);
225 if (set
!= NULL
) CFRelease(set
);
231 quiet(Boolean
*timeout
)
233 CFDictionaryRef dict
;
234 Boolean _quiet
= FALSE
;
235 Boolean _timeout
= FALSE
;
238 dict
= SCDynamicStoreCopyValue(store
, initKey
);
240 if (isA_CFDictionary(dict
)) {
241 if (CFDictionaryContainsKey(dict
, CFSTR("*QUIET*"))) {
244 if (CFDictionaryContainsKey(dict
, CFSTR("*TIMEOUT*"))) {
251 if (timeout
!= NULL
) {
261 if ((initKey
== NULL
) || (initRls
== NULL
)) {
265 (void) SCDynamicStoreSetNotificationKeys(store
, NULL
, NULL
);
267 CFRunLoopSourceInvalidate(initRls
);
284 initKey
= SCDynamicStoreKeyCreate(NULL
,
285 CFSTR("%@" "InterfaceNamer"),
286 kSCDynamicStoreDomainPlugin
);
288 initRls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
289 CFRunLoopAddSource(CFRunLoopGetCurrent(), initRls
, kCFRunLoopDefaultMode
);
291 keys
= CFArrayCreate(NULL
, (const void **)&initKey
, 1, &kCFTypeArrayCallBacks
);
292 ok
= SCDynamicStoreSetNotificationKeys(store
, keys
, NULL
);
296 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s\n"), SCErrorString(SCError()));
307 watchQuietCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *info
)
310 Boolean _timeout
= FALSE
;
312 _quiet
= quiet(&_timeout
);
314 #if !TARGET_OS_IPHONE
316 #endif /* !TARGET_OS_IPHONE */
321 if (_quiet
|| _timeout
) {
322 static int logged
= 0;
324 (void) establishNewPreferences();
325 if (_timeout
&& (logged
++ == 0)) {
326 SCLog(TRUE
, LOG_NOTICE
,
327 CFSTR("Network configuration creation timed out waiting for IORegistry"));
336 updateCache(const void *key
, const void *value
, void *context
)
338 CFStringRef configKey
= (CFStringRef
)key
;
339 CFPropertyListRef configData
= (CFPropertyListRef
)value
;
340 CFPropertyListRef cacheData
;
343 cacheData
= CFDictionaryGetValue(currentPrefs
, configKey
);
346 if (CFEqual(cacheData
, configData
)) {
348 * if the old & new property list values have
349 * not changed then we don't need to update
352 CFArrayAppendValue(unchangedPrefsKeys
, configKey
);
356 /* in any case, this key should not be removed */
357 i
= CFArrayGetFirstIndexOfValue(removedPrefsKeys
,
358 CFRangeMake(0, CFArrayGetCount(removedPrefsKeys
)),
360 if (i
!= kCFNotFound
) {
361 CFArrayRemoveValueAtIndex(removedPrefsKeys
, i
);
369 flatten(SCPreferencesRef prefs
,
371 CFDictionaryRef base
)
373 CFDictionaryRef subset
;
375 CFMutableDictionaryRef myDict
;
382 if (!CFDictionaryGetValueIfPresent(base
, kSCResvLink
, (const void **)&link
)) {
383 /* if this dictionary is not linked */
386 /* if __LINK__ key is present */
387 subset
= SCPreferencesPathGetValue(prefs
, link
);
389 /* if error with link */
391 CFSTR("SCPreferencesPathGetValue(,%@,) failed: %s"),
393 SCErrorString(SCError()));
398 if (CFDictionaryContainsKey(subset
, kSCResvInactive
)) {
399 /* if __INACTIVE__ key is present */
403 myKey
= CFStringCreateWithFormat(NULL
,
406 kSCDynamicStoreDomainSetup
,
409 myDict
= (CFMutableDictionaryRef
)CFDictionaryGetValue(newPrefs
, myKey
);
411 myDict
= CFDictionaryCreateMutableCopy(NULL
,
413 (CFDictionaryRef
)myDict
);
415 myDict
= CFDictionaryCreateMutable(NULL
,
417 &kCFTypeDictionaryKeyCallBacks
,
418 &kCFTypeDictionaryValueCallBacks
);
421 nKeys
= CFDictionaryGetCount(subset
);
423 keys
= CFAllocatorAllocate(NULL
, nKeys
* sizeof(CFStringRef
) , 0);
424 vals
= CFAllocatorAllocate(NULL
, nKeys
* sizeof(CFPropertyListRef
), 0);
425 CFDictionaryGetKeysAndValues(subset
, keys
, vals
);
426 for (i
= 0; i
< nKeys
; i
++) {
427 if (CFGetTypeID((CFTypeRef
)vals
[i
]) != CFDictionaryGetTypeID()) {
428 /* add this key/value to the current dictionary */
429 CFDictionarySetValue(myDict
, keys
[i
], vals
[i
]);
433 /* flatten [sub]dictionaries */
434 subKey
= CFStringCreateWithFormat(NULL
,
438 CFEqual(key
, CFSTR("/")) ? "" : "/",
440 flatten(prefs
, subKey
, vals
[i
]);
444 CFAllocatorDeallocate(NULL
, keys
);
445 CFAllocatorDeallocate(NULL
, vals
);
448 if (CFDictionaryGetCount(myDict
) > 0) {
449 /* add this dictionary to the new preferences */
450 CFDictionarySetValue(newPrefs
, myKey
, myDict
);
461 updateSCDynamicStore(SCPreferencesRef prefs
)
463 CFStringRef current
= NULL
;
464 CFDateRef date
= NULL
;
465 CFMutableDictionaryRef dict
= NULL
;
466 CFDictionaryRef global
= NULL
;
471 CFMutableArrayRef patterns
;
472 CFDictionaryRef set
= NULL
;
475 * initialize old preferences, new preferences, an array
476 * of keys which have not changed, and an array of keys
477 * to be removed (cleaned up).
480 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
481 pattern
= CFStringCreateWithFormat(NULL
,
484 kSCDynamicStoreDomainSetup
);
485 CFArrayAppendValue(patterns
, pattern
);
486 dict
= (CFMutableDictionaryRef
)SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
490 currentPrefs
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
493 currentPrefs
= CFDictionaryCreateMutable(NULL
,
495 &kCFTypeDictionaryKeyCallBacks
,
496 &kCFTypeDictionaryValueCallBacks
);
499 unchangedPrefsKeys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
501 i
= CFDictionaryGetCount(currentPrefs
);
503 const void **currentKeys
;
506 currentKeys
= CFAllocatorAllocate(NULL
, i
* sizeof(CFStringRef
), 0);
507 CFDictionaryGetKeysAndValues(currentPrefs
, currentKeys
, NULL
);
508 array
= CFArrayCreate(NULL
, currentKeys
, i
, &kCFTypeArrayCallBacks
);
509 removedPrefsKeys
= CFArrayCreateMutableCopy(NULL
, 0, array
);
511 CFAllocatorDeallocate(NULL
, currentKeys
);
513 removedPrefsKeys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
517 * The "newPrefs" dictionary will contain the new / updated
518 * configuration which will be written to the configuration cache.
520 newPrefs
= CFDictionaryCreateMutable(NULL
,
522 &kCFTypeDictionaryKeyCallBacks
,
523 &kCFTypeDictionaryValueCallBacks
);
526 * create status dictionary associated with current configuration
527 * information including:
528 * - current set "name" to cache
529 * - time stamp indicating when the cache preferences were
532 dict
= CFDictionaryCreateMutable(NULL
,
534 &kCFTypeDictionaryKeyCallBacks
,
535 &kCFTypeDictionaryValueCallBacks
);
536 date
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
541 keys
= SCPreferencesCopyKeyList(prefs
);
542 if ((keys
== NULL
) || (CFArrayGetCount(keys
) == 0)) {
543 SCLog(TRUE
, LOG_NOTICE
, CFSTR("updateConfiguration(): no preferences."));
548 * get "global" system preferences
550 global
= SCPreferencesGetValue(prefs
, kSCPrefSystem
);
552 /* if no global preferences are defined */
556 if (!isA_CFDictionary(global
)) {
558 CFSTR("updateConfiguration(): %@ is not a dictionary."),
563 /* flatten property list */
564 flatten(prefs
, CFSTR("/"), global
);
569 * get current set name
571 current
= SCPreferencesGetValue(prefs
, kSCPrefCurrentSet
);
573 /* if current set not defined */
577 if (!isA_CFString(current
)) {
579 CFSTR("updateConfiguration(): %@ is not a string."),
587 set
= SCPreferencesPathGetValue(prefs
, current
);
589 /* if error with path */
591 CFSTR("%@ value (%@) not valid"),
597 if (!isA_CFDictionary(set
)) {
599 CFSTR("updateConfiguration(): %@ is not a dictionary."),
604 /* flatten property list */
605 flatten(prefs
, CFSTR("/"), set
);
607 CFDictionarySetValue(dict
, kSCDynamicStorePropSetupCurrentSet
, current
);
611 /* add last updated time stamp */
612 CFDictionarySetValue(dict
, kSCDynamicStorePropSetupLastUpdated
, date
);
615 CFDictionarySetValue(newPrefs
, kSCDynamicStoreDomainSetup
, dict
);
617 /* compare current and new preferences */
618 CFDictionaryApplyFunction(newPrefs
, updateCache
, NULL
);
620 /* remove those keys which have not changed from the update */
621 n
= CFArrayGetCount(unchangedPrefsKeys
);
622 for (i
= 0; i
< n
; i
++) {
625 key
= CFArrayGetValueAtIndex(unchangedPrefsKeys
, i
);
626 CFDictionaryRemoveValue(newPrefs
, key
);
629 /* Update the dynamic store */
631 if (!SCDynamicStoreSetMultiple(store
, newPrefs
, removedPrefsKeys
, NULL
)) {
633 CFSTR("SCDynamicStoreSetMultiple() failed: %s"),
634 SCErrorString(SCError()));
637 SCLog(TRUE
, LOG_NOTICE
,
638 CFSTR("SCDynamicStore\nset: %@\nremove: %@\n"),
643 CFRelease(currentPrefs
);
645 CFRelease(unchangedPrefsKeys
);
646 CFRelease(removedPrefsKeys
);
647 if (dict
) CFRelease(dict
);
648 if (date
) CFRelease(date
);
649 if (keys
) CFRelease(keys
);
655 updateConfiguration(SCPreferencesRef prefs
,
656 SCPreferencesNotification notificationType
,
661 #if !TARGET_OS_IPHONE
662 if ((notificationType
& kSCPreferencesNotificationCommit
) == kSCPreferencesNotificationCommit
) {
663 SCNetworkSetRef current
;
665 current
= SCNetworkSetCopyCurrent(prefs
);
666 if (current
!= NULL
) {
667 /* network configuration available, disable template creation */
672 #endif /* !TARGET_OS_IPHONE */
674 if ((notificationType
& kSCPreferencesNotificationApply
) != kSCPreferencesNotificationApply
) {
678 SCLog(_verbose
, LOG_DEBUG
, CFSTR("updating configuration"));
680 /* update SCDynamicStore (Setup:) */
681 updateSCDynamicStore(prefs
);
683 /* finished with current prefs, wait for changes */
685 SCPreferencesSynchronize(prefs
);
694 prime_PreferencesMonitor()
696 SCLog(_verbose
, LOG_DEBUG
, CFSTR("prime() called"));
698 /* load the initial configuration from the database */
699 updateConfiguration(prefs
, kSCPreferencesNotificationApply
, (void *)store
);
707 load_PreferencesMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
709 Boolean initPrefs
= TRUE
;
715 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
716 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
718 /* open a SCDynamicStore session to allow cache updates */
719 store
= SCDynamicStoreCreate(NULL
,
720 CFSTR("PreferencesMonitor.bundle"),
725 CFSTR("SCDynamicStoreCreate() failed: %s"),
726 SCErrorString(SCError()));
730 /* open a SCPreferences session */
732 prefs
= SCPreferencesCreate(NULL
, CFSTR("PreferencesMonitor.bundle"), NULL
);
734 prefs
= SCPreferencesCreate(NULL
, CFSTR("PreferencesMonitor.bundle"), CFSTR("/tmp/preferences.plist"));
737 Boolean need_update
= FALSE
;
738 CFStringRef new_model
;
740 new_model
= _SC_hw_model(FALSE
);
742 /* Need to regenerate the new configuration for new model */
743 if (new_model
!= NULL
) {
744 CFStringRef old_model
;
746 old_model
= SCPreferencesGetValue(prefs
, MODEL
);
747 if (old_model
!= NULL
&& !_SC_CFEqual(old_model
, new_model
)) {
753 if (need_update
== FALSE
) {
754 SCNetworkSetRef current
;
756 current
= SCNetworkSetCopyCurrent(prefs
);
757 if (current
!= NULL
) {
758 /* network configuration available, disable template creation */
765 CFSTR("SCPreferencesCreate() failed: %s"),
766 SCErrorString(SCError()));
771 * register for change notifications.
773 if (!SCPreferencesSetCallback(prefs
, updateConfiguration
, NULL
)) {
775 CFSTR("SCPreferencesSetCallBack() failed: %s"),
776 SCErrorString(SCError()));
780 if (!SCPreferencesScheduleWithRunLoop(prefs
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
)) {
782 CFSTR("SCPreferencesScheduleWithRunLoop() failed: %s"),
783 SCErrorString(SCError()));
788 * if no preferences, initialize with a template (now or
789 * when IOKit has quiesced).
793 watchQuietCallback(store
, NULL
, NULL
);
801 if (store
!= NULL
) CFRelease(store
);
802 if (prefs
!= NULL
) CFRelease(prefs
);
810 main(int argc
, char **argv
)
813 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
815 load_PreferencesMonitor(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
816 prime_PreferencesMonitor();