]> git.saurik.com Git - apple/configd.git/blobdiff - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
configd-596.13.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCDNotifierInformViaCallback.c
index 481d5b1e62d3dea93868fe4346aa5dee86e9e97c..02e7d48911fb7f11382af001d486c667a7197e71 100644 (file)
 /*
- * 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@
  */
 
+/*
+ * Modification History
+ *
+ * June 1, 2001                        Allan Nathanson <ajn@apple.com>
+ * - public API conversion
+ *
+ * March 31, 2000              Allan Nathanson <ajn@apple.com>
+ * - initial revision
+ */
+
+#include <Availability.h>
+#include <TargetConditionals.h>
+#include <sys/cdefs.h>
+#include <dispatch/dispatch.h>
 #include <mach/mach.h>
 #include <mach/mach_error.h>
 
-#include <SystemConfiguration/SCD.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <SystemConfiguration/SCPrivate.h>
+#include "SCDynamicStoreInternal.h"
 #include "config.h"            /* MiG generated file */
-#include "SCDPrivate.h"
+
+
+static CFStringRef
+notifyMPCopyDescription(const void *info)
+{
+       SCDynamicStoreRef       store   = (SCDynamicStoreRef)info;
+
+       return CFStringCreateWithFormat(NULL,
+                                       NULL,
+                                       CFSTR("<SCDynamicStore notification MP> {store = %p}"),
+                                       store);
+}
 
 
 static void
-informCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
+rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
 {
-       SCDSessionRef           session        = (SCDSessionRef)info;
-       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
-       mach_msg_empty_rcv_t    *buf           = msg;
-       mach_msg_id_t           msgid          = buf->header.msgh_id;
-       SCDCallbackRoutine_t    cbFunc         = sessionPrivate->callbackFunction;
-       void                    *cbArg         = sessionPrivate->callbackArgument;
+       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 */
-               SCDLog(LOG_DEBUG, CFSTR("  notifier port closed, disabling notifier"));
-       } else if (cbFunc == NULL) {
-               /* there is no (longer) a callback function, disable additional callbacks */
-               SCDLog(LOG_DEBUG, CFSTR("  no callback function, disabling notifier"));
-       } else {
-               SCDLog(LOG_DEBUG, CFSTR("  executing notifiction function"));
-               if ((*cbFunc)(session, cbArg)) {
+#ifdef DEBUG
+               SCLog(_sc_verbose, LOG_INFO, CFSTR("  rlsCallback(), notifier port closed"));
+#endif /* DEBUG */
+
+#ifdef DEBUG
+               if (port != storePrivate->rlsNotifyPort) {
+                       SCLog(_sc_verbose, LOG_DEBUG, CFSTR("rlsCallback(), why is port != rlsNotifyPort?"));
+               }
+#endif /* DEBUG */
+
+               /* re-establish notification and inform the client */
+               (void)__SCDynamicStoreReconnectNotifications(store);
+       }
+
+       /* signal the real runloop source */
+       if (storePrivate->rls != NULL) {
+               CFRunLoopSourceSignal(storePrivate->rls);
+       }
+       return;
+}
+
+
+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 /* DEBUG */
+
+       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(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) {
                        /*
-                        * callback function returned 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("rlsSchedule mach_port_insert_right(): %s"), mach_error_string(status));
                        return;
-               } else {
-                       SCDLog(LOG_DEBUG, CFSTR("  callback returned error, disabling notifier"));
                }
+
+               /* 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("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"));
+               }
+
+           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) || (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;
+               }
+
+               __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);
        }
 
+       return;
+}
+
+
+static void
+rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
+{
+       CFIndex                         n               = 0;
+       SCDynamicStoreRef               store           = (SCDynamicStoreRef)info;
+       SCDynamicStorePrivateRef        storePrivate    = (SCDynamicStorePrivateRef)store;
+
 #ifdef DEBUG
-       if (port != sessionPrivate->callbackPort) {
-               SCDLog(LOG_DEBUG, CFSTR("informCallback, why is port != callbackPort?"));
+       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);
+                       }
+               }
        }
+
+       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));
 
-       /* we have encountered some type of error, disable additional callbacks */
+               if (storePrivate->rlList != NULL) {
+                       CFRelease(storePrivate->rlList);
+                       storePrivate->rlList = NULL;
+               }
+
+               if (storePrivate->rlsNotifyRLS != NULL) {
+                       /* invalidate & remove the run loop source */
+                       CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS);
+                       CFRelease(storePrivate->rlsNotifyRLS);
+                       storePrivate->rlsNotifyRLS = NULL;
+               }
 
