]>
Commit | Line | Data |
---|---|---|
5958d7c0 A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | ||
23 | ||
24 | #include "configd.h" | |
25 | ||
26 | ||
27 | CFMutableDictionaryRef sessionData = NULL; | |
28 | ||
29 | CFMutableDictionaryRef cacheData = NULL; | |
30 | CFMutableDictionaryRef cacheData_s = NULL; | |
31 | ||
32 | CFMutableSetRef changedKeys = NULL; | |
33 | CFMutableSetRef changedKeys_s = NULL; | |
34 | ||
35 | CFMutableSetRef deferredRemovals = NULL; | |
36 | CFMutableSetRef deferredRemovals_s = NULL; | |
37 | ||
38 | CFMutableSetRef removedSessionKeys = NULL; | |
39 | CFMutableSetRef removedSessionKeys_s = NULL; | |
40 | ||
41 | CFMutableSetRef needsNotification = NULL; | |
42 | ||
43 | ||
44 | void | |
45 | _swapLockedCacheData() | |
46 | { | |
47 | void *temp; | |
48 | ||
49 | temp = cacheData; | |
50 | cacheData = cacheData_s; | |
51 | cacheData_s = temp; | |
52 | ||
53 | temp = changedKeys; | |
54 | changedKeys = changedKeys_s; | |
55 | changedKeys_s = temp; | |
56 | ||
57 | temp = deferredRemovals; | |
58 | deferredRemovals = deferredRemovals_s; | |
59 | deferredRemovals_s = temp; | |
60 | ||
61 | temp = removedSessionKeys; | |
62 | removedSessionKeys = removedSessionKeys_s; | |
63 | removedSessionKeys_s = temp; | |
64 | ||
65 | return; | |
66 | } | |
67 | ||
68 | ||
69 | void | |
70 | _addWatcher(CFNumberRef sessionNum, CFStringRef watchedKey) | |
71 | { | |
72 | CFDictionaryRef dict; | |
73 | CFMutableDictionaryRef newDict; | |
74 | CFArrayRef watchers; | |
75 | CFMutableArrayRef newWatchers; | |
76 | CFArrayRef watcherRefs; | |
77 | CFMutableArrayRef newWatcherRefs; | |
78 | CFIndex i; | |
79 | int refCnt; | |
80 | CFNumberRef refNum; | |
81 | ||
82 | /* | |
83 | * Get the dictionary associated with this key out of the cache | |
84 | */ | |
85 | dict = CFDictionaryGetValue(cacheData, watchedKey); | |
86 | if (dict) { | |
87 | newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict); | |
88 | } else { | |
89 | newDict = CFDictionaryCreateMutable(NULL, | |
90 | 0, | |
91 | &kCFTypeDictionaryKeyCallBacks, | |
92 | &kCFTypeDictionaryValueCallBacks); | |
93 | } | |
94 | ||
95 | /* | |
96 | * Get the set of watchers out of the keys dictionary | |
97 | */ | |
98 | watchers = CFDictionaryGetValue(newDict, kSCDWatchers); | |
99 | watcherRefs = CFDictionaryGetValue(newDict, kSCDWatcherRefs); | |
100 | if (watchers) { | |
101 | newWatchers = CFArrayCreateMutableCopy(NULL, 0, watchers); | |
102 | newWatcherRefs = CFArrayCreateMutableCopy(NULL, 0, watcherRefs); | |
103 | } else { | |
104 | newWatchers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
105 | newWatcherRefs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
106 | } | |
107 | ||
108 | /* | |
109 | * Add my session to the set of watchers | |
110 | */ | |
111 | i = CFArrayGetFirstIndexOfValue(newWatchers, | |
112 | CFRangeMake(0, CFArrayGetCount(newWatchers)), | |
113 | sessionNum); | |
114 | if (i == -1) { | |
115 | /* if this is the first instance of this session watching this key */ | |
116 | CFArrayAppendValue(newWatchers, sessionNum); | |
117 | refCnt = 1; | |
118 | refNum = CFNumberCreate(NULL, kCFNumberIntType, &refCnt); | |
119 | CFArrayAppendValue(newWatcherRefs, refNum); | |
120 | CFRelease(refNum); | |
121 | } else { | |
122 | /* if this is another instance of this session watching this key */ | |
123 | refNum = CFArrayGetValueAtIndex(newWatcherRefs, i); | |
124 | CFNumberGetValue(refNum, kCFNumberIntType, &refCnt); | |
125 | refCnt++; | |
126 | refNum = CFNumberCreate(NULL, kCFNumberIntType, &refCnt); | |
127 | CFArraySetValueAtIndex(newWatcherRefs, i, refNum); | |
128 | CFRelease(refNum); | |
129 | } | |
130 | ||
131 | /* | |
132 | * Update the keys dictionary | |
133 | */ | |
134 | CFDictionarySetValue(newDict, kSCDWatchers, newWatchers); | |
135 | CFRelease(newWatchers); | |
136 | CFDictionarySetValue(newDict, kSCDWatcherRefs, newWatcherRefs); | |
137 | CFRelease(newWatcherRefs); | |
138 | ||
139 | /* | |
140 | * Update the cache for this key | |
141 | */ | |
142 | CFDictionarySetValue(cacheData, watchedKey, newDict); | |
143 | CFRelease(newDict); | |
144 | ||
145 | SCDLog(LOG_DEBUG, CFSTR(" _addWatcher: %@, %@"), sessionNum, watchedKey); | |
146 | ||
147 | return; | |
148 | } | |
149 | ||
150 | ||
151 | /* | |
152 | * _addRegexWatcherByKey() | |
153 | * | |
154 | * This is a CFDictionaryApplierFunction which will iterate over each key | |
155 | * defined in the "cacheData" dictionary. The arguments are the dictionary | |
156 | * key, it's associated cache dictionary, and a context structure which | |
157 | * includes the following: | |
158 | * | |
159 | * 1. the session which has just added a regex notification request | |
160 | * 2. the compiled regular expression associated with the above key. | |
161 | * | |
162 | * If a (real) dictionary key is found which matches the provided regular | |
163 | * expression then we mark that key as being watched by the session. | |
164 | */ | |
165 | void | |
166 | _addRegexWatcherByKey(const void *key, void *val, void *context) | |
167 | { | |
168 | CFStringRef cacheStr = key; | |
169 | CFDictionaryRef info = val; | |
170 | mach_port_t sessionID = ((addContextRef)context)->session->server; | |
171 | regex_t *preg = ((addContextRef)context)->preg; | |
172 | int cacheKeyLen; | |
173 | char *cacheKey; | |
174 | CFNumberRef sessionNum; | |
175 | int reError; | |
176 | char reErrBuf[256]; | |
177 | int reErrStrLen; | |
178 | ||
179 | if (CFDictionaryContainsKey(info, kSCDData) == FALSE) { | |
180 | /* if no data (yet) */ | |
181 | return; | |
182 | } | |
183 | ||
184 | /* convert cache key to C string */ | |
185 | cacheKeyLen = CFStringGetLength(cacheStr) + 1; | |
186 | cacheKey = CFAllocatorAllocate(NULL, cacheKeyLen, 0); | |
187 | if (!CFStringGetCString(cacheStr, cacheKey, cacheKeyLen, kCFStringEncodingMacRoman)) { | |
188 | SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString: could not convert cache key to C string")); | |
189 | CFAllocatorDeallocate(NULL, cacheKey); | |
190 | return; | |
191 | } | |
192 | ||
193 | /* compare cache key to new notification keys regular expression pattern */ | |
194 | reError = regexec(preg, cacheKey, 0, NULL, 0); | |
195 | switch (reError) { | |
196 | case 0 : | |
197 | /* we've got a match */ | |
198 | sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionID); | |
199 | _addWatcher(sessionNum, cacheStr); | |
200 | CFRelease(sessionNum); | |
201 | break; | |
202 | case REG_NOMATCH : | |
203 | /* no match */ | |
204 | break; | |
205 | default : | |
206 | reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf)); | |
207 | SCDLog(LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf); | |
208 | break; | |
209 | } | |
210 | CFAllocatorDeallocate(NULL, cacheKey); | |
211 | } | |
212 | ||
213 | ||
214 | /* | |
215 | * _addRegexWatchersBySession() | |
216 | * | |
217 | * This is a CFDictionaryApplierFunction which will iterate over each session | |
218 | * defined in the "sessionData" dictionary. The arguments are the session | |
219 | * key, it's associated session dictionary, , and the cache key being added. | |
220 | * | |
221 | * If an active session includes any regular expression keys which match the | |
222 | * key being added to the "cacheData" dictionary then we mark this key as being | |
223 | * watched by the session. | |
224 | */ | |
225 | void | |
226 | _addRegexWatchersBySession(const void *key, void *val, void *context) | |
227 | { | |
228 | CFStringRef sessionKey = key; | |
229 | CFDictionaryRef info = val; | |
230 | CFStringRef addedKey = context; | |
231 | CFIndex newKeyLen; | |
232 | char *newKeyStr; | |
233 | CFArrayRef rKeys; | |
234 | CFArrayRef rData; | |
235 | CFIndex i; | |
236 | ||
237 | if (info == NULL) { | |
238 | /* if no dictionary for this session */ | |
239 | return; | |
240 | } | |
241 | ||
242 | rKeys = CFDictionaryGetValue(info, kSCDRegexKeys); | |
243 | if (rKeys == NULL) { | |
244 | /* if no regex keys for this session */ | |
245 | return; | |
246 | } | |
247 | rData = CFDictionaryGetValue(info, kSCDRegexData); | |
248 | ||
249 | /* convert new key to C string */ | |
250 | newKeyLen = CFStringGetLength(addedKey) + 1; | |
251 | newKeyStr = CFAllocatorAllocate(NULL, newKeyLen, 0); | |
252 | if (!CFStringGetCString(addedKey, newKeyStr, newKeyLen, kCFStringEncodingMacRoman)) { | |
253 | SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString: could not convert new key to C string")); | |
254 | CFAllocatorDeallocate(NULL, newKeyStr); | |
255 | return; | |
256 | } | |
257 | ||
258 | /* iterate over the regex keys looking for an pattern which matches the new key */ | |
259 | for (i=0; i<CFArrayGetCount(rKeys); i++) { | |
260 | CFDataRef regexData = CFArrayGetValueAtIndex(rData, i); | |
261 | regex_t *preg = (regex_t *)CFDataGetBytePtr(regexData); | |
262 | int reError; | |
263 | char reErrBuf[256]; | |
264 | int reErrStrLen; | |
265 | SInt32 sessionInt; | |
266 | CFNumberRef sessionNum; | |
267 | ||
268 | /* check if this key matches the regular expression */ | |
269 | reError = regexec(preg, newKeyStr, 0, NULL, 0); | |
270 | switch (reError) { | |
271 | case 0 : | |
272 | /* we've got a match, add a reference */ | |
273 | sessionInt = CFStringGetIntValue(sessionKey); | |
274 | sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionInt); | |
275 | _addWatcher(sessionNum, addedKey); | |
276 | CFRelease(sessionNum); | |
277 | break; | |
278 | case REG_NOMATCH : | |
279 | /* no match */ | |
280 | break; | |
281 | default : | |
282 | reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf)); | |
283 | SCDLog(LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf); | |
284 | break; | |
285 | } | |
286 | ||
287 | } | |
288 | CFAllocatorDeallocate(NULL, newKeyStr); | |
289 | ||
290 | return; | |
291 | } | |
292 | ||
293 | ||
294 | void | |
295 | _removeWatcher(CFNumberRef sessionNum, CFStringRef watchedKey) | |
296 | { | |
297 | CFDictionaryRef dict; | |
298 | CFMutableDictionaryRef newDict; | |
299 | CFArrayRef watchers; | |
300 | CFMutableArrayRef newWatchers; | |
301 | CFArrayRef watcherRefs; | |
302 | CFMutableArrayRef newWatcherRefs; | |
303 | CFIndex i; | |
304 | int refCnt; | |
305 | CFNumberRef refNum; | |
306 | ||
307 | /* | |
308 | * Get the dictionary associated with this key out of the cache | |
309 | */ | |
310 | dict = CFDictionaryGetValue(cacheData, watchedKey); | |
311 | if ((dict == NULL) || (CFDictionaryContainsKey(dict, kSCDWatchers) == FALSE)) { | |
312 | /* key doesn't exist (isn't this really fatal?) */ | |
313 | SCDLog(LOG_DEBUG, CFSTR("_removeWatcher: key not present in dictionary.")); | |
314 | return; | |
315 | } | |
316 | newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict); | |
317 | ||
318 | /* | |
319 | * Get the set of watchers out of the keys dictionary and | |
320 | * remove this session from the list. | |
321 | */ | |
322 | watchers = CFDictionaryGetValue(newDict, kSCDWatchers); | |
323 | newWatchers = CFArrayCreateMutableCopy(NULL, 0, watchers); | |
324 | ||
325 | watcherRefs = CFDictionaryGetValue(newDict, kSCDWatcherRefs); | |
326 | newWatcherRefs = CFArrayCreateMutableCopy(NULL, 0, watcherRefs); | |
327 | ||
328 | /* locate the session reference */ | |
329 | i = CFArrayGetFirstIndexOfValue(newWatchers, | |
330 | CFRangeMake(0, CFArrayGetCount(newWatchers)), | |
331 | sessionNum); | |
332 | if (i == -1) { | |
333 | SCDLog(LOG_DEBUG, CFSTR("_removeWatcher: no reference for session %@"), sessionNum); | |
334 | CFRelease(newDict); | |
335 | CFRelease(newWatchers); | |
336 | CFRelease(newWatcherRefs); | |
337 | return; | |
338 | } | |
339 | ||
340 | /* remove one session reference */ | |
341 | refNum = CFArrayGetValueAtIndex(newWatcherRefs, i); | |
342 | CFNumberGetValue(refNum, kCFNumberIntType, &refCnt); | |
343 | if (--refCnt > 0) { | |
344 | refNum = CFNumberCreate(NULL, kCFNumberIntType, &refCnt); | |
345 | CFArraySetValueAtIndex(newWatcherRefs, i, refNum); | |
346 | CFRelease(refNum); | |
347 | } else { | |
348 | /* if this was the last reference */ | |
349 | CFArrayRemoveValueAtIndex(newWatchers, i); | |
350 | CFArrayRemoveValueAtIndex(newWatcherRefs, i); | |
351 | } | |
352 | ||
353 | if (CFArrayGetCount(newWatchers) > 0) { | |
354 | /* if this key is still being "watched" */ | |
355 | CFDictionarySetValue(newDict, kSCDWatchers, newWatchers); | |
356 | CFDictionarySetValue(newDict, kSCDWatcherRefs, newWatcherRefs); | |
357 | } else { | |
358 | /* no watchers left, remove the empty set */ | |
359 | CFDictionaryRemoveValue(newDict, kSCDWatchers); | |
360 | CFDictionaryRemoveValue(newDict, kSCDWatcherRefs); | |
361 | } | |
362 | CFRelease(newWatchers); | |
363 | CFRelease(newWatcherRefs); | |
364 | ||
365 | if (CFDictionaryGetCount(newDict) > 0) { | |
366 | /* if this key is still active */ | |
367 | CFDictionarySetValue(cacheData, watchedKey, newDict); | |
368 | } else { | |
369 | /* no information left, remove the empty dictionary */ | |
370 | CFDictionaryRemoveValue(cacheData, watchedKey); | |
371 | } | |
372 | CFRelease(newDict); | |
373 | ||
374 | SCDLog(LOG_DEBUG, CFSTR(" _removeWatcher: %@, %@"), sessionNum, watchedKey); | |
375 | ||
376 | return; | |
377 | } | |
378 | ||
379 | ||
380 | /* | |
381 | * _removeRegexWatcherByKey() | |
382 | * | |
383 | * This is a CFDictionaryApplierFunction which will iterate over each key | |
384 | * defined in the "cacheData" dictionary. The arguments are the dictionary | |
385 | * key, it's associated cache dictionary, and a context structure which | |
386 | * includes the following: | |
387 | * | |
388 | * 1. the session which has just removed a regex notification request | |
389 | * 2. the compiled regular expression associated with the above key. | |
390 | * | |
391 | * If a key is found and it matches the provided regular expression then | |
392 | * it will its "being watched" status will be cleared. | |
393 | */ | |
394 | void | |
395 | _removeRegexWatcherByKey(const void *key, void *val, void *context) | |
396 | { | |
397 | CFStringRef cacheStr = key; | |
398 | CFDictionaryRef info = val; | |
399 | mach_port_t sessionID = ((removeContextRef)context)->session->server; | |
400 | regex_t *preg = ((removeContextRef)context)->preg; | |
401 | CFNumberRef sessionNum; | |
402 | CFArrayRef watchers; | |
403 | int cacheKeyLen; | |
404 | char *cacheKey; | |
405 | int reError; | |
406 | char reErrBuf[256]; | |
407 | int reErrStrLen; | |
408 | ||
409 | if ((info == NULL) || (CFDictionaryContainsKey(info, kSCDWatchers) == FALSE)) { | |
410 | /* no dictionary or no watchers */ | |
411 | return; | |
412 | } | |
413 | ||
414 | sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionID); | |
415 | ||
416 | watchers = CFDictionaryGetValue(info, kSCDWatchers); | |
417 | if (CFArrayContainsValue(watchers, | |
418 | CFRangeMake(0, CFArrayGetCount(watchers)), | |
419 | sessionNum) == FALSE) { | |
420 | /* this session is not watching this key */ | |
421 | CFRelease(sessionNum); | |
422 | return; | |
423 | } | |
424 | ||
425 | /* convert key to C string */ | |
426 | cacheKeyLen = CFStringGetLength(cacheStr) + 1; | |
427 | cacheKey = CFAllocatorAllocate(NULL, cacheKeyLen, 0); | |
428 | if (!CFStringGetCString(cacheStr, cacheKey, cacheKeyLen, kCFStringEncodingMacRoman)) { | |
429 | SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString: could not convert key to C string")); | |
430 | CFAllocatorDeallocate(NULL, cacheKey); | |
431 | CFRelease(sessionNum); | |
432 | return; | |
433 | } | |
434 | ||
435 | /* check if this key matches the regular expression */ | |
436 | reError = regexec(preg, cacheKey, 0, NULL, 0); | |
437 | switch (reError) { | |
438 | case 0 : | |
439 | /* we've got a match */ | |
440 | _removeWatcher(sessionNum, cacheStr); | |
441 | break; | |
442 | case REG_NOMATCH : | |
443 | /* no match */ | |
444 | break; | |
445 | default : | |
446 | reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf)); | |
447 | SCDLog(LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf); | |
448 | break; | |
449 | } | |
450 | CFAllocatorDeallocate(NULL, cacheKey); | |
451 | CFRelease(sessionNum); | |
452 | } | |
453 | ||
454 | ||
455 | /* | |
456 | * _removeRegexWatchersBySession() | |
457 | * | |
458 | * This is a CFDictionaryApplierFunction which will iterate over each session | |
459 | * defined in the "sessionData" dictionary. The arguments are the session | |
460 | * key, it's associated session dictionary, and the cache key being removed. | |
461 | * | |
462 | * If an active session includes any regular expression keys which match the | |
463 | * key being removed from the "cacheData" dictionary then we clear this keys | |
464 | * reference of being watched. | |
465 | */ | |
466 | void | |
467 | _removeRegexWatchersBySession(const void *key, void *val, void *context) | |
468 | { | |
469 | CFStringRef sessionKey = key; | |
470 | CFDictionaryRef info = val; | |
471 | CFStringRef removedKey = context; | |
472 | CFIndex oldKeyLen; | |
473 | char *oldKeyStr; | |
474 | CFArrayRef rKeys; | |
475 | CFArrayRef rData; | |
476 | CFIndex i; | |
477 | ||
478 | if (info == NULL) { | |
479 | /* if no dictionary for this session */ | |
480 | return; | |
481 | } | |
482 | ||
483 | rKeys = CFDictionaryGetValue(info, kSCDRegexKeys); | |
484 | if (rKeys == NULL) { | |
485 | /* if no regex keys for this session */ | |
486 | return; | |
487 | } | |
488 | rData = CFDictionaryGetValue(info, kSCDRegexData); | |
489 | ||
490 | /* convert new key to C string */ | |
491 | oldKeyLen = CFStringGetLength(removedKey) + 1; | |
492 | oldKeyStr = CFAllocatorAllocate(NULL, oldKeyLen, 0); | |
493 | if (!CFStringGetCString(removedKey, oldKeyStr, oldKeyLen, kCFStringEncodingMacRoman)) { | |
494 | SCDLog(LOG_DEBUG, CFSTR("CFStringGetCString: could not convert old key to C string")); | |
495 | CFAllocatorDeallocate(NULL, oldKeyStr); | |
496 | return; | |
497 | } | |
498 | ||
499 | /* iterate over the regex keys looking for an pattern which matches the old key */ | |
500 | for (i=0; i<CFArrayGetCount(rKeys); i++) { | |
501 | CFDataRef regexData = CFArrayGetValueAtIndex(rData, i); | |
502 | regex_t *preg = (regex_t *)CFDataGetBytePtr(regexData); | |
503 | int reError; | |
504 | char reErrBuf[256]; | |
505 | int reErrStrLen; | |
506 | SInt32 sessionInt; | |
507 | CFNumberRef sessionNum; | |
508 | ||
509 | /* check if this key matches the regular expression */ | |
510 | reError = regexec(preg, oldKeyStr, 0, NULL, 0); | |
511 | switch (reError) { | |
512 | case 0 : | |
513 | /* we've got a match, remove a reference */ | |
514 | sessionInt = CFStringGetIntValue(sessionKey); | |
515 | sessionNum = CFNumberCreate(NULL, kCFNumberIntType, &sessionInt); | |
516 | _removeWatcher(sessionNum, removedKey); | |
517 | CFRelease(sessionNum); | |
518 | break; | |
519 | case REG_NOMATCH : | |
520 | /* no match */ | |
521 | break; | |
522 | default : | |
523 | reErrStrLen = regerror(reError, preg, reErrBuf, sizeof(reErrBuf)); | |
524 | SCDLog(LOG_DEBUG, CFSTR("regexec(): %s"), reErrBuf); | |
525 | break; | |
526 | } | |
527 | ||
528 | } | |
529 | CFAllocatorDeallocate(NULL, oldKeyStr); | |
530 | ||
531 | return; | |
532 | } |