]> git.saurik.com Git - apple/configd.git/blobdiff - configd.tproj/_SCD.c
configd-888.51.2.tar.gz
[apple/configd.git] / configd.tproj / _SCD.c
index b087f8db31f9fb9e59f6739a0d13a2ea97e13677..a9e1c507366aa09a63e1b587973edc6ca1479c20 100644 (file)
@@ -1,21 +1,22 @@
 /*
- * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000, 2001, 2003-2005, 2009, 2011, 2012, 2015, 2016 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 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.
  *
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * 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@
  */
  */
 
 
-#include "configd.h"
-
-
-CFMutableDictionaryRef sessionData             = NULL;
-
-CFMutableDictionaryRef storeData               = NULL;
-CFMutableDictionaryRef storeData_s             = NULL;
-
-CFMutableSetRef                changedKeys             = NULL;
-CFMutableSetRef                changedKeys_s           = NULL;
+#include <unistd.h>
 
-CFMutableSetRef                deferredRemovals        = NULL;
-CFMutableSetRef                deferredRemovals_s      = NULL;
-
-CFMutableSetRef                removedSessionKeys      = NULL;
-CFMutableSetRef                removedSessionKeys_s    = NULL;
-
-CFMutableSetRef                needsNotification       = NULL;
+#include "configd.h"
+#include "configd_server.h"
+#include "session.h"
 
-int                    storeLocked             = 0;            /* > 0 if dynamic store locked */
 
+__private_extern__ CFMutableDictionaryRef      sessionData             = NULL;
 
-void
-_swapLockedStoreData()
-{
-       void    *temp;
+__private_extern__ CFMutableDictionaryRef      storeData               = NULL;
 
-       temp                 = storeData;
-       storeData            = storeData_s;
-       storeData_s          = temp;
+__private_extern__ CFMutableDictionaryRef      patternData             = NULL;
 
-       temp                 = changedKeys;
-       changedKeys          = changedKeys_s;
-       changedKeys_s        = temp;
+__private_extern__ CFMutableSetRef             changedKeys             = NULL;
 
-       temp                 = deferredRemovals;
-       deferredRemovals     = deferredRemovals_s;
-       deferredRemovals_s   = temp;
+__private_extern__ CFMutableSetRef             deferredRemovals        = NULL;
 
-       temp                 = removedSessionKeys;
-       removedSessionKeys   = removedSessionKeys_s;
-       removedSessionKeys_s = temp;
+__private_extern__ CFMutableSetRef             removedSessionKeys      = NULL;
 
-       return;
-}
+__private_extern__ CFMutableSetRef             needsNotification       = NULL;
 
 
+__private_extern__
 void
 _addWatcher(CFNumberRef sessionNum, CFStringRef watchedKey)
 {
@@ -95,7 +72,7 @@ _addWatcher(CFNumberRef sessionNum, CFStringRef watchedKey)
         * Get the dictionary associated with this key out of the store
         */
        dict = CFDictionaryGetValue(storeData, watchedKey);
-       if (dict) {
+       if (dict != NULL) {
                newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
        } else {
                newDict = CFDictionaryCreateMutable(NULL,
@@ -123,7 +100,7 @@ _addWatcher(CFNumberRef sessionNum, CFStringRef watchedKey)
        i = CFArrayGetFirstIndexOfValue(newWatchers,
                                        CFRangeMake(0, CFArrayGetCount(newWatchers)),
                                        sessionNum);
-       if (i == -1) {
+       if (i == kCFNotFound) {
                /* if this is the first instance of this session watching this key */
                CFArrayAppendValue(newWatchers, sessionNum);
                refCnt = 1;
@@ -154,150 +131,15 @@ _addWatcher(CFNumberRef sessionNum, CFStringRef watchedKey)
        CFDictionarySetValue(storeData, watchedKey, newDict);
        CFRelease(newDict);
 
-       SCLog(_configd_verbose, LOG_DEBUG, CFSTR("  _addWatcher: %@, %@"), sessionNum, watchedKey);
-
-       return;
-}
-
-
-/*
- * _addRegexWatcherByKey()
- *
- * This is a CFDictionaryApplierFunction which will iterate over each key
- * defined in the "storeData" dictionary. The arguments are the dictionary
- * key, it's associated store dictionary, and a context structure which
- * includes the following:
- *
- *   1. the session which has just added a regex notification request
- *   2. the compiled regular expression associated with the above key.
- *
- * If a (real) dictionary key is found which matches the provided regular
- * expression then we mark that key as being watched by the session.
- */
-void
-_addRegexWatcherByKey(const void *key, void *val, void *context)
-{
-       CFStringRef     storeStr  = key;
-       CFDictionaryRef info      = val;
-       mach_port_t     sessionID = ((addContextRef)context)->store->server;
-       regex_t         *preg     = ((addContextRef)context)->preg;
-       int             storeKeyLen;
-       char            *storeKey;
-       CFNumberRef     sessionNum;
-       int             reError;
-       char            reErrBuf[256];
-       int             reErrStrLen;
-
-       if (!CFDictionaryContainsKey(info, kSCDData)) {
-               /* if no data (yet) */
-               return;
-       }
-
-       /* convert store key to C string */
-       storeKeyLen = CFStringGetLength(storeStr) + 1;
-       storeKey    = CFAllocatorAllocate(NULL, storeKeyLen, 0);
-       if (!CFStringGetCString(storeStr, storeKey, storeKeyLen, kCFStringEncodingMacRoman)) {
-               SCLog(_configd_verbose, LOG_DEBUG, CFSTR("CFStringGetCString: could not convert store key to C string"));
-               CFAllocatorDeallocate(NULL, storeKey);
-               return;
-       }
-
-       /* compare store key to new notification keys regular expression pattern */
-       reError = regexec(preg, storeKey, 0, NULL, 0);
-       switch (reError) {
-               case 0 :
-                       /* we've got a match */
-                       sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionID);
-                       _addWatcher(sessionNum, storeStr);
-                       CFRelease(sessionNum);
-                       break;
-               case REG_NOMATCH :
-                       /* no match */
-                       break;
-               default :
-                       reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
-                       SCLog(_configd_verbose, LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
-                       break;
-       }
-       CFAllocatorDeallocate(NULL, storeKey);
-}
-
-
-/*
- * _addRegexWatchersBySession()
- *
- * This is a CFDictionaryApplierFunction which will iterate over each session
- * defined in the "sessionData" dictionary. The arguments are the session
- * key, it's associated session dictionary, and the store key being added.
- *
- * If an active session includes any regular expression keys which match the
- * key being added to the "storeData" dictionary then we mark this key as being
- * watched by the session.
- */
-void
-_addRegexWatchersBySession(const void *key, void *val, void *context)
-{
-       CFStringRef     sessionKey = key;
-       CFDictionaryRef info       = val;
-       CFStringRef     addedKey   = context;
-       CFIndex         newKeyLen;
-       char            *newKeyStr;
-       CFArrayRef      rKeys;
-       CFArrayRef      rData;
-       CFIndex         i;
-
-       rKeys = CFDictionaryGetValue(info, kSCDRegexKeys);
-       if (rKeys == NULL) {
-               /* if no regex keys for this session */
-               return;
-       }
-       rData = CFDictionaryGetValue(info, kSCDRegexData);
-
-       /* convert new key to C string */
-       newKeyLen = CFStringGetLength(addedKey) + 1;
-       newKeyStr = CFAllocatorAllocate(NULL, newKeyLen, 0);
-       if (!CFStringGetCString(addedKey, newKeyStr, newKeyLen, kCFStringEncodingMacRoman)) {
-               SCLog(_configd_verbose, LOG_DEBUG, CFSTR("CFStringGetCString: could not convert new key to C string"));
-               CFAllocatorDeallocate(NULL, newKeyStr);
-               return;
-       }
-
-       /* iterate over the regex keys looking for an pattern which matches the new key */
-       for (i=0; i<CFArrayGetCount(rKeys); i++) {
-               CFDataRef       regexData = CFArrayGetValueAtIndex(rData, i);
-               regex_t         *preg     = (regex_t *)CFDataGetBytePtr(regexData);
-               int             reError;
-               char            reErrBuf[256];
-               int             reErrStrLen;
-               SInt32          sessionInt;
-               CFNumberRef     sessionNum;
-
-               /* check if this key matches the regular expression */
-               reError = regexec(preg, newKeyStr, 0, NULL, 0);
-               switch (reError) {
-                       case 0 :
-                               /* we've got a match, add a reference */
-                               sessionInt = CFStringGetIntValue(sessionKey);
-                               sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionInt);
-                               _addWatcher(sessionNum, addedKey);
-                               CFRelease(sessionNum);
-                               break;
-                       case REG_NOMATCH :
-                               /* no match */
-                               break;
-                       default :
-                               reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
-                               SCLog(_configd_verbose, LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
-                               break;
-               }
-
-       }
-       CFAllocatorDeallocate(NULL, newKeyStr);
+#ifdef DEBUG
+       SC_log(LOG_DEBUG, "  _addWatcher: %@, %@", sessionNum, watchedKey);
+#endif /* DEBUG */
 
        return;
 }
 
 
+__private_extern__
 void
 _removeWatcher(CFNumberRef sessionNum, CFStringRef watchedKey)
 {
@@ -315,9 +157,11 @@ _removeWatcher(CFNumberRef sessionNum, CFStringRef watchedKey)
         * Get the dictionary associated with this key out of the store
         */
        dict = CFDictionaryGetValue(storeData, watchedKey);
-       if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDWatchers) == FALSE)) {
+       if ((dict == NULL) || !CFDictionaryContainsKey(dict, kSCDWatchers)) {
                /* key doesn't exist (isn't this really fatal?) */
-               SCLog(_configd_verbose, LOG_DEBUG, CFSTR("  _removeWatcher: %@, %@, key not watched"), sessionNum, watchedKey);
+#ifdef DEBUG
+               SC_log(LOG_DEBUG, "  _removeWatcher: %@, %@, key not watched", sessionNum, watchedKey);
+#endif /* DEBUG */
                return;
        }
        newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
@@ -336,8 +180,10 @@ _removeWatcher(CFNumberRef sessionNum, CFStringRef watchedKey)
        i = CFArrayGetFirstIndexOfValue(newWatchers,
                                        CFRangeMake(0, CFArrayGetCount(newWatchers)),
                                        sessionNum);
-       if (i == -1) {
-               SCLog(_configd_verbose, LOG_DEBUG, CFSTR("  _removeWatcher: %@, %@, session not watching"), sessionNum, watchedKey);
+       if (i == kCFNotFound) {
+#ifdef DEBUG
+               SC_log(LOG_DEBUG, "  _removeWatcher: %@, %@, session not watching", sessionNum, watchedKey);
+#endif /* DEBUG */
                CFRelease(newDict);
                CFRelease(newWatchers);
                CFRelease(newWatcherRefs);
@@ -378,157 +224,161 @@ _removeWatcher(CFNumberRef sessionNum, CFStringRef watchedKey)
        }
        CFRelease(newDict);
 
-       SCLog(_configd_verbose, LOG_DEBUG, CFSTR("  _removeWatcher: %@, %@"), sessionNum, watchedKey);
+#ifdef DEBUG
+       SC_log(LOG_DEBUG, "  _removeWatcher: %@, %@", sessionNum, watchedKey);
+#endif /* DEBUG */
 
        return;
 }
 
 
-/*
- * _removeRegexWatcherByKey()
- *
- * This is a CFDictionaryApplierFunction which will iterate over each key
- * defined in the "storeData" dictionary. The arguments are the dictionary
- * key, it's associated store dictionary, and a context structure which
- * includes the following:
- *
- *   1. the session which has just removed a regex notification request
- *   2. the compiled regular expression associated with the above key.
- *
- * If a key is found and it matches the provided regular expression then
- * it will its "being watched" status will be cleared.
- */
-void
-_removeRegexWatcherByKey(const void *key, void *val, void *context)
-{
-       CFStringRef     storeStr  = key;
-       CFDictionaryRef info      = val;
-       mach_port_t     sessionID = ((removeContextRef)context)->store->server;
-       regex_t         *preg     = ((removeContextRef)context)->preg;
-       CFNumberRef     sessionNum;
-       CFArrayRef      watchers;
-       int             storeKeyLen;
-       char            *storeKey;
-       int             reError;
-       char            reErrBuf[256];
-       int             reErrStrLen;
-
-       if (CFDictionaryContainsKey(info, kSCDWatchers) == FALSE) {
-               /* if no watchers */
-               return;
-       }
-
-       sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionID);
+#define N_QUICK        64
 
-       watchers = CFDictionaryGetValue(info, kSCDWatchers);
-       if (CFArrayContainsValue(watchers,
-                                CFRangeMake(0, CFArrayGetCount(watchers)),
-                                sessionNum) == FALSE) {
-               /* this session is not watching this key */
-               CFRelease(sessionNum);
-               return;
-       }
 
-       /* convert key to C string */
-       storeKeyLen = CFStringGetLength(storeStr) + 1;
-       storeKey    = CFAllocatorAllocate(NULL, storeKeyLen, 0);
-       if (!CFStringGetCString(storeStr, storeKey, storeKeyLen, kCFStringEncodingMacRoman)) {
-               SCLog(_configd_verbose, LOG_DEBUG, CFSTR("CFStringGetCString: could not convert key to C string"));
-               CFAllocatorDeallocate(NULL, storeKey);
-               CFRelease(sessionNum);
-               return;
-       }
-
-       /* check if this key matches the regular expression */
-       reError = regexec(preg, storeKey, 0, NULL, 0);
-       switch (reError) {
-               case 0 :
-                       /* we've got a match */
-                       _removeWatcher(sessionNum, storeStr);
-                       break;
-               case REG_NOMATCH :
-                       /* no match */
-                       break;
-               default :
-                       reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
-                       SCLog(_configd_verbose, LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
-                       break;
-       }
-       CFAllocatorDeallocate(NULL, storeKey);
-       CFRelease(sessionNum);
-}
-
-
-/*
- * _removeRegexWatchersBySession()
- *
- * This is a CFDictionaryApplierFunction which will iterate over each session
- * defined in the "sessionData" dictionary. The arguments are the session
- * key, it's associated session dictionary, and the store key being removed.
- *
- * If an active session includes any regular expression keys which match the
- * key being removed from the "storeData" dictionary then we clear this keys
- * reference of being watched.
- */
+__private_extern__
 void
-_removeRegexWatchersBySession(const void *key, void *val, void *context)
+pushNotifications()
 {
-       CFStringRef     sessionKey = key;
-       CFDictionaryRef info       = val;
-       CFStringRef     removedKey = context;
-       CFIndex         oldKeyLen;
-       char            *oldKeyStr;
-       CFArrayRef      rKeys;
-       CFArrayRef      rData;
-       CFIndex         i;
-
-       rKeys = CFDictionaryGetValue(info, kSCDRegexKeys);
-       if (rKeys == NULL) {
-               /* if no regex keys for this session */
-               return;
-       }
-       rData = CFDictionaryGetValue(info, kSCDRegexData);
-
-       /* convert new key to C string */
-       oldKeyLen = CFStringGetLength(removedKey) + 1;
-       oldKeyStr = CFAllocatorAllocate(NULL, oldKeyLen, 0);
-       if (!CFStringGetCString(removedKey, oldKeyStr, oldKeyLen, kCFStringEncodingMacRoman)) {
-               SCLog(_configd_verbose, LOG_DEBUG, CFSTR("CFStringGetCString: could not convert old key to C string"));
-               CFAllocatorDeallocate(NULL, oldKeyStr);
-               return;
-       }
+       CFIndex                         notifyCnt;
+       int                             server;
+       const void *                    sessionsToNotify_q[N_QUICK];
+       const void **                   sessionsToNotify        = sessionsToNotify_q;
+       SCDynamicStorePrivateRef        storePrivate;
+       serverSessionRef                theSession;
+
+       if (needsNotification == NULL)
+               return;         /* if no sessions need to be kicked */
+
+       notifyCnt = CFSetGetCount(needsNotification);
+       if (notifyCnt > (CFIndex)(sizeof(sessionsToNotify_q) / sizeof(CFNumberRef)))
+               sessionsToNotify = CFAllocatorAllocate(NULL, notifyCnt * sizeof(CFNumberRef), 0);
+       CFSetGetValues(needsNotification, sessionsToNotify);
+       while (--notifyCnt >= 0) {
+               (void) CFNumberGetValue(sessionsToNotify[notifyCnt],
+                                       kCFNumberIntType,
+                                       &server);
+               theSession = getSession(server);
+               assert(theSession != NULL);
+
+               storePrivate = (SCDynamicStorePrivateRef)theSession->store;
+
+               /*
+                * deliver notifications to client sessions
+                */
+               if ((storePrivate->notifyStatus == Using_NotifierInformViaMachPort) &&
+                   (storePrivate->notifyPort != MACH_PORT_NULL)) {
+                       /*
+                        * Associate notification activity with the client
+                        */
+                       os_activity_scope(theSession->activity);
+
+                       /*
+                        * Post notification as mach message
+                        */
+                       SC_trace("-->port : %5d : port = %d",
+                                storePrivate->server,
+                                storePrivate->notifyPort);
+
+                       /* use a random (and non-zero) identifier */
+                       while (storePrivate->notifyPortIdentifier == 0) {
+                               storePrivate->notifyPortIdentifier = (mach_msg_id_t)random();
+                       }
+
+                       _SC_sendMachMessage(storePrivate->notifyPort, storePrivate->notifyPortIdentifier);
+               }
 
-       /* iterate over the regex keys looking for an pattern which matches the old key */
-       for (i=0; i<CFArrayGetCount(rKeys); i++) {
-               CFDataRef       regexData = CFArrayGetValueAtIndex(rData, i);
-               regex_t         *preg     = (regex_t *)CFDataGetBytePtr(regexData);
-               int             reError;
-               char            reErrBuf[256];
-               int             reErrStrLen;
-               SInt32          sessionInt;
-               CFNumberRef     sessionNum;
-
-               /* check if this key matches the regular expression */
-               reError = regexec(preg, oldKeyStr, 0, NULL, 0);
-               switch (reError) {
-                       case 0 :
-                               /* we've got a match, remove a reference */
-                               sessionInt = CFStringGetIntValue(sessionKey);
-                               sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionInt);
-                               _removeWatcher(sessionNum, removedKey);
-                               CFRelease(sessionNum);
-                               break;
-                       case REG_NOMATCH :
-                               /* no match */
-                               break;
-                       default :
-                               reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
-                               SCLog(_configd_verbose, LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf);
-                               break;
+               if ((storePrivate->notifyStatus == Using_NotifierInformViaFD) &&
+                   (storePrivate->notifyFile >= 0)) {
+                       ssize_t         written;
+
+                       /*
+                        * Associate notification activity with the client
+                        */
+                       os_activity_scope(theSession->activity);
+
+                       SC_trace("-->fd   : %5d : fd = %d, msgid = %d",
+                                storePrivate->server,
+                                storePrivate->notifyFile,
+                                storePrivate->notifyFileIdentifier);
+
+                       written = write(storePrivate->notifyFile,
+                                       &storePrivate->notifyFileIdentifier,
+                                       sizeof(storePrivate->notifyFileIdentifier));
+                       if (written == -1) {
+                               if (errno == EWOULDBLOCK) {
+#ifdef DEBUG
+                                       SC_log(LOG_DEBUG, "sorry, only one outstanding notification per session");
+#endif /* DEBUG */
+                               } else {
+#ifdef DEBUG
+                                       SC_log(LOG_DEBUG, "could not send notification, write() failed: %s",
+                                             strerror(errno));
+#endif /* DEBUG */
+                                       storePrivate->notifyFile = -1;
+                               }
+                       } else if (written != sizeof(storePrivate->notifyFileIdentifier)) {
+#ifdef DEBUG
+                               SC_log(LOG_DEBUG, "could not send notification, incomplete write()");
+#endif /* DEBUG */
+                               storePrivate->notifyFile = -1;
+                       }
                }
 
+               if ((storePrivate->notifyStatus == Using_NotifierInformViaSignal) &&
+                   (storePrivate->notifySignal > 0)) {
+                       kern_return_t   status;
+                       pid_t           pid;
+
+                       /*
+                        * Associate notification activity with the client
+                        */
+                       os_activity_scope(theSession->activity);
+
+                       /*
+                        * Post notification as signal
+                        */
+                       status = pid_for_task(storePrivate->notifySignalTask, &pid);
+                       if (status == KERN_SUCCESS) {
+                               SC_trace("-->sig  : %5d : pid = %d, signal = sig%s (%d)",
+                                        storePrivate->server,
+                                        pid,
+                                        sys_signame[storePrivate->notifySignal],
+                                        storePrivate->notifySignal);
+
+                               if (kill(pid, storePrivate->notifySignal) != 0) {
+                                       if (errno != ESRCH) {
+                                               SC_log(LOG_NOTICE, "could not send sig%s to PID %d: %s",
+                                                      sys_signame[storePrivate->notifySignal],
+                                                      pid,
+                                                      strerror(errno));
+                                       }
+                               }
+                       } else {
+                               mach_port_type_t        pt;
+
+                               __MACH_PORT_DEBUG(TRUE, "*** pushNotifications pid_for_task failed: releasing task", storePrivate->notifySignalTask);
+                               if (mach_port_type(mach_task_self(), storePrivate->notifySignalTask, &pt) == KERN_SUCCESS) {
+                                       if ((pt & MACH_PORT_TYPE_DEAD_NAME) != 0) {
+                                               SC_log(LOG_NOTICE, "pid_for_task() failed: %s", mach_error_string(status));
+                                       }
+                               } else {
+                                       SC_log(LOG_NOTICE, "mach_port_type() failed: %s", mach_error_string(status));
+                               }
+
+                               /* don't bother with any more attempts */
+                               (void) mach_port_deallocate(mach_task_self(), storePrivate->notifySignalTask);
+                               storePrivate->notifySignal     = 0;
+                               storePrivate->notifySignalTask = TASK_NULL;
+                       }
+              }
        }
-       CFAllocatorDeallocate(NULL, oldKeyStr);
+       if (sessionsToNotify != sessionsToNotify_q) CFAllocatorDeallocate(NULL, sessionsToNotify);
+
+       /*
+        * this list of notifications have been posted, wait for some more.
+        */
+       CFRelease(needsNotification);
+       needsNotification = NULL;
 
        return;
 }