X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/a40a14f8bcc57d8bed0203ddee43e8d64db39796..085a2e6a11110e1303f99ce18f80f98e067cb876:/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c?ds=inline diff --git a/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c b/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c index 3e22cc4..02e7d48 100644 --- a/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c +++ b/SystemConfiguration.fproj/SCDNotifierInformViaCallback.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (c) 2000-2005, 2008-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -34,9 +34,7 @@ #include #include #include -#if !TARGET_OS_IPHONE #include -#endif // !TARGET_OS_IPHONE #include #include @@ -46,72 +44,6 @@ #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 */ -#ifdef DEBUG - SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" notifier port closed, disabling notifier")); -#endif /* DEBUG */ - } else if (cbFunc == NULL) { - /* there is no (longer) a callback function, disable additional callbacks */ -#ifdef DEBUG - SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" no callback function, disabling notifier")); -#endif /* DEBUG */ - } else { -#ifdef DEBUG - SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" executing notification function")); -#endif /* DEBUG */ - if ((*cbFunc)(store, cbArg)) { - /* - * callback function returned success. - */ - return; - } else { -#ifdef DEBUG - SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" callback returned error, disabling notifier")); -#endif /* DEBUG */ - } - } - -#ifdef DEBUG - if (port != storePrivate->callbackPort) { - SCLog(_sc_verbose, LOG_DEBUG, CFSTR("informCallback, why is port != callbackPort?")); - } -#endif /* DEBUG */ - - /* invalidate the run loop source */ - if (storePrivate->callbackRLS != NULL) { - CFRunLoopSourceInvalidate(storePrivate->callbackRLS); - CFRelease(storePrivate->callbackRLS); - storePrivate->callbackRLS = NULL; - } - - /* invalidate port */ - if (storePrivate->callbackPort != NULL) { - __MACH_PORT_DEBUG(TRUE, "*** informCallback", CFMachPortGetPort(storePrivate->callbackPort)); - CFMachPortInvalidate(storePrivate->callbackPort); - CFRelease(storePrivate->callbackPort); - storePrivate->callbackPort = NULL; - } - - /* disable notifier */ - storePrivate->notifyStatus = NotifierNotRegistered; - storePrivate->callbackArgument = NULL; - storePrivate->callbackFunction = NULL; - - return; -} - - static CFStringRef notifyMPCopyDescription(const void *info) { @@ -124,140 +56,11 @@ notifyMPCopyDescription(const void *info) } -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) { - /* 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) */ - 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) { - /* - * 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("mach_port_insert_right(): %s"), mach_error_string(status)); - _SCErrorSet(status); - return FALSE; - } - - /* Request a notification when/if the server dies */ - 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) { - /* - * 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("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")); - } - - /* Requesting notification via mach port */ - status = notifyviaport(storePrivate->server, - port, - 0, - (int *)&sc_status); - - if (status != KERN_SUCCESS) { - if (status == MACH_SEND_INVALID_DEST) { - /* the server's gone and our session port's dead, remove the dead name right */ - (void) mach_port_deallocate(mach_task_self(), storePrivate->server); - } else { - /* we got an unexpected error, leave the [session] port alone */ - SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreNotifyCallback notifyviaport(): %s"), mach_error_string(status)); - } - storePrivate->server = MACH_PORT_NULL; - - if (status == MACH_SEND_INVALID_DEST) { - /* 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); - _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 */ - __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; + 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; @@ -268,27 +71,13 @@ rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) #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 */ - /* invalidate the run loop source(s) */ - if (storePrivate->callbackRLS != NULL) { - CFRunLoopSourceInvalidate(storePrivate->callbackRLS); - CFRelease(storePrivate->callbackRLS); - storePrivate->callbackRLS = NULL; - } - - /* invalidate port */ - 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; + /* re-establish notification and inform the client */ + (void)__SCDynamicStoreReconnectNotifications(store); } /* signal the real runloop source */ @@ -299,16 +88,6 @@ rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) } -static void -portInvalidate(CFMachPortRef port, void *info) { - mach_port_t mp = CFMachPortGetPort(port); - - __MACH_PORT_DEBUG(TRUE, "*** portInvalidate", mp); - /* remove our receive right */ - (void)mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1); -} - - static void rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) { @@ -321,13 +100,13 @@ rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) (rl != NULL) ? mode : CFSTR("libdispatch")); #endif /* DEBUG */ - if (storePrivate->rlsRefs++ == 0) { - CFMachPortContext context = { 0 - , (void *)store - , CFRetain - , CFRelease - , notifyMPCopyDescription - }; + if (storePrivate->rlList == NULL) { + CFMachPortContext context = { 0 + , (void *)store + , CFRetain + , CFRelease + , notifyMPCopyDescription + }; mach_port_t oldNotify; mach_port_t port; int sc_status; @@ -380,19 +159,20 @@ rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) 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 (status != KERN_SUCCESS) { - if (status == MACH_SEND_INVALID_DEST) { - /* the server's gone and our session port's dead, remove the dead name right */ - (void) mach_port_deallocate(mach_task_self(), storePrivate->server); - } else { - /* we got an unexpected error, leave the [session] port alone */ - SCLog(TRUE, LOG_ERR, CFSTR("rlsSchedule notifyviaport(): %s"), mach_error_string(status)); - } - storePrivate->server = MACH_PORT_NULL; - if (status == MACH_SEND_INVALID_DEST) { + if (__SCDynamicStoreCheckRetryAndHandleError(store, + status, + &sc_status, + "rlsSchedule notifyviaport()")) { + goto retry; + } + + if (status != KERN_SUCCESS) { + 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); } @@ -402,28 +182,32 @@ rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) 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\"")); - - /* the server's gone and our session port's dead, remove the dead name right */ - (void) mach_port_deallocate(mach_task_self(), storePrivate->server); - storePrivate->server = MACH_PORT_NULL; - - /* remove our receive right */ + 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; } - CFMachPortSetInvalidationCallBack(storePrivate->callbackPort, portInvalidate); - storePrivate->callbackRLS = 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->callbackRLS != NULL)) { - CFRunLoopAddSource(rl, storePrivate->callbackRLS, mode); - __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->callbackPort)); + 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; @@ -433,6 +217,7 @@ 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; @@ -442,49 +227,71 @@ rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode) (rl != NULL) ? mode : CFSTR("libdispatch")); #endif /* DEBUG */ - if ((rl != NULL) && (storePrivate->callbackRLS != NULL)) { - CFRunLoopRemoveSource(rl, storePrivate->callbackRLS, 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->callbackPort != NULL), + __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL), "*** rlsCancel", - CFMachPortGetPort(storePrivate->callbackPort)); + CFMachPortGetPort(storePrivate->rlsNotifyPort)); + + if (storePrivate->rlList != NULL) { + CFRelease(storePrivate->rlList); + storePrivate->rlList = NULL; + } - if (storePrivate->callbackRLS != NULL) { + if (storePrivate->rlsNotifyRLS != NULL) { /* invalidate & remove the run loop source */ - CFRunLoopSourceInvalidate(storePrivate->callbackRLS); - CFRelease(storePrivate->callbackRLS); - storePrivate->callbackRLS = NULL; + CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS); + CFRelease(storePrivate->rlsNotifyRLS); + storePrivate->rlsNotifyRLS = NULL; } - if (storePrivate->callbackPort != NULL) { - /* invalidate port */ - __MACH_PORT_DEBUG((storePrivate->callbackPort != NULL), - "*** rlsCancel (before invalidating CFMachPort)", - CFMachPortGetPort(storePrivate->callbackPort)); - 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 != 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) { - /* the server's gone and our session port's dead, remove the dead name right */ - (void) mach_port_deallocate(mach_task_self(), storePrivate->server); - } else { - /* we got an unexpected error, leave the [session] port alone */ - SCLog(TRUE, LOG_ERR, CFSTR("rlsCancel notifycancel(): %s"), mach_error_string(status)); - } - storePrivate->server = MACH_PORT_NULL; return; } } @@ -509,13 +316,18 @@ rlsPerform(void *info) #endif /* DEBUG */ changedKeys = SCDynamicStoreCopyNotifiedKeys(store); - if (changedKeys == NULL) { - /* if no changes or something happened to the server */ - return; - } - - if (CFArrayGetCount(changedKeys) == 0) { - goto done; + 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; @@ -527,8 +339,10 @@ rlsPerform(void *info) 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); } @@ -545,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; @@ -562,12 +383,21 @@ 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; } @@ -599,9 +429,6 @@ rlsCopyDescription(const void *info) } 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("}")); @@ -664,136 +491,162 @@ SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator, } -#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) { + 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 */ + // 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 */ + // sorry, you must have an open session to play _SCErrorSet(kSCStatusNoStoreServer); return FALSE; } - if (queue != NULL) { - dispatch_queue_attr_t attr; - mach_port_t mp; - long res; + if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) { + _SCErrorSet(kSCStatusInvalidArgument); + 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; + } - 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; } - /* - * mark our using of the SCDynamicStore notifications, create and schedule - * the notification port (storePrivate->callbackPort), and a bunch of other - * "setup" - */ - storePrivate->notifyStatus = Using_NotifierInformViaDispatch; - rlsSchedule((void*)store, NULL, NULL); - storePrivate->dispatchQueue = queue; - dispatch_retain(storePrivate->dispatchQueue); - - /* - * create a queue for the mig source, we'll use this queue's context - * to carry the store pointer for the callback code. - */ - attr = dispatch_queue_attr_create(); - res = dispatch_queue_attr_set_finalizer(attr, - ^(dispatch_queue_t dq) { - SCDynamicStoreRef store; - - store = (SCDynamicStoreRef)dispatch_get_context(dq); - CFRelease(store); - }); - if (res != 0) { - SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_queue_attr_set_finalizer() failed")); - dispatch_release(attr); - _SCErrorSet(kSCStatusFailed); - goto cleanup; - } - storePrivate->callbackQueue = dispatch_queue_create("com.apple.SCDynamicStore.notifications", attr); - dispatch_release(attr); - if (storePrivate->callbackQueue == NULL){ - SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_queue_create() failed")); - _SCErrorSet(kSCStatusFailed); - goto cleanup; - } - CFRetain(store); // Note: will be released when the dispatch queue is released - dispatch_set_context(storePrivate->callbackQueue, (void *)store); - - dispatch_suspend(storePrivate->callbackQueue); - mp = CFMachPortGetPort(storePrivate->callbackPort); - storePrivate->callbackSource = dispatch_source_mig_create(mp, sizeof(mach_msg_header_t), NULL, storePrivate->callbackQueue, SCDynamicStoreNotifyMIGCallback); - if (storePrivate->callbackSource == NULL) { - SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStore dispatch_source_mig_create() failed")); - _SCErrorSet(kSCStatusFailed); - goto cleanup; - } - dispatch_resume(storePrivate->callbackQueue); + msgid = notify_msg.msg.header.msgh_id; - ok = TRUE; - goto done; - } else { - if (storePrivate->dispatchQueue == NULL) { - _SCErrorSet(kSCStatusInvalidArgument); - return FALSE; - } + 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); + }); + }); - ok = TRUE; - } + dispatch_source_set_cancel_handler(source, ^{ + dispatch_release(source); + }); + + storePrivate->dispatchSource = source; + dispatch_resume(source); + + return TRUE; cleanup : - if (storePrivate->callbackSource != NULL) { - dispatch_cancel(storePrivate->callbackSource); - dispatch_release(storePrivate->callbackSource); - storePrivate->callbackSource = NULL; - } - if (storePrivate->callbackQueue != NULL) { - dispatch_release(storePrivate->callbackQueue); - storePrivate->callbackQueue = NULL; + CFRetain(store); + + if (storePrivate->dispatchSource != NULL) { + dispatch_source_cancel(storePrivate->dispatchSource); + storePrivate->dispatchSource = NULL; } - dispatch_release(storePrivate->dispatchQueue); + 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; - done : + CFRelease(store); return ok; } -#endif // !TARGET_OS_IPHONE