static void updateSCDynamicStore(SCPreferencesRef prefs) { 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; /* * 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 */ 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 */ 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 */ 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 */ #ifndef MAIN if (!SCDynamicStoreSetMultiple(store, newPrefs, removedPrefsKeys, NULL)) { SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetMultiple() failed: %s"), SCErrorString(SCError())); } #else // !MAIN SCLog(TRUE, LOG_NOTICE, CFSTR("SCDynamicStore\nset: %@\nremove: %@\n"), newPrefs, removedPrefsKeys); #endif // !MAIN CFRelease(currentPrefs); CFRelease(newPrefs); CFRelease(unchangedPrefsKeys); CFRelease(removedPrefsKeys); if (dict) CFRelease(dict); if (date) CFRelease(date); if (keys) CFRelease(keys); return; }
static Boolean establishNewPreferences() { CFBundleRef bundle; SCNetworkSetRef current = NULL; CFStringRef new_model; Boolean ok = FALSE; int sc_status = kSCStatusFailed; SCNetworkSetRef set = NULL; CFStringRef setName = NULL; Boolean updated = FALSE; while (TRUE) { ok = SCPreferencesLock(prefs, TRUE); if (ok) { break; } sc_status = SCError(); if (sc_status == kSCStatusStale) { SCPreferencesSynchronize(prefs); } else { SCLog(TRUE, LOG_ERR, CFSTR("Could not acquire network configuration lock: %s"), SCErrorString(sc_status)); return FALSE; } } /* Ensure that the preferences has the new model */ new_model = _SC_hw_model(FALSE); /* Need to regenerate the new configuration for new model */ if (new_model != NULL) { CFStringRef old_model; old_model = SCPreferencesGetValue(prefs, MODEL); if ((old_model != NULL) && !_SC_CFEqual(old_model, new_model)) { CFIndex count; CFIndex index; CFArrayRef keys; keys = SCPreferencesCopyKeyList(prefs); count = (keys != NULL) ? CFArrayGetCount(keys) : 0; // if new hardware for (index = 0; index < count; index++) { CFStringRef existing_key; existing_key = CFArrayGetValueAtIndex(keys, index); if (isA_CFString(existing_key) != NULL) { CFStringRef new_key; CFPropertyListRef value; /* If it already contains a Model or if it already contains a MODEL:KEY key skip it*/ if (CFEqual(existing_key, MODEL) || CFStringFind(existing_key, CFSTR(":"), 0).location != kCFNotFound) { continue; } value = SCPreferencesGetValue(prefs, existing_key); /* Create a new key as OLD_MODEL:OLD_KEY */ new_key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:%@"), old_model, existing_key); SCPreferencesSetValue(prefs, new_key, value); if (!CFEqual(existing_key, kSCPrefSystem)) { /* preserve existing host names */ SCPreferencesRemoveValue(prefs, existing_key); } CFRelease(new_key); } } if (keys != NULL) { CFRelease(keys); } } /* Set the new model */ SCPreferencesSetValue(prefs, MODEL, new_model); } current = SCNetworkSetCopyCurrent(prefs); if (current != NULL) { set = current; } if (set == NULL) { set = SCNetworkSetCreate(prefs); if (set == NULL) { ok = FALSE; sc_status = SCError(); goto done; } bundle = _SC_CFBundleGet(); if (bundle != NULL) { setName = CFBundleCopyLocalizedString(bundle, CFSTR("DEFAULT_SET_NAME"), CFSTR("Automatic"), NULL); } ok = SCNetworkSetSetName(set, (setName != NULL) ? setName : CFSTR("Automatic")); if (!ok) { sc_status = SCError(); goto done; } ok = SCNetworkSetSetCurrent(set); if (!ok) { sc_status = SCError(); goto done; } } ok = SCNetworkSetEstablishDefaultConfiguration(set); if (!ok) { sc_status = SCError(); goto done; } done : if (ok) { ok = SCPreferencesCommitChanges(prefs); if (ok) { SCLog(TRUE, LOG_NOTICE, CFSTR("New network configuration saved")); updated = TRUE; } else { sc_status = SCError(); if (sc_status == EROFS) { /* a read-only fileysstem is OK */ ok = TRUE; /* ... but we don't want to synchronize */ rofs = TRUE; } } /* apply (committed or temporary/read-only) changes */ (void) SCPreferencesApplyChanges(prefs); } else if ((current == NULL) && (set != NULL)) { (void) SCNetworkSetRemove(set); } if (!ok) { SCLog(TRUE, LOG_ERR, CFSTR("Could not establish network configuration: %s"), SCErrorString(sc_status)); } (void)SCPreferencesUnlock(prefs); if (setName != NULL) CFRelease(setName); if (set != NULL) CFRelease(set); return updated; }
static void LeakTest(TestFunc tester) // Given a test name and a pointer to a test function, // this routine calls the test function repeatedly to check // for SCF preferences reference count leaks. { OSStatus err; SCPreferencesRef prefsRef; CFArrayRef allKeys; int i; SInt32 startCount; SInt32 endCount; // This is kinda cheesy. We need to use the same SCPreferencesRef // that MoreSCF uses because otherwise we can't see the reference // count changes done by MoreSCF. Ideally, we wouldn't want to run // within a MoreSCOpen/MoreSCClose pair because then our changes // aren't necessarily being committed to the database. However, // given the current MoreSCF architecture, where the SCPreferenceRef // is only valid inside the MoreSCOpen/MoreSCClose pair, that's // exactly what we have to do. allKeys = NULL; err = MoreSCOpen(false, false); if (err == noErr) { prefsRef = MoreSCGetSCPreferencesRef(); } // Now get a copy of the array of all keys in the database. // We make this copy using CFPropertyListCreateDeepCopy so we // know that it's completely independent of the SCF. if (err == noErr) { CFArrayRef allKeysOrig; allKeysOrig = NULL; allKeysOrig = SCPreferencesCopyKeyList(prefsRef); if (allKeysOrig == NULL) { err = SCError(); } if (err == noErr) { allKeys = (CFArrayRef) CFPropertyListCreateDeepCopy(NULL, allKeysOrig, kCFPropertyListMutableContainersAndLeaves); // C++ requires cast if (allKeys == NULL) { err = coreFoundationUnknownErr; } } CFQRelease(allKeysOrig); } // Now do the reference counting test. Call the tester function // a few times to allow the refcounts to stabilise. Then get // a summ of all the nodes in all of the keys in SCF. Then // run the test a 10 more times and get another count. If // the counts are different, we're in trouble. if (err == noErr) { for (i = 0; i < 3; i++) { tester(); } startCount = TotalAllRefCounts(prefsRef, allKeys); for (i = 0; i < 10; i++) { tester(); } endCount = TotalAllRefCounts(prefsRef, allKeys); if (startCount != endCount) { CFIndex keyCount; CFIndex keyIndex; fprintf(stderr, "*** Leaked %ld reference counts.\n", endCount - startCount); keyCount = CFArrayGetCount(allKeys); for (keyIndex = 0; keyIndex < keyCount; keyIndex++) { CFPropertyListRef thisPref; // The commented out code is only needed when you're actively // trying to track down a leak. Given that leaks are rare // I decided against making a proper architecture for this. // Just uncomment the code and modify it appropriately. startCount = 0; thisPref = SCPreferencesGetValue(prefsRef, (CFStringRef) CFArrayGetValueAtIndex(allKeys, keyIndex)); // C++ requires cast CFQPropertyListDeepApplyFunction(thisPref, RefCounter, (void *) &startCount ); // if (keyIndex == 0) { // fprintf(stderr, "*** BEFORE ***\n"); // PrintPropertyList(thisPref); // } tester(); endCount = 0; thisPref = SCPreferencesGetValue(prefsRef, (CFStringRef) CFArrayGetValueAtIndex(allKeys, keyIndex)); // C++ requires cast CFQPropertyListDeepApplyFunction(thisPref, RefCounter, (void *) &endCount ); // if (keyIndex == 0) { // fprintf(stderr, "*** AFTER ***\n"); // PrintPropertyList(thisPref); // } if (startCount != endCount) { fprintf(stderr, "*** Leaked %ld reference counts in set number %ld\n", endCount - startCount, keyIndex); CFShow(CFArrayGetValueAtIndex(allKeys, keyIndex)); } } } } MoreSCClose(&err, false); CFQRelease(allKeys); if (err != noErr) { fprintf(stderr, "*** Failed with error %ld!\n", err); } }