configd-293.8.tar.gz
[apple/configd.git] / configd.tproj / pattern.c
CommitLineData
009ee3c6 1/*
a40a14f8 2 * Copyright (c) 2003, 2004, 2006-2008 Apple Inc. All rights reserved.
009ee3c6
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
009ee3c6
A
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
48typedef struct {
49 CFMutableArrayRef pInfo;
50 regex_t *preg;
51} addContext, *addContextRef;
52
53
54static __inline__ void
55my_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
70static void
71identifyKeyForPattern(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 */
edebe297 89 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(storeKey), kCFStringEncodingASCII) + 1;
009ee3c6
A
90 if (len > (CFIndex)sizeof(str_q))
91 str = CFAllocatorAllocate(NULL, len, 0);
92 if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) {
dbf6a266 93 SCLog(TRUE, LOG_DEBUG, CFSTR("identifyKeyForPattern(): could not convert store key to C string"));
009ee3c6
A
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));
dbf6a266 111 SCLog(TRUE, LOG_DEBUG, CFSTR("identifyKeyForPattern regexec(): %s"), reErrBuf);
009ee3c6
A
112 break;
113 }
114 }
115
116 done :
117
118 if (str != str_q) CFAllocatorDeallocate(NULL, str);
119 return;
120}
121
122
edebe297 123static Boolean
009ee3c6
A
124patternCompile(CFStringRef pattern, regex_t *preg, CFStringRef *error)
125{
126 Boolean append = FALSE;
127 Boolean insert = FALSE;
128 CFIndex len = 0;
129 Boolean ok;
130 char str_q[256];
131 char * str = str_q;
132
133 if (!CFStringHasPrefix(pattern, CFSTR("^"))) {
134 insert = TRUE;
135 }
136
137 if (!CFStringHasSuffix(pattern, CFSTR("$")) ||
138 CFStringHasSuffix(pattern, CFSTR("\\$"))) {
139 append = TRUE;
140 }
141
142 /* if regex pattern is not bounded at both ends */
143 if (insert || append) {
144 pattern = CFStringCreateWithFormat(NULL,
145 NULL,
146 CFSTR("%s%@%s"),
147 insert ? "^" : "",
148 pattern,
149 append ? "$" : "");
150 }
151
152 (void)CFStringGetBytes(pattern,
153 CFRangeMake(0, CFStringGetLength(pattern)),
154 kCFStringEncodingASCII,
155 0,
156 FALSE,
157 NULL,
158 0,
159 &len);
160 if (++len > (CFIndex)sizeof(str_q)) {
161 str = CFAllocatorAllocate(NULL, len, 0);
162 }
163 ok = (_SC_cfstring_to_cstring(pattern, str, len, kCFStringEncodingASCII) != NULL);
164 if (insert || append) {
165 CFRelease(pattern);
166 }
167 if (ok) {
168 int reError;
169
170 reError = regcomp(preg, str, REG_EXTENDED);
171 if (reError != 0) {
172 char reErrBuf[256];
173
174 (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
175 *error = CFStringCreateWithCString(NULL, reErrBuf, kCFStringEncodingASCII);
dbf6a266
A
176#ifdef DEBUG
177 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("patternCompile regcomp(%s) failed: %s"), str, reErrBuf);
178#endif /* DEBUG */
009ee3c6
A
179 ok = FALSE;
180 }
181 } else {
182 *error = CFRetain(CFSTR("could not convert pattern to regex string"));
dbf6a266 183#ifdef DEBUG
009ee3c6 184 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("%@"), *error);
dbf6a266 185#endif /* DEBUG */
009ee3c6
A
186 }
187
188 if (str != str_q) CFAllocatorDeallocate(NULL, str);
189 return ok;
190}
191
192
edebe297 193static CFMutableArrayRef
009ee3c6
A
194patternCopy(CFStringRef pattern)
195{
196 CFArrayRef pInfo;
197
198 pInfo = CFDictionaryGetValue(patternData, pattern);
199 return pInfo ? CFArrayCreateMutableCopy(NULL, 0, pInfo) : NULL;
200}
201
202
edebe297 203static CFMutableArrayRef
009ee3c6
A
204patternNew(CFStringRef pattern)
205{
206 addContext context;
edebe297 207 CFStringRef err = NULL;
009ee3c6
A
208 CFMutableArrayRef pInfo;
209 CFMutableDataRef pRegex;
210 CFArrayRef pSessions;
211
212 /* create the pattern info */
213 pInfo = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
214
215 /* compile the regular expression from the pattern string. */
216 pRegex = CFDataCreateMutable(NULL, sizeof(regex_t));
217 CFDataSetLength(pRegex, sizeof(regex_t));
218 if (!patternCompile(pattern, (regex_t *)CFDataGetBytePtr(pRegex), &err)) {
219 CFRelease(err);
220 CFRelease(pRegex);
221 CFRelease(pInfo);
222 return NULL;
223 }
224
225 /* add the compiled regular expression to the pattern info */
226 CFArrayAppendValue(pInfo, pRegex);
227
228 /* add the initial (empty) list of sessions watching this pattern */
229 pSessions = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
230 CFArrayAppendValue(pInfo, pSessions);
231 CFRelease(pSessions);
232
233 /* identify/add all existing keys that match the specified pattern */
234 context.pInfo = pInfo;
235 context.preg = (regex_t *)CFDataGetBytePtr(pRegex);
236 my_CFDictionaryApplyFunction(storeData,
237 (CFDictionaryApplierFunction)identifyKeyForPattern,
238 &context);
239
240 CFRelease(pRegex);
241 return pInfo;
242}
243
244
edebe297
A
245__private_extern__
246CFArrayRef
247patternCopyMatches(CFStringRef pattern)
248{
249 Boolean isNew = FALSE;
250 CFArrayRef keys;
251 CFMutableArrayRef pInfo;
252
253 /* find (or create new instance of) this pattern */
254 pInfo = patternCopy(pattern);
255 if (pInfo == NULL) {
256 /* if new pattern */
257 pInfo = patternNew(pattern);
258 if (pInfo == NULL) {
259 return NULL;
260 }
261
262 isNew = TRUE;
263 }
264
265 if (isNew) {
266 CFDataRef pRegex;
267
268 pRegex = CFArrayGetValueAtIndex(pInfo, 0);
269 regfree((regex_t *)CFDataGetBytePtr(pRegex));
270 }
271
272 CFArrayReplaceValues(pInfo, CFRangeMake(0, 2), NULL, 0);
273 keys = CFArrayCreateCopy(NULL, pInfo);
274 CFRelease(pInfo);
275
276 return keys;
277}
278
279
009ee3c6
A
280__private_extern__
281Boolean
282patternAddSession(CFStringRef pattern, CFNumberRef sessionNum)
283{
284 CFIndex i;
285 CFIndex n;
286 CFMutableArrayRef pInfo;
287 CFMutableArrayRef pSessions;
288
289 /* find (or create new instance of) this pattern */
290 pInfo = patternCopy(pattern);
edebe297 291 if (pInfo == NULL) {
009ee3c6
A
292 /* if new pattern */
293 pInfo = patternNew(pattern);
edebe297 294 if (pInfo == NULL) {
009ee3c6
A
295 return FALSE;
296 }
297 }
298
299 /* add this session as a pattern watcher */
300 pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1);
301 pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions);
302 CFArrayAppendValue(pSessions, sessionNum);
303 CFArraySetValueAtIndex(pInfo, 1, pSessions);
304 CFRelease(pSessions);
305
306 /* update pattern watcher info */
307 CFDictionarySetValue(patternData, pattern, pInfo);
308
309 /* add this session as a watcher of any existing keys */
310 n = CFArrayGetCount(pInfo);
311 for (i = 2; i < n; i++) {
312 CFStringRef matchingKey;
313
314 matchingKey = CFArrayGetValueAtIndex(pInfo, i);
315 _addWatcher(sessionNum, matchingKey);
316 }
317
318 CFRelease(pInfo);
319 return TRUE;
320}
321
322
323__private_extern__
324void
325patternRemoveSession(CFStringRef pattern, CFNumberRef sessionNum)
326{
327 CFIndex i;
328 CFIndex n;
329 CFMutableArrayRef pInfo;
330 CFDataRef pRegex;
331 CFMutableArrayRef pSessions;
332
333 /* find instance of this pattern */
334 pInfo = patternCopy(pattern);
335
336 /* remove this session as a watcher from all matching keys */
337 n = CFArrayGetCount(pInfo);
338 for (i = 2; i < n; i++) {
339 CFStringRef matchingKey;
340
341 matchingKey = CFArrayGetValueAtIndex(pInfo, i);
342 _removeWatcher(sessionNum, matchingKey);
343 }
344
345 /* remove session from watchers */
346 pSessions = (CFMutableArrayRef)CFArrayGetValueAtIndex(pInfo, 1);
347 n = CFArrayGetCount(pSessions);
348 if (n > 1) {
349 /* if other sessions are watching this pattern */
350
351 pSessions = CFArrayCreateMutableCopy(NULL, 0, pSessions);
352 i = CFArrayGetFirstIndexOfValue(pSessions, CFRangeMake(0, n), sessionNum);
353 CFArrayRemoveValueAtIndex(pSessions, i);
354 CFArraySetValueAtIndex(pInfo, 1, pSessions);
355 CFRelease(pSessions);
356
357 CFDictionarySetValue(patternData, pattern, pInfo);
358 } else {
359 /* if no other sessions are watching this pattern */
360
361 pRegex = CFArrayGetValueAtIndex(pInfo, 0);
362 regfree((regex_t *)CFDataGetBytePtr(pRegex));
363 CFDictionaryRemoveValue(patternData, pattern);
364 }
365
366 CFRelease(pInfo);
367 return;
368}
369
370
371static void
372addKeyForPattern(const void *key, void *val, void *context)
373{
374 CFStringRef pattern = (CFStringRef)key;
375 CFArrayRef pInfo = (CFArrayRef)val;
376 CFStringRef storeKey = (CFStringRef)context;
377
378 CFIndex len;
379 regex_t *preg;
380 int reError;
381 char str_q[256];
382 char * str = str_q;
383
384 /* convert store key to C string */
edebe297 385 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(storeKey), kCFStringEncodingASCII) + 1;
009ee3c6
A
386 if (len > (CFIndex)sizeof(str_q))
387 str = CFAllocatorAllocate(NULL, len, 0);
388 if (_SC_cfstring_to_cstring(storeKey, str, len, kCFStringEncodingASCII) == NULL) {
dbf6a266 389 SCLog(TRUE, LOG_DEBUG, CFSTR("addKeyForPattern(): could not convert store key to C string"));
009ee3c6
A
390 goto done;
391 }
392
393 /* compare new store key to regular expression pattern */
394 preg = (regex_t *)CFDataGetBytePtr(CFArrayGetValueAtIndex(pInfo, 0));
395 reError = regexec(preg, str, 0, NULL, 0);
396 switch (reError) {
397 case 0 : {
398 /*
399 * we've got a match
400 */
401 CFIndex i;
402 CFIndex n;
403 CFMutableArrayRef pInfo_new;
404 CFArrayRef pSessions;
405
406 /* add watchers */
407 pSessions = CFArrayGetValueAtIndex(pInfo, 1);
408 n = CFArrayGetCount(pSessions);
409 for (i = 0; i < n; i++) {
410 CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i);
411
412 _addWatcher(sessionNum, storeKey);
413 }
414
415 /* add key, update pattern watcher info */
416 pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo);
417 CFArrayAppendValue(pInfo_new, storeKey);
418 CFDictionarySetValue(patternData, pattern, pInfo_new);
419 CFRelease(pInfo_new);
420 break;
421 }
422 case REG_NOMATCH :
423 /* no match */
424 break;
425 default : {
426 char reErrBuf[256];
427
428 (void)regerror(reError, preg, reErrBuf, sizeof(reErrBuf));
dbf6a266 429 SCLog(TRUE, LOG_DEBUG, CFSTR("addKeyForPattern regexec(): %s"), reErrBuf);
009ee3c6
A
430 break;
431 }
432 }
433
434 done :
435
436 if (str != str_q) CFAllocatorDeallocate(NULL, str);
437 return;
438}
439
440
441__private_extern__
442void
443patternAddKey(CFStringRef key)
444{
a40a14f8
A
445 void *context = (void *)key;
446
009ee3c6
A
447 my_CFDictionaryApplyFunction(patternData,
448 (CFDictionaryApplierFunction)addKeyForPattern,
a40a14f8 449 context);
009ee3c6
A
450
451 return;
452}
453
454
455static void
456removeKeyFromPattern(const void *key, void *val, void *context)
457{
458 CFStringRef pattern = (CFStringRef)key;
459 CFArrayRef pInfo = (CFArrayRef)val;
460 CFStringRef storeKey = (CFStringRef)context;
461
462 CFIndex i;
463 CFIndex n;
464 CFMutableArrayRef pInfo_new;
465 CFArrayRef pSessions;
466
467 n = CFArrayGetCount(pInfo);
468 if (n <= 2) {
469 /* if no keys match this pattern */
470 return;
471 }
472
473 i = CFArrayGetFirstIndexOfValue(pInfo, CFRangeMake(2, n-2), storeKey);
dbf6a266 474 if (i == kCFNotFound) {
009ee3c6
A
475 /* if this key wasn't matched by this pattern */
476 return;
477 }
478
479 /* remove key from pattern info */
480 pInfo_new = CFArrayCreateMutableCopy(NULL, 0, pInfo);
481 CFArrayRemoveValueAtIndex(pInfo_new, i);
482
483 /* remove watchers */
484 pSessions = CFArrayGetValueAtIndex(pInfo_new, 1);
485 n = CFArrayGetCount(pSessions);
486 for (i = 0; i < n; i++) {
487 CFNumberRef sessionNum = CFArrayGetValueAtIndex(pSessions, i);
488
489 _removeWatcher(sessionNum, storeKey);
490 }
491
492 CFDictionarySetValue(patternData, pattern, pInfo_new);
493 CFRelease(pInfo_new);
494 return;
495}
496
497
498__private_extern__
499void
500patternRemoveKey(CFStringRef key)
501{
a40a14f8
A
502 void *context = (void *)key;
503
009ee3c6
A
504 my_CFDictionaryApplyFunction(patternData,
505 (CFDictionaryApplierFunction)removeKeyFromPattern,
a40a14f8 506 context);
009ee3c6
A
507
508 return;
509}