X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/0fae82ee9e32dcee00597b75650c675a75eab32e..085a2e6a11110e1303f99ce18f80f98e067cb876:/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c diff --git a/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c b/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c index 9b1cf56..02e7d48 100644 --- a/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c +++ b/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c @@ -1,22 +1,23 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2005, 2008-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * * @APPLE_LICENSE_HEADER_END@ */ @@ -30,6 +31,10 @@ * - initial revision */ +#include +#include +#include +#include #include #include @@ -38,195 +43,47 @@ #include "SCDynamicStoreInternal.h" #include "config.h" /* MiG generated file */ -#include "v1Compatibility.h" -static void -informCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) +static CFStringRef +notifyMPCopyDescription(const 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")); - } - } + SCDynamicStoreRef store = (SCDynamicStoreRef)info; -#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(CFRunLoopGetCurrent(), - storePrivate->callbackRunLoopSource, - kCFRunLoopDefaultMode); - CFRelease(storePrivate->callbackRunLoopSource); - - /* invalidate port */ - CFMachPortInvalidate(storePrivate->callbackPort); - CFRelease(storePrivate->callbackPort); - - /* disable notifier */ - 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) { - /* 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); - - /* invalidate port */ - CFMachPortInvalidate(storePrivate->callbackPort); - CFRelease(storePrivate->callbackPort); - - 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; } @@ -237,32 +94,46 @@ 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, NULL, NULL, 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; } @@ -275,31 +146,70 @@ 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); - 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 ((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); } - CFRunLoopAddSource(rl, storePrivate->callbackRunLoopSource, mode); return; } @@ -307,37 +217,90 @@ 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 ((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); + } + } + } - CFRunLoopRemoveSource(rl, storePrivate->callbackRunLoopSource, 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)); - /* remove the run loop source */ - CFRelease(storePrivate->callbackRunLoopSource); + if (storePrivate->rlList != NULL) { + CFRelease(storePrivate->rlList); + storePrivate->rlList = NULL; + } - /* invalidate port */ - CFMachPortInvalidate(storePrivate->callbackPort); - CFRelease(storePrivate->callbackPort); + if (storePrivate->rlsNotifyRLS != NULL) { + /* invalidate & remove the run loop source */ + CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS); + CFRelease(storePrivate->rlsNotifyRLS); + storePrivate->rlsNotifyRLS = NULL; + } - 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; + 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 != MACH_PORT_NULL) { + status = notifycancel(storePrivate->server, (int *)&sc_status); + + (void) __SCDynamicStoreCheckRetryAndHandleError(store, + status, + &sc_status, + "rlsCancel notifycancel()"); + + if (status != KERN_SUCCESS) { + return; + } } } + return; } + static void rlsPerform(void *info) { @@ -348,38 +311,139 @@ 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 (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, storePrivate->rlsContext.info); - if (context_release) { + if (rlsFunction != NULL) { + (*rlsFunction)(store, changedKeys, context_info); + } + if (context_release != NULL) { context_release(context_info); } + done : + CFRelease(changedKeys); return; } +static CFTypeRef +rlsRetain(CFTypeRef cf) +{ + SCDynamicStoreRef store = (SCDynamicStoreRef)cf; + SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)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; +} + + +static void +rlsRelease(CFTypeRef cf) +{ + SCDynamicStoreRef store = (SCDynamicStoreRef)cf; + SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; + + 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); + 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, CFIndex order) { - SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; - - SCLog(_sc_verbose, LOG_DEBUG, CFSTR("SCDynamicStoreCreateRunLoopSource:")); + SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; - if (!store) { + if (store == NULL) { /* sorry, you must provide a session */ _SCErrorSet(kSCStatusNoStoreSession); return NULL; @@ -394,8 +458,7 @@ SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator, switch (storePrivate->notifyStatus) { case NotifierNotRegistered : case Using_NotifierInformViaRunLoop : - /* set notifier active */ - storePrivate->notifyStatus = Using_NotifierInformViaRunLoop; + /* OK to enable runloop notification */ break; default : /* sorry, you can only have one notification registered at once */ @@ -403,23 +466,187 @@ SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator, 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; + if (storePrivate->rls != NULL) { + CFRetain(storePrivate->rls); + } else { + CFRunLoopSourceContext context = { 0 // version + , (void *)store // info + , rlsRetain // retain + , rlsRelease // release + , rlsCopyDescription // copyDescription + , CFEqual // equal + , CFHash // hash + , rlsSchedule // schedule + , rlsCancel // cancel + , rlsPerform // perform + }; storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context); + if (storePrivate->rls == NULL) { + _SCErrorSet(kSCStatusFailed); + } } - return (CFRunLoopSourceRef)CFRetain(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); + goto cleanup; + } + + // 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; +}