#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include <SystemConfiguration/SCValidation.h>
static SCPreferencesRef prefs = NULL;
static SCDynamicStoreRef store = NULL;
static CFStringRef initKey = NULL;
static CFRunLoopSourceRef initRls = NULL;
static CFMutableDictionaryRef currentPrefs;
static CFMutableDictionaryRef newPrefs;
static CFMutableArrayRef unchangedPrefsKeys;
static CFMutableArrayRef removedPrefsKeys;
static Boolean _verbose = FALSE;
static void
establishNewPreferences()
{
CFBundleRef bundle;
Boolean ok = FALSE;
int sc_status = kSCStatusFailed;
SCNetworkSetRef set = NULL;
CFStringRef setName = NULL;
while (TRUE) {
ok = SCPreferencesLock(prefs, TRUE);
if (ok) {
break;
}
sc_status = SCError();
if (sc_status == kSCStatusStale) {
(void) SCPreferencesSynchronize(prefs);
} else {
SCLog(TRUE, LOG_ERR,
CFSTR("Could not acquire network configuration lock: %s"),
SCErrorString(sc_status));
return;
}
}
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"));
} else {
sc_status = SCError();
if (sc_status == EROFS) {
ok = TRUE;
}
}
(void) SCPreferencesApplyChanges(prefs);
} else if (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;
}
static Boolean
quiet()
{
CFDictionaryRef dict;
Boolean quiet = FALSE;
dict = SCDynamicStoreCopyValue(store, initKey);
if (dict != NULL) {
if (isA_CFDictionary(dict) &&
(CFDictionaryContainsKey(dict, CFSTR("*QUIET*")) ||
CFDictionaryContainsKey(dict, CFSTR("*TIMEOUT*")))) {
quiet = TRUE;
}
CFRelease(dict);
}
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) {
SCPrint(TRUE, stderr,
CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s\n"), SCErrorString(SCError()));
watchQuietDisable();
}
return;
}
static void
watchQuietCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
{
if (quiet()) {
watchQuietDisable();
establishNewPreferences();
}
return;
}
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) {
if (CFEqual(cacheData, configData)) {
CFArrayAppendValue(unchangedPrefsKeys, configKey);
}
}
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)) {
subset = base;
} else {
subset = SCPreferencesPathGetValue(prefs, link);
if (!subset) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCPreferencesPathGetValue(,%@,) failed: %s"),
link,
SCErrorString(SCError()));
return;
}
}
if (CFDictionaryContainsKey(subset, kSCResvInactive)) {
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()) {
CFDictionarySetValue(myDict, keys[i], vals[i]);
} else {
CFStringRef subKey;
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) {
CFDictionarySetValue(newPrefs, myKey, myDict);
}
CFRelease(myDict);
CFRelease(myKey);
return;
}
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;
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);
}
newPrefs = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
dict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
date = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
keys = SCPreferencesCopyKeyList(prefs);
if ((keys == NULL) || (CFArrayGetCount(keys) == 0)) {
SCLog(TRUE, LOG_NOTICE, CFSTR("updateConfiguration(): no preferences."));
goto done;
}
global = SCPreferencesGetValue(prefs, kSCPrefSystem);
if (!global) {
goto getSet;
}
if (!isA_CFDictionary(global)) {
SCLog(TRUE, LOG_ERR,
CFSTR("updateConfiguration(): %@ is not a dictionary."),
kSCPrefSystem);
goto done;
}
flatten(prefs, CFSTR("/"), global);
getSet :
current = SCPreferencesGetValue(prefs, kSCPrefCurrentSet);
if (!current) {
goto done;
}
if (!isA_CFString(current)) {
SCLog(TRUE, LOG_ERR,
CFSTR("updateConfiguration(): %@ is not a string."),
kSCPrefCurrentSet);
goto done;
}
set = SCPreferencesPathGetValue(prefs, current);
if (!set) {
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(prefs, CFSTR("/"), set);
CFDictionarySetValue(dict, kSCDynamicStorePropSetupCurrentSet, current);
done :
CFDictionarySetValue(dict, kSCDynamicStorePropSetupLastUpdated, date);
CFDictionarySetValue(newPrefs, kSCDynamicStoreDomainSetup, dict);
CFDictionaryApplyFunction(newPrefs, updateCache, NULL);
n = CFArrayGetCount(unchangedPrefsKeys);
for (i = 0; i < n; i++) {
CFStringRef key;
key = CFArrayGetValueAtIndex(unchangedPrefsKeys, i);
CFDictionaryRemoveValue(newPrefs, key);
}
if (!SCDynamicStoreSetMultiple(store, newPrefs, removedPrefsKeys, NULL)) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCDynamicStoreSetMultiple() failed: %s"),
SCErrorString(SCError()));
}
CFRelease(currentPrefs);
CFRelease(newPrefs);
CFRelease(unchangedPrefsKeys);
CFRelease(removedPrefsKeys);
if (dict) CFRelease(dict);
if (date) CFRelease(date);
if (keys) CFRelease(keys);
return;
}
static void
updateConfiguration(SCPreferencesRef prefs,
SCPreferencesNotification notificationType,
void *info)
{
if ((notificationType & kSCPreferencesNotificationCommit) == kSCPreferencesNotificationCommit) {
SCNetworkSetRef current;
current = SCNetworkSetCopyCurrent(prefs);
if (current != NULL) {
watchQuietDisable();
CFRelease(current);
}
}
if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) {
return;
}
SCLog(_verbose, LOG_DEBUG, CFSTR("updating configuration"));
updateSCDynamicStore(prefs);
SCPreferencesSynchronize(prefs);
return;
}
__private_extern__
void
stop_PreferencesMonitor(CFRunLoopSourceRef stopRls)
{
watchQuietDisable();
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"));
updateConfiguration(prefs, kSCPreferencesNotificationApply, (void *)store);
return;
}
__private_extern__
void
load_PreferencesMonitor(CFBundleRef bundle, Boolean bundleVerbose)
{
Boolean initPrefs = TRUE;
if (bundleVerbose) {
_verbose = TRUE;
}
SCLog(_verbose, LOG_DEBUG, CFSTR("load() called"));
SCLog(_verbose, LOG_DEBUG, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle));
store = SCDynamicStoreCreate(NULL,
CFSTR("PreferencesMonitor.bundle"),
watchQuietCallback,
NULL);
if (store == NULL) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCDynamicStoreCreate() failed: %s"),
SCErrorString(SCError()));
goto error;
}
prefs = SCPreferencesCreate(NULL, CFSTR("PreferencesMonitor.bundle"), NULL);
if (prefs != NULL) {
SCNetworkSetRef current;
current = SCNetworkSetCopyCurrent(prefs);
if (current != NULL) {
initPrefs = FALSE;
CFRelease(current);
}
} else {
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;
}
if (!SCPreferencesScheduleWithRunLoop(prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCPreferencesScheduleWithRunLoop() failed: %s"),
SCErrorString(SCError()));
goto error;
}
if (initPrefs) {
watchQuietEnable();
watchQuietCallback(store, NULL, NULL);
}
return;
error :
watchQuietDisable();
if (store != NULL) CFRelease(store);
if (prefs != NULL) 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();
exit(0);
return 0;
}
#endif