]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/pattern.c
configd-453.16.tar.gz
[apple/configd.git] / configd.tproj / pattern.c
1 /*
2 * Copyright (c) 2003, 2004, 2006-2008, 2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * April 23, 2003 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31
32 #include "configd.h"
33 #include "pattern.h"
34
35
36 /*
37 * Notes:
38 *
39 * - pattern matching information is maintained in a dictionary (patternData).
40 * - the dictionary "key" is the regular expression being matched
41 * - the dictionary "value" is a CFArray with the following contents
42 * [0] = CFData consisting of the pre-compiled regular expression
43 * [1] = CFArray[CFNumber] consisting of the sessions watching this pattern
44 * [2-n] = dynamic store keys which match this pattern
45 */
46
47
48 typedef struct {
49 CFMutableArrayRef pInfo;
50 regex_t *preg;
51 } addContext, *addContextRef;
52
53
54 static __inline__ void
55 my_CFDictionaryApplyFunction(CFDictionaryRef theDict,
56 CFDictionaryApplierFunction applier,
57 void *context)
58 {
59 CFAllocatorRef myAllocator;
60 CFDictionaryRef myDict;
61
62 myAllocator = CFGetAllocator(theDict);
63 myDict = CFDictionaryCreateCopy(myAllocator, theDict);
64 CFDictionaryApplyFunction(myDict, applier, context);
65 CFRelease(myDict);
66 return;
67 }
68
69
70 static void
71 identifyKeyForPattern(const void *key, void *val, void *context)
72 {
73 CFStringRef storeKey = (CFStringRef)key;
74 CFDictionaryRef storeValue = (CFDictionaryRef)val;
75 CFMutableArrayRef pInfo = ((addContextRef)context)->pInfo;
76 regex_t * preg = ((addContextRef)context)->preg;
77
78 CFIndex len;
79 int reError;
80 char str_q[256];
81 char * str = str_q;
82
83 if (!CFDictionaryContainsKey(storeValue, kSCDData)) {
84 /* if no data (yet) */
85 return;
86 }
87
88 /* convert store key to C string */
89 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(storeKey), kCFStringEncodingASCII) + 1;
90 if (len > (CFIndex)sizeof(str_q))
91 str = CFAllocatorAllocate(NULL, len, 0);
92 if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) {
93 SCLog(TRUE, LOG_DEBUG, CFSTR("identifyKeyForPattern(): could not convert store key to C string"));
94 goto done;
95 }
96
97 /* compare store key to new notification keys regular expression pattern */
98 reError = regexec(preg, str, 0, NULL, 0);
99 switch (reError) {
100 case 0 :
101 /* we've got a match */
102 CFArrayAppendValue(pInfo, storeKey);
103 break;
104 case REG_NOMATCH :
105 /* no match */
106 break;
107 default : {
108 char reErrBuf[256];
109
110 (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
111 SCLog(TRUE, LOG_DEBUG, CFSTR("identifyKeyForPattern regexec(): %s"), reErrBuf);
112 break;
113 }
114 }
115
116 done :
117
118 if (str != str_q) CFAllocatorDeallocate(NULL, str);
119 return;
120 }
121
122
123 static Boolean
124 patternCompile(CFStringRef pattern, regex_t *preg, CFStringRef *error)
125 {
126 Boolean append = FALSE;
127 Boolean insert = FALSE;
128 CFIndex len = 0;
129 CFIndex len_c;
130 Boolean ok;
131 char str_q[256];
132 char * str = str_q;
133
134 if (CFStringGetLength(pattern) == 0) {
135 SCLog(TRUE, LOG_ERR, CFSTR("patternCompile(): empty string"));
136 }
137
138 if (!CFStringHasPrefix(pattern, CFSTR("^"))) {
139 insert = TRUE;
140 }
141
142 if (!CFStringHasSuffix(pattern, CFSTR("$")) ||
143 CFStringHasSuffix(pattern, CFSTR("\\$"))) {
144 append = TRUE;
145 }
146
147 /* if regex pattern is not bounded at both ends */
148 if (insert || append) {
149 pattern = CFStringCreateWithFormat(NULL,
150 NULL,
151 CFSTR("%s%@%s"),
152 insert ? "^" : "",
153 pattern,
154 append ? "$" : "");
155 }
156
157 len_c = CFStringGetBytes(pattern,
158 CFRangeMake(0, CFStringGetLength(pattern)),
159 kCFStringEncodingASCII,
160 0,
161 FALSE,
162 NULL,
163 0,
164 &len);
165 if (len_c <= 0) {
166 SCLog(TRUE, LOG_ERR, CFSTR("patternCompile(): could not get buffer length for \"%@\""), pattern);
167 len = sizeof(str_q) - 1;
168 }
169 if (++len > (CFIndex)sizeof(str_q)) {
170 str = CFAllocatorAllocate(NULL, len, 0);
171 }
172 ok = (_SC_cfstring_to_cstring(pattern, str, len, kCFStringEncodingASCII) != NULL);
173 if (insert || append) {
174 CFRelease(pattern);
175 }
176 if (ok) {
177 int reError;
178
179 reError = regcomp(preg, str, REG_EXTENDED);
180 if (reError != 0) {
181 char reErrBuf[256];
182
183 (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
184 *error = CFStringCreateWithCString(NULL, reErrBuf, kCFStringEncodingASCII);
185 #ifdef DEBUG
186 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("patternCompile regcomp(%s) failed: %s"), str, reErrBuf);
187 #endif /* DEBUG */
188 ok = FALSE;
189 }
190 } else {
191 *error = CFRetain(CFSTR("could not convert pattern to regex string"));
192 #ifdef DEBUG
193 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("%@"), *error);
194 #endif /* DEBUG */
195 }
196
197 if (str != str_q) CFAllocatorDeallocate(NULL, str);
198 return ok;
199 }
200
201
202 static CF_RETURNS_RETAINED CFMutableArrayRef
203 patternCopy(CFStringRef pattern)
204 {
205 CFArrayRef pInfo;
206
207 pInfo = CFDictionaryGetValue(patternData, pattern);
208 return pInfo ? CFArrayCreateMutableCopy(NULL, 0, pInfo) : NULL;
209 }
210
211
212 static CF_RETURNS_RETAINED CFMutableArrayRef
213 patternNew(CFStringRef pattern)
214 {
215 addContext context;
216 CFStringRef err = NULL;
217 CFMutableArrayRef pInfo;
218 CFMutableDataRef pRegex;
219 CFArrayRef pSessions;
220
221 /* create the pattern info */
222 pInfo = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
223
224 /* compile the regular expression from the pattern string. */
225 pRegex = CFDataCreateMutable(NULL, sizeof(regex_t));
226 CFDataSetLength(pRegex, sizeof(regex_t));
227 /* ALIGN: CF aligns to >8 byte boundries */
228 if (!patternCompile(pattern, (regex_t *)(void *)CFDataGetBytePtr(pRegex), &err)) {
229 CFRelease(err);
230 CFRelease(pRegex);
231 CFRelease(pInfo);
232 return NULL;
233 }
234
235 /* add the compiled regular expression to the pattern info */
236 CFArrayAppendValue(pInfo, pRegex);
237
238 /* add the initial (empty) list of sessions watching this pattern */
239 pSessions = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
240 CFArrayAppendValue(pInfo, pSessions);
241 CFRelease(pSessions);
242
243 /* identify/add all existing keys that match the specified pattern */
244 context.pInfo = pInfo;
245 /* ALIGN: CF aligns to >8 byte boundries */
246 context.preg = (regex_t *)(void *)CFDataGetBytePtr(pRegex);
247 my_CFDictionaryApplyFunction(storeData,
248 (CFDictionaryApplierFunction)identifyKeyForPattern,
249 &context);
250
251 CFRelease(pRegex);
252 return pInfo;
253 }
254
255
256 __private_extern__
257 CFArrayRef
258 patternCopyMatches(CFStringRef pattern)
259 {
260 Boolean isNew = FALSE;
261 CFArrayRef keys;
262 CFMutableArrayRef pInfo;
263
264 /* find (or create new instance of) this pattern */
265 pInfo = patternCopy(pattern);
266 if (pInfo == NULL) {
267 /* if new pattern */
268 pInfo = patternNew(pattern);
269 if (pInfo == NULL) {
270 return NULL;
271 }
272
273 isNew = TRUE;
274 }
275
276 if (isNew) {
277 CFDataRef pRegex;
278
279 pRegex = CFArrayGetValueAtIndex(pInfo, 0);
280 /* ALIGN: CF aligns to >8 byte boundries */
281 regfree((regex_t *)(void *)CFDataGetBytePtr(pRegex));
282 }
283
284 CFArrayReplaceValues(pInfo, CFRangeMake(0, 2), NULL, 0);
285 keys = CFArrayCreateCopy(NULL, pInfo);
286 CFRelease(pInfo);
287
288 return keys;
289 }
290
291
292 __private_extern__
293 Boolean
294 patternAddSession(CFStringRef pattern, CFNumberRef sessionNum)
295 {
296 CFIndex i;
297 CFIndex n;
298 CFMutableArrayRef pInfo;
299 CFMutableArrayRef pSessions;
300
301 /* find (or create new instance of) this pattern */
302 pInfo = patternCopy(pattern);
303 if (pInfo == NULL) {
304 /* if new pattern */
305 pInfo = patternNew(pattern);
306 if (pInfo == NULL) {
307 return FALSE;
308 }
309 }
310
311 /* add this session as a pattern watcher */
312 pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1);
313 pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions);
314 CFArrayAppendValue(pSessions, sessionNum);
315 CFArraySetValueAtIndex(pInfo, 1, pSessions);
316 CFRelease(pSessions);
317
318 /* update pattern watcher info */
319 CFDictionarySetValue(patternData, pattern, pInfo);
320
321 /* add this session as a watcher of any existing keys */
322 n = CFArrayGetCount(pInfo);
323 for (i = 2; i < n; i++) {
324 CFStringRef matchingKey;
325
326 matchingKey = CFArrayGetValueAtIndex(pInfo, i);
327 _addWatcher(sessionNum, matchingKey);
328 }
329
330 CFRelease(pInfo);
331 return TRUE;
332 }
333
334
335 __private_extern__
336 void
337 patternRemoveSession(CFStringRef pattern, CFNumberRef sessionNum)
338 {
339 CFIndex i;
340 CFIndex n;
341 CFMutableArrayRef pInfo;
342 CFDataRef pRegex;
343 CFMutableArrayRef pSessions;
344
345 /* find instance of this pattern */
346 pInfo = patternCopy(pattern);
347
348 /* remove this session as a watcher from all matching keys */
349 n = CFArrayGetCount(pInfo);
350 for (i = 2; i < n; i++) {
351 CFStringRef matchingKey;
352
353 matchingKey = CFArrayGetValueAtIndex(pInfo, i);
354 _removeWatcher(sessionNum, matchingKey);
355 }
356
357 /* remove session from watchers */
358 pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1);
359 n = CFArrayGetCount(pSessions);
360 if (n > 1) {
361 /* if other sessions are watching this pattern */
362
363 pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions);
364 i = CFArrayGetFirstIndexOfValue(pSessions, CFRangeMake(0, n), sessionNum);
365 CFArrayRemoveValueAtIndex(pSessions, i);
366 CFArraySetValueAtIndex(pInfo, 1, pSessions);
367 CFRelease(pSessions);
368
369 CFDictionarySetValue(patternData, pattern, pInfo);
370 } else {
371 /* if no other sessions are watching this pattern */
372
373 pRegex = CFArrayGetValueAtIndex(pInfo, 0);
374 /* ALIGN: CF aligns to >8 byte boundries */
375 regfree((regex_t *)(void *)CFDataGetBytePtr(pRegex));
376 CFDictionaryRemoveValue(patternData, pattern);
377 }
378
379 CFRelease(pInfo);
380 return;
381 }
382
383
384 static void
385 addKeyForPattern(const void *key, void *val, void *context)
386 {
387 CFStringRef pattern = (CFStringRef)key;
388 CFArrayRef pInfo = (CFArrayRef)val;
389 CFStringRef storeKey = (CFStringRef)context;
390
391 CFIndex len;
392 regex_t *preg;
393 int reError;
394 char str_q[256];
395 char * str = str_q;
396
397 /* convert store key to C string */
398 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(storeKey), kCFStringEncodingASCII) + 1;
399 if (len > (CFIndex)sizeof(str_q))
400 str = CFAllocatorAllocate(NULL, len, 0);
401 if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) {
402 SCLog(TRUE, LOG_DEBUG, CFSTR("addKeyForPattern(): could not convert store key to C string"));
403 goto done;
404 }
405
406 /* compare new store key to regular expression pattern */
407 /* ALIGN: CF aligns to >8 byte boundries */
408 preg = (regex_t *)(void *)CFDataGetBytePtr(CFArrayGetValueAtIndex(pInfo, 0));
409 reError = regexec(preg, str, 0, NULL, 0);
410 switch (reError) {
411 case 0 : {
412 /*
413 * we've got a match
414 */
415 CFIndex i;
416 CFIndex n;
417 CFMutableArrayRef pInfo_new;
418 CFArrayRef pSessions;
419
420 /* add watchers */
421 pSessions = CFArrayGetValueAtIndex(pInfo, 1);
422 n = CFArrayGetCount(pSessions);
423 for (i = 0; i < n; i++) {
424 CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i);
425
426 _addWatcher(sessionNum, storeKey);
427 }
428
429 /* add key, update pattern watcher info */
430 pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo);
431 CFArrayAppendValue(pInfo_new, storeKey);
432 CFDictionarySetValue(patternData, pattern, pInfo_new);
433 CFRelease(pInfo_new);
434 break;
435 }
436 case REG_NOMATCH :
437 /* no match */
438 break;
439 default : {
440 char reErrBuf[256];
441
442 (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
443 SCLog(TRUE, LOG_DEBUG, CFSTR("addKeyForPattern regexec(): %s"), reErrBuf);
444 break;
445 }
446 }
447
448 done :
449
450 if (str != str_q) CFAllocatorDeallocate(NULL, str);
451 return;
452 }
453
454
455 __private_extern__
456 void
457 patternAddKey(CFStringRef key)
458 {
459 void *context = (void *)key;
460
461 my_CFDictionaryApplyFunction(patternData,
462 (CFDictionaryApplierFunction)addKeyForPattern,
463 context);
464
465 return;
466 }
467
468
469 static void
470 removeKeyFromPattern(const void *key, void *val, void *context)
471 {
472 CFStringRef pattern = (CFStringRef)key;
473 CFArrayRef pInfo = (CFArrayRef)val;
474 CFStringRef storeKey = (CFStringRef)context;
475
476 CFIndex i;
477 CFIndex n;
478 CFMutableArrayRef pInfo_new;
479 CFArrayRef pSessions;
480
481 n = CFArrayGetCount(pInfo);
482 if (n <= 2) {
483 /* if no keys match this pattern */
484 return;
485 }
486
487 i = CFArrayGetFirstIndexOfValue(pInfo, CFRangeMake(2, n-2), storeKey);
488 if (i == kCFNotFound) {
489 /* if this key wasn't matched by this pattern */
490 return;
491 }
492
493 /* remove key from pattern info */
494 pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo);
495 CFArrayRemoveValueAtIndex(pInfo_new, i);
496
497 /* remove watchers */
498 pSessions = CFArrayGetValueAtIndex(pInfo_new, 1);
499 n = CFArrayGetCount(pSessions);
500 for (i = 0; i < n; i++) {
501 CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i);
502
503 _removeWatcher(sessionNum, storeKey);
504 }
505
506 CFDictionarySetValue(patternData, pattern, pInfo_new);
507 CFRelease(pInfo_new);
508 return;
509 }
510
511
512 __private_extern__
513 void
514 patternRemoveKey(CFStringRef key)
515 {
516 void *context = (void *)key;
517
518 my_CFDictionaryApplyFunction(patternData,
519 (CFDictionaryApplierFunction)removeKeyFromPattern,
520 context);
521
522 return;
523 }