SCDNotifierInformViaCallback.c [plain text]
#include <Availability.h>
#include <TargetConditionals.h>
#include <sys/cdefs.h>
#if !TARGET_OS_IPHONE
#include <dispatch/dispatch.h>
#endif // !TARGET_OS_IPHONE
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include "SCDynamicStoreInternal.h"
#include "config.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) {
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" notifier port closed, disabling notifier"));
#endif
} else if (cbFunc == NULL) {
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" no callback function, disabling notifier"));
#endif
} else {
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function"));
#endif
if ((*cbFunc)(store, cbArg)) {
return;
} else {
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" callback returned error, disabling notifier"));
#endif
}
}
#ifdef DEBUG
if (port != storePrivate->callbackPort) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("informCallback, why is port != callbackPort?"));
}
#endif
if (storePrivate->callbackRLS != NULL) {
CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
CFRelease(storePrivate->callbackRLS);
storePrivate->callbackRLS = NULL;
}
if (storePrivate->callbackPort != NULL) {
__MACH_PORT_DEBUG(TRUE, "*** informCallback", CFMachPortGetPort(storePrivate->callbackPort));
CFMachPortInvalidate(storePrivate->callbackPort);
CFRelease(storePrivate->callbackPort);
storePrivate->callbackPort = NULL;
}
storePrivate->notifyStatus = NotifierNotRegistered;
storePrivate->callbackArgument = NULL;
storePrivate->callbackFunction = NULL;
return;
}
static CFStringRef
notifyMPCopyDescription(const void *info)
{
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
return CFStringCreateWithFormat(NULL,
NULL,
CFSTR("<SCDynamicStore notification MP> {store = %p}"),
store);
}
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
, CFRetain
, CFRelease
, notifyMPCopyDescription
};
if (store == NULL) {
_SCErrorSet(kSCStatusNoStoreSession);
return FALSE;
}
if (storePrivate->server == MACH_PORT_NULL) {
_SCErrorSet(kSCStatusNoStoreServer);
return FALSE;
}
if (storePrivate->notifyStatus != NotifierNotRegistered) {
_SCErrorSet(kSCStatusNotifierActive);
return FALSE;
}
status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
if (status != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR, CFSTR("mach_port_allocate(): %s"), mach_error_string(status));
_SCErrorSet(status);
return FALSE;
}
status = mach_port_insert_right(mach_task_self(),
port,
port,
MACH_MSG_TYPE_MAKE_SEND);
if (status != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR, CFSTR("mach_port_insert_right(): %s"), mach_error_string(status));
_SCErrorSet(status);
return FALSE;
}
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(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback mach_port_request_notification(): %s"), mach_error_string(status));
_SCErrorSet(status);
return FALSE;
}
if (oldNotify != MACH_PORT_NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback(): oldNotify != MACH_PORT_NULL"));
}
status = notifyviaport(storePrivate->server,
port,
0,
(int *)&sc_status);
if (status != KERN_SUCCESS) {
if (status == MACH_SEND_INVALID_DEST) {
(void) mach_port_deallocate(mach_task_self(), storePrivate->server);
} else {
SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback notifyviaport(): %s"), mach_error_string(status));
}
storePrivate->server = MACH_PORT_NULL;
if (status == MACH_SEND_INVALID_DEST) {
(void) mach_port_deallocate(mach_task_self(), port);
}
(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
_SCErrorSet(status);
return FALSE;
}
if (sc_status != kSCStatusOK) {
_SCErrorSet(sc_status);
return FALSE;
}
storePrivate->notifyStatus = Using_NotifierInformViaCallback;
__MACH_PORT_DEBUG(TRUE, "*** SCDynamicStoreNotifyCallback", port);
storePrivate->callbackArgument = arg;
storePrivate->callbackFunction = func;
storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, informCallback, &context, NULL);
storePrivate->callbackRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
CFRunLoopAddSource(runLoop, storePrivate->callbackRLS, 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) {
#ifdef DEBUG
SCLog(_sc_verbose, LOG_INFO, CFSTR(" rlsCallback(), notifier port closed"));
#endif
#ifdef DEBUG
if (port != storePrivate->callbackPort) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != callbackPort?"));
}
#endif
if (storePrivate->callbackRLS != NULL) {
CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
CFRelease(storePrivate->callbackRLS);
storePrivate->callbackRLS = NULL;
}
if (storePrivate->callbackPort != NULL) {
__MACH_PORT_DEBUG(TRUE, "*** rlsCallback w/MACH_NOTIFY_NO_SENDERS", CFMachPortGetPort(storePrivate->callbackPort));
CFMachPortInvalidate(storePrivate->callbackPort);
CFRelease(storePrivate->callbackPort);
storePrivate->callbackPort = NULL;
}
return;
}
if (storePrivate->rls != NULL) {
CFRunLoopSourceSignal(storePrivate->rls);
}
return;
}
static void
portInvalidate(CFMachPortRef port, void *info) {
mach_port_t mp = CFMachPortGetPort(port);
__MACH_PORT_DEBUG(TRUE, "*** portInvalidate", mp);
(void)mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
}
static void
rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
{
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG,
CFSTR("schedule notifications for mode %@"),
(rl != NULL) ? mode : CFSTR("libdispatch"));
#endif
if (storePrivate->rlsRefs++ == 0) {
CFMachPortContext context = { 0
, (void *)store
, CFRetain
, CFRelease
, notifyMPCopyDescription
};
mach_port_t oldNotify;
mach_port_t port;
int sc_status;
kern_return_t status;
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" activate callback runloop source"));
#endif
status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
if (status != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_allocate(): %s"), mach_error_string(status));
return;
}
status = mach_port_insert_right(mach_task_self(),
port,
port,
MACH_MSG_TYPE_MAKE_SEND);
if (status != KERN_SUCCESS) {
SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_insert_right(): %s"), mach_error_string(status));
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(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_request_notification(): %s"), mach_error_string(status));
return;
}
if (oldNotify != MACH_PORT_NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule(): oldNotify != MACH_PORT_NULL"));
}
__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule", port);
status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
if (status != KERN_SUCCESS) {
if (status == MACH_SEND_INVALID_DEST) {
(void) mach_port_deallocate(mach_task_self(), storePrivate->server);
} else {
SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule notifyviaport(): %s"), mach_error_string(status));
}
storePrivate->server = MACH_PORT_NULL;
if (status == MACH_SEND_INVALID_DEST) {
(void) mach_port_deallocate(mach_task_self(), port);
}
(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
return;
}
__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port);
storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, rlsCallback, &context, NULL);
if (storePrivate->callbackPort == NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("*** CFMachPortCreateWithPort returned NULL while attempting to schedule"));
SCLog(TRUE, LOG_ERR, CFSTR("*** a SCDynamicStore notification. Did this process call \"fork\" without"));
SCLog(TRUE, LOG_ERR, CFSTR("*** calling \"exec\""));
(void) mach_port_deallocate(mach_task_self(), storePrivate->server);
storePrivate->server = MACH_PORT_NULL;
(void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
return;
}
CFMachPortSetInvalidationCallBack(storePrivate->callbackPort, portInvalidate);
storePrivate->callbackRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0);
}
if ((rl != NULL) && (storePrivate->callbackRLS != NULL)) {
CFRunLoopAddSource(rl, storePrivate->callbackRLS, mode);
__MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->callbackPort));
}
return;
}
static void
rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
{
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG,
CFSTR("cancel notifications for mode %@"),
(rl != NULL) ? mode : CFSTR("libdispatch"));
#endif
if ((rl != NULL) && (storePrivate->callbackRLS != NULL)) {
CFRunLoopRemoveSource(rl, storePrivate->callbackRLS, mode);
}
if (--storePrivate->rlsRefs == 0) {
int sc_status;
kern_return_t status;
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" cancel callback runloop source"));
#endif
__MACH_PORT_DEBUG((storePrivate->callbackPort != NULL),
"*** rlsCancel",
CFMachPortGetPort(storePrivate->callbackPort));
if (storePrivate->callbackRLS != NULL) {
CFRunLoopSourceInvalidate(storePrivate->callbackRLS);
CFRelease(storePrivate->callbackRLS);
storePrivate->callbackRLS = NULL;
}
if (storePrivate->callbackPort != NULL) {
__MACH_PORT_DEBUG((storePrivate->callbackPort != NULL),
"*** rlsCancel (before invalidating CFMachPort)",
CFMachPortGetPort(storePrivate->callbackPort));
CFMachPortInvalidate(storePrivate->callbackPort);
CFRelease(storePrivate->callbackPort);
storePrivate->callbackPort = NULL;
}
if (storePrivate->server != MACH_PORT_NULL) {
status = notifycancel(storePrivate->server, (int *)&sc_status);
if (status != KERN_SUCCESS) {
if (status == MACH_SEND_INVALID_DEST) {
(void) mach_port_deallocate(mach_task_self(), storePrivate->server);
} else {
SCLog(TRUE, LOG_ERR, CFSTR("rlsCancel notifycancel(): %s"), mach_error_string(status));
}
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;
#ifdef DEBUG
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function"));
#endif
changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
if (changedKeys == NULL) {
return;
}
if (CFArrayGetCount(changedKeys) == 0) {
goto done;
}
rlsFunction = storePrivate->rlsFunction;
if (storePrivate->rlsContext.retain != NULL) {
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, context_info);
if (context_release) {
context_release(context_info);
}
done :
CFRelease(changedKeys);
return;
}
static CFTypeRef
rlsRetain(CFTypeRef cf)
{
SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
if (storePrivate->notifyStatus != Using_NotifierInformViaRunLoop) {
storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
CFRetain(store);
}
return cf;
}
static void
rlsRelease(CFTypeRef cf)
{
SCDynamicStoreRef store = (SCDynamicStoreRef)cf;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
storePrivate->notifyStatus = NotifierNotRegistered;
storePrivate->rls = NULL;
CFRelease(store);
return;
}
static CFStringRef
rlsCopyDescription(const void *info)
{
CFMutableStringRef result;
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
result = CFStringCreateMutable(NULL, 0);
CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore RLS> {"));
CFStringAppendFormat(result, NULL, CFSTR("store = %p"), store);
if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) {
CFStringRef description = NULL;
CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->rlsFunction);
if ((storePrivate->rlsContext.info != NULL) && (storePrivate->rlsContext.copyDescription != NULL)) {
description = (*storePrivate->rlsContext.copyDescription)(storePrivate->rlsContext.info);
}
if (description == NULL) {
description = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCDynamicStore context %p>"), storePrivate->rlsContext.info);
}
if (description == NULL) {
description = CFRetain(CFSTR("<no description>"));
}
CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description);
CFRelease(description);
} else {
CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->callbackFunction);
CFStringAppendFormat(result, NULL, CFSTR(", info = %p"), storePrivate->callbackArgument);
}
CFStringAppendFormat(result, NULL, CFSTR("}"));
return result;
}
CFRunLoopSourceRef
SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
SCDynamicStoreRef store,
CFIndex order)
{
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
if (store == NULL) {
_SCErrorSet(kSCStatusNoStoreSession);
return NULL;
}
if (storePrivate->server == MACH_PORT_NULL) {
_SCErrorSet(kSCStatusNoStoreServer);
return NULL;
}
switch (storePrivate->notifyStatus) {
case NotifierNotRegistered :
case Using_NotifierInformViaRunLoop :
break;
default :
_SCErrorSet(kSCStatusNotifierActive);
return NULL;
}
if (storePrivate->rls != NULL) {
CFRetain(storePrivate->rls);
} else {
CFRunLoopSourceContext context = { 0 , (void *)store , rlsRetain , rlsRelease , rlsCopyDescription , CFEqual , CFHash , rlsSchedule , rlsCancel , rlsPerform };
storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
if (storePrivate->rls == NULL) {
_SCErrorSet(kSCStatusFailed);
}
}
return storePrivate->rls;
}
#if !TARGET_OS_IPHONE
static boolean_t
SCDynamicStoreNotifyMIGCallback(mach_msg_header_t *message, mach_msg_header_t *reply)
{
SCDynamicStorePrivateRef storePrivate;
storePrivate = dispatch_get_context(dispatch_get_current_queue());
if (storePrivate != NULL) {
CFRetain(storePrivate);
dispatch_async(storePrivate->dispatchQueue, ^{
rlsPerform(storePrivate);
CFRelease(storePrivate);
});
}
reply->msgh_remote_port = MACH_PORT_NULL;
return false;
}
Boolean
SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store, dispatch_queue_t queue)
{
Boolean ok = FALSE;
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
if (store == NULL) {
_SCErrorSet(kSCStatusNoStoreSession);
return FALSE;
}
if (storePrivate->server == MACH_PORT_NULL) {
_SCErrorSet(kSCStatusNoStoreServer);
return FALSE;
}
if (queue != NULL) {
mach_port_t mp;
if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (storePrivate->notifyStatus != NotifierNotRegistered) {
_SCErrorSet(kSCStatusNotifierActive);
return FALSE;
}
storePrivate->notifyStatus = Using_NotifierInformViaDispatch;
rlsSchedule((void*)store, NULL, NULL);
storePrivate->dispatchQueue = queue;
dispatch_retain(storePrivate->dispatchQueue);
storePrivate->callbackQueue = dispatch_queue_create("com.apple.SCDynamicStore.notifications", NULL);
if (storePrivate->callbackQueue == NULL){
SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_queue_create() failed"));
_SCErrorSet(kSCStatusFailed);
goto cleanup;
}
CFRetain(store); dispatch_set_context(storePrivate->callbackQueue, (void *)store);
dispatch_set_finalizer_f(storePrivate->callbackQueue, (dispatch_function_t)CFRelease);
mp = CFMachPortGetPort(storePrivate->callbackPort);
storePrivate->callbackSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
mp,
0,
storePrivate->callbackQueue);
if (storePrivate->callbackSource == NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_source_create() failed"));
_SCErrorSet(kSCStatusFailed);
goto cleanup;
}
dispatch_source_set_event_handler(storePrivate->callbackSource, ^{
dispatch_mig_server(storePrivate->callbackSource,
sizeof(mach_msg_header_t),
SCDynamicStoreNotifyMIGCallback);
});
dispatch_resume(storePrivate->callbackSource);
ok = TRUE;
goto done;
} else {
if (storePrivate->dispatchQueue == NULL) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
ok = TRUE;
}
cleanup :
if (storePrivate->callbackSource != NULL) {
dispatch_source_cancel(storePrivate->callbackSource);
if (storePrivate->callbackQueue != dispatch_get_current_queue()) {
dispatch_sync(storePrivate->callbackQueue, ^{});
}
dispatch_release(storePrivate->callbackSource);
storePrivate->callbackSource = NULL;
}
if (storePrivate->callbackQueue != NULL) {
dispatch_release(storePrivate->callbackQueue);
storePrivate->callbackQueue = NULL;
}
dispatch_release(storePrivate->dispatchQueue);
storePrivate->dispatchQueue = NULL;
rlsCancel((void*)store, NULL, NULL);
storePrivate->notifyStatus = NotifierNotRegistered;
done :
return ok;
}
#endif // !TARGET_OS_IPHONE