/* * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * 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, 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 * - public API conversion * * March 24, 2000 Allan Nathanson * - initial revision */ #include "configd.h" #include "configd_server.h" #include "session.h" #include "pattern.h" #define N_QUICK 32 static void _notifyWatchers() { CFIndex keyCnt; const void * keys_q[N_QUICK]; const void ** keys = keys_q; keyCnt = CFSetGetCount(changedKeys); if (keyCnt == 0) return; /* if nothing to do */ if (keyCnt > (CFIndex)(sizeof(keys_q) / sizeof(CFStringRef))) keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0); CFSetGetValues(changedKeys, keys); while (--keyCnt >= 0) { CFArrayRef changes; CFDictionaryRef dict; CFDictionaryRef info; CFMutableDictionaryRef newInfo; CFMutableArrayRef newChanges; CFArrayRef sessionsWatchingKey; CFIndex watcherCnt; const void * watchers_q[N_QUICK]; const void ** watchers = watchers_q; dict = CFDictionaryGetValue(storeData, (CFStringRef)keys[keyCnt]); if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDWatchers) == FALSE)) { /* key doesn't exist or nobody cares if it changed */ continue; } /* * Add this key to the list of changes for each of the * sessions which is "watching". */ sessionsWatchingKey = CFDictionaryGetValue(dict, kSCDWatchers); watcherCnt = CFArrayGetCount(sessionsWatchingKey); if (watcherCnt == 0) { /* if no watchers */ continue; } if (watcherCnt > (CFIndex)(sizeof(watchers_q) / sizeof(CFNumberRef))) watchers = CFAllocatorAllocate(NULL, watcherCnt * sizeof(CFNumberRef), 0); CFArrayGetValues(sessionsWatchingKey, CFRangeMake(0, watcherCnt), 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); /* * flag this session as needing a kick */ if (needsNotification == NULL) needsNotification = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); CFSetAddValue(needsNotification, watchers[watcherCnt]); } if (watchers != watchers_q) CFAllocatorDeallocate(NULL, watchers); } if (keys != keys_q) CFAllocatorDeallocate(NULL, keys); /* * The list of changed keys have been updated for any sessions * monitoring changes to the "store". The next step, handled by * the "configd" server, is to push out any needed notifications. */ CFSetRemoveAllValues(changedKeys); } static void _processDeferredRemovals() { CFIndex keyCnt; const void * keys_q[N_QUICK]; const void ** keys = keys_q; keyCnt = CFSetGetCount(deferredRemovals); if (keyCnt == 0) return; /* if nothing to do */ if (keyCnt > (CFIndex)(sizeof(keys_q) / sizeof(CFStringRef))) keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0); CFSetGetValues(deferredRemovals, keys); while (--keyCnt >= 0) { patternRemoveKey((CFStringRef)keys[keyCnt]); } if (keys != keys_q) CFAllocatorDeallocate(NULL, keys); /* * All regex keys associated with removed store dictionary keys have * been removed. Start the list fresh again. */ 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)); /* * remove the key from the session key list */ sessionDict = CFDictionaryGetValue(sessionData, sessionKey); if (!sessionDict) { /* if no session */ goto done; } sessionKeys = CFDictionaryGetValue(sessionDict, kSCDSessionKeys); if (!sessionKeys) { /* if no session keys */ goto done; } i = CFArrayGetFirstIndexOfValue(sessionKeys, CFRangeMake(0, CFArrayGetCount(sessionKeys)), key); if (i == kCFNotFound) { /* if this session key has already been removed */ goto done; } newSessionDict = CFDictionaryCreateMutableCopy(NULL, 0, sessionDict); if (CFArrayGetCount(sessionKeys) == 1) { /* remove the last (session) key */ CFDictionaryRemoveValue(newSessionDict, kSCDSessionKeys); } else { CFMutableArrayRef newSessionKeys; /* remove the (session) key */ 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; } __private_extern__ int __SCDynamicStoreUnlock(SCDynamicStoreRef store, Boolean recursive) { serverSessionRef mySession; SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; if (!store || (storePrivate->server == MACH_PORT_NULL)) { return kSCStatusNoStoreSession; /* you must have an open session to play */ } if ((storeLocked == 0) || !storePrivate->locked) { return kSCStatusNeedLock; /* sorry, you don't have the lock */ } if ((storeLocked > 1) && recursive) { /* if the lock is being held for a recursive (internal) request */ storeLocked--; return kSCStatusOK; } if (!recursive && _configd_trace) { SCTrace(TRUE, _configd_trace, CFSTR("unlock : %5d\n"), storePrivate->server); } /* * all of the changes can be committed to the (real) store. */ CFDictionaryRemoveAllValues(storeData_s); CFDictionaryRemoveAllValues(patternData_s); CFSetRemoveAllValues (changedKeys_s); CFSetRemoveAllValues (deferredRemovals_s); CFSetRemoveAllValues (removedSessionKeys_s); /* * push notifications to any session watching those keys which * were recently changed. */ _notifyWatchers(); /* * process any deferred key deletions. */ _processDeferredRemovals(); /* * clean up any removed session keys */ CFSetApplyFunction(removedSessionKeys, _cleanupRemovedSessionKeys, NULL); CFSetRemoveAllValues(removedSessionKeys); /* Remove the "locked" run loop source for this port */ mySession = getSession(storePrivate->server); CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mySession->serverRunLoopSource, CFSTR("locked")); storeLocked = 0; /* global lock flag */ storePrivate->locked = FALSE; /* per-session lock flag */ return kSCStatusOK; } __private_extern__ kern_return_t _configunlock(mach_port_t server, int *sc_status) { serverSessionRef mySession = getSession(server); if (!mySession) { *sc_status = kSCStatusNoStoreSession; /* you must have an open session to play */ return KERN_SUCCESS; } *sc_status = __SCDynamicStoreUnlock(mySession->store, FALSE); if (*sc_status != kSCStatusOK) { return KERN_SUCCESS; } return KERN_SUCCESS; }