#include "configd.h"
#include "configd_server.h"
#include "session.h"
static void
_notifyWatchers()
{
CFIndex keyCnt;
void **keys;
if ((keyCnt = CFSetGetCount(changedKeys)) == 0)
return;
keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
CFSetGetValues(changedKeys, keys);
while (--keyCnt >= 0) {
CFDictionaryRef dict;
CFArrayRef sessionsWatchingKey;
CFIndex watcherCnt;
void **watchers;
CFDictionaryRef info;
CFMutableDictionaryRef newInfo;
CFArrayRef changes;
CFMutableArrayRef newChanges;
dict = CFDictionaryGetValue(cacheData, (CFStringRef)keys[keyCnt]);
if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDWatchers) == FALSE)) {
continue;
}
sessionsWatchingKey = CFDictionaryGetValue(dict, kSCDWatchers);
watcherCnt = CFArrayGetCount(sessionsWatchingKey);
watchers = CFAllocatorAllocate(NULL, watcherCnt * sizeof(CFNumberRef), 0);
CFArrayGetValues(sessionsWatchingKey,
CFRangeMake(0, CFArrayGetCount(sessionsWatchingKey)),
watchers);
while (--watcherCnt >= 0) {
CFStringRef sessionKey;
sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), watchers[watcherCnt]);
info = CFDictionaryGetValue(sessionData, sessionKey);
if (info) {
newInfo = CFDictionaryCreateMutableCopy(NULL, 0, info);
} else {
newInfo = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
changes = CFDictionaryGetValue(newInfo, kSCDChangedKeys);
if (changes) {
newChanges = CFArrayCreateMutableCopy(NULL, 0, changes);
} else {
newChanges = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
if (CFArrayContainsValue(newChanges,
CFRangeMake(0, CFArrayGetCount(newChanges)),
(CFStringRef)keys[keyCnt]) == FALSE) {
CFArrayAppendValue(newChanges, (CFStringRef)keys[keyCnt]);
}
CFDictionarySetValue(newInfo, kSCDChangedKeys, newChanges);
CFRelease(newChanges);
CFDictionarySetValue(sessionData, sessionKey, newInfo);
CFRelease(newInfo);
CFRelease(sessionKey);
if (needsNotification == NULL)
needsNotification = CFSetCreateMutable(NULL,
0,
&kCFTypeSetCallBacks);
CFSetAddValue(needsNotification, watchers[watcherCnt]);
}
CFAllocatorDeallocate(NULL, watchers);
}
CFAllocatorDeallocate(NULL, keys);
CFSetRemoveAllValues(changedKeys);
}
static void
_processDeferredRemovals()
{
CFIndex keyCnt;
void **keys;
if ((keyCnt = CFSetGetCount(deferredRemovals)) == 0)
return;
keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0);
CFSetGetValues(deferredRemovals, keys);
while (--keyCnt >= 0) {
CFDictionaryApplyFunction(sessionData,
(CFDictionaryApplierFunction)_removeRegexWatchersBySession,
keys[keyCnt]);
}
CFAllocatorDeallocate(NULL, keys);
CFSetRemoveAllValues(deferredRemovals);
return;
}
static void
_cleanupRemovedSessionKeys(const void *value, void *context)
{
CFStringRef removedKey = (CFStringRef)value;
CFRange dRange;
CFStringRef sessionKey;
CFStringRef key;
CFDictionaryRef sessionDict;
CFArrayRef sessionKeys;
CFIndex i;
CFMutableDictionaryRef newSessionDict;
dRange = CFStringFind(removedKey, CFSTR(":"), 0);
sessionKey = CFStringCreateWithSubstring(NULL,
removedKey,
CFRangeMake(0, dRange.location));
key = CFStringCreateWithSubstring(NULL,
removedKey,
CFRangeMake(dRange.location+dRange.length,
CFStringGetLength(removedKey)-dRange.location-dRange.length));
sessionDict = CFDictionaryGetValue(sessionData, sessionKey);
if (!sessionDict) {
goto done;
}
sessionKeys = CFDictionaryGetValue(sessionDict, kSCDSessionKeys);
if (!sessionKeys) {
goto done;
}
i = CFArrayGetFirstIndexOfValue(sessionKeys,
CFRangeMake(0, CFArrayGetCount(sessionKeys)),
key);
if (i == -1) {
goto done;
}
newSessionDict = CFDictionaryCreateMutableCopy(NULL, 0, sessionDict);
if (CFArrayGetCount(sessionKeys) == 1) {
CFDictionaryRemoveValue(newSessionDict, kSCDSessionKeys);
} else {
CFMutableArrayRef newSessionKeys;
newSessionKeys = CFArrayCreateMutableCopy(NULL, 0, sessionKeys);
CFArrayRemoveValueAtIndex(newSessionKeys, i);
CFDictionarySetValue(newSessionDict, kSCDSessionKeys, newSessionKeys);
CFRelease(newSessionKeys);
}
CFDictionarySetValue(sessionData, sessionKey, newSessionDict);
CFRelease(newSessionDict);
done:
CFRelease(sessionKey);
CFRelease(key);
return;
}
SCDStatus
_SCDUnlock(SCDSessionRef session)
{
SCDSessionPrivateRef sessionPrivate = (SCDSessionPrivateRef)session;
serverSessionRef mySession;
SCDLog(LOG_DEBUG, CFSTR("_SCDUnlock:"));
if ((session == NULL) || (sessionPrivate->server == MACH_PORT_NULL)) {
return SCD_NOSESSION;
}
if (!SCDOptionGet(NULL, kSCDOptionIsLocked) || !SCDOptionGet(session, kSCDOptionIsLocked)) {
return SCD_NEEDLOCK;
}
CFDictionaryRemoveAllValues(cacheData_s);
CFSetRemoveAllValues (changedKeys_s);
CFSetRemoveAllValues (deferredRemovals_s);
CFSetRemoveAllValues (removedSessionKeys_s);
#ifdef DEBUG
SCDLog(LOG_DEBUG, CFSTR("keys I changed = %@"), changedKeys);
SCDLog(LOG_DEBUG, CFSTR("keys flagged for removal = %@"), deferredRemovals);
SCDLog(LOG_DEBUG, CFSTR("keys I'm watching = %@"), sessionPrivate->keys);
SCDLog(LOG_DEBUG, CFSTR("regex keys I'm watching = %@"), sessionPrivate->reKeys);
#endif
_notifyWatchers();
_processDeferredRemovals();
CFSetApplyFunction(removedSessionKeys, _cleanupRemovedSessionKeys, NULL);
CFSetRemoveAllValues(removedSessionKeys);
#ifdef DEBUG
SCDLog(LOG_DEBUG, CFSTR("sessions to notify = %@"), needsNotification);
#endif
mySession = getSession(sessionPrivate->server);
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mySession->serverRunLoopSource, CFSTR("locked"));
SCDOptionSet(NULL, kSCDOptionIsLocked, FALSE);
SCDOptionSet(session, kSCDOptionIsLocked, FALSE);
return SCD_OK;
}
kern_return_t
_configunlock(mach_port_t server, int *scd_status)
{
serverSessionRef mySession = getSession(server);
SCDLog(LOG_DEBUG, CFSTR("Unlock configuration database."));
SCDLog(LOG_DEBUG, CFSTR(" server = %d"), server);
*scd_status = _SCDUnlock(mySession->session);
if (*scd_status != SCD_OK) {
SCDLog(LOG_DEBUG, CFSTR(" _SCDUnlock(): %s"), SCDError(*scd_status));
return KERN_SUCCESS;
}
return KERN_SUCCESS;
}