#include <mach-o/dyld.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <dirent.h>
#include <unistd.h>
#include <NSSystemDirectories.h>
#include "configd.h"
typedef struct {
char bundle[MAXNAMLEN + 1];
char path [MAXPATHLEN];
SCDBundleStartRoutine_t start;
SCDBundlePrimeRoutine_t prime;
} plugin, *pluginRef;
CFMutableArrayRef plugins;
typedef kern_return_t (*cer_func_t) (mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt);
typedef kern_return_t (*cer_state_func_t) (mach_port_t exception_port,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt);
typedef kern_return_t (*cer_identity_func_t) (mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt);
static cer_func_t catch_exception_raise_func = NULL;
static cer_state_func_t catch_exception_raise_state_func = NULL;
static cer_identity_func_t catch_exception_raise_identity_func = NULL;
kern_return_t
catch_exception_raise(mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt)
{
if (catch_exception_raise_func == NULL) {
abort();
}
return (*catch_exception_raise_func)(exception_port,
thread,
task,
exception,
code,
codeCnt);
}
kern_return_t
catch_exception_raise_state(mach_port_t exception_port,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt)
{
if (catch_exception_raise_state_func == 0) {
abort();
}
return (*catch_exception_raise_state_func)(exception_port,
exception,
code,
codeCnt,
flavor,
old_state,
old_stateCnt,
new_state,
new_stateCnt);
}
kern_return_t
catch_exception_raise_state_identity(mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt)
{
if (catch_exception_raise_identity_func == 0) {
abort();
}
return (*catch_exception_raise_identity_func)(exception_port,
thread,
task,
exception,
code,
codeCnt,
flavor,
old_state,
old_stateCnt,
new_state,
new_stateCnt);
}
static boolean_t
bundleLoad(pluginRef info)
{
int len;
NSObjectFileImage image;
NSObjectFileImageReturnCode status;
NSModule module;
NSSymbol symbol;
unsigned long options;
char *bundleExe;
struct stat sb;
len = strlen(info->path);
len += sizeof(BUNDLE_NEW_SUBDIR) - 1;
len += strlen(info->bundle);
len += sizeof(BUNDLE_DEBUG_EXTENSION);
bundleExe = CFAllocatorAllocate(NULL, len, 0);
strcpy(bundleExe, info->path);
strcat(bundleExe, BUNDLE_OLD_SUBDIR);
strcat(bundleExe, info->bundle);
if (stat(bundleExe, &sb) == 0) {
goto load;
}
strcat(bundleExe, BUNDLE_DEBUG_EXTENSION);
if (stat(bundleExe, &sb) == 0) {
goto load;
}
strcpy(bundleExe, info->path);
strcat(bundleExe, BUNDLE_NEW_SUBDIR);
strcat(bundleExe, info->bundle);
if (stat(bundleExe, &sb) == 0) {
goto load;
}
strcat(bundleExe, BUNDLE_DEBUG_EXTENSION);
if (stat(bundleExe, &sb) == 0) {
goto load;
}
SCDLog(LOG_ERR,
CFSTR("bundleLoad() failed, no executable for %s in %s"),
info->bundle,
info->path);
CFAllocatorDeallocate(NULL, bundleExe);
return FALSE;
load :
SCDLog(LOG_DEBUG, CFSTR("loading %s"), bundleExe);
status = NSCreateObjectFileImageFromFile(bundleExe, &image);
if (status != NSObjectFileImageSuccess) {
char *err;
switch (status) {
case NSObjectFileImageFailure :
err = "NSObjectFileImageFailure";
break;
case NSObjectFileImageInappropriateFile :
err = "NSObjectFileImageInappropriateFile";
break;
case NSObjectFileImageArch :
err = "NSObjectFileImageArch";
break;
case NSObjectFileImageFormat :
err = "NSObjectFileImageFormat";
break;
case NSObjectFileImageAccess :
err = "NSObjectFileImageAccess";
break;
default :
err = "Unknown";
break;
}
SCDLog(LOG_ERR, CFSTR("NSCreateObjectFileImageFromFile() failed"));
SCDLog(LOG_ERR, CFSTR(" executable path = %s"), bundleExe);
SCDLog(LOG_ERR, CFSTR(" error status = %s"), err);
CFAllocatorDeallocate(NULL, bundleExe);
return FALSE;
}
options = NSLINKMODULE_OPTION_BINDNOW;
options |= NSLINKMODULE_OPTION_PRIVATE;
options |= NSLINKMODULE_OPTION_RETURN_ON_ERROR;
module = NSLinkModule(image, bundleExe, options);
if (module == NULL) {
NSLinkEditErrors c;
int errorNumber;
const char *fileName;
const char *errorString;
SCDLog(LOG_ERR, CFSTR("NSLinkModule() failed"));
SCDLog(LOG_ERR, CFSTR(" executable path = %s"), bundleExe);
NSLinkEditError(&c, &errorNumber, &fileName, &errorString);
SCDLog(LOG_ERR, CFSTR(" NSLinkEditErrors = %d"), (int)c);
SCDLog(LOG_ERR, CFSTR(" errorNumber = %d"), errorNumber);
if((fileName != NULL) && (*fileName != '\0'))
SCDLog(LOG_ERR, CFSTR(" fileName = %s"), fileName);
if((errorString != NULL) && (*errorString != '\0'))
SCDLog(LOG_ERR, CFSTR(" errorString = %s"), errorString);
CFAllocatorDeallocate(NULL, bundleExe);
return FALSE;
}
CFAllocatorDeallocate(NULL, bundleExe);
symbol = NSLookupSymbolInModule(module, BUNDLE_ENTRY_POINT);
if (symbol) {
info->start = NSAddressOfSymbol(symbol);
}
symbol = NSLookupSymbolInModule(module, BUNDLE_ENTRY_POINT2);
if (symbol) {
info->prime = NSAddressOfSymbol(symbol);
}
if ((info->start == NULL) && (info->prime == NULL)) {
SCDLog(LOG_DEBUG, CFSTR(" no entry points"));
return FALSE;
}
symbol = NSLookupSymbolInModule(module, "_catch_exception_raise");
if (symbol) {
catch_exception_raise_func = NSAddressOfSymbol(symbol);
}
symbol = NSLookupSymbolInModule(module, "_catch_exception_raise_state");
if (symbol) {
catch_exception_raise_state_func = NSAddressOfSymbol(symbol);
}
symbol = NSLookupSymbolInModule(module, "_catch_exception_raise_identity");
if (symbol) {
catch_exception_raise_identity_func = NSAddressOfSymbol(symbol);
}
return TRUE;
}
static void
bundleStart(const void *value, void *context)
{
CFDataRef data = (CFDataRef)value;
pluginRef info;
info = (pluginRef)CFDataGetBytePtr(data);
if (info->start) {
(*info->start)(info->bundle, info->path);
}
}
static void
bundlePrime(const void *value, void *context)
{
CFDataRef data = (CFDataRef)value;
pluginRef info;
info = (pluginRef)CFDataGetBytePtr(data);
if (info->prime) {
(*info->prime)(info->bundle, info->path);
}
}
static void
loadOne(const char *bundleDir, const char *bundleName)
{
CFMutableDataRef info;
pluginRef pluginInfo;
int len;
len = strlen(bundleName);
if (len <= sizeof(BUNDLE_DIR_EXTENSION)) {
return;
}
len -= sizeof(BUNDLE_DIR_EXTENSION) - 1;
if (strcmp(&bundleName[len], BUNDLE_DIR_EXTENSION) != 0) {
return;
}
info = CFDataCreateMutable(NULL, sizeof(plugin));
pluginInfo = (pluginRef)CFDataGetBytePtr(info);
pluginInfo->start = NULL;
pluginInfo->prime = NULL;
pluginInfo->bundle[0] = '\0';
(void) strncat(pluginInfo->bundle, bundleName, len);
(void) sprintf(pluginInfo->path, "%s/%s", bundleDir, bundleName);
if (bundleLoad(pluginInfo)) {
SCDLog(LOG_INFO, CFSTR("%s loaded"), bundleName);
CFArrayAppendValue(plugins, info);
} else {
SCDLog(LOG_ERR, CFSTR("load of \"%s\" failed"), bundleName);
}
CFRelease(info);
return;
}
static void
loadAll(const char *bundleDir)
{
DIR *dirp;
struct dirent *dp;
dirp = opendir(bundleDir);
if (dirp == NULL) {
return;
}
while ((dp = readdir(dirp)) != NULL) {
loadOne(bundleDir, dp->d_name);
}
closedir(dirp);
return;
}
void
timerCallback(CFRunLoopTimerRef timer, void *info)
{
SCDLog(LOG_INFO, CFSTR("the CFRunLoop is waiting for something to happen...."));
return;
}
void *
plugin_exec(void *arg)
{
NSSearchPathEnumerationState state;
char path[MAXPATHLEN];
plugins = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (arg == NULL) {
state = NSStartSearchPathEnumeration(NSLibraryDirectory,
NSLocalDomainMask|NSSystemDomainMask);
while ((state = NSGetNextSearchPathEnumeration(state, path))) {
strcat(path, BUNDLE_DIRECTORY);
SCDLog(LOG_DEBUG, CFSTR("searching for plugins in \"%s\""), path);
loadAll(path);
}
if (SCDOptionGet(NULL, kSCDOptionDebug)) {
SCDLog(LOG_DEBUG, CFSTR("searching for plugins in \".\""));
loadAll(".");
}
} else {
char *bn, *bd;
if ((bn = strrchr((char *)arg, '/')) != NULL) {
int len;
len = bn - (char *)arg;
if (len == 0)
len++;
bd = CFAllocatorAllocate(NULL, len + 1, 0);
bd[0] = '\0';
(void) strncat(bd, (char *)arg, len);
bn++;
} else {
bd = CFAllocatorAllocate(NULL, sizeof("."), 0);
(void) strcpy(bd, ".");
bn = (char *)arg;
}
loadOne(bd, bn);
CFAllocatorDeallocate(NULL, bd);
if (CFArrayGetCount(plugins)) {
CFRunLoopTimerRef timer;
timer = CFRunLoopTimerCreate(NULL,
CFAbsoluteTimeGetCurrent() + 1.0,
60.0,
0,
0,
timerCallback,
NULL);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
CFRelease(timer);
}
}
SCDLog(LOG_DEBUG, CFSTR("calling plugin start() functions"));
CFArrayApplyFunction(plugins,
CFRangeMake(0, CFArrayGetCount(plugins)),
bundleStart,
NULL);
SCDLog(LOG_DEBUG, CFSTR("calling plugin prime() functions"));
CFArrayApplyFunction(plugins,
CFRangeMake(0, CFArrayGetCount(plugins)),
bundlePrime,
NULL);
CFRelease(plugins);
if (!SCDOptionGet(NULL, kSCDOptionDebug) && (arg == NULL)) {
kill(getppid(), SIGTERM);
}
SCDLog(LOG_DEBUG, CFSTR("starting plugin CFRunLoop"));
CFRunLoopRun();
SCDLog(LOG_INFO, CFSTR("what, no more work for the \"configd\" plugins?"));
return NULL;
}
void
plugin_init()
{
pthread_attr_t tattr;
pthread_t tid;
SCDLog(LOG_DEBUG, CFSTR("Starting thread for plug-ins..."));
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);
SCDLog(LOG_DEBUG, CFSTR(" thread id=0x%08x"), tid);
return;
}