/*
- * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2008, 2010, 2012-2015 Apple 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,
* 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@
*/
*/
+#include <TargetConditionals.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
+/* globals */
static SCPreferencesRef prefs = NULL;
static SCDynamicStoreRef store = NULL;
+/* preferences "initialization" globals */
+static CFStringRef initKey = NULL;
+static CFRunLoopSourceRef initRls = NULL;
+
+/* SCDynamicStore (Setup:) */
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 Boolean rofs = FALSE;
+static Boolean restorePrefs = FALSE;
+
+#define MY_PLUGIN_NAME "PreferencesMonitor"
+#define MY_PLUGIN_ID CFSTR("com.apple.SystemConfiguration." MY_PLUGIN_NAME)
+
+static Boolean
+restorePreferences()
+{
+ Boolean ok = FALSE;
+ CFStringRef currentModel = NULL;
+ CFMutableStringRef modelPrefixStr = NULL;
+ CFArrayRef keyList = NULL;
+ CFIndex keyListCount;
+ CFIndex idx;
+ Boolean modified = FALSE;
+ int sc_status = kSCStatusFailed;
+
+ while (TRUE) {
+ ok = SCPreferencesLock(prefs, TRUE);
+ if (ok) {
+ break;
+ }
+
+ sc_status = SCError();
+ if (sc_status == kSCStatusStale) {
+ SCPreferencesSynchronize(prefs);
+ } else {
+ SC_log(LOG_NOTICE, "Could not acquire network configuration lock: %s",
+ SCErrorString(sc_status));
+ return FALSE;
+ }
+ }
+
+ keyList = SCPreferencesCopyKeyList(prefs);
+ if (keyList == NULL) {
+ goto error;
+ }
+
+ currentModel = _SC_hw_model(FALSE);
+ if (currentModel == NULL) {
+ goto error;
+ }
+
+ /* Create "model:" string for prefix-check */
+ modelPrefixStr = CFStringCreateMutableCopy(NULL, 0, currentModel);
+ CFStringAppend(modelPrefixStr, CFSTR(":"));
+
+ keyListCount = CFArrayGetCount(keyList);
+ for (idx = 0; idx < keyListCount; idx++) {
+ CFStringRef existingKey = CFArrayGetValueAtIndex(keyList, idx);
+ CFStringRef key;
+ CFArrayRef splitKey = NULL;
+ CFPropertyListRef value;
+
+ if (isA_CFString(existingKey) == NULL) {
+ continue;
+ }
+
+ if (CFStringHasPrefix(existingKey, modelPrefixStr) == FALSE) {
+ continue;
+ }
+
+ splitKey = CFStringCreateArrayBySeparatingStrings(NULL, existingKey, CFSTR(":"));
+ key = CFArrayGetValueAtIndex(splitKey, 1);
+ value = SCPreferencesGetValue(prefs, existingKey);
+ SCPreferencesSetValue(prefs, key, value);
+ SCPreferencesRemoveValue(prefs, existingKey);
+ modified = TRUE;
+ CFRelease(splitKey);
+ }
+
+ if (modified == TRUE) {
+ SCPreferencesRef ni_prefs = NULL;
+ ni_prefs = SCPreferencesCreate(NULL, MY_PLUGIN_ID, CFSTR("NetworkInterfaces.plist"));
+ if (ni_prefs == NULL) {
+ goto error;
+ }
+
+ ok = _SCNetworkConfigurationCheckValidityWithPreferences(prefs, ni_prefs, NULL);
+ CFRelease(ni_prefs);
+
+ //Commit the changes only if prefs files valid
+ if (ok == TRUE) {
+ if (!SCPreferencesCommitChanges(prefs)) {
+ if (SCError() != EROFS) {
+ SC_log(LOG_NOTICE, "SCPreferencesCommitChanges() failed: %s",
+ SCErrorString(SCError()));
+ }
+ goto error;
+
+ }
+
+ (void) SCPreferencesApplyChanges(prefs);
+ }
+ }
+
+error:
+ (void) SCPreferencesUnlock(prefs);
+
+ if (keyList != NULL) {
+ CFRelease(keyList);
+ }
+ if (modelPrefixStr != NULL) {
+ CFRelease(modelPrefixStr);
+ }
+
+ return modified;
+}
+
+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 {
+ SC_log(LOG_NOTICE, "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) {
+ SC_log(LOG_NOTICE, "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) {
+ SC_log(LOG_NOTICE, "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 Boolean
+quiet(Boolean *timeout)
+{
+ CFDictionaryRef dict;
+ Boolean _quiet = FALSE;
+ Boolean _timeout = FALSE;
+
+ // check if quiet
+ dict = SCDynamicStoreCopyValue(store, initKey);
+ if (dict != NULL) {
+ if (isA_CFDictionary(dict)) {
+ if (CFDictionaryContainsKey(dict, CFSTR("*QUIET*"))) {
+ _quiet = TRUE;
+ }
+ if (CFDictionaryContainsKey(dict, CFSTR("*TIMEOUT*"))) {
+ _timeout = TRUE;
+ }
+ }
+ CFRelease(dict);
+ }
+
+ if (timeout != NULL) {
+ *timeout = _timeout;
+ }
+ return _quiet;
+}
+
+
+static void
+watchQuietDisable()
+{
+ if ((initKey == NULL) || (initRls == NULL)) {
+ return;
+ }
+
+ (void) SCDynamicStoreSetNotificationKeys(store, NULL, NULL);
+
+ CFRunLoopSourceInvalidate(initRls);
+ CFRelease(initRls);
+ initRls = NULL;
+
+ CFRelease(initKey);
+ initKey = NULL;
+
+ return;
+}
+
+
+static void
+watchQuietEnable()
+{
+ CFArrayRef keys;
+ Boolean ok;
+
+ initKey = SCDynamicStoreKeyCreate(NULL,
+ CFSTR("%@" "InterfaceNamer"),
+ kSCDynamicStoreDomainPlugin);
+
+ initRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), initRls, kCFRunLoopDefaultMode);
+
+ keys = CFArrayCreate(NULL, (const void **)&initKey, 1, &kCFTypeArrayCallBacks);
+ ok = SCDynamicStoreSetNotificationKeys(store, keys, NULL);
+ CFRelease(keys);
+ if (!ok) {
+ SC_log(LOG_NOTICE, "SCDynamicStoreSetNotificationKeys() failed: %s", SCErrorString(SCError()));
+ watchQuietDisable();
+ }
+
+ return;
+}
+
+
+
+static Boolean
+previousConfigurationAvailable()
+{
+ CFStringRef backupKey = NULL;
+ CFStringRef currentModel = NULL;
+ CFPropertyListRef properties = NULL;
+
+ currentModel = _SC_hw_model(FALSE);
+ if (currentModel == NULL) {
+ goto done;
+ }
+
+ /* Currently relying only if a backup of "Sets" is present */
+ backupKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:Sets"), currentModel);
+ properties = SCPreferencesGetValue(prefs, backupKey);
+ CFRelease(backupKey);
+done:
+ return (properties != NULL);
+}
+
+static void
+watchQuietCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
+{
+ Boolean _quiet;
+ Boolean _timeout = FALSE;
+
+ _quiet = quiet(&_timeout);
+ if (_quiet
+#if !TARGET_OS_IPHONE
+ || _timeout
+#endif /* !TARGET_OS_IPHONE */
+ ) {
+ watchQuietDisable();
+ }
+
+ if (_quiet || _timeout) {
+ static int logged = 0;
+
+ (void) establishNewPreferences();
+
+ if (restorePrefs == TRUE) {
+ (void) restorePreferences();
+ restorePrefs = FALSE;
+ }
+
+ if (_timeout && (logged++ == 0)) {
+ SC_log(LOG_ERR, "Network configuration creation timed out waiting for IORegistry");
+ }
+ }
+
+ return;
+}
static void
subset = SCPreferencesPathGetValue(prefs, link);
if (!subset) {
/* if error with link */
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCPreferencesPathGetValue(,%@,) failed: %s"),
- link,
- SCErrorString(SCError()));
+ SC_log(LOG_NOTICE, "SCPreferencesPathGetValue(,%@,) failed: %s",
+ link,
+ SCErrorString(SCError()));
return;
}
}
static void
-updateConfiguration(SCPreferencesRef prefs,
- SCPreferencesNotification notificationType,
- void *info)
+updateSCDynamicStore(SCPreferencesRef prefs)
{
CFStringRef current = NULL;
CFDateRef date = NULL;
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
*/
keys = SCPreferencesCopyKeyList(prefs);
if ((keys == NULL) || (CFArrayGetCount(keys) == 0)) {
- SCLog(TRUE, LOG_NOTICE, CFSTR("updateConfiguration(): no preferences."));
+ SC_log(LOG_NOTICE, "updateConfiguration(): no preferences");
goto done;
}
}
if (!isA_CFDictionary(global)) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("updateConfiguration(): %@ is not a dictionary."),
- kSCPrefSystem);
+ SC_log(LOG_NOTICE, "updateConfiguration(): %@ is not a dictionary",
+ kSCPrefSystem);
goto done;
}
}
if (!isA_CFString(current)) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("updateConfiguration(): %@ is not a string."),
- kSCPrefCurrentSet);
+ SC_log(LOG_NOTICE, "updateConfiguration(): %@ is not a string",
+ kSCPrefCurrentSet);
goto done;
}
set = SCPreferencesPathGetValue(prefs, current);
if (!set) {
/* if error with path */
- SCLog(TRUE, LOG_ERR,
- CFSTR("%@ value (%@) not valid"),
- kSCPrefCurrentSet,
- current);
+ SC_log(LOG_NOTICE, "%@ value (%@) not valid",
+ kSCPrefCurrentSet,
+ current);
goto done;
}
if (!isA_CFDictionary(set)) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("updateConfiguration(): %@ is not a dictionary."),
- current);
+ SC_log(LOG_NOTICE, "updateConfiguration(): %@ is not a dictionary",
+ current);
goto done;
}
}
/* Update the dynamic store */
+#ifndef MAIN
if (!SCDynamicStoreSetMultiple(store, newPrefs, removedPrefsKeys, NULL)) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCDynamicStoreSetMultiple() failed: %s"),
- SCErrorString(SCError()));
+ SC_log(LOG_NOTICE, "SCDynamicStoreSetMultiple() failed: %s", SCErrorString(SCError()));
}
-
- /* finished with current prefs, wait for changes */
- SCPreferencesSynchronize(prefs);
+#else // !MAIN
+ SC_log(LOG_DEBUG, "SCDynamicStore\nset: %@\nremove: %@",
+ newPrefs,
+ removedPrefsKeys);
+#endif // !MAIN
CFRelease(currentPrefs);
CFRelease(newPrefs);
}
-__private_extern__
-void
-stop_PreferencesMonitor(CFRunLoopSourceRef stopRls)
+static void
+updateConfiguration(SCPreferencesRef prefs,
+ SCPreferencesNotification notificationType,
+ void *info)
{
- // cleanup
+ os_activity_t activity_id;
- if (prefs != NULL) {
- if (!SCPreferencesUnscheduleFromRunLoop(prefs,
- CFRunLoopGetCurrent(),
- kCFRunLoopDefaultMode)) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCPreferencesUnscheduleFromRunLoop() failed: %s"),
- SCErrorString(SCError()));
+
+ activity_id = os_activity_start("processing [SC] preferences.plist changes",
+ OS_ACTIVITY_FLAG_DEFAULT);
+
+#if !TARGET_OS_IPHONE
+ if ((notificationType & kSCPreferencesNotificationCommit) == kSCPreferencesNotificationCommit) {
+ SCNetworkSetRef current;
+
+ current = SCNetworkSetCopyCurrent(prefs);
+ if (current != NULL) {
+ /* network configuration available, disable template creation */
+ watchQuietDisable();
+ CFRelease(current);
}
- CFRelease(prefs);
- prefs = NULL;
}
+#endif /* !TARGET_OS_IPHONE */
- if (store != NULL) {
- CFRelease(store);
- store = NULL;
+ if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) {
+ goto done;
}
- CFRunLoopSourceSignal(stopRls);
+ SC_log(LOG_INFO, "updating configuration");
+
+ /* update SCDynamicStore (Setup:) */
+ updateSCDynamicStore(prefs);
+
+ /* finished with current prefs, wait for changes */
+ if (!rofs) {
+ SCPreferencesSynchronize(prefs);
+ }
+
+ done :
+
+ os_activity_end(activity_id);
+
return;
}
void
prime_PreferencesMonitor()
{
- SCLog(_verbose, LOG_DEBUG, CFSTR("prime() called"));
+ SC_log(LOG_DEBUG, "prime() called");
/* load the initial configuration from the database */
updateConfiguration(prefs, kSCPreferencesNotificationApply, (void *)store);
void
load_PreferencesMonitor(CFBundleRef bundle, Boolean bundleVerbose)
{
- if (bundleVerbose) {
- _verbose = TRUE;
- }
+ Boolean initPrefs = TRUE;
- SCLog(_verbose, LOG_DEBUG, CFSTR("load() called"));
- SCLog(_verbose, LOG_DEBUG, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle));
+ SC_log(LOG_DEBUG, "load() called");
+ SC_log(LOG_DEBUG, " bundle ID = %@", CFBundleGetIdentifier(bundle));
/* open a SCDynamicStore session to allow cache updates */
- store = SCDynamicStoreCreate(NULL, CFSTR("PreferencesMonitor.bundle"), NULL, NULL);
+ store = SCDynamicStoreCreate(NULL,
+ CFSTR("PreferencesMonitor.bundle"),
+ watchQuietCallback,
+ NULL);
if (store == NULL) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCDynamicStoreCreate() failed: %s"),
- SCErrorString(SCError()));
+ SC_log(LOG_NOTICE, "SCDynamicStoreCreate() failed: %s", SCErrorString(SCError()));
goto error;
}
/* open a SCPreferences session */
+#ifndef MAIN
prefs = SCPreferencesCreate(NULL, CFSTR("PreferencesMonitor.bundle"), NULL);
- if (prefs == NULL) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCPreferencesCreate() failed: %s"),
- SCErrorString(SCError()));
- goto error;
- }
+#else // !MAIN
+ prefs = SCPreferencesCreate(NULL, CFSTR("PreferencesMonitor.bundle"), CFSTR("/tmp/preferences.plist"));
+#endif // !MAIN
+ if (prefs != NULL) {
+ Boolean need_update = FALSE;
+ CFStringRef new_model;
- if (!SCPreferencesSetCallback(prefs, updateConfiguration, NULL)) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCPreferencesSetCallBack() failed: %s"),
- SCErrorString(SCError()));
+ 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)) {
+ // if new hardware
+ need_update = TRUE;
+ restorePrefs = previousConfigurationAvailable();
+ }
+ }
+
+ if (need_update == FALSE) {
+ SCNetworkSetRef current;
+
+ current = SCNetworkSetCopyCurrent(prefs);
+ if (current != NULL) {
+ /* network configuration available, disable template creation */
+ initPrefs = FALSE;
+ CFRelease(current);
+ }
+ }
+ } else {
+ SC_log(LOG_NOTICE, "SCPreferencesCreate() failed: %s", SCErrorString(SCError()));
goto error;
}
/*
* register for change notifications.
*/
+ if (!SCPreferencesSetCallback(prefs, updateConfiguration, NULL)) {
+ SC_log(LOG_NOTICE, "SCPreferencesSetCallBack() failed: %s", SCErrorString(SCError()));
+ goto error;
+ }
+
if (!SCPreferencesScheduleWithRunLoop(prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
- SCLog(TRUE, LOG_ERR,
- CFSTR("SCPreferencesScheduleWithRunLoop() failed: %s"),
- SCErrorString(SCError()));
+ SC_log(LOG_NOTICE, "SCPreferencesScheduleWithRunLoop() failed: %s", SCErrorString(SCError()));
goto error;
}
+ /*
+ * if no preferences, initialize with a template (now or
+ * when IOKit has quiesced).
+ */
+ if (initPrefs) {
+ watchQuietEnable();
+ watchQuietCallback(store, NULL, NULL);
+ }
+
return;
error :
- if (store) CFRelease(store);
- if (prefs) CFRelease(prefs);
+ watchQuietDisable();
+ if (store != NULL) CFRelease(store);
+ if (prefs != NULL) CFRelease(prefs);
return;
}