2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
27 CFMutableDictionaryRef sessionData
= NULL
;
29 CFMutableDictionaryRef cacheData
= NULL
;
30 CFMutableDictionaryRef cacheData_s
= NULL
;
32 CFMutableSetRef changedKeys
= NULL
;
33 CFMutableSetRef changedKeys_s
= NULL
;
35 CFMutableSetRef deferredRemovals
= NULL
;
36 CFMutableSetRef deferredRemovals_s
= NULL
;
38 CFMutableSetRef removedSessionKeys
= NULL
;
39 CFMutableSetRef removedSessionKeys_s
= NULL
;
41 CFMutableSetRef needsNotification
= NULL
;
45 _swapLockedCacheData()
50 cacheData
= cacheData_s
;
54 changedKeys
= changedKeys_s
;
57 temp
= deferredRemovals
;
58 deferredRemovals
= deferredRemovals_s
;
59 deferredRemovals_s
= temp
;
61 temp
= removedSessionKeys
;
62 removedSessionKeys
= removedSessionKeys_s
;
63 removedSessionKeys_s
= temp
;
70 _addWatcher(CFNumberRef sessionNum
, CFStringRef watchedKey
)
73 CFMutableDictionaryRef newDict
;
75 CFMutableArrayRef newWatchers
;
76 CFArrayRef watcherRefs
;
77 CFMutableArrayRef newWatcherRefs
;
83 * Get the dictionary associated with this key out of the cache
85 dict
= CFDictionaryGetValue(cacheData
, watchedKey
);
87 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
89 newDict
= CFDictionaryCreateMutable(NULL
,
91 &kCFTypeDictionaryKeyCallBacks
,
92 &kCFTypeDictionaryValueCallBacks
);
96 * Get the set of watchers out of the keys dictionary
98 watchers
= CFDictionaryGetValue(newDict
, kSCDWatchers
);
99 watcherRefs
= CFDictionaryGetValue(newDict
, kSCDWatcherRefs
);
101 newWatchers
= CFArrayCreateMutableCopy(NULL
, 0, watchers
);
102 newWatcherRefs
= CFArrayCreateMutableCopy(NULL
, 0, watcherRefs
);
104 newWatchers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
105 newWatcherRefs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
109 * Add my session to the set of watchers
111 i
= CFArrayGetFirstIndexOfValue(newWatchers
,
112 CFRangeMake(0, CFArrayGetCount(newWatchers
)),
115 /* if this is the first instance of this session watching this key */
116 CFArrayAppendValue(newWatchers
, sessionNum
);
118 refNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &refCnt
);
119 CFArrayAppendValue(newWatcherRefs
, refNum
);
122 /* if this is another instance of this session watching this key */
123 refNum
= CFArrayGetValueAtIndex(newWatcherRefs
, i
);
124 CFNumberGetValue(refNum
, kCFNumberIntType
, &refCnt
);
126 refNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &refCnt
);
127 CFArraySetValueAtIndex(newWatcherRefs
, i
, refNum
);
132 * Update the keys dictionary
134 CFDictionarySetValue(newDict
, kSCDWatchers
, newWatchers
);
135 CFRelease(newWatchers
);
136 CFDictionarySetValue(newDict
, kSCDWatcherRefs
, newWatcherRefs
);
137 CFRelease(newWatcherRefs
);
140 * Update the cache for this key
142 CFDictionarySetValue(cacheData
, watchedKey
, newDict
);
145 SCDLog(LOG_DEBUG
, CFSTR(" _addWatcher: %@, %@"), sessionNum
, watchedKey
);
152 * _addRegexWatcherByKey()
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:
159 * 1. the session which has just added a regex notification request
160 * 2. the compiled regular expression associated with the above key.
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.
166 _addRegexWatcherByKey(const void *key
, void *val
, void *context
)
168 CFStringRef cacheStr
= key
;
169 CFDictionaryRef info
= val
;
170 mach_port_t sessionID
= ((addContextRef
)context
)->session
->server
;
171 regex_t
*preg
= ((addContextRef
)context
)->preg
;
174 CFNumberRef sessionNum
;
179 if (CFDictionaryContainsKey(info
, kSCDData
) == FALSE
) {
180 /* if no data (yet) */
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
);
193 /* compare cache key to new notification keys regular expression pattern */
194 reError
= regexec(preg
, cacheKey
, 0, NULL
, 0);
197 /* we've got a match */
198 sessionNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &sessionID
);
199 _addWatcher(sessionNum
, cacheStr
);
200 CFRelease(sessionNum
);
206 reErrStrLen
= regerror(reError
, preg
, reErrBuf
, sizeof(reErrBuf
));
207 SCDLog(LOG_DEBUG
, CFSTR("regexec(): %s"), reErrBuf
);
210 CFAllocatorDeallocate(NULL
, cacheKey
);
215 * _addRegexWatchersBySession()
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.
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.
226 _addRegexWatchersBySession(const void *key
, void *val
, void *context
)
228 CFStringRef sessionKey
= key
;
229 CFDictionaryRef info
= val
;
230 CFStringRef addedKey
= context
;
238 /* if no dictionary for this session */
242 rKeys
= CFDictionaryGetValue(info
, kSCDRegexKeys
);
244 /* if no regex keys for this session */
247 rData
= CFDictionaryGetValue(info
, kSCDRegexData
);
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
);
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
);
266 CFNumberRef sessionNum
;
268 /* check if this key matches the regular expression */
269 reError
= regexec(preg
, newKeyStr
, 0, NULL
, 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
);
282 reErrStrLen
= regerror(reError
, preg
, reErrBuf
, sizeof(reErrBuf
));
283 SCDLog(LOG_DEBUG
, CFSTR("regexec(): %s"), reErrBuf
);
288 CFAllocatorDeallocate(NULL
, newKeyStr
);
295 _removeWatcher(CFNumberRef sessionNum
, CFStringRef watchedKey
)
297 CFDictionaryRef dict
;
298 CFMutableDictionaryRef newDict
;
300 CFMutableArrayRef newWatchers
;
301 CFArrayRef watcherRefs
;
302 CFMutableArrayRef newWatcherRefs
;
308 * Get the dictionary associated with this key out of the cache
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."));
316 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
319 * Get the set of watchers out of the keys dictionary and
320 * remove this session from the list.
322 watchers
= CFDictionaryGetValue(newDict
, kSCDWatchers
);
323 newWatchers
= CFArrayCreateMutableCopy(NULL
, 0, watchers
);
325 watcherRefs
= CFDictionaryGetValue(newDict
, kSCDWatcherRefs
);
326 newWatcherRefs
= CFArrayCreateMutableCopy(NULL
, 0, watcherRefs
);
328 /* locate the session reference */
329 i
= CFArrayGetFirstIndexOfValue(newWatchers
,
330 CFRangeMake(0, CFArrayGetCount(newWatchers
)),
333 SCDLog(LOG_DEBUG
, CFSTR("_removeWatcher: no reference for session %@"), sessionNum
);
335 CFRelease(newWatchers
);
336 CFRelease(newWatcherRefs
);
340 /* remove one session reference */
341 refNum
= CFArrayGetValueAtIndex(newWatcherRefs
, i
);
342 CFNumberGetValue(refNum
, kCFNumberIntType
, &refCnt
);
344 refNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &refCnt
);
345 CFArraySetValueAtIndex(newWatcherRefs
, i
, refNum
);
348 /* if this was the last reference */
349 CFArrayRemoveValueAtIndex(newWatchers
, i
);
350 CFArrayRemoveValueAtIndex(newWatcherRefs
, i
);
353 if (CFArrayGetCount(newWatchers
) > 0) {
354 /* if this key is still being "watched" */
355 CFDictionarySetValue(newDict
, kSCDWatchers
, newWatchers
);
356 CFDictionarySetValue(newDict
, kSCDWatcherRefs
, newWatcherRefs
);
358 /* no watchers left, remove the empty set */
359 CFDictionaryRemoveValue(newDict
, kSCDWatchers
);
360 CFDictionaryRemoveValue(newDict
, kSCDWatcherRefs
);
362 CFRelease(newWatchers
);
363 CFRelease(newWatcherRefs
);
365 if (CFDictionaryGetCount(newDict
) > 0) {
366 /* if this key is still active */
367 CFDictionarySetValue(cacheData
, watchedKey
, newDict
);
369 /* no information left, remove the empty dictionary */
370 CFDictionaryRemoveValue(cacheData
, watchedKey
);
374 SCDLog(LOG_DEBUG
, CFSTR(" _removeWatcher: %@, %@"), sessionNum
, watchedKey
);
381 * _removeRegexWatcherByKey()
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:
388 * 1. the session which has just removed a regex notification request
389 * 2. the compiled regular expression associated with the above key.
391 * If a key is found and it matches the provided regular expression then
392 * it will its "being watched" status will be cleared.
395 _removeRegexWatcherByKey(const void *key
, void *val
, void *context
)
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
;
409 if ((info
== NULL
) || (CFDictionaryContainsKey(info
, kSCDWatchers
) == FALSE
)) {
410 /* no dictionary or no watchers */
414 sessionNum
= CFNumberCreate(NULL
, kCFNumberIntType
, &sessionID
);
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
);
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
);
435 /* check if this key matches the regular expression */
436 reError
= regexec(preg
, cacheKey
, 0, NULL
, 0);
439 /* we've got a match */
440 _removeWatcher(sessionNum
, cacheStr
);
446 reErrStrLen
= regerror(reError
, preg
, reErrBuf
, sizeof(reErrBuf
));
447 SCDLog(LOG_DEBUG
, CFSTR("regexec(): %s"), reErrBuf
);
450 CFAllocatorDeallocate(NULL
, cacheKey
);
451 CFRelease(sessionNum
);
456 * _removeRegexWatchersBySession()
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.
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.
467 _removeRegexWatchersBySession(const void *key
, void *val
, void *context
)
469 CFStringRef sessionKey
= key
;
470 CFDictionaryRef info
= val
;
471 CFStringRef removedKey
= context
;
479 /* if no dictionary for this session */
483 rKeys
= CFDictionaryGetValue(info
, kSCDRegexKeys
);
485 /* if no regex keys for this session */
488 rData
= CFDictionaryGetValue(info
, kSCDRegexData
);
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
);
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
);
507 CFNumberRef sessionNum
;
509 /* check if this key matches the regular expression */
510 reError
= regexec(preg
, oldKeyStr
, 0, NULL
, 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
);
523 reErrStrLen
= regerror(reError
, preg
, reErrBuf
, sizeof(reErrBuf
));
524 SCDLog(LOG_DEBUG
, CFSTR("regexec(): %s"), reErrBuf
);
529 CFAllocatorDeallocate(NULL
, oldKeyStr
);