]> git.saurik.com Git - apple/configd.git/blame - configd.tproj/_SCD.c
configd-24.1.tar.gz
[apple/configd.git] / configd.tproj / _SCD.c
CommitLineData
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
27CFMutableDictionaryRef sessionData = NULL;
28
29CFMutableDictionaryRef cacheData = NULL;
30CFMutableDictionaryRef cacheData_s = NULL;
31
32CFMutableSetRef changedKeys = NULL;
33CFMutableSetRef changedKeys_s = NULL;
34
35CFMutableSetRef deferredRemovals = NULL;
36CFMutableSetRef deferredRemovals_s = NULL;
37
38CFMutableSetRef removedSessionKeys = NULL;
39CFMutableSetRef removedSessionKeys_s = NULL;
40
41CFMutableSetRef needsNotification = NULL;
42
43
44void
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
69void
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 */
165void
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 */
225void
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
294void
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 */
394void
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 */
466void
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}