1/* 2 * Copyright (c) 2000-2004, 2006, 2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * Modification History 26 * 27 * June 1, 2001 Allan Nathanson <ajn@apple.com> 28 * - public API conversion 29 * 30 * March 24, 2000 Allan Nathanson <ajn@apple.com> 31 * - initial revision 32 */ 33 34 35#include "configd.h" 36#include "configd_server.h" 37#include "session.h" 38#include "pattern.h" 39 40 41#define N_QUICK 32 42 43 44static void 45_notifyWatchers() 46{ 47 CFIndex keyCnt; 48 const void * keys_q[N_QUICK]; 49 const void ** keys = keys_q; 50 51 keyCnt = CFSetGetCount(changedKeys); 52 if (keyCnt == 0) 53 return; /* if nothing to do */ 54 55 if (keyCnt > (CFIndex)(sizeof(keys_q) / sizeof(CFStringRef))) 56 keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0); 57 58 CFSetGetValues(changedKeys, keys); 59 60 while (--keyCnt >= 0) { 61 CFArrayRef changes; 62 CFDictionaryRef dict; 63 CFDictionaryRef info; 64 CFMutableDictionaryRef newInfo; 65 CFMutableArrayRef newChanges; 66 CFArrayRef sessionsWatchingKey; 67 CFIndex watcherCnt; 68 const void * watchers_q[N_QUICK]; 69 const void ** watchers = watchers_q; 70 71 dict = CFDictionaryGetValue(storeData, (CFStringRef)keys[keyCnt]); 72 if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDWatchers) == FALSE)) { 73 /* key doesn't exist or nobody cares if it changed */ 74 continue; 75 } 76 77 /* 78 * Add this key to the list of changes for each of the 79 * sessions which is "watching". 80 */ 81 sessionsWatchingKey = CFDictionaryGetValue(dict, kSCDWatchers); 82 watcherCnt = CFArrayGetCount(sessionsWatchingKey); 83 if (watcherCnt == 0) { 84 /* if no watchers */ 85 continue; 86 } 87 88 if (watcherCnt > (CFIndex)(sizeof(watchers_q) / sizeof(CFNumberRef))) 89 watchers = CFAllocatorAllocate(NULL, watcherCnt * sizeof(CFNumberRef), 0); 90 91 CFArrayGetValues(sessionsWatchingKey, CFRangeMake(0, watcherCnt), watchers); 92 93 while (--watcherCnt >= 0) { 94 CFStringRef sessionKey; 95 96 sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), watchers[watcherCnt]); 97 info = CFDictionaryGetValue(sessionData, sessionKey); 98 if (info) { 99 newInfo = CFDictionaryCreateMutableCopy(NULL, 0, info); 100 } else { 101 newInfo = CFDictionaryCreateMutable(NULL, 102 0, 103 &kCFTypeDictionaryKeyCallBacks, 104 &kCFTypeDictionaryValueCallBacks); 105 } 106 107 changes = CFDictionaryGetValue(newInfo, kSCDChangedKeys); 108 if (changes) { 109 newChanges = CFArrayCreateMutableCopy(NULL, 0, changes); 110 } else { 111 newChanges = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 112 } 113 114 if (CFArrayContainsValue(newChanges, 115 CFRangeMake(0, CFArrayGetCount(newChanges)), 116 (CFStringRef)keys[keyCnt]) == FALSE) { 117 CFArrayAppendValue(newChanges, (CFStringRef)keys[keyCnt]); 118 } 119 CFDictionarySetValue(newInfo, kSCDChangedKeys, newChanges); 120 CFRelease(newChanges); 121 CFDictionarySetValue(sessionData, sessionKey, newInfo); 122 CFRelease(newInfo); 123 CFRelease(sessionKey); 124 125 /* 126 * flag this session as needing a kick 127 */ 128 if (needsNotification == NULL) 129 needsNotification = CFSetCreateMutable(NULL, 130 0, 131 &kCFTypeSetCallBacks); 132 CFSetAddValue(needsNotification, watchers[watcherCnt]); 133 } 134 135 if (watchers != watchers_q) CFAllocatorDeallocate(NULL, watchers); 136 } 137 138 if (keys != keys_q) CFAllocatorDeallocate(NULL, keys); 139 140 /* 141 * The list of changed keys have been updated for any sessions 142 * monitoring changes to the "store". The next step, handled by 143 * the "configd" server, is to push out any needed notifications. 144 */ 145 CFSetRemoveAllValues(changedKeys); 146 147} 148 149 150static void 151_processDeferredRemovals() 152{ 153 CFIndex keyCnt; 154 const void * keys_q[N_QUICK]; 155 const void ** keys = keys_q; 156 157 keyCnt = CFSetGetCount(deferredRemovals); 158 if (keyCnt == 0) 159 return; /* if nothing to do */ 160 161 if (keyCnt > (CFIndex)(sizeof(keys_q) / sizeof(CFStringRef))) 162 keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0); 163 164 CFSetGetValues(deferredRemovals, keys); 165 166 while (--keyCnt >= 0) { 167 patternRemoveKey((CFStringRef)keys[keyCnt]); 168 } 169 170 if (keys != keys_q) CFAllocatorDeallocate(NULL, keys); 171 172 /* 173 * All regex keys associated with removed store dictionary keys have 174 * been removed. Start the list fresh again. 175 */ 176 CFSetRemoveAllValues(deferredRemovals); 177 178 return; 179} 180 181 182static void 183_cleanupRemovedSessionKeys(const void *value, void *context) 184{ 185 CFStringRef removedKey = (CFStringRef)value; 186 CFRange dRange; 187 CFStringRef sessionKey; 188 CFStringRef key; 189 CFDictionaryRef sessionDict; 190 CFArrayRef sessionKeys; 191 CFIndex i; 192 CFMutableDictionaryRef newSessionDict; 193 194 dRange = CFStringFind(removedKey, CFSTR(":"), 0); 195 sessionKey = CFStringCreateWithSubstring(NULL, 196 removedKey, 197 CFRangeMake(0, dRange.location)); 198 key = CFStringCreateWithSubstring(NULL, 199 removedKey, 200 CFRangeMake(dRange.location+dRange.length, 201 CFStringGetLength(removedKey)-dRange.location-dRange.length)); 202 203 /* 204 * remove the key from the session key list 205 */ 206 sessionDict = CFDictionaryGetValue(sessionData, sessionKey); 207 if (!sessionDict) { 208 /* if no session */ 209 goto done; 210 } 211 212 sessionKeys = CFDictionaryGetValue(sessionDict, kSCDSessionKeys); 213 if (!sessionKeys) { 214 /* if no session keys */ 215 goto done; 216 } 217 218 i = CFArrayGetFirstIndexOfValue(sessionKeys, 219 CFRangeMake(0, CFArrayGetCount(sessionKeys)), 220 key); 221 if (i == kCFNotFound) { 222 /* if this session key has already been removed */ 223 goto done; 224 } 225 226 newSessionDict = CFDictionaryCreateMutableCopy(NULL, 0, sessionDict); 227 if (CFArrayGetCount(sessionKeys) == 1) { 228 /* remove the last (session) key */ 229 CFDictionaryRemoveValue(newSessionDict, kSCDSessionKeys); 230 } else { 231 CFMutableArrayRef newSessionKeys; 232 233 /* remove the (session) key */ 234 newSessionKeys = CFArrayCreateMutableCopy(NULL, 0, sessionKeys); 235 CFArrayRemoveValueAtIndex(newSessionKeys, i); 236 CFDictionarySetValue(newSessionDict, kSCDSessionKeys, newSessionKeys); 237 CFRelease(newSessionKeys); 238 } 239 CFDictionarySetValue(sessionData, sessionKey, newSessionDict); 240 CFRelease(newSessionDict); 241 242 done: 243 244 CFRelease(sessionKey); 245 CFRelease(key); 246 247 return; 248} 249 250 251__private_extern__ 252int 253__SCDynamicStorePush(void) 254{ 255 /* 256 * push notifications to any session watching those keys which 257 * were recently changed. 258 */ 259 _notifyWatchers(); 260 261 /* 262 * process any deferred key deletions. 263 */ 264 _processDeferredRemovals(); 265 266 /* 267 * clean up any removed session keys 268 */ 269 CFSetApplyFunction(removedSessionKeys, _cleanupRemovedSessionKeys, NULL); 270 CFSetRemoveAllValues(removedSessionKeys); 271 272 return kSCStatusOK; 273} 274