X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/4c5e92e2493bdfbbce40e998f3b607c72c47af2c..085a2e6a11110e1303f99ce18f80f98e067cb876:/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c?ds=sidebyside diff --git a/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c b/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c index f5cac08..02e7d48 100644 --- a/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c +++ b/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2005, 2008-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -31,6 +31,10 @@ * - initial revision */ +#include +#include +#include +#include #include #include @@ -39,251 +43,97 @@ #include "SCDynamicStoreInternal.h" #include "config.h" /* MiG generated file */ -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) { - /* the server died, disable additional callbacks */ - SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" notifier port closed, disabling notifier")); - } else if (cbFunc == NULL) { - /* there is no (longer) a callback function, disable additional callbacks */ - 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)) { - /* - * callback function returned success. - */ - 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 /* DEBUG */ - - /* remove the run loop source */ - CFRunLoopRemoveSource(storePrivate->callbackRunLoop, - storePrivate->callbackRunLoopSource, - kCFRunLoopDefaultMode); - CFRelease(storePrivate->callbackRunLoopSource); - storePrivate->callbackRunLoop = NULL; - storePrivate->callbackRunLoopSource = NULL; - - /* invalidate port */ - CFMachPortInvalidate(storePrivate->callbackPort); - CFRelease(storePrivate->callbackPort); - storePrivate->callbackPort = NULL; - - /* disable notifier */ - storePrivate->notifyStatus = NotifierNotRegistered; - storePrivate->callbackArgument = NULL; - storePrivate->callbackFunction = NULL; - - return; -} - - -Boolean -SCDynamicStoreNotifyCallback(SCDynamicStoreRef store, - CFRunLoopRef runLoop, - SCDynamicStoreCallBack_v1 func, - void *arg) +static CFStringRef +notifyMPCopyDescription(const void *info) { - 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 - , NULL - }; + SCDynamicStoreRef store = (SCDynamicStoreRef)info; - SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreNotifyCallback:")); - - if (!store) { - /* sorry, you must provide a session */ - _SCErrorSet(kSCStatusNoStoreSession); - return FALSE; - } - - if (storePrivate->server == MACH_PORT_NULL) { - /* sorry, you must have an open session to play */ - _SCErrorSet(kSCStatusNoStoreServer); - return FALSE; - } - - if (storePrivate->notifyStatus != NotifierNotRegistered) { - /* sorry, you can only have one notification registered at once */ - _SCErrorSet(kSCStatusNotifierActive); - return FALSE; - } - - /* Allocating port (for server response) */ - storePrivate->callbackPort = CFMachPortCreate(NULL, - informCallback, - &context, - NULL); - - /* Request a notification when/if the server dies */ - 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?")); - } - - /* Requesting notification via mach port */ - 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; - } - - /* set notifier active */ - storePrivate->notifyStatus = Using_NotifierInformViaCallback; - - /* Creating/adding a run loop source for the port */ - storePrivate->callbackArgument = arg; - storePrivate->callbackFunction = func; - storePrivate->callbackRunLoop = runLoop; - storePrivate->callbackRunLoopSource = - CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0); - - CFRunLoopAddSource(storePrivate->callbackRunLoop, - storePrivate->callbackRunLoopSource, - kCFRunLoopDefaultMode); - - return TRUE; + return CFStringCreateWithFormat(NULL, + NULL, + CFSTR(" {store = %p}"), + store); } 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; + mach_no_senders_notification_t *buf = msg; + mach_msg_id_t msgid = buf->not_header.msgh_id; SCDynamicStoreRef store = (SCDynamicStoreRef)info; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; if (msgid == MACH_NOTIFY_NO_SENDERS) { /* the server died, disable additional callbacks */ +#ifdef DEBUG SCLog(_sc_verbose, LOG_INFO, CFSTR(" rlsCallback(), notifier port closed")); +#endif /* DEBUG */ #ifdef DEBUG - if (port != storePrivate->callbackPort) { - SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != callbackPort?")); + if (port != storePrivate->rlsNotifyPort) { + SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != rlsNotifyPort?")); } #endif /* DEBUG */ - /* remove the run loop source(s) */ - CFRunLoopSourceInvalidate(storePrivate->callbackRunLoopSource); - CFRelease(storePrivate->callbackRunLoopSource); - storePrivate->callbackRunLoopSource = NULL; - - /* invalidate port */ - CFMachPortInvalidate(storePrivate->callbackPort); - CFRelease(storePrivate->callbackPort); - storePrivate->callbackPort = NULL; - - return; + /* re-establish notification and inform the client */ + (void)__SCDynamicStoreReconnectNotifications(store); } /* signal the real runloop source */ - CFRunLoopSourceSignal(storePrivate->rls); + if (storePrivate->rls != NULL) { + CFRunLoopSourceSignal(storePrivate->rls); + } return; } -static void -rlsPortInvalidate(CFMachPortRef mp, void *info) { - mach_port_t port = CFMachPortGetPort(mp); - - // A simple deallocate won't get rid of all the references we've accumulated - SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" invalidate = %d"), port); - (void)mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1); -} - - 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); +#ifdef DEBUG + SCLog(_sc_verbose, LOG_DEBUG, + CFSTR("schedule notifications for mode %@"), + (rl != NULL) ? mode : CFSTR("libdispatch")); +#endif /* DEBUG */ - if (storePrivate->rlsRefs++ == 0) { - CFMachPortContext context = { 0 - , (void *)store - , CFRetain - , CFRelease - , NULL - }; + if (storePrivate->rlList == NULL) { + 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 /* DEBUG */ /* Allocating port (for server response) */ 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)); + SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule 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); + /* + * We can't insert a send right into our own port! This should + * only happen if someone stomped on OUR port (so let's leave + * the port alone). + */ + SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_insert_right(): %s"), mach_error_string(status)); return; } @@ -296,33 +146,68 @@ rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) 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); + /* + * We can't request a notification for our own port! This should + * only happen if someone stomped on OUR port (so let's leave + * the port alone). + */ + SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule mach_port_request_notification(): %s"), mach_error_string(status)); return; } if (oldNotify != MACH_PORT_NULL) { - SCLog(_sc_verbose, LOG_ERR, CFSTR("rlsSchedule(): why is oldNotify != MACH_PORT_NULL?")); + SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule(): oldNotify != MACH_PORT_NULL")); } + retry : + + __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule", port); status = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status); + + if (__SCDynamicStoreCheckRetryAndHandleError(store, + status, + &sc_status, + "rlsSchedule notifyviaport()")) { + goto retry; + } + 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; + if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) { + /* remove the send right that we tried (but failed) to pass to the server */ + (void) mach_port_deallocate(mach_task_self(), port); + } + + /* remove our receive right */ + (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1); + return; + } + + if (sc_status != kSCStatusOK) { + /* something [else] didn't work, remove our receive right */ + (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1); return; } - storePrivate->callbackPort = CFMachPortCreateWithPort(NULL, port, rlsCallback, &context, NULL); - CFMachPortSetInvalidationCallBack(storePrivate->callbackPort, rlsPortInvalidate); - storePrivate->callbackRunLoopSource = CFMachPortCreateRunLoopSource(NULL, storePrivate->callbackPort, 0); + __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port); + storePrivate->rlsNotifyPort = _SC_CFMachPortCreateWithPort("SCDynamicStore", + port, + rlsCallback, + &context); + storePrivate->rlsNotifyRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->rlsNotifyPort, 0); + + storePrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); } - if (storePrivate->callbackRunLoopSource) { - CFRunLoopAddSource(rl, storePrivate->callbackRunLoopSource, mode); + if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) { + if (!_SC_isScheduled(store, rl, mode, storePrivate->rlList)) { + /* + * if we are not already scheduled with this runLoop / runLoopMode + */ + CFRunLoopAddSource(rl, storePrivate->rlsNotifyRLS, mode); + __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->rlsNotifyPort)); + } + + _SC_schedule(store, rl, mode, storePrivate->rlList); } return; @@ -332,45 +217,86 @@ rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) static void rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode) { + CFIndex n = 0; SCDynamicStoreRef store = (SCDynamicStoreRef)info; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; - SCLog(_sc_verbose, LOG_DEBUG, CFSTR("cancel notifications for mode %@"), mode); +#ifdef DEBUG + SCLog(_sc_verbose, LOG_DEBUG, + CFSTR("cancel notifications for mode %@"), + (rl != NULL) ? mode : CFSTR("libdispatch")); +#endif /* DEBUG */ - if (storePrivate->callbackRunLoopSource) { - CFRunLoopRemoveSource(rl, storePrivate->callbackRunLoopSource, mode); + if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) { + if (_SC_unschedule(store, rl, mode, storePrivate->rlList, FALSE)) { + /* + * if currently scheduled on this runLoop / runLoopMode + */ + n = CFArrayGetCount(storePrivate->rlList); + if (n == 0 || !_SC_isScheduled(store, rl, mode, storePrivate->rlList)) { + /* + * if we are no longer scheduled to receive notifications for + * this runLoop / runLoopMode + */ + CFRunLoopRemoveSource(rl, storePrivate->rlsNotifyRLS, mode); + } + } } - if (--storePrivate->rlsRefs == 0) { + if (n == 0) { int sc_status; kern_return_t status; +#ifdef DEBUG SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" cancel callback runloop source")); +#endif /* DEBUG */ + __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL), + "*** rlsCancel", + CFMachPortGetPort(storePrivate->rlsNotifyPort)); + + if (storePrivate->rlList != NULL) { + CFRelease(storePrivate->rlList); + storePrivate->rlList = NULL; + } - if (storePrivate->callbackRunLoopSource) { - /* remove the run loop source */ - CFRelease(storePrivate->callbackRunLoopSource); - storePrivate->callbackRunLoopSource = NULL; + if (storePrivate->rlsNotifyRLS != NULL) { + /* invalidate & remove the run loop source */ + CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS); + CFRelease(storePrivate->rlsNotifyRLS); + storePrivate->rlsNotifyRLS = NULL; } - if (storePrivate->callbackPort) { - /* invalidate port */ - CFMachPortInvalidate(storePrivate->callbackPort); - CFRelease(storePrivate->callbackPort); - storePrivate->callbackPort = NULL; + if (storePrivate->rlsNotifyPort != NULL) { + mach_port_t mp; + + mp = CFMachPortGetPort(storePrivate->rlsNotifyPort); + __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL), + "*** rlsCancel (before invalidating/releasing CFMachPort)", + mp); + + /* invalidate and release port */ + CFMachPortInvalidate(storePrivate->rlsNotifyPort); + CFRelease(storePrivate->rlsNotifyPort); + storePrivate->rlsNotifyPort = NULL; + + /* and, finally, remove our receive right */ + (void)mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1); } - if (storePrivate->server) { + if (storePrivate->server != MACH_PORT_NULL) { status = notifycancel(storePrivate->server, (int *)&sc_status); + + (void) __SCDynamicStoreCheckRetryAndHandleError(store, + status, + &sc_status, + "rlsCancel notifycancel()"); + 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; } @@ -385,28 +311,43 @@ rlsPerform(void *info) SCDynamicStoreRef store = (SCDynamicStoreRef)info; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; - SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notifiction function")); +#ifdef DEBUG + SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function")); +#endif /* DEBUG */ changedKeys = SCDynamicStoreCopyNotifiedKeys(store); - if (!changedKeys) { - /* something happened to the server */ - return; + if (storePrivate->disconnectForceCallBack) { + storePrivate->disconnectForceCallBack = FALSE; + if (changedKeys == NULL) { + changedKeys = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); + } + } else { + if (changedKeys == NULL) { + /* if no changes or something happened to the server */ + return; + } else if (CFArrayGetCount(changedKeys) == 0) { + goto done; + } } rlsFunction = storePrivate->rlsFunction; - if (NULL != storePrivate->rlsContext.retain) { + 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) { + if (rlsFunction != NULL) { + (*rlsFunction)(store, changedKeys, context_info); + } + if (context_release != NULL) { context_release(context_info); } + done : + CFRelease(changedKeys); return; } @@ -418,11 +359,18 @@ rlsRetain(CFTypeRef cf) SCDynamicStoreRef store = (SCDynamicStoreRef)cf; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; - if (storePrivate->notifyStatus != Using_NotifierInformViaRunLoop) { - /* mark RLS active */ - storePrivate->notifyStatus = Using_NotifierInformViaRunLoop; - /* keep a reference to the store */ - CFRetain(store); + switch (storePrivate->notifyStatus) { + case NotifierNotRegistered : + /* mark RLS active */ + storePrivate->notifyStatus = Using_NotifierInformViaRunLoop; + /* keep a reference to the store */ + CFRetain(store); + break; + case Using_NotifierInformViaRunLoop : + break; + default : + SCLog(TRUE, LOG_ERR, CFSTR("rlsRetain() error: notifyStatus=%d"), storePrivate->notifyStatus); + break; } return cf; @@ -435,17 +383,59 @@ rlsRelease(CFTypeRef cf) SCDynamicStoreRef store = (SCDynamicStoreRef)cf; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; - /* mark RLS inactive */ - storePrivate->notifyStatus = NotifierNotRegistered; - storePrivate->rls = NULL; + switch (storePrivate->notifyStatus) { + case NotifierNotRegistered : + break; + case Using_NotifierInformViaRunLoop : + /* mark RLS inactive */ + storePrivate->notifyStatus = NotifierNotRegistered; + storePrivate->rls = NULL; - /* release our reference to the store */ - CFRelease(store); + /* release our reference to the store */ + CFRelease(store); + break; + default : + SCLog(TRUE, LOG_ERR, CFSTR("rlsRelease() error: notifyStatus=%d"), storePrivate->notifyStatus); + break; + } 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(" {")); + 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(""), storePrivate->rlsContext.info); + } + if (description == NULL) { + description = CFRetain(CFSTR("")); + } + CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description); + CFRelease(description); + } + CFStringAppendFormat(result, NULL, CFSTR("}")); + + return result; +} + + CFRunLoopSourceRef SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator, SCDynamicStoreRef store, @@ -453,9 +443,7 @@ SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator, { SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; - SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreCreateRunLoopSource:")); - - if (!store) { + if (store == NULL) { /* sorry, you must provide a session */ _SCErrorSet(kSCStatusNoStoreSession); return NULL; @@ -478,14 +466,14 @@ SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator, return NULL; } - if (storePrivate->rls) { + if (storePrivate->rls != NULL) { CFRetain(storePrivate->rls); } else { CFRunLoopSourceContext context = { 0 // version , (void *)store // info , rlsRetain // retain , rlsRelease // release - , CFCopyDescription // copyDescription + , rlsCopyDescription // copyDescription , CFEqual // equal , CFHash // hash , rlsSchedule // schedule @@ -494,12 +482,171 @@ SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator, }; storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context); + if (storePrivate->rls == NULL) { + _SCErrorSet(kSCStatusFailed); + } } - if (!storePrivate->rls) { + return storePrivate->rls; +} + + +Boolean +SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store, dispatch_queue_t queue) +{ + dispatch_group_t drainGroup = NULL; + dispatch_queue_t drainQueue = NULL; + dispatch_group_t group = NULL; + mach_port_t mp; + Boolean ok = FALSE; + dispatch_source_t source; + SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; + + if (store == NULL) { + // sorry, you must provide a session + _SCErrorSet(kSCStatusNoStoreSession); + return FALSE; + } + + if (queue == NULL) { + if (storePrivate->dispatchQueue == NULL) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + ok = TRUE; + goto cleanup; + } + + if (storePrivate->server == MACH_PORT_NULL) { + // sorry, you must have an open session to play + _SCErrorSet(kSCStatusNoStoreServer); + return FALSE; + } + + if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) { + _SCErrorSet(kSCStatusInvalidArgument); + return FALSE; + } + + if (storePrivate->notifyStatus != NotifierNotRegistered) { + // sorry, you can only have one notification registered at once... + _SCErrorSet(kSCStatusNotifierActive); + return FALSE; + } + + /* + * mark our using of the SCDynamicStore notifications, create and schedule + * the notification port (storePrivate->rlsNotifyPort), and a bunch of other + * "setup" + */ + storePrivate->notifyStatus = Using_NotifierInformViaDispatch; + rlsSchedule((void*)store, NULL, NULL); + if (storePrivate->rlsNotifyPort == NULL) { + /* if we could not schedule the notification */ _SCErrorSet(kSCStatusFailed); - return NULL; + goto cleanup; } - return storePrivate->rls; + // retain the dispatch queue + storePrivate->dispatchQueue = queue; + dispatch_retain(storePrivate->dispatchQueue); + + // + // We've taken a reference to the callers dispatch_queue and we + // want to hold on to that reference until we've processed any/all + // notifications. To facilitate this we create a group, dispatch + // any notification blocks to via that group, and when the caller + // has told us to stop the notifications (unschedule) we wait for + // the group to empty and use the group's finalizer to release + // our reference to the SCDynamicStore. + // + group = dispatch_group_create(); + storePrivate->dispatchGroup = group; + CFRetain(store); + dispatch_set_context(storePrivate->dispatchGroup, (void *)store); + dispatch_set_finalizer_f(storePrivate->dispatchGroup, (dispatch_function_t)CFRelease); + + // create a dispatch source for the mach notifications + mp = CFMachPortGetPort(storePrivate->rlsNotifyPort); + source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue); + if (source == NULL) { + SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_source_create() failed")); + _SCErrorSet(kSCStatusFailed); + goto cleanup; + } + + dispatch_source_set_event_handler(source, ^{ + kern_return_t kr; + mach_msg_id_t msgid; + union { + u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE]; + mach_msg_empty_rcv_t msg; + mach_no_senders_notification_t no_senders; + } notify_msg; + + kr = mach_msg(¬ify_msg.msg.header, // msg + MACH_RCV_MSG, // options + 0, // send_size + sizeof(notify_msg), // rcv_size + mp, // rcv_name + MACH_MSG_TIMEOUT_NONE, // timeout + MACH_PORT_NULL); // notify + if (kr != KERN_SUCCESS) { + SCLog(TRUE, LOG_ERR, + CFSTR("SCDynamicStore notification handler, kr=0x%x"), + kr); + return; + } + + msgid = notify_msg.msg.header.msgh_id; + + CFRetain(store); + dispatch_group_async(group, queue, ^{ + if (msgid == MACH_NOTIFY_NO_SENDERS) { + // re-establish notification and inform the client + (void)__SCDynamicStoreReconnectNotifications(store); + } + rlsPerform(storePrivate); + CFRelease(store); + }); + }); + + dispatch_source_set_cancel_handler(source, ^{ + dispatch_release(source); + }); + + storePrivate->dispatchSource = source; + dispatch_resume(source); + + return TRUE; + + cleanup : + + CFRetain(store); + + if (storePrivate->dispatchSource != NULL) { + dispatch_source_cancel(storePrivate->dispatchSource); + storePrivate->dispatchSource = NULL; + } + drainGroup = storePrivate->dispatchGroup; + storePrivate->dispatchGroup = NULL; + drainQueue = storePrivate->dispatchQueue; + storePrivate->dispatchQueue = NULL; + + rlsCancel((void*)store, NULL, NULL); + + if (drainGroup != NULL) { + dispatch_group_notify(drainGroup, drainQueue, ^{ + // release group/queue references + dispatch_release(drainQueue); + dispatch_release(drainGroup); // releases our store reference + }); + } + + storePrivate->notifyStatus = NotifierNotRegistered; + + CFRelease(store); + + return ok; }