2 * Copyright (c) 2000-2005 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 * 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>
51 static SCPreferencesRef prefs
= NULL
;
52 static SCDynamicStoreRef store
= NULL
;
54 static CFMutableDictionaryRef currentPrefs
; /* current prefs */
55 static CFMutableDictionaryRef newPrefs
; /* new prefs */
56 static CFMutableArrayRef unchangedPrefsKeys
; /* new prefs keys which match current */
57 static CFMutableArrayRef removedPrefsKeys
; /* old prefs keys to be removed */
59 static Boolean _verbose
= FALSE
;
63 updateCache(const void *key
, const void *value
, void *context
)
65 CFStringRef configKey
= (CFStringRef
)key
;
66 CFPropertyListRef configData
= (CFPropertyListRef
)value
;
67 CFPropertyListRef cacheData
;
70 cacheData
= CFDictionaryGetValue(currentPrefs
, configKey
);
73 if (CFEqual(cacheData
, configData
)) {
75 * if the old & new property list values have
76 * not changed then we don't need to update
79 CFArrayAppendValue(unchangedPrefsKeys
, configKey
);
83 /* in any case, this key should not be removed */
84 i
= CFArrayGetFirstIndexOfValue(removedPrefsKeys
,
85 CFRangeMake(0, CFArrayGetCount(removedPrefsKeys
)),
87 if (i
!= kCFNotFound
) {
88 CFArrayRemoveValueAtIndex(removedPrefsKeys
, i
);
96 flatten(SCPreferencesRef prefs
,
100 CFDictionaryRef subset
;
102 CFMutableDictionaryRef myDict
;
109 if (!CFDictionaryGetValueIfPresent(base
, kSCResvLink
, (const void **)&link
)) {
110 /* if this dictionary is not linked */
113 /* if __LINK__ key is present */
114 subset
= SCPreferencesPathGetValue(prefs
, link
);
116 /* if error with link */
118 CFSTR("SCPreferencesPathGetValue(,%@,) failed: %s"),
120 SCErrorString(SCError()));
125 if (CFDictionaryContainsKey(subset
, kSCResvInactive
)) {
126 /* if __INACTIVE__ key is present */
130 myKey
= CFStringCreateWithFormat(NULL
,
133 kSCDynamicStoreDomainSetup
,
136 myDict
= (CFMutableDictionaryRef
)CFDictionaryGetValue(newPrefs
, myKey
);
138 myDict
= CFDictionaryCreateMutableCopy(NULL
,
140 (CFDictionaryRef
)myDict
);
142 myDict
= CFDictionaryCreateMutable(NULL
,
144 &kCFTypeDictionaryKeyCallBacks
,
145 &kCFTypeDictionaryValueCallBacks
);
148 nKeys
= CFDictionaryGetCount(subset
);
150 keys
= CFAllocatorAllocate(NULL
, nKeys
* sizeof(CFStringRef
) , 0);
151 vals
= CFAllocatorAllocate(NULL
, nKeys
* sizeof(CFPropertyListRef
), 0);
152 CFDictionaryGetKeysAndValues(subset
, keys
, vals
);
153 for (i
= 0; i
< nKeys
; i
++) {
154 if (CFGetTypeID((CFTypeRef
)vals
[i
]) != CFDictionaryGetTypeID()) {
155 /* add this key/value to the current dictionary */
156 CFDictionarySetValue(myDict
, keys
[i
], vals
[i
]);
160 /* flatten [sub]dictionaries */
161 subKey
= CFStringCreateWithFormat(NULL
,
165 CFEqual(key
, CFSTR("/")) ? "" : "/",
167 flatten(prefs
, subKey
, vals
[i
]);
171 CFAllocatorDeallocate(NULL
, keys
);
172 CFAllocatorDeallocate(NULL
, vals
);
175 if (CFDictionaryGetCount(myDict
) > 0) {
176 /* add this dictionary to the new preferences */
177 CFDictionarySetValue(newPrefs
, myKey
, myDict
);
188 updateConfiguration(SCPreferencesRef prefs
,
189 SCPreferencesNotification notificationType
,
192 CFStringRef current
= NULL
;
193 CFDateRef date
= NULL
;
194 CFMutableDictionaryRef dict
= NULL
;
195 CFDictionaryRef global
= NULL
;
200 CFMutableArrayRef patterns
;
201 CFDictionaryRef set
= NULL
;
204 if ((notificationType
& kSCPreferencesNotificationApply
) != kSCPreferencesNotificationApply
) {
208 SCLog(_verbose
, LOG_DEBUG
, CFSTR("updating configuration"));
211 * initialize old preferences, new preferences, an array
212 * of keys which have not changed, and an array of keys
213 * to be removed (cleaned up).
216 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
217 pattern
= CFStringCreateWithFormat(NULL
,
220 kSCDynamicStoreDomainSetup
);
221 CFArrayAppendValue(patterns
, pattern
);
222 dict
= (CFMutableDictionaryRef
)SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
226 currentPrefs
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
229 currentPrefs
= CFDictionaryCreateMutable(NULL
,
231 &kCFTypeDictionaryKeyCallBacks
,
232 &kCFTypeDictionaryValueCallBacks
);
235 unchangedPrefsKeys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
237 i
= CFDictionaryGetCount(currentPrefs
);
239 const void **currentKeys
;
242 currentKeys
= CFAllocatorAllocate(NULL
, i
* sizeof(CFStringRef
), 0);
243 CFDictionaryGetKeysAndValues(currentPrefs
, currentKeys
, NULL
);
244 array
= CFArrayCreate(NULL
, currentKeys
, i
, &kCFTypeArrayCallBacks
);
245 removedPrefsKeys
= CFArrayCreateMutableCopy(NULL
, 0, array
);
247 CFAllocatorDeallocate(NULL
, currentKeys
);
249 removedPrefsKeys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
253 * The "newPrefs" dictionary will contain the new / updated
254 * configuration which will be written to the configuration cache.
256 newPrefs
= CFDictionaryCreateMutable(NULL
,
258 &kCFTypeDictionaryKeyCallBacks
,
259 &kCFTypeDictionaryValueCallBacks
);
262 * create status dictionary associated with current configuration
263 * information including:
264 * - current set "name" to cache
265 * - time stamp indicating when the cache preferences were
268 dict
= CFDictionaryCreateMutable(NULL
,
270 &kCFTypeDictionaryKeyCallBacks
,
271 &kCFTypeDictionaryValueCallBacks
);
272 date
= CFDateCreate(NULL
, CFAbsoluteTimeGetCurrent());
277 keys
= SCPreferencesCopyKeyList(prefs
);
278 if ((keys
== NULL
) || (CFArrayGetCount(keys
) == 0)) {
279 SCLog(TRUE
, LOG_NOTICE
, CFSTR("updateConfiguration(): no preferences."));
284 * get "global" system preferences
286 global
= SCPreferencesGetValue(prefs
, kSCPrefSystem
);
288 /* if no global preferences are defined */
292 if (!isA_CFDictionary(global
)) {
294 CFSTR("updateConfiguration(): %@ is not a dictionary."),
299 /* flatten property list */
300 flatten(prefs
, CFSTR("/"), global
);
305 * get current set name
307 current
= SCPreferencesGetValue(prefs
, kSCPrefCurrentSet
);
309 /* if current set not defined */
313 if (!isA_CFString(current
)) {
315 CFSTR("updateConfiguration(): %@ is not a string."),
323 set
= SCPreferencesPathGetValue(prefs
, current
);
325 /* if error with path */
327 CFSTR("%@ value (%@) not valid"),
333 if (!isA_CFDictionary(set
)) {
335 CFSTR("updateConfiguration(): %@ is not a dictionary."),
340 /* flatten property list */
341 flatten(prefs
, CFSTR("/"), set
);
343 CFDictionarySetValue(dict
, kSCDynamicStorePropSetupCurrentSet
, current
);
347 /* add last updated time stamp */
348 CFDictionarySetValue(dict
, kSCDynamicStorePropSetupLastUpdated
, date
);
351 CFDictionarySetValue(newPrefs
, kSCDynamicStoreDomainSetup
, dict
);
353 /* compare current and new preferences */
354 CFDictionaryApplyFunction(newPrefs
, updateCache
, NULL
);
356 /* remove those keys which have not changed from the update */
357 n
= CFArrayGetCount(unchangedPrefsKeys
);
358 for (i
= 0; i
< n
; i
++) {
361 key
= CFArrayGetValueAtIndex(unchangedPrefsKeys
, i
);
362 CFDictionaryRemoveValue(newPrefs
, key
);
365 /* Update the dynamic store */
366 if (!SCDynamicStoreSetMultiple(store
, newPrefs
, removedPrefsKeys
, NULL
)) {
368 CFSTR("SCDynamicStoreSetMultiple() failed: %s"),
369 SCErrorString(SCError()));
372 /* finished with current prefs, wait for changes */
373 SCPreferencesSynchronize(prefs
);
375 CFRelease(currentPrefs
);
377 CFRelease(unchangedPrefsKeys
);
378 CFRelease(removedPrefsKeys
);
379 if (dict
) CFRelease(dict
);
380 if (date
) CFRelease(date
);
381 if (keys
) CFRelease(keys
);
388 stop_PreferencesMonitor(CFRunLoopSourceRef stopRls
)
393 if (!SCPreferencesUnscheduleFromRunLoop(prefs
,
394 CFRunLoopGetCurrent(),
395 kCFRunLoopDefaultMode
)) {
397 CFSTR("SCPreferencesUnscheduleFromRunLoop() failed: %s"),
398 SCErrorString(SCError()));
409 CFRunLoopSourceSignal(stopRls
);
416 prime_PreferencesMonitor()
418 SCLog(_verbose
, LOG_DEBUG
, CFSTR("prime() called"));
420 /* load the initial configuration from the database */
421 updateConfiguration(prefs
, kSCPreferencesNotificationApply
, (void *)store
);
429 load_PreferencesMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
435 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
436 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
438 /* open a SCDynamicStore session to allow cache updates */
439 store
= SCDynamicStoreCreate(NULL
, CFSTR("PreferencesMonitor.bundle"), NULL
, NULL
);
442 CFSTR("SCDynamicStoreCreate() failed: %s"),
443 SCErrorString(SCError()));
447 /* open a SCPreferences session */
448 prefs
= SCPreferencesCreate(NULL
, CFSTR("PreferencesMonitor.bundle"), NULL
);
451 CFSTR("SCPreferencesCreate() failed: %s"),
452 SCErrorString(SCError()));
456 if (!SCPreferencesSetCallback(prefs
, updateConfiguration
, NULL
)) {
458 CFSTR("SCPreferencesSetCallBack() failed: %s"),
459 SCErrorString(SCError()));
464 * register for change notifications.
466 if (!SCPreferencesScheduleWithRunLoop(prefs
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
)) {
468 CFSTR("SCPreferencesScheduleWithRunLoop() failed: %s"),
469 SCErrorString(SCError()));
477 if (store
) CFRelease(store
);
478 if (prefs
) CFRelease(prefs
);
486 main(int argc
, char **argv
)
489 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
491 load_PreferencesMonitor(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);
492 prime_PreferencesMonitor();