X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/4c5e92e2493bdfbbce40e998f3b607c72c47af2c..dbf6a266c384fc8b55e00a396eebe5cb62e21547:/Plugins/PreferencesMonitor/prefsmon.c diff --git a/Plugins/PreferencesMonitor/prefsmon.c b/Plugins/PreferencesMonitor/prefsmon.c new file mode 100644 index 0000000..9071ef7 --- /dev/null +++ b/Plugins/PreferencesMonitor/prefsmon.c @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * Modification History + * + * April 2, 2004 Allan Nathanson + * - use SCPreference notification APIs + * + * June 24, 2001 Allan Nathanson + * - update to public SystemConfiguration.framework APIs + * + * November 10, 2000 Allan Nathanson + * - initial revision + */ + + +#include +#include +#include +#include + + +#include +#include +#include + + +static SCPreferencesRef prefs = NULL; +static SCDynamicStoreRef store = NULL; + +static CFMutableDictionaryRef currentPrefs; /* current prefs */ +static CFMutableDictionaryRef newPrefs; /* new prefs */ +static CFMutableArrayRef unchangedPrefsKeys; /* new prefs keys which match current */ +static CFMutableArrayRef removedPrefsKeys; /* old prefs keys to be removed */ + +static Boolean _verbose = FALSE; + + +static void +updateCache(const void *key, const void *value, void *context) +{ + CFStringRef configKey = (CFStringRef)key; + CFPropertyListRef configData = (CFPropertyListRef)value; + CFPropertyListRef cacheData; + CFIndex i; + + cacheData = CFDictionaryGetValue(currentPrefs, configKey); + if (cacheData) { + /* key exists */ + if (CFEqual(cacheData, configData)) { + /* + * if the old & new property list values have + * not changed then we don't need to update + * the preference. + */ + CFArrayAppendValue(unchangedPrefsKeys, configKey); + } + } + + /* in any case, this key should not be removed */ + i = CFArrayGetFirstIndexOfValue(removedPrefsKeys, + CFRangeMake(0, CFArrayGetCount(removedPrefsKeys)), + configKey); + if (i != kCFNotFound) { + CFArrayRemoveValueAtIndex(removedPrefsKeys, i); + } + + return; +} + + +static void +flatten(SCPreferencesRef prefs, + CFStringRef key, + CFDictionaryRef base) +{ + CFDictionaryRef subset; + CFStringRef link; + CFMutableDictionaryRef myDict; + CFStringRef myKey; + CFIndex i; + CFIndex nKeys; + const void **keys; + const void **vals; + + if (!CFDictionaryGetValueIfPresent(base, kSCResvLink, (const void **)&link)) { + /* if this dictionary is not linked */ + subset = base; + } else { + /* if __LINK__ key is present */ + subset = SCPreferencesPathGetValue(prefs, link); + if (!subset) { + /* if error with link */ + SCLog(TRUE, LOG_ERR, + CFSTR("SCPreferencesPathGetValue(,%@,) failed: %s"), + link, + SCErrorString(SCError())); + return; + } + } + + if (CFDictionaryContainsKey(subset, kSCResvInactive)) { + /* if __INACTIVE__ key is present */ + return; + } + + myKey = CFStringCreateWithFormat(NULL, + NULL, + CFSTR("%@%@"), + kSCDynamicStoreDomainSetup, + key); + + myDict = (CFMutableDictionaryRef)CFDictionaryGetValue(newPrefs, myKey); + if (myDict) { + myDict = CFDictionaryCreateMutableCopy(NULL, + 0, + (CFDictionaryRef)myDict); + } else { + myDict = CFDictionaryCreateMutable(NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + } + + nKeys = CFDictionaryGetCount(subset); + if (nKeys > 0) { + keys = CFAllocatorAllocate(NULL, nKeys * sizeof(CFStringRef) , 0); + vals = CFAllocatorAllocate(NULL, nKeys * sizeof(CFPropertyListRef), 0); + CFDictionaryGetKeysAndValues(subset, keys, vals); + for (i = 0; i < nKeys; i++) { + if (CFGetTypeID((CFTypeRef)vals[i]) != CFDictionaryGetTypeID()) { + /* add this key/value to the current dictionary */ + CFDictionarySetValue(myDict, keys[i], vals[i]); + } else { + CFStringRef subKey; + + /* flatten [sub]dictionaries */ + subKey = CFStringCreateWithFormat(NULL, + NULL, + CFSTR("%@%s%@"), + key, + CFEqual(key, CFSTR("/")) ? "" : "/", + keys[i]); + flatten(prefs, subKey, vals[i]); + CFRelease(subKey); + } + } + CFAllocatorDeallocate(NULL, keys); + CFAllocatorDeallocate(NULL, vals); + } + + if (CFDictionaryGetCount(myDict) > 0) { + /* add this dictionary to the new preferences */ + CFDictionarySetValue(newPrefs, myKey, myDict); + } + + CFRelease(myDict); + CFRelease(myKey); + + return; +} + + +static void +updateConfiguration(SCPreferencesRef prefs, + SCPreferencesNotification notificationType, + void *info) +{ + CFStringRef current = NULL; + CFDateRef date = NULL; + CFMutableDictionaryRef dict = NULL; + CFDictionaryRef global = NULL; + CFIndex i; + CFArrayRef keys; + CFIndex n; + CFStringRef pattern; + CFMutableArrayRef patterns; + CFDictionaryRef set = NULL; + + if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) { + return; + } + + SCLog(_verbose, LOG_DEBUG, CFSTR("updating configuration")); + + /* + * initialize old preferences, new preferences, an array + * of keys which have not changed, and an array of keys + * to be removed (cleaned up). + */ + + patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + pattern = CFStringCreateWithFormat(NULL, + NULL, + CFSTR("^%@.*"), + kSCDynamicStoreDomainSetup); + CFArrayAppendValue(patterns, pattern); + dict = (CFMutableDictionaryRef)SCDynamicStoreCopyMultiple(store, NULL, patterns); + CFRelease(patterns); + CFRelease(pattern); + if (dict) { + currentPrefs = CFDictionaryCreateMutableCopy(NULL, 0, dict); + CFRelease(dict); + } else { + currentPrefs = CFDictionaryCreateMutable(NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + } + + unchangedPrefsKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + i = CFDictionaryGetCount(currentPrefs); + if (i > 0) { + const void **currentKeys; + CFArrayRef array; + + currentKeys = CFAllocatorAllocate(NULL, i * sizeof(CFStringRef), 0); + CFDictionaryGetKeysAndValues(currentPrefs, currentKeys, NULL); + array = CFArrayCreate(NULL, currentKeys, i, &kCFTypeArrayCallBacks); + removedPrefsKeys = CFArrayCreateMutableCopy(NULL, 0, array); + CFRelease(array); + CFAllocatorDeallocate(NULL, currentKeys); + } else { + removedPrefsKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + } + + /* + * The "newPrefs" dictionary will contain the new / updated + * configuration which will be written to the configuration cache. + */ + newPrefs = CFDictionaryCreateMutable(NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + /* + * create status dictionary associated with current configuration + * information including: + * - current set "name" to cache + * - time stamp indicating when the cache preferences were + * last updated. + */ + dict = CFDictionaryCreateMutable(NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + date = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent()); + + /* + * load preferences + */ + keys = SCPreferencesCopyKeyList(prefs); + if ((keys == NULL) || (CFArrayGetCount(keys) == 0)) { + SCLog(TRUE, LOG_NOTICE, CFSTR("updateConfiguration(): no preferences.")); + goto done; + } + + /* + * get "global" system preferences + */ + (CFPropertyListRef)global = SCPreferencesGetValue(prefs, kSCPrefSystem); + if (!global) { + /* if no global preferences are defined */ + goto getSet; + } + + if (!isA_CFDictionary(global)) { + SCLog(TRUE, LOG_ERR, + CFSTR("updateConfiguration(): %@ is not a dictionary."), + kSCPrefSystem); + goto done; + } + + /* flatten property list */ + flatten(prefs, CFSTR("/"), global); + + getSet : + + /* + * get current set name + */ + (CFPropertyListRef)current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet); + if (!current) { + /* if current set not defined */ + goto done; + } + + if (!isA_CFString(current)) { + SCLog(TRUE, LOG_ERR, + CFSTR("updateConfiguration(): %@ is not a string."), + kSCPrefCurrentSet); + goto done; + } + + /* + * get current set + */ + (CFPropertyListRef)set = SCPreferencesPathGetValue(prefs, current); + if (!set) { + /* if error with path */ + SCLog(TRUE, LOG_ERR, + CFSTR("%@ value (%@) not valid"), + kSCPrefCurrentSet, + current); + goto done; + } + + if (!isA_CFDictionary(set)) { + SCLog(TRUE, LOG_ERR, + CFSTR("updateConfiguration(): %@ is not a dictionary."), + current); + goto done; + } + + /* flatten property list */ + flatten(prefs, CFSTR("/"), set); + + CFDictionarySetValue(dict, kSCDynamicStorePropSetupCurrentSet, current); + + done : + + /* add last updated time stamp */ + CFDictionarySetValue(dict, kSCDynamicStorePropSetupLastUpdated, date); + + /* add Setup: key */ + CFDictionarySetValue(newPrefs, kSCDynamicStoreDomainSetup, dict); + + /* compare current and new preferences */ + CFDictionaryApplyFunction(newPrefs, updateCache, NULL); + + /* remove those keys which have not changed from the update */ + n = CFArrayGetCount(unchangedPrefsKeys); + for (i = 0; i < n; i++) { + CFStringRef key; + + key = CFArrayGetValueAtIndex(unchangedPrefsKeys, i); + CFDictionaryRemoveValue(newPrefs, key); + } + + /* Update the dynamic store */ + if (!SCDynamicStoreSetMultiple(store, newPrefs, removedPrefsKeys, NULL)) { + SCLog(TRUE, LOG_ERR, + CFSTR("SCDynamicStoreSetMultiple() failed: %s"), + SCErrorString(SCError())); + } + + /* finished with current prefs, wait for changes */ + SCPreferencesSynchronize(prefs); + + CFRelease(currentPrefs); + CFRelease(newPrefs); + CFRelease(unchangedPrefsKeys); + CFRelease(removedPrefsKeys); + if (dict) CFRelease(dict); + if (date) CFRelease(date); + if (keys) CFRelease(keys); + return; +} + + +__private_extern__ +void +stop_PreferencesMonitor(CFRunLoopSourceRef stopRls) +{ + // cleanup + + if (prefs != NULL) { + if (!SCPreferencesUnscheduleFromRunLoop(prefs, + CFRunLoopGetCurrent(), + kCFRunLoopDefaultMode)) { + SCLog(TRUE, LOG_ERR, + CFSTR("SCPreferencesUnscheduleFromRunLoop() failed: %s"), + SCErrorString(SCError())); + } + CFRelease(prefs); + prefs = NULL; + } + + if (store != NULL) { + CFRelease(store); + store = NULL; + } + + CFRunLoopSourceSignal(stopRls); + return; +} + + +__private_extern__ +void +prime_PreferencesMonitor() +{ + SCLog(_verbose, LOG_DEBUG, CFSTR("prime() called")); + + /* load the initial configuration from the database */ + updateConfiguration(prefs, kSCPreferencesNotificationApply, (void *)store); + + return; +} + + +__private_extern__ +void +load_PreferencesMonitor(CFBundleRef bundle, Boolean bundleVerbose) +{ + if (bundleVerbose) { + _verbose = TRUE; + } + + SCLog(_verbose, LOG_DEBUG, CFSTR("load() called")); + SCLog(_verbose, LOG_DEBUG, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle)); + + /* open a SCDynamicStore session to allow cache updates */ + store = SCDynamicStoreCreate(NULL, CFSTR("PreferencesMonitor.bundle"), NULL, NULL); + if (store == NULL) { + SCLog(TRUE, LOG_ERR, + CFSTR("SCDynamicStoreCreate() failed: %s"), + SCErrorString(SCError())); + goto error; + } + + /* open a SCPreferences session */ + prefs = SCPreferencesCreate(NULL, CFSTR("PreferencesMonitor.bundle"), NULL); + if (prefs == NULL) { + SCLog(TRUE, LOG_ERR, + CFSTR("SCPreferencesCreate() failed: %s"), + SCErrorString(SCError())); + goto error; + } + + if (!SCPreferencesSetCallback(prefs, updateConfiguration, NULL)) { + SCLog(TRUE, LOG_ERR, + CFSTR("SCPreferencesSetCallBack() failed: %s"), + SCErrorString(SCError())); + goto error; + } + + /* + * register for change notifications. + */ + if (!SCPreferencesScheduleWithRunLoop(prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { + SCLog(TRUE, LOG_ERR, + CFSTR("SCPreferencesScheduleWithRunLoop() failed: %s"), + SCErrorString(SCError())); + goto error; + } + + return; + + error : + + if (store) CFRelease(store); + if (prefs) CFRelease(prefs); + + return; +} + + +#ifdef MAIN +int +main(int argc, char **argv) +{ + _sc_log = FALSE; + _sc_verbose = (argc > 1) ? TRUE : FALSE; + + load_PreferencesMonitor(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE); + prime_PreferencesMonitor(); + CFRunLoopRun(); + /* not reached */ + exit(0); + return 0; +} +#endif