-       /* XXX invalidating the port is not sufficient, remove the run loop source */
-       CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
-                             sessionPrivate->callbackRunLoopSource,
-                             kCFRunLoopDefaultMode);
-       CFRelease(sessionPrivate->callbackRunLoopSource);
+               if (storePrivate->rlsNotifyPort != NULL) {
+                       mach_port_t     mp;
 
-       /* invalidate port */
-       CFMachPortInvalidate(port);
-       CFRelease(port);
+                       mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
+                       __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
+                                         "*** rlsCancel (before invalidating/releasing CFMachPort)",
+                                         mp);
 
-       sessionPrivate->notifyStatus            = NotifierNotRegistered;
-       sessionPrivate->callbackFunction        = NULL;
-       sessionPrivate->callbackArgument        = NULL;
-       sessionPrivate->callbackPort            = NULL;
-       sessionPrivate->callbackRunLoopSource   = NULL; /* XXX */
+                       /* 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
-cleanupMachPort(void *ptr)
+rlsPerform(void *info)
 {
-       mach_port_t     *port = (mach_port_t *)ptr;
+       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 /* DEBUG */
 
-       SCDLog(LOG_DEBUG, CFSTR("  cleaning up notification port %d"), *port);
-       if (*port != MACH_PORT_NULL) {
-               (void) mach_port_destroy(mach_task_self(), *port);
-               free(port);
+       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 (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;
+       }
+       if (rlsFunction != NULL) {
+               (*rlsFunction)(store, changedKeys, context_info);
+       }
+       if (context_release != NULL) {
+               context_release(context_info);
+       }
+
+    done :
+
+       CFRelease(changedKeys);
        return;
 }
 
 
-static void *
-watcherThread(void *arg)
+static CFTypeRef
+rlsRetain(CFTypeRef cf)
 {
-       SCDSessionRef           session        = (SCDSessionRef)arg;
-       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
-       SCDCallbackRoutine_t    cbFunc         = sessionPrivate->callbackFunction;
-       void                    *cbArg         = sessionPrivate->callbackArgument;
-       mach_port_t             *port          = malloc(sizeof(mach_port_t));
+       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;
+       }
 
-       *port = CFMachPortGetPort(sessionPrivate->callbackPort);
-       pthread_cleanup_push(cleanupMachPort, (void *)port);
+       return cf;
+}
 
-       while (TRUE) {
-               mach_msg_id_t   msgid;
 
-               SCDLog(LOG_DEBUG, CFSTR("Callback thread waiting, port=%d, tid=0x%08x"),
-                      *port, pthread_self());
+static void
+rlsRelease(CFTypeRef cf)
+{
+       SCDynamicStoreRef               store           = (SCDynamicStoreRef)cf;
+       SCDynamicStorePrivateRef        storePrivate    = (SCDynamicStorePrivateRef)store;
 
-               msgid = _waitForMachMessage(*port);
+       switch (storePrivate->notifyStatus) {
+               case NotifierNotRegistered :
+                       break;
+               case Using_NotifierInformViaRunLoop :
+                       /* mark RLS inactive */
+                       storePrivate->notifyStatus = NotifierNotRegistered;
+                       storePrivate->rls = NULL;
 
-               if (msgid == MACH_NOTIFY_NO_SENDERS) {
-                       /* the server closed the notifier port, disable additional callbacks */
-                       SCDLog(LOG_DEBUG, CFSTR("  notifier port closed, disabling notifier"));
+                       /* release our reference to the store */
+                       CFRelease(store);
                        break;
-               }
+               default :
+                       SCLog(TRUE, LOG_ERR, CFSTR("rlsRelease() error: notifyStatus=%d"), storePrivate->notifyStatus);
+                       break;
+       }
 
-               if (msgid == -1) {
-                       mach_port_type_t        pt;
+       return;
+}
 
-                       /* an error was detected, disable additional callbacks */
-                       SCDLog(LOG_DEBUG, CFSTR("  server failure, disabling notifier"));
 
-                       /* check if the server connection is not valid, close if necessary */
-                       if ((mach_port_type(mach_task_self(), sessionPrivate->server, &pt) == KERN_SUCCESS) &&
-                           (pt & MACH_PORT_TYPE_DEAD_NAME)) {
-                               SCDLog(LOG_DEBUG, CFSTR("  server process died, destroying (dead) port"));
-                               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
-                               sessionPrivate->server = MACH_PORT_NULL;
-                       }
-                       break;
-               }
+static CFStringRef
+rlsCopyDescription(const void *info)
+{
+       CFMutableStringRef              result;
+       SCDynamicStoreRef               store           = (SCDynamicStoreRef)info;
+       SCDynamicStorePrivateRef        storePrivate    = (SCDynamicStorePrivateRef)store;
 
-               if (cbFunc == NULL) {
-                       /* there is no (longer) a callback function, disable additional callbacks */
-                       SCDLog(LOG_DEBUG, CFSTR("  no callback function, disabling notifier"));
-                       break;
-               }
+       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;
 
-               SCDLog(LOG_DEBUG, CFSTR("  executing notifiction function"));
+               CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->rlsFunction);
 
-               if (!(*cbFunc)(session, cbArg)) {
-                       /*
-                        * callback function returned an error, exit the thread
-                        */
-                       break;
+               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);
        }
+       CFStringAppendFormat(result, NULL, CFSTR("}"));
 
-       /*
-        * pop the cleanup routine for the "port" mach port. We end up calling
-        * mach_port_destroy() in the process.
-        */
-       pthread_cleanup_pop(1);
-
-       pthread_exit (NULL);
-       return NULL;
+       return result;
 }
 
 
-SCDStatus
-SCDNotifierInformViaCallback(SCDSessionRef session, SCDCallbackRoutine_t func, void *arg)
+CFRunLoopSourceRef
+SCDynamicStoreCreateRunLoopSource(CFAllocatorRef       allocator,
+                                 SCDynamicStoreRef     store,
+                                 CFIndex               order)
 {
-       SCDSessionPrivateRef    sessionPrivate = (SCDSessionPrivateRef)session;
-       kern_return_t           status;
-       mach_port_t             port;
-       mach_port_t             oldNotify;
-       SCDStatus               scd_status;
-       CFMachPortContext       context = { 0, (void *)session, NULL, NULL, NULL };
+       SCDynamicStorePrivateRef        storePrivate    = (SCDynamicStorePrivateRef)store;
 
-       SCDLog(LOG_DEBUG, CFSTR("SCDNotifierInformViaCallback:"));
+       if (store == NULL) {
+               /* sorry, you must provide a session */
+               _SCErrorSet(kSCStatusNoStoreSession);
+               return NULL;
+       }
 
-       if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
-               return SCD_NOSESSION;   /* you must have an open session to play */
+       if (storePrivate->server == MACH_PORT_NULL) {
+               /* sorry, you must have an open session to play */
+               _SCErrorSet(kSCStatusNoStoreServer);
+               return NULL;
        }
 
-       if (sessionPrivate->notifyStatus != NotifierNotRegistered) {
-               /* sorry, you can only have one notification registered at once */
-               return SCD_NOTIFIERACTIVE;
+       switch (storePrivate->notifyStatus) {
+               case NotifierNotRegistered :
+               case Using_NotifierInformViaRunLoop :
+                       /* OK to enable runloop notification */
+                       break;
+               default :
+                       /* sorry, you can only have one notification registered at once */
+                       _SCErrorSet(kSCStatusNotifierActive);
+                       return NULL;
        }
 
-       if (func == NULL) {
-               /* sorry, you must specify a callback function */
-               return SCD_INVALIDARGUMENT;
+       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);
+               }
        }
 
-       /* Allocating port (for server response) */
-       sessionPrivate->callbackPort = CFMachPortCreate(NULL,
-                                                       informCallback,
-                                                       &context,
-                                                       NULL);
+       return storePrivate->rls;
+}
 
-       /* Request a notification when/if the server dies */
-       port = CFMachPortGetPort(sessionPrivate->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) {
-               SCDLog(LOG_DEBUG, CFSTR("mach_port_request_notification(): %s"), mach_error_string(status));
-               CFMachPortInvalidate(sessionPrivate->callbackPort);
-               CFRelease(sessionPrivate->callbackPort);
-               return SCD_FAILED;
+
+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;
        }
 
-#ifdef DEBUG
-       if (oldNotify != MACH_PORT_NULL) {
-               SCDLog(LOG_DEBUG, CFSTR("SCDNotifierInformViaCallback(): why is oldNotify != MACH_PORT_NULL?"));
+       if (queue == NULL) {
+               if (storePrivate->dispatchQueue == NULL) {
+                       _SCErrorSet(kSCStatusInvalidArgument);
+                       return FALSE;
+               }
+
+               ok = TRUE;
+               goto cleanup;
        }
-#endif /* DEBUG */
 
-       /* Requesting notification via mach port */
-       status = notifyviaport(sessionPrivate->server,
-                              port,
-                              0,
-                              (int *)&scd_status);
-
-       if (status != KERN_SUCCESS) {
-               if (status != MACH_SEND_INVALID_DEST)
-                       SCDLog(LOG_DEBUG, CFSTR("notifyviaport(): %s"), mach_error_string(status));
-               CFMachPortInvalidate(sessionPrivate->callbackPort);
-               CFRelease(sessionPrivate->callbackPort);
-               (void) mach_port_destroy(mach_task_self(), sessionPrivate->server);
-               sessionPrivate->server = MACH_PORT_NULL;
-               return SCD_NOSERVER;
+       if (storePrivate->server == MACH_PORT_NULL) {
+               // sorry, you must have an open session to play
+               _SCErrorSet(kSCStatusNoStoreServer);
+               return FALSE;
        }
 
-       if (scd_status != SCD_OK) {
-               return scd_status;
+       if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) {
+               _SCErrorSet(kSCStatusInvalidArgument);
+               return FALSE;
        }
 
-       /* set notifier active */
-       sessionPrivate->notifyStatus     = Using_NotifierInformViaCallback;
-       sessionPrivate->callbackFunction = func;
-       sessionPrivate->callbackArgument = arg;
-
-       if (SCDOptionGet(session, kSCDOptionUseCFRunLoop)) {
-               /* Creating/adding a run loop source for the port */
-               sessionPrivate->callbackRunLoopSource =
-                       CFMachPortCreateRunLoopSource(NULL, sessionPrivate->callbackPort, 0);
-               CFRunLoopAddSource(CFRunLoopGetCurrent(),
-                                  sessionPrivate->callbackRunLoopSource,
-                                  kCFRunLoopDefaultMode);
-       } else {
-               pthread_attr_t          tattr;
-
-               SCDLog(LOG_DEBUG, CFSTR("Starting background thread to watch for notifications..."));
-               pthread_attr_init(&tattr);
-               pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
-               pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
-               pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
-               pthread_create(&sessionPrivate->callbackHelper,
-                              &tattr,
-                              watcherThread,
-                              (void *)session);
-               pthread_attr_destroy(&tattr);
-               SCDLog(LOG_DEBUG, CFSTR("  thread id=0x%08x"), sessionPrivate->callbackHelper);
+       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(&notify_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
+               });
        }
 
-       return SCD_OK;
+       storePrivate->notifyStatus = NotifierNotRegistered;
+
+       CFRelease(store);
+
+       return ok;
 }