/*
- * 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)
{
* 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,
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;
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)
{
* 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);
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);
}
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;
}