SCDNotifierInformViaCallback.c [plain text]
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include "SCDynamicStoreInternal.h"
#include "config.h"
#include "v1Compatibility.h"
static void
informCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
mach_msg_empty_rcv_t *buf = msg;
mach_msg_id_t msgid = buf->header.msgh_id;
SCDynamicStoreCallBack_v1 cbFunc = storePrivate->callbackFunction;
void *cbArg = storePrivate->callbackArgument;
if (msgid == MACH_NOTIFY_NO_SENDERS) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" notifier port closed, disabling notifier"));
} else if (cbFunc == NULL) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" no callback function, disabling notifier"));
} else {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notifiction function"));
if ((*cbFunc)(store, cbArg)) {
return;
} else {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" callback returned error, disabling notifier"));
}
}
#ifdef DEBUG
if (port != storePrivate->callbackPort) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("informCallback, why is port != callbackPort?"));
}
#endif
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
storePrivate->callbackRunLoopSource,
kCFRunLoopDefaultMode);
CFRelease(storePrivate->callbackRunLoopSource);
CFMachPortInvalidate(storePrivate->callbackPort);
CFRelease(storePrivate->callbackPort);
storePrivate->notifyStatus = NotifierNotRegistered;
storePrivate->callbackArgument = NULL;
storePrivate->callbackFunction = NULL;
storePrivate->callbackPort = NULL;
storePrivate->callbackRunLoop = NULL;
storePrivate->callbackRunLoopSource = NULL;
return;
}
Boolean
SCDynamicStoreNotifyCallback(SCDynamicStoreRef store,
CFRunLoopRef runLoop,
SCDynamicStoreCallBack_v1 func,
void *arg)
{
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
kern_return_t status;
mach_port_t port;
mach_port_t oldNotify;
int sc_status;
CFMachPortContext context = { 0, (void *)store, NULL, NULL, NULL };
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyCallback:"));
if (!store) {
_SCErrorSet(kSCStatusNoStoreSession);
return FALSE;
}
if (storePrivate->server == MACH_PORT_NULL) {
_SCErrorSet(kSCStatusNoStoreServer);
return FALSE;
}
if (storePrivate->notifyStatus != NotifierNotRegistered) {
_SCErrorSet(kSCStatusNotifierActive);
return FALSE;
}
storePrivate->callbackPort = CFMachPortCreate(NULL,
informCallback,
&context,
NULL);
port = CFMachPortGetPort(storePrivate->callbackPort);
status = mach_port_request_notification(mach_task_self(),
port,
MACH_NOTIFY_NO_SENDERS,
1,
port,
MACH_MSG_TYPE_MAKE_SEND_ONCE,
&oldNotify);
if (status != KERN_SUCCESS) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
CFMachPortInvalidate(storePrivate->callbackPort);
CFRelease(storePrivate->callbackPort);
_SCErrorSet(status);
return FALSE;
}
if (oldNotify != MACH_PORT_NULL) {
SCLog(_sc_verbose, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback(): why is oldNotify != MACH_PORT_NULL?"));
}
status = notifyviaport(storePrivate->server,
port,
0,
(int *)&sc_status);
if (status != KERN_SUCCESS) {
if (status != MACH_SEND_INVALID_DEST)
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
CFMachPortInvalidate(storePrivate->callbackPort);
CFRelease(storePrivate->callbackPort);
(void) mach_port_destroy(mach_task_self(), storePrivate->server);
storePrivate->server = MACH_PORT_NULL;
_SCErrorSet(status);
return FALSE;
}
if (sc_status != kSCStatusOK) {
_SCErrorSet(sc_status);
return FALSE;
}
storePrivate->notifyStatus = Using_NotifierInformViaCallback;
storePrivate->callbackArgument = arg;
storePrivate->callbackFunction = func;
storePrivate->callbackRunLoop = runLoop;
storePrivate->callbackRunLoopSource =
CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
CFRunLoopAddSource(storePrivate->callbackRunLoop,
storePrivate->callbackRunLoopSource,
kCFRunLoopDefaultMode);
return TRUE;
}
static void
rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
mach_msg_empty_rcv_t *buf = msg;
mach_msg_id_t msgid = buf->header.msgh_id;
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
if (msgid == MACH_NOTIFY_NO_SENDERS) {
SCLog(_sc_verbose, LOG_INFO, CFSTR(" rlsCallback(), notifier port closed"));
#ifdef DEBUG
if (port != storePrivate->callbackPort) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != callbackPort?"));
}
#endif
CFRunLoopSourceInvalidate(storePrivate->callbackRunLoopSource);
CFRelease(storePrivate->callbackRunLoopSource);
CFMachPortInvalidate(storePrivate->callbackPort);
CFRelease(storePrivate->callbackPort);
return;
}
CFRunLoopSourceSignal(storePrivate->rls);
return;
}
static void
rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
{
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("schedule notifications for mode %@"), mode);
if (storePrivate->rlsRefs++ == 0) {
CFMachPortContext context = { 0, (void *)store, NULL, NULL, NULL };
mach_port_t oldNotify;
mach_port_t port;
int sc_status;
kern_return_t status;
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" activate callback runloop source"));
status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
if (status != KERN_SUCCESS) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("mach_port_allocate(): %s"), mach_error_string(status));
return;
}
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" port = %d"), port);
status = mach_port_insert_right(mach_task_self(),
port,
port,
MACH_MSG_TYPE_MAKE_SEND);
if (status != KERN_SUCCESS) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("mach_port_insert_right(): %s"), mach_error_string(status));
(void) mach_port_destroy(mach_task_self(), port);
return;
}
status = mach_port_request_notification(mach_task_self(),
port,
MACH_NOTIFY_NO_SENDERS,
1,
port,
MACH_MSG_TYPE_MAKE_SEND_ONCE,
&oldNotify);
if (status != KERN_SUCCESS) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
(void) mach_port_destroy(mach_task_self(), port);
return;
}
if (oldNotify != MACH_PORT_NULL) {
SCLog(_sc_verbose, LOG_ERR, CFSTR("rlsSchedule(): why is oldNotify != MACH_PORT_NULL?"));
}
status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
if (status != KERN_SUCCESS) {
if (status != MACH_SEND_INVALID_DEST)
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
(void) mach_port_destroy(mach_task_self(), port);
port = MACH_PORT_NULL;
(void) mach_port_destroy(mach_task_self(), storePrivate->server);
storePrivate->server = MACH_PORT_NULL;
return;
}
storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, rlsCallback, &context, NULL);
storePrivate->callbackRunLoopSource = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
}
CFRunLoopAddSource(rl, storePrivate->callbackRunLoopSource, mode);
return;
}
static void
rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
{
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("cancel notifications for mode %@"), mode);
CFRunLoopRemoveSource(rl, storePrivate->callbackRunLoopSource, mode);
if (--storePrivate->rlsRefs == 0) {
int sc_status;
kern_return_t status;
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" cancel callback runloop source"));
CFRelease(storePrivate->callbackRunLoopSource);
CFMachPortInvalidate(storePrivate->callbackPort);
CFRelease(storePrivate->callbackPort);
status = notifycancel(storePrivate->server, (int *)&sc_status);
if (status != KERN_SUCCESS) {
if (status != MACH_SEND_INVALID_DEST)
SCLog(_sc_verbose, LOG_INFO, CFSTR("notifycancel(): %s"), mach_error_string(status));
(void) mach_port_destroy(mach_task_self(), storePrivate->server);
storePrivate->server = MACH_PORT_NULL;
return;
}
}
return;
}
static void
rlsPerform(void *info)
{
CFArrayRef changedKeys;
void *context_info;
void (*context_release)(const void *);
SCDynamicStoreCallBack rlsFunction;
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notifiction function"));
changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
rlsFunction = storePrivate->rlsFunction;
if (NULL != storePrivate->rlsContext.retain) {
context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
context_release = storePrivate->rlsContext.release;
} else {
context_info = storePrivate->rlsContext.info;
context_release = NULL;
}
(*rlsFunction)(store, changedKeys, storePrivate->rlsContext.info);
if (context_release) {
context_release(context_info);
}
CFRelease(changedKeys);
return;
}
CFRunLoopSourceRef
SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
SCDynamicStoreRef store,
CFIndex order)
{
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreCreateRunLoopSource:"));
if (!store) {
_SCErrorSet(kSCStatusNoStoreSession);
return NULL;
}
if (storePrivate->server == MACH_PORT_NULL) {
_SCErrorSet(kSCStatusNoStoreServer);
return NULL;
}
switch (storePrivate->notifyStatus) {
case NotifierNotRegistered :
case Using_NotifierInformViaRunLoop :
storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
break;
default :
_SCErrorSet(kSCStatusNotifierActive);
return NULL;
}
if (!storePrivate->rls) {
CFRunLoopSourceContext context;
context.version = 0;
context.info = (void *)store;
context.retain = (const void *(*)(const void *))CFRetain;
context.release = (void (*)(const void *))CFRelease;
context.copyDescription = (CFStringRef (*)(const void *))CFCopyDescription;
context.equal = (Boolean (*)(const void *, const void *))CFEqual;
context.hash = (CFHashCode (*)(const void *))CFHash;
context.schedule = rlsSchedule;
context.cancel = rlsCancel;
context.perform = rlsPerform;
storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
}
return (CFRunLoopSourceRef)CFRetain(storePrivate->rls);
}