#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include "SCDynamicStoreInternal.h"
#include "config.h"
static int _sc_active = 0;
static CFStringRef _sc_bundleID = NULL;
static pthread_mutex_t _sc_lock = PTHREAD_MUTEX_INITIALIZER;
static mach_port_t _sc_server = MACH_PORT_NULL;
static CFStringRef
__SCDynamicStoreCopyDescription(CFTypeRef cf) {
CFAllocatorRef allocator = CFGetAllocator(cf);
CFMutableStringRef result;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)cf;
result = CFStringCreateMutable(allocator, 0);
CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore %p [%p]> { "), cf, allocator);
if (storePrivate->server != MACH_PORT_NULL) {
CFStringAppendFormat(result, NULL, CFSTR("server port=%d"), storePrivate->server);
} else {
CFStringAppendFormat(result, NULL, CFSTR("server not (no longer) available"));
}
if (storePrivate->locked) {
CFStringAppendFormat(result, NULL, CFSTR(", locked"));
}
switch (storePrivate->notifyStatus) {
case Using_NotifierWait :
CFStringAppendFormat(result, NULL, CFSTR(", waiting for a notification"));
break;
case Using_NotifierInformViaMachPort :
CFStringAppendFormat(result, NULL, CFSTR(", mach port notifications"));
break;
case Using_NotifierInformViaFD :
CFStringAppendFormat(result, NULL, CFSTR(", FD notifications"));
break;
case Using_NotifierInformViaSignal :
CFStringAppendFormat(result, NULL, CFSTR(", BSD signal notifications"));
break;
case Using_NotifierInformViaRunLoop :
case Using_NotifierInformViaCallback :
if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) {
CFStringAppendFormat(result, NULL, CFSTR(", runloop notifications"));
CFStringAppendFormat(result, NULL, CFSTR(" (func=0x%8.8x"), storePrivate->rlsFunction);
CFStringAppendFormat(result, NULL, CFSTR(", info=0x%8.8x"), storePrivate->rlsContext.info);
CFStringAppendFormat(result, NULL, CFSTR(", rls=0x%8.8x" ), storePrivate->rls);
CFStringAppendFormat(result, NULL, CFSTR(", refs=%d" ), storePrivate->rlsRefs);
} else {
CFStringAppendFormat(result, NULL, CFSTR(", mach port/callback notifications"));
CFStringAppendFormat(result, NULL, CFSTR(" (func=0x%8.8x"), storePrivate->callbackFunction);
CFStringAppendFormat(result, NULL, CFSTR(", info=0x%8.8x"), storePrivate->callbackArgument);
}
if (storePrivate->callbackRLS != NULL) {
CFStringAppendFormat(result, NULL, CFSTR(", notify rls=%@" ), storePrivate->callbackRLS);
}
CFStringAppendFormat(result, NULL, CFSTR(")"));
break;
default :
CFStringAppendFormat(result, NULL, CFSTR(", notification delivery not requested%s"),
storePrivate->rlsFunction ? " (yet)" : "");
break;
}
CFStringAppendFormat(result, NULL, CFSTR(" }"));
return result;
}
static void
__SCDynamicStoreDeallocate(CFTypeRef cf)
{
int oldThreadState;
int sc_status;
kern_return_t status;
SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
(void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldThreadState);
(void) SCDynamicStoreNotifyCancel(store);
if ((storePrivate->server != MACH_PORT_NULL) && storePrivate->locked) {
(void) SCDynamicStoreUnlock(store);
}
if (storePrivate->server != MACH_PORT_NULL) {
status = configclose(storePrivate->server, (int *)&sc_status);
#ifdef DEBUG
if (status != KERN_SUCCESS) {
if (status != MACH_SEND_INVALID_DEST)
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCDynamicStoreDeallocate configclose(): %s"), mach_error_string(status));
}
#endif
(void) mach_port_destroy(mach_task_self(), storePrivate->server);
storePrivate->server = MACH_PORT_NULL;
}
(void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldThreadState);
pthread_testcancel();
if (storePrivate->rlsContext.release != NULL) {
(*storePrivate->rlsContext.release)(storePrivate->rlsContext.info);
}
CFRelease(storePrivate->keys);
CFRelease(storePrivate->patterns);
pthread_mutex_lock(&_sc_lock);
_sc_active--;
if ((_sc_active == 0) && (_sc_server != MACH_PORT_NULL)) {
(void)mach_port_deallocate(mach_task_self(), _sc_server);
_sc_server = MACH_PORT_NULL;
}
pthread_mutex_unlock(&_sc_lock);
return;
}
static CFTypeID __kSCDynamicStoreTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __SCDynamicStoreClass = {
0, "SCDynamicStore", NULL, NULL, __SCDynamicStoreDeallocate, NULL, NULL, NULL, __SCDynamicStoreCopyDescription };
static void
childForkHandler()
{
_sc_active = 0;
_sc_server = MACH_PORT_NULL;
return;
}
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
static void
__SCDynamicStoreInitialize(void) {
CFBundleRef bundle;
__kSCDynamicStoreTypeID = _CFRuntimeRegisterClass(&__SCDynamicStoreClass);
(void) pthread_atfork(NULL, NULL, childForkHandler);
bundle = CFBundleGetMainBundle();
if (bundle != NULL) {
_sc_bundleID = CFBundleGetIdentifier(bundle);
if (_sc_bundleID != NULL) {
CFRetain(_sc_bundleID);
} else {
CFURLRef url;
url = CFBundleCopyExecutableURL(bundle);
if (url != NULL) {
_sc_bundleID = CFURLCopyPath(url);
CFRelease(url);
}
}
if (_sc_bundleID != NULL) {
if (CFEqual(_sc_bundleID, CFSTR("/"))) {
CFRelease(_sc_bundleID);
_sc_bundleID = CFStringCreateWithFormat(NULL, NULL, CFSTR("(%d)"), getpid());
}
}
}
return;
}
SCDynamicStorePrivateRef
__SCDynamicStoreCreatePrivate(CFAllocatorRef allocator,
const CFStringRef name,
SCDynamicStoreCallBack callout,
SCDynamicStoreContext *context)
{
int sc_status = kSCStatusOK;
uint32_t size;
SCDynamicStorePrivateRef storePrivate;
pthread_once(&initialized, __SCDynamicStoreInitialize);
size = sizeof(SCDynamicStorePrivate) - sizeof(CFRuntimeBase);
storePrivate = (SCDynamicStorePrivateRef)_CFRuntimeCreateInstance(allocator,
__kSCDynamicStoreTypeID,
size,
NULL);
if (storePrivate == NULL) {
_SCErrorSet(kSCStatusFailed);
return NULL;
}
storePrivate->server = MACH_PORT_NULL;
storePrivate->locked = FALSE;
storePrivate->useSessionKeys = FALSE;
storePrivate->notifyStatus = NotifierNotRegistered;
storePrivate->rlsRefs = 0;
storePrivate->rls = NULL;
storePrivate->rlsFunction = callout;
storePrivate->rlsContext.info = NULL;
storePrivate->rlsContext.retain = NULL;
storePrivate->rlsContext.release = NULL;
storePrivate->rlsContext.copyDescription = NULL;
if (context) {
bcopy(context, &storePrivate->rlsContext, sizeof(SCDynamicStoreContext));
if (context->retain != NULL) {
storePrivate->rlsContext.info = (void *)(*context->retain)(context->info);
}
}
storePrivate->callbackFunction = NULL;
storePrivate->callbackArgument = NULL;
storePrivate->callbackPort = NULL;
storePrivate->callbackRLS = NULL;
storePrivate->keys = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
storePrivate->patterns = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
storePrivate->notifyPort = MACH_PORT_NULL;
storePrivate->notifyPortIdentifier = 0;
storePrivate->notifyFile = -1;
storePrivate->notifyFileIdentifier = 0;
storePrivate->notifySignal = 0;
storePrivate->notifySignalTask = TASK_NULL;
pthread_mutex_lock(&_sc_lock);
if (_sc_server == MACH_PORT_NULL) {
char *server_name;
kern_return_t status;
server_name = getenv("SCD_SERVER");
if (!server_name) {
server_name = SCD_SERVER;
}
status = bootstrap_look_up(bootstrap_port, server_name, &_sc_server);
switch (status) {
case BOOTSTRAP_SUCCESS :
break;
case BOOTSTRAP_UNKNOWN_SERVICE :
sc_status = status;
goto done;
default :
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreCreate[WithOptions] bootstrap_look_up() failed: status=%d"), status);
#endif
sc_status = status;
goto done;
}
}
_sc_active++;
done :
pthread_mutex_unlock(&_sc_lock);
if (sc_status != kSCStatusOK) {
_SCErrorSet(sc_status);
CFRelease(storePrivate);
storePrivate = NULL;
}
return storePrivate;
}
const CFStringRef kSCDynamicStoreUseSessionKeys = CFSTR("UseSessionKeys");
SCDynamicStoreRef
SCDynamicStoreCreateWithOptions(CFAllocatorRef allocator,
CFStringRef name,
CFDictionaryRef storeOptions,
SCDynamicStoreCallBack callout,
SCDynamicStoreContext *context)
{
SCDynamicStorePrivateRef storePrivate;
kern_return_t status;
CFDataRef utfName;
xmlData_t myNameRef;
CFIndex myNameLen;
CFDataRef xmlOptions = NULL;
xmlData_t myOptionsRef = NULL;
CFIndex myOptionsLen = 0;
int sc_status = kSCStatusFailed;
storePrivate = __SCDynamicStoreCreatePrivate(allocator, name, callout, context);
if (storePrivate == NULL) {
return NULL;
}
if (_sc_bundleID != NULL) {
CFStringRef fullName;
fullName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:%@"), _sc_bundleID, name);
name = fullName;
} else {
CFRetain(name);
}
if (!_SCSerializeString(name, &utfName, (void **)&myNameRef, &myNameLen)) {
CFRelease(name);
goto done;
}
CFRelease(name);
if (storeOptions) {
if (!_SCSerialize(storeOptions, &xmlOptions, (void **)&myOptionsRef, &myOptionsLen)) {
CFRelease(utfName);
goto done;
}
}
status = configopen(_sc_server,
myNameRef,
myNameLen,
myOptionsRef,
myOptionsLen,
&storePrivate->server,
(int *)&sc_status);
CFRelease(utfName);
if (xmlOptions) CFRelease(xmlOptions);
if (status != KERN_SUCCESS) {
#ifdef DEBUG
if (status != MACH_SEND_INVALID_DEST)
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreCreate[WithOptions] configopen(): %s"), mach_error_string(status));
#endif
sc_status = status;
goto done;
}
done :
if (sc_status != kSCStatusOK) {
_SCErrorSet(sc_status);
CFRelease(storePrivate);
storePrivate = NULL;
}
return (SCDynamicStoreRef)storePrivate;
}
SCDynamicStoreRef
SCDynamicStoreCreate(CFAllocatorRef allocator,
CFStringRef name,
SCDynamicStoreCallBack callout,
SCDynamicStoreContext *context)
{
return SCDynamicStoreCreateWithOptions(allocator, name, NULL, callout, context);
}
CFTypeID
SCDynamicStoreGetTypeID(void) {
pthread_once(&initialized, __SCDynamicStoreInitialize);
return __kSCDynamicStoreTypeID;
}