#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <dirent.h>
#include <sysexits.h>
#include <unistd.h>
#include <NSSystemDirectories.h>
#include "configd.h"
#include "configd_server.h"
#include <SystemConfiguration/SCDPlugin.h>
#include "SCNetworkReachabilityInternal.h"
void _SCDPluginExecInit();
#define BUNDLE_DIRECTORY "/SystemConfiguration"
#define BUNDLE_DIR_EXTENSION ".bundle"
#define PLUGIN_ALL(p) CFSTR(p)
#if !TARGET_OS_IPHONE
#define PLUGIN_MACOSX(p) CFSTR(p)
#define PLUGIN_IOS(p) NULL
#else // !TARGET_OS_IPHONE
#define PLUGIN_MACOSX(p) NULL
#define PLUGIN_IOS(p) CFSTR(p)
#endif // !TARGET_OS_IPHONE
static const CFStringRef pluginWhitelist[] = {
PLUGIN_MACOSX("com.apple.SystemConfiguration.ApplicationFirewall"),
PLUGIN_ALL ("com.apple.SystemConfiguration.EAPOLController"),
PLUGIN_ALL ("com.apple.SystemConfiguration.IPConfiguration"),
PLUGIN_ALL ("com.apple.SystemConfiguration.IPMonitor"),
PLUGIN_MACOSX("com.apple.SystemConfiguration.ISPreference"),
PLUGIN_ALL ("com.apple.SystemConfiguration.InterfaceNamer"),
PLUGIN_ALL ("com.apple.SystemConfiguration.KernelEventMonitor"),
PLUGIN_ALL ("com.apple.SystemConfiguration.LinkConfiguration"),
PLUGIN_ALL ("com.apple.SystemConfiguration.Logger"),
PLUGIN_ALL ("com.apple.SystemConfiguration.PPPController"),
PLUGIN_ALL ("com.apple.SystemConfiguration.PreferencesMonitor"),
PLUGIN_ALL ("com.apple.SystemConfiguration.SCNetworkReachability"),
PLUGIN_MACOSX("com.apple.print.notification"),
};
#define N_PLUGIN_WHITELIST (sizeof(pluginWhitelist) / sizeof(pluginWhitelist[0]))
typedef struct {
CFBundleRef bundle;
Boolean loaded;
Boolean builtin;
Boolean enabled;
Boolean forced;
Boolean verbose;
SCDynamicStoreBundleLoadFunction load;
SCDynamicStoreBundleStartFunction start;
SCDynamicStoreBundlePrimeFunction prime;
SCDynamicStoreBundleStopFunction stop;
} *bundleInfoRef;
static CFMutableArrayRef allBundles = NULL;
static CFMutableDictionaryRef exiting = NULL;
__private_extern__
CFRunLoopRef plugin_runLoop = NULL;
extern SCDynamicStoreBundleLoadFunction load_IPMonitor;
extern SCDynamicStoreBundlePrimeFunction prime_IPMonitor;
#if !TARGET_IPHONE_SIMULATOR
extern SCDynamicStoreBundleLoadFunction load_InterfaceNamer;
extern SCDynamicStoreBundleLoadFunction load_KernelEventMonitor;
extern SCDynamicStoreBundlePrimeFunction prime_KernelEventMonitor;
extern SCDynamicStoreBundleLoadFunction load_LinkConfiguration;
extern SCDynamicStoreBundleLoadFunction load_PreferencesMonitor;
extern SCDynamicStoreBundlePrimeFunction prime_PreferencesMonitor;
#endif // !TARGET_IPHONE_SIMULATOR
typedef struct {
const CFStringRef bundleID;
const void *load; const void *start; const void *prime; const void *stop; } builtin, *builtinRef;
static const builtin builtin_plugins[] = {
{
CFSTR("com.apple.SystemConfiguration.IPMonitor"),
&load_IPMonitor,
NULL,
&prime_IPMonitor,
NULL
},
#if !TARGET_IPHONE_SIMULATOR
{
CFSTR("com.apple.SystemConfiguration.InterfaceNamer"),
&load_InterfaceNamer,
NULL,
NULL,
NULL
},
{
CFSTR("com.apple.SystemConfiguration.KernelEventMonitor"),
&load_KernelEventMonitor,
NULL,
&prime_KernelEventMonitor,
NULL
},
{
CFSTR("com.apple.SystemConfiguration.LinkConfiguration"),
&load_LinkConfiguration,
NULL,
NULL,
NULL
},
{
CFSTR("com.apple.SystemConfiguration.PreferencesMonitor"),
&load_PreferencesMonitor,
NULL,
&prime_PreferencesMonitor,
NULL
},
#endif // !TARGET_IPHONE_SIMULATOR
};
#ifdef DEBUG
static void
traceBundle(const char *op, CFBundleRef bundle)
{
if (bundle != NULL) {
CFStringRef bundleID = CFBundleGetIdentifier(bundle);
SC_trace(_configd_trace, "bundle : %s %@\n",
op,
bundleID);
} else {
SC_trace(_configd_trace, "bundle : %s\n",
op);
}
const char *path = getenv("LOG_CONFIGD_BUNDLE_USAGE");
if (path != NULL) {
FILE *file;
int status;
char *top_command;
file = fopen(path, "a");
if (file == NULL) {
return;
}
(void)sleep(2);
SCPrint(TRUE, file, CFSTR("\n--------------------\n\n"), op);
if (bundle != NULL) {
CFStringRef bundleID = CFBundleGetIdentifier(bundle);
SC_trace(file, "%s bundle \"%@\"\n\n", op, bundleID);
} else {
SC_trace(file, "%s\n\n", op);
}
SCPrint(TRUE, file, CFSTR("%@\n\n"), CFRunLoopGetCurrent());
fclose(file);
if (asprintf(&top_command, "/usr/bin/top -o+pid -l 1 >> %s", path) == -1) {
return;
}
status = system(top_command);
if ((status == -1) ||
!WIFEXITED(status) ||
(WEXITSTATUS(status) != 0)) {
SC_log(LOG_NOTICE, "system(\"%s\") failed", top_command);
}
free(top_command);
}
return;
}
#endif
static void
addBundle(CFBundleRef bundle, Boolean forceEnabled)
{
CFDictionaryRef bundleDict;
bundleInfoRef bundleInfo;
bundleInfo = CFAllocatorAllocate(NULL, sizeof(*bundleInfo), 0);
bundleInfo->bundle = (CFBundleRef)CFRetain(bundle);
bundleInfo->loaded = FALSE;
bundleInfo->builtin = FALSE;
bundleInfo->enabled = TRUE;
bundleInfo->forced = forceEnabled;
bundleInfo->verbose = FALSE;
bundleInfo->load = NULL;
bundleInfo->start = NULL;
bundleInfo->prime = NULL;
bundleInfo->stop = NULL;
bundleDict = CFBundleGetInfoDictionary(bundle);
if (isA_CFDictionary(bundleDict)) {
CFBooleanRef bVal;
bVal = CFDictionaryGetValue(bundleDict, kSCBundleIsBuiltinKey);
if (isA_CFBoolean(bVal)) {
bundleInfo->builtin = CFBooleanGetValue(bVal);
}
bVal = CFDictionaryGetValue(bundleDict, kSCBundleEnabledKey);
if (isA_CFBoolean(bVal)) {
bundleInfo->enabled = CFBooleanGetValue(bVal);
}
bVal = CFDictionaryGetValue(bundleDict, kSCBundleVerboseKey);
if (isA_CFBoolean(bVal)) {
bundleInfo->verbose = CFBooleanGetValue(bVal);
}
}
CFArrayAppendValue(allBundles, bundleInfo);
return;
}
static CF_RETURNS_RETAINED CFStringRef
shortBundleIdentifier(CFStringRef bundleID)
{
CFIndex len = CFStringGetLength(bundleID);
CFRange range;
CFStringRef shortID = NULL;
if (CFStringFindWithOptions(bundleID,
CFSTR("."),
CFRangeMake(0, len),
kCFCompareBackwards,
&range)) {
range.location = range.location + range.length;
range.length = len - range.location;
shortID = CFStringCreateWithSubstring(NULL, bundleID, range);
}
return shortID;
}
static void *
getBundleSymbol(CFBundleRef bundle, CFStringRef functionName, CFStringRef shortID)
{
void *func;
func = CFBundleGetFunctionPointerForName(bundle, functionName);
if (func != NULL) {
return func;
}
if (shortID != NULL) {
CFStringRef altFunctionName;
altFunctionName = CFStringCreateWithFormat(NULL,
NULL,
CFSTR("%@_%@"),
functionName,
shortID);
func = CFBundleGetFunctionPointerForName(bundle, altFunctionName);
CFRelease(altFunctionName);
}
return func;
}
static const char *
getBundleDirNameAndPath(CFBundleRef bundle, char *buf, size_t buf_len)
{
char *cp;
size_t len;
Boolean ok;
CFURLRef url;
url = CFBundleCopyBundleURL(bundle);
if (url == NULL) {
return NULL;
}
ok = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)buf, buf_len);
CFRelease(url);
if (!ok) {
return NULL;
}
cp = strrchr(buf, '/');
if (cp != NULL) {
cp++;
} else {
cp = buf;
}
len = strlen(cp);
if (len <= sizeof(BUNDLE_DIR_EXTENSION)) {
return NULL;
}
len -= sizeof(BUNDLE_DIR_EXTENSION) - 1;
if (strcmp(&cp[len], BUNDLE_DIR_EXTENSION) != 0) {
return NULL;
}
return cp;
}
#pragma mark -
#pragma mark load
static void
loadBundle(const void *value, void *context) {
CFStringRef bundleID;
Boolean bundleAllowed;
bundleInfoRef bundleInfo = (bundleInfoRef)value;
Boolean bundleExclude;
CFIndex *nLoaded = (CFIndex *)context;
CFStringRef shortID;
bundleID = CFBundleGetIdentifier(bundleInfo->bundle);
if (bundleID == NULL) {
SC_log(LOG_NOTICE, "skipped %@ (no bundle ID)", bundleInfo->bundle);
return;
}
shortID = shortBundleIdentifier(bundleID);
bundleAllowed = ((CFSetGetCount(_plugins_allowed) == 0) || CFSetContainsValue(_plugins_allowed, bundleID) || ((shortID != NULL) &&
CFSetContainsValue(_plugins_allowed, shortID))|| bundleInfo->forced );
if (!bundleAllowed) {
SC_log(LOG_INFO, "skipped %@ (not allowed)", bundleID);
goto done;
}
bundleExclude = (CFSetContainsValue(_plugins_exclude, bundleID) || ((shortID != NULL) &&
CFSetContainsValue(_plugins_exclude, shortID)) );
if (bundleExclude) {
SC_log(LOG_INFO, "skipped %@ (excluded)", bundleID);
goto done;
}
if (!bundleInfo->enabled && !bundleInfo->forced) {
SC_log(LOG_INFO, "skipped %@ (disabled)", bundleID);
goto done;
}
if (!bundleInfo->verbose) {
bundleInfo->verbose = CFSetContainsValue(_plugins_verbose, bundleID);
if (!bundleInfo->verbose) {
if (shortID != NULL) {
bundleInfo->verbose = CFSetContainsValue(_plugins_verbose, shortID);
}
}
}
if (bundleInfo->builtin) {
int i;
SC_log(LOG_INFO, "adding %@", bundleID);
for (i = 0; i < sizeof(builtin_plugins)/sizeof(builtin_plugins[0]); i++) {
if (CFEqual(bundleID, builtin_plugins[i].bundleID)) {
bundleInfo->load = builtin_plugins[i].load;
bundleInfo->start = builtin_plugins[i].start;
bundleInfo->prime = builtin_plugins[i].prime;
bundleInfo->stop = builtin_plugins[i].stop;
break;
}
}
if ((bundleInfo->load == NULL) &&
(bundleInfo->start == NULL) &&
(bundleInfo->prime == NULL) &&
(bundleInfo->stop == NULL)) {
SC_log(LOG_NOTICE, "%@ add failed", bundleID);
goto done;
}
} else {
CFErrorRef error = NULL;
SC_log(LOG_INFO, "loading %@", bundleID);
#ifdef DEBUG
traceBundle("loading", bundleInfo->bundle);
#endif
if (!CFBundleLoadExecutableAndReturnError(bundleInfo->bundle, &error)) {
CFDictionaryRef user_info;
SC_log(LOG_NOTICE, "%@ load failed", bundleID);
user_info = CFErrorCopyUserInfo(error);
if (user_info != NULL) {
CFStringRef link_error_string;
link_error_string = CFDictionaryGetValue(user_info,
CFSTR("NSDebugDescription"));
if (link_error_string != NULL) {
SC_log(LOG_NOTICE, "%@", link_error_string);
}
CFRelease(user_info);
}
CFRelease(error);
goto done;
}
bundleInfo->load = getBundleSymbol(bundleInfo->bundle, CFSTR("load" ), shortID);
bundleInfo->start = getBundleSymbol(bundleInfo->bundle, CFSTR("start"), shortID);
bundleInfo->prime = getBundleSymbol(bundleInfo->bundle, CFSTR("prime"), shortID);
bundleInfo->stop = getBundleSymbol(bundleInfo->bundle, CFSTR("stop" ), shortID);
}
bundleInfo->loaded = TRUE;
*nLoaded = *nLoaded + 1;
done :
if (shortID != NULL) CFRelease(shortID);
return;
}
void
callLoadFunction(const void *value, void *context) {
bundleInfoRef bundleInfo = (bundleInfoRef)value;
if (!bundleInfo->loaded) {
return;
}
if (bundleInfo->load == NULL) {
return;
}
#ifdef DEBUG
traceBundle("calling load() for", bundleInfo->bundle);
#endif
(*bundleInfo->load)(bundleInfo->bundle, bundleInfo->verbose);
return;
}
#pragma mark -
#pragma mark start
void
callStartFunction(const void *value, void *context) {
const char *bundleDirName;
bundleInfoRef bundleInfo = (bundleInfoRef)value;
char bundleName[MAXNAMLEN + 1];
char bundlePath[MAXPATHLEN];
size_t len;
if (!bundleInfo->loaded) {
return;
}
if (bundleInfo->start == NULL) {
return;
}
bundleDirName = getBundleDirNameAndPath(bundleInfo->bundle, bundlePath, sizeof(bundlePath));
if (bundleDirName == NULL) {
return;
}
if (strlcpy(bundleName, bundleDirName, sizeof(bundleName)) > sizeof(bundleName)) {
return;
}
len = strlen(bundleName) - (sizeof(BUNDLE_DIR_EXTENSION) - 1);
bundleName[len] = '\0';
#ifdef DEBUG
traceBundle("calling start() for", bundleInfo->bundle);
#endif
(*bundleInfo->start)(bundleName, bundlePath);
return;
}
#pragma mark -
#pragma mark prime
void
callPrimeFunction(const void *value, void *context) {
bundleInfoRef bundleInfo = (bundleInfoRef)value;
if (!bundleInfo->loaded) {
return;
}
if (bundleInfo->prime == NULL) {
return;
}
#ifdef DEBUG
traceBundle("calling prime() for", bundleInfo->bundle);
#endif
(*bundleInfo->prime)();
return;
}
#pragma mark -
#pragma mark stop
static void
stopComplete(void *info)
{
CFBundleRef bundle = (CFBundleRef)info;
CFStringRef bundleID = CFBundleGetIdentifier(bundle);
CFRunLoopSourceRef stopRls;
SC_log(LOG_INFO, "** %@ complete (%f)", bundleID, CFAbsoluteTimeGetCurrent());
stopRls = (CFRunLoopSourceRef)CFDictionaryGetValue(exiting, bundle);
if (stopRls == NULL) {
return;
}
CFRunLoopSourceInvalidate(stopRls);
CFDictionaryRemoveValue(exiting, bundle);
if (CFDictionaryGetCount(exiting) == 0) {
int status;
status = server_shutdown();
SC_log(LOG_INFO, "server shutdown complete (%f)", CFAbsoluteTimeGetCurrent());
exit (status);
}
return;
}
static void
stopDelayed(CFRunLoopTimerRef timer, void *info)
{
const void **keys;
CFIndex i;
CFIndex n;
int status;
SC_log(LOG_INFO, "server shutdown was delayed, unresponsive plugins:");
n = CFDictionaryGetCount(exiting);
keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
CFDictionaryGetKeysAndValues(exiting, keys, NULL);
for (i = 0; i < n; i++) {
CFBundleRef bundle;
CFStringRef bundleID;
bundle = (CFBundleRef)keys[i];
bundleID = CFBundleGetIdentifier(bundle);
SC_log(LOG_NOTICE, "** %@", bundleID);
}
CFAllocatorDeallocate(NULL, keys);
status = server_shutdown();
exit (status);
}
static CFStringRef
stopRLSCopyDescription(const void *info)
{
CFBundleRef bundle = (CFBundleRef)info;
return CFStringCreateWithFormat(NULL,
NULL,
CFSTR("<stopRLS %p> {bundleID = %@}"),
info,
CFBundleGetIdentifier(bundle));
}
static void
stopBundle(const void *value, void *context) {
bundleInfoRef bundleInfo = (bundleInfoRef)value;
CFRunLoopSourceRef stopRls;
CFRunLoopSourceContext stopContext = { 0 , bundleInfo->bundle , CFRetain , CFRelease , stopRLSCopyDescription , CFEqual , CFHash , NULL , NULL , stopComplete };
if (!bundleInfo->loaded) {
return;
}
if (bundleInfo->stop == NULL) {
return;
}
stopRls = CFRunLoopSourceCreate(NULL, 0, &stopContext);
CFRunLoopAddSource(CFRunLoopGetCurrent(), stopRls, kCFRunLoopDefaultMode);
CFDictionaryAddValue(exiting, bundleInfo->bundle, stopRls);
CFRelease(stopRls);
(*bundleInfo->stop)(stopRls);
return;
}
static void
stopBundles()
{
SC_log(LOG_DEBUG, "calling bundle stop() functions");
CFArrayApplyFunction(allBundles,
CFRangeMake(0, CFArrayGetCount(allBundles)),
stopBundle,
NULL);
if (CFDictionaryGetCount(exiting) == 0) {
int status;
status = server_shutdown();
SC_log(LOG_INFO, "server shutdown complete (%f)", CFAbsoluteTimeGetCurrent());
exit (status);
} else {
CFRunLoopTimerRef timer;
timer = CFRunLoopTimerCreate(NULL,
CFAbsoluteTimeGetCurrent() + 15.0,
0.0,
0,
0,
stopDelayed,
NULL);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
CFRelease(timer);
}
return;
}
#pragma mark -
#pragma mark term
static CFStringRef
termRLSCopyDescription(const void *info)
{
return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SIGTERM RLS>"));
}
__private_extern__
Boolean
plugin_term(int *status)
{
CFRunLoopSourceContext termContext = { 0 , (void *)1 , NULL , NULL , termRLSCopyDescription , NULL , NULL , NULL , NULL , stopBundles };
CFRunLoopSourceRef termRls;
if (plugin_runLoop == NULL) {
*status = EX_OK;
return FALSE; }
if (exiting != NULL) {
return TRUE;
}
SC_log(LOG_INFO, "starting server shutdown (%f)", CFAbsoluteTimeGetCurrent());
exiting = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
termRls = CFRunLoopSourceCreate(NULL, 0, &termContext);
CFRunLoopAddSource(plugin_runLoop, termRls, kCFRunLoopDefaultMode);
CFRunLoopSourceSignal(termRls);
CFRelease(termRls);
CFRunLoopWakeUp(plugin_runLoop);
return TRUE;
}
#pragma mark -
#pragma mark initialization
#ifdef DEBUG
static void
timerCallback(CFRunLoopTimerRef timer, void *info)
{
static int pass = 0;
pass++;
if ((pass > 120) && ((pass % 60) != 0)) {
return;
}
traceBundle("the [plugin] CFRunLoop is waiting...", NULL);
return;
}
#endif
static void
sortBundles(CFMutableArrayRef orig)
{
CFIndex i;
CFIndex n;
CFMutableArrayRef new;
CFMutableSetRef orig_bundleIDs;
orig_bundleIDs = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
n = CFArrayGetCount(orig);
for (i = 0; i < n; i++) {
bundleInfoRef bundleInfo = (bundleInfoRef)CFArrayGetValueAtIndex(orig, i);
CFStringRef bundleID = CFBundleGetIdentifier(bundleInfo->bundle);
if (bundleID != NULL) {
CFSetAddValue(orig_bundleIDs, bundleID);
}
}
new = CFArrayCreateMutable(NULL, 0, NULL);
while (n > 0) {
Boolean inserted = FALSE;
for (i = 0; i < n; i++) {
bundleInfoRef bundleInfo1 = (bundleInfoRef)CFArrayGetValueAtIndex(orig, i);
CFStringRef bundleID1 = CFBundleGetIdentifier(bundleInfo1->bundle);
CFIndex count;
CFDictionaryRef dict;
CFIndex j;
CFIndex nRequires;
CFArrayRef requires = NULL;
dict = isA_CFDictionary(CFBundleGetInfoDictionary(bundleInfo1->bundle));
if (dict) {
requires = CFDictionaryGetValue(dict, kSCBundleRequiresKey);
requires = isA_CFArray(requires);
}
if (bundleID1 == NULL || requires == NULL) {
CFArrayInsertValueAtIndex(new, 0, bundleInfo1);
CFArrayRemoveValueAtIndex(orig, i);
inserted = TRUE;
break;
}
count = nRequires = CFArrayGetCount(requires);
for (j = 0; j < nRequires; j++) {
CFIndex k;
CFIndex nNew;
CFStringRef r = CFArrayGetValueAtIndex(requires, j);
if (!CFSetContainsValue(orig_bundleIDs, r)) {
count--;
continue;
}
nNew = CFArrayGetCount(new);
for (k = 0; k < nNew; k++) {
bundleInfoRef bundleInfo2 = (bundleInfoRef)CFArrayGetValueAtIndex(new, k);
CFStringRef bundleID2 = CFBundleGetIdentifier(bundleInfo2->bundle);
if (bundleID2 && CFEqual(bundleID2, r)) {
count--;
}
}
}
if (count == 0) {
CFArrayAppendValue(new, bundleInfo1);
CFArrayRemoveValueAtIndex(orig, i);
inserted = TRUE;
break;
}
}
if (inserted == FALSE) {
SC_log(LOG_NOTICE, "Bundles have circular dependency!!!");
break;
}
n = CFArrayGetCount(orig);
}
if (CFArrayGetCount(orig) > 0) {
CFArrayAppendArray(new, orig, CFRangeMake(0, CFArrayGetCount(orig)));
}
else {
}
CFArrayRemoveAllValues(orig);
CFArrayAppendArray(orig, new, CFRangeMake(0, CFArrayGetCount(new)));
CFRelease(new);
CFRelease(orig_bundleIDs);
return;
}
static __inline__ void
ALT_CFRelease(CFTypeRef cf)
{
CFRelease(cf);
}
__private_extern__
void *
plugin_exec(void *arg)
{
int i;
CFIndex nLoaded = 0;
allBundles = CFArrayCreateMutable(NULL, 0, NULL);
for (i = 0; i < N_PLUGIN_WHITELIST; i++) {
if (pluginWhitelist[i] != NULL) {
CFSetSetValue(_plugins_allowed, pluginWhitelist[i]);
}
}
_SCDPluginExecInit();
if (arg == NULL) {
char path[MAXPATHLEN];
NSSearchPathEnumerationState state;
state = NSStartSearchPathEnumeration(NSLibraryDirectory,
NSSystemDomainMask);
while ((state = NSGetNextSearchPathEnumeration(state, path))) {
CFArrayRef bundles;
CFURLRef url;
#if TARGET_IPHONE_SIMULATOR
const char *path_sim_prefix;
path_sim_prefix = getenv("IPHONE_SIMULATOR_ROOT");
if ((path_sim_prefix != NULL) && (strcmp(path_sim_prefix, ".") != 0)) {
char path_sim[MAXPATHLEN];
strlcpy(path_sim, path_sim_prefix, sizeof(path_sim));
strlcat(path_sim, path, sizeof(path_sim));
strlcpy(path, path_sim, sizeof(path));
} else {
path[0] = '\0';
}
#endif // TARGET_IPHONE_SIMULATOR
strlcat(path, BUNDLE_DIRECTORY, sizeof(path));
SC_log(LOG_DEBUG, "searching for bundles in \"%s\"", path);
url = CFURLCreateFromFileSystemRepresentation(NULL,
(UInt8 *)path,
strlen(path),
TRUE);
bundles = CFBundleCreateBundlesFromDirectory(NULL, url, CFSTR(".bundle"));
CFRelease(url);
if (bundles != NULL) {
CFIndex i;
CFIndex n;
n = CFArrayGetCount(bundles);
for (i = 0; i < n; i++) {
CFBundleRef bundle;
bundle = (CFBundleRef)CFArrayGetValueAtIndex(bundles, i);
addBundle(bundle, FALSE);
ALT_CFRelease(bundle);
}
CFRelease(bundles);
}
}
sortBundles(allBundles);
} else {
CFBundleRef bundle;
CFURLRef url;
url = CFURLCreateFromFileSystemRepresentation(NULL,
(UInt8 *)arg,
strlen((char *)arg),
TRUE);
bundle = CFBundleCreate(NULL, url);
if (bundle != NULL) {
addBundle(bundle, TRUE);
CFRelease(bundle);
}
CFRelease(url);
}
for (i = 0; i < CFArrayGetCount(allBundles); i++) {
bundleInfoRef bi = (bundleInfoRef)CFArrayGetValueAtIndex(allBundles, i);
CFStringRef bundleID = CFBundleGetIdentifier(bi->bundle);
if (_SC_CFEqual(bundleID,
CFSTR("com.apple.SystemConfiguration.InterfaceNamer")))
{
CFArrayRemoveValueAtIndex(allBundles, i);
CFArrayInsertValueAtIndex(allBundles, 0, bi);
break;
}
}
#ifdef DEBUG
traceBundle("before loading any plugins", NULL);
#endif
SC_log(LOG_DEBUG, "loading bundles");
CFArrayApplyFunction(allBundles,
CFRangeMake(0, CFArrayGetCount(allBundles)),
loadBundle,
&nLoaded);
SC_log(LOG_DEBUG, "calling bundle load() functions");
CFArrayApplyFunction(allBundles,
CFRangeMake(0, CFArrayGetCount(allBundles)),
callLoadFunction,
NULL);
if (nLoaded == 0) {
goto done;
}
SC_log(LOG_DEBUG, "calling bundle start() functions");
CFArrayApplyFunction(allBundles,
CFRangeMake(0, CFArrayGetCount(allBundles)),
callStartFunction,
NULL);
SC_log(LOG_DEBUG, "calling bundle prime() functions");
CFArrayApplyFunction(allBundles,
CFRangeMake(0, CFArrayGetCount(allBundles)),
callPrimeFunction,
NULL);
#ifdef DEBUG
if (arg == NULL && (nLoaded > 0)) {
CFRunLoopTimerRef timer;
timer = CFRunLoopTimerCreate(NULL,
CFAbsoluteTimeGetCurrent() + 1.0,
1.0,
0,
0,
timerCallback,
NULL);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
CFRelease(timer);
}
#endif
#ifdef DEBUG
traceBundle("about to start plugin CFRunLoop", NULL);
#endif
SC_log(LOG_DEBUG, "starting plugin CFRunLoop");
plugin_runLoop = CFRunLoopGetCurrent();
pthread_setname_np("Main plugin thread");
CFRunLoopRun();
done :
SC_log(LOG_INFO, "No more work for the \"configd\" plugin thread");
plugin_runLoop = NULL;
return NULL;
}
__private_extern__
void
plugin_init()
{
pthread_attr_t tattr;
pthread_t tid;
SC_log(LOG_DEBUG, "Starting \"configd\" plugin thread");
pthread_attr_init(&tattr);
pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &tattr, plugin_exec, NULL);
pthread_attr_destroy(&tattr);
SC_log(LOG_DEBUG, " thread id=%p", tid);
return;
}