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