]>
Commit | Line | Data |
---|---|---|
5958d7c0 | 1 | /* |
edebe297 | 2 | * Copyright (c) 2000-2004, 2006 Apple Computer, Inc. All rights reserved. |
5958d7c0 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
009ee3c6 | 5 | * |
009ee3c6 A |
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 | |
5958d7c0 A |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
009ee3c6 A |
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 | * | |
5958d7c0 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
0fae82ee A |
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 | ||
5958d7c0 A |
34 | |
35 | #include "configd.h" | |
36 | #include "configd_server.h" | |
37 | #include "session.h" | |
009ee3c6 A |
38 | #include "pattern.h" |
39 | ||
40 | ||
41 | #define N_QUICK 32 | |
5958d7c0 A |
42 | |
43 | ||
44 | static void | |
45 | _notifyWatchers() | |
46 | { | |
47 | CFIndex keyCnt; | |
009ee3c6 A |
48 | const void * keys_q[N_QUICK]; |
49 | const void ** keys = keys_q; | |
5958d7c0 | 50 | |
a5f60add A |
51 | keyCnt = CFSetGetCount(changedKeys); |
52 | if (keyCnt == 0) | |
5958d7c0 A |
53 | return; /* if nothing to do */ |
54 | ||
009ee3c6 A |
55 | if (keyCnt > (CFIndex)(sizeof(keys_q) / sizeof(CFStringRef))) |
56 | keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0); | |
57 | ||
5958d7c0 | 58 | CFSetGetValues(changedKeys, keys); |
009ee3c6 | 59 | |
5958d7c0 | 60 | while (--keyCnt >= 0) { |
009ee3c6 | 61 | CFArrayRef changes; |
5958d7c0 | 62 | CFDictionaryRef dict; |
5958d7c0 A |
63 | CFDictionaryRef info; |
64 | CFMutableDictionaryRef newInfo; | |
5958d7c0 | 65 | CFMutableArrayRef newChanges; |
009ee3c6 A |
66 | CFArrayRef sessionsWatchingKey; |
67 | CFIndex watcherCnt; | |
68 | const void * watchers_q[N_QUICK]; | |
69 | const void ** watchers = watchers_q; | |
5958d7c0 | 70 | |
0fae82ee | 71 | dict = CFDictionaryGetValue(storeData, (CFStringRef)keys[keyCnt]); |
5958d7c0 A |
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); | |
009ee3c6 A |
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); | |
5958d7c0 | 105 | } |
009ee3c6 A |
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]); | |
5958d7c0 | 133 | } |
009ee3c6 A |
134 | |
135 | if (watchers != watchers_q) CFAllocatorDeallocate(NULL, watchers); | |
5958d7c0 | 136 | } |
009ee3c6 A |
137 | |
138 | if (keys != keys_q) CFAllocatorDeallocate(NULL, keys); | |
5958d7c0 A |
139 | |
140 | /* | |
141 | * The list of changed keys have been updated for any sessions | |
0fae82ee | 142 | * monitoring changes to the "store". The next step, handled by |
5958d7c0 A |
143 | * the "configd" server, is to push out any needed notifications. |
144 | */ | |
145 | CFSetRemoveAllValues(changedKeys); | |
146 | ||
147 | } | |
148 | ||
149 | ||
150 | static void | |
151 | _processDeferredRemovals() | |
152 | { | |
153 | CFIndex keyCnt; | |
009ee3c6 A |
154 | const void * keys_q[N_QUICK]; |
155 | const void ** keys = keys_q; | |
5958d7c0 | 156 | |
a5f60add A |
157 | keyCnt = CFSetGetCount(deferredRemovals); |
158 | if (keyCnt == 0) | |
5958d7c0 A |
159 | return; /* if nothing to do */ |
160 | ||
009ee3c6 A |
161 | if (keyCnt > (CFIndex)(sizeof(keys_q) / sizeof(CFStringRef))) |
162 | keys = CFAllocatorAllocate(NULL, keyCnt * sizeof(CFStringRef), 0); | |
163 | ||
5958d7c0 | 164 | CFSetGetValues(deferredRemovals, keys); |
009ee3c6 | 165 | |
5958d7c0 | 166 | while (--keyCnt >= 0) { |
009ee3c6 | 167 | patternRemoveKey((CFStringRef)keys[keyCnt]); |
5958d7c0 | 168 | } |
009ee3c6 A |
169 | |
170 | if (keys != keys_q) CFAllocatorDeallocate(NULL, keys); | |
5958d7c0 A |
171 | |
172 | /* | |
0fae82ee | 173 | * All regex keys associated with removed store dictionary keys have |
5958d7c0 A |
174 | * been removed. Start the list fresh again. |
175 | */ | |
176 | CFSetRemoveAllValues(deferredRemovals); | |
177 | ||
178 | return; | |
179 | } | |
180 | ||
181 | ||
182 | static 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); | |
dbf6a266 | 221 | if (i == kCFNotFound) { |
5958d7c0 A |
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 | ||
009ee3c6 | 251 | __private_extern__ |
0fae82ee A |
252 | int |
253 | __SCDynamicStoreUnlock(SCDynamicStoreRef store, Boolean recursive) | |
5958d7c0 | 254 | { |
0fae82ee | 255 | serverSessionRef mySession; |
dbf6a266 | 256 | SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store; |
5958d7c0 | 257 | |
edebe297 | 258 | if ((store == NULL) || (storePrivate->server == MACH_PORT_NULL)) { |
0fae82ee A |
259 | return kSCStatusNoStoreSession; /* you must have an open session to play */ |
260 | } | |
5958d7c0 | 261 | |
0fae82ee A |
262 | if ((storeLocked == 0) || !storePrivate->locked) { |
263 | return kSCStatusNeedLock; /* sorry, you don't have the lock */ | |
5958d7c0 A |
264 | } |
265 | ||
0fae82ee A |
266 | if ((storeLocked > 1) && recursive) { |
267 | /* if the lock is being held for a recursive (internal) request */ | |
268 | storeLocked--; | |
269 | return kSCStatusOK; | |
5958d7c0 A |
270 | } |
271 | ||
009ee3c6 A |
272 | if (!recursive && _configd_trace) { |
273 | SCTrace(TRUE, _configd_trace, CFSTR("unlock : %5d\n"), storePrivate->server); | |
274 | } | |
275 | ||
5958d7c0 | 276 | /* |
0fae82ee | 277 | * all of the changes can be committed to the (real) store. |
5958d7c0 | 278 | */ |
0fae82ee | 279 | CFDictionaryRemoveAllValues(storeData_s); |
009ee3c6 | 280 | CFDictionaryRemoveAllValues(patternData_s); |
5958d7c0 A |
281 | CFSetRemoveAllValues (changedKeys_s); |
282 | CFSetRemoveAllValues (deferredRemovals_s); | |
283 | CFSetRemoveAllValues (removedSessionKeys_s); | |
284 | ||
5958d7c0 A |
285 | /* |
286 | * push notifications to any session watching those keys which | |
287 | * were recently changed. | |
288 | */ | |
289 | _notifyWatchers(); | |
290 | ||
291 | /* | |
292 | * process any deferred key deletions. | |
293 | */ | |
294 | _processDeferredRemovals(); | |
295 | ||
296 | /* | |
297 | * clean up any removed session keys | |
298 | */ | |
299 | CFSetApplyFunction(removedSessionKeys, _cleanupRemovedSessionKeys, NULL); | |
300 | CFSetRemoveAllValues(removedSessionKeys); | |
301 | ||
5958d7c0 | 302 | /* Remove the "locked" run loop source for this port */ |
0fae82ee | 303 | mySession = getSession(storePrivate->server); |
5958d7c0 A |
304 | CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mySession->serverRunLoopSource, CFSTR("locked")); |
305 | ||
0fae82ee A |
306 | storeLocked = 0; /* global lock flag */ |
307 | storePrivate->locked = FALSE; /* per-session lock flag */ | |
5958d7c0 | 308 | |
0fae82ee | 309 | return kSCStatusOK; |
5958d7c0 A |
310 | } |
311 | ||
312 | ||
009ee3c6 | 313 | __private_extern__ |
5958d7c0 | 314 | kern_return_t |
0fae82ee | 315 | _configunlock(mach_port_t server, int *sc_status) |
5958d7c0 A |
316 | { |
317 | serverSessionRef mySession = getSession(server); | |
318 | ||
edebe297 | 319 | if (mySession == NULL) { |
009ee3c6 A |
320 | *sc_status = kSCStatusNoStoreSession; /* you must have an open session to play */ |
321 | return KERN_SUCCESS; | |
322 | } | |
5958d7c0 | 323 | |
0fae82ee A |
324 | *sc_status = __SCDynamicStoreUnlock(mySession->store, FALSE); |
325 | if (*sc_status != kSCStatusOK) { | |
5958d7c0 A |
326 | return KERN_SUCCESS; |
327 | } | |
328 | ||
329 | return KERN_SUCCESS; | |
330 | } |