]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/plugin_support.c
configd-53.tar.gz
[apple/configd.git] / configd.tproj / plugin_support.c
1 /*
2 * Copyright (c) 2000-2002 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 11, 2001 Allan Nathanson <ajn@apple.com>
27 * - start using CFBundle code
28 *
29 * June 1, 2001 Allan Nathanson <ajn@apple.com>
30 * - public API conversion
31 *
32 * May 26, 2000 Allan Nathanson <ajn@apple.com>
33 * - initial revision
34 */
35
36 #include <mach-o/dyld.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/param.h>
40 #include <dirent.h>
41 #include <unistd.h>
42 #include <NSSystemDirectories.h>
43
44 #include "configd.h"
45 #include <SystemConfiguration/SCDPlugin.h>
46 void _SCDPluginExecInit();
47
48
49 /*
50 * path components, extensions, entry points, ...
51 */
52 #define BUNDLE_DIRECTORY "/SystemConfiguration" /* [/System/Library]/... */
53 #define BUNDLE_DIR_EXTENSION ".bundle"
54
55
56 static CFMutableArrayRef allBundles = NULL;
57
58
59 /* exception handling functions */
60 typedef kern_return_t (*cer_func_t) (mach_port_t exception_port,
61 mach_port_t thread,
62 mach_port_t task,
63 exception_type_t exception,
64 exception_data_t code,
65 mach_msg_type_number_t codeCnt);
66
67 typedef kern_return_t (*cer_state_func_t) (mach_port_t exception_port,
68 exception_type_t exception,
69 exception_data_t code,
70 mach_msg_type_number_t codeCnt,
71 int *flavor,
72 thread_state_t old_state,
73 mach_msg_type_number_t old_stateCnt,
74 thread_state_t new_state,
75 mach_msg_type_number_t *new_stateCnt);
76
77 typedef kern_return_t (*cer_identity_func_t) (mach_port_t exception_port,
78 mach_port_t thread,
79 mach_port_t task,
80 exception_type_t exception,
81 exception_data_t code,
82 mach_msg_type_number_t codeCnt,
83 int *flavor,
84 thread_state_t old_state,
85 mach_msg_type_number_t old_stateCnt,
86 thread_state_t new_state,
87 mach_msg_type_number_t *new_stateCnt);
88
89 static cer_func_t catch_exception_raise_func = NULL;
90 static cer_state_func_t catch_exception_raise_state_func = NULL;
91 static cer_identity_func_t catch_exception_raise_identity_func = NULL;
92
93 kern_return_t
94 catch_exception_raise(mach_port_t exception_port,
95 mach_port_t thread,
96 mach_port_t task,
97 exception_type_t exception,
98 exception_data_t code,
99 mach_msg_type_number_t codeCnt)
100 {
101
102 if (catch_exception_raise_func == NULL) {
103 /* The user hasn't defined catch_exception_raise in their binary */
104 abort();
105 }
106 return (*catch_exception_raise_func)(exception_port,
107 thread,
108 task,
109 exception,
110 code,
111 codeCnt);
112 }
113
114
115 kern_return_t
116 catch_exception_raise_state(mach_port_t exception_port,
117 exception_type_t exception,
118 exception_data_t code,
119 mach_msg_type_number_t codeCnt,
120 int *flavor,
121 thread_state_t old_state,
122 mach_msg_type_number_t old_stateCnt,
123 thread_state_t new_state,
124 mach_msg_type_number_t *new_stateCnt)
125 {
126 if (catch_exception_raise_state_func == 0) {
127 /* The user hasn't defined catch_exception_raise_state in their binary */
128 abort();
129 }
130 return (*catch_exception_raise_state_func)(exception_port,
131 exception,
132 code,
133 codeCnt,
134 flavor,
135 old_state,
136 old_stateCnt,
137 new_state,
138 new_stateCnt);
139 }
140
141
142 kern_return_t
143 catch_exception_raise_state_identity(mach_port_t exception_port,
144 mach_port_t thread,
145 mach_port_t task,
146 exception_type_t exception,
147 exception_data_t code,
148 mach_msg_type_number_t codeCnt,
149 int *flavor,
150 thread_state_t old_state,
151 mach_msg_type_number_t old_stateCnt,
152 thread_state_t new_state,
153 mach_msg_type_number_t *new_stateCnt)
154 {
155 if (catch_exception_raise_identity_func == 0) {
156 /* The user hasn't defined catch_exception_raise_identify in their binary */
157 abort();
158 }
159 return (*catch_exception_raise_identity_func)(exception_port,
160 thread,
161 task,
162 exception,
163 code,
164 codeCnt,
165 flavor,
166 old_state,
167 old_stateCnt,
168 new_state,
169 new_stateCnt);
170 }
171
172
173 static CFStringRef
174 shortBundleIdentifier(CFStringRef bundleID)
175 {
176 CFIndex len = CFStringGetLength(bundleID);
177 CFRange range;
178 CFStringRef shortID = NULL;
179
180 if (CFStringFindWithOptions(bundleID,
181 CFSTR("."),
182 CFRangeMake(0, len),
183 kCFCompareBackwards,
184 &range)) {
185 range.location = range.location + range.length;
186 range.length = len - range.location;
187 shortID = CFStringCreateWithSubstring(NULL, bundleID, range);
188 }
189
190 return shortID;
191 }
192
193
194 static void
195 loadBundle(const void *value, void *context) {
196 CFBundleRef bundle = (CFBundleRef)value;
197 CFStringRef bundleID = CFBundleGetIdentifier(bundle);
198 Boolean bundleExclude = FALSE;
199 Boolean bundleVerbose = FALSE;
200 CFDictionaryRef dict;
201 void *func;
202 SCDynamicStoreBundleLoadFunction load;
203 Boolean loaded;
204 CFIndex *nLoaded = (CFIndex *)context;
205
206 SCLog(TRUE, LOG_DEBUG, CFSTR("loading %@"), bundleID);
207
208 bundleExclude = CFSetContainsValue(_plugins_exclude, bundleID);
209 if (!bundleExclude) {
210 CFStringRef shortID = shortBundleIdentifier(bundleID);
211
212 if (shortID) {
213 bundleExclude = CFSetContainsValue(_plugins_exclude, shortID);
214 CFRelease(shortID);
215 }
216 }
217
218 if (bundleExclude) {
219 SCLog(TRUE,
220 LOG_DEBUG,
221 CFSTR("%@ load skipped"),
222 bundleID);
223 return;
224 }
225
226 loaded = CFBundleLoadExecutable(bundle);
227 if (!loaded) {
228 SCLog(TRUE,
229 LOG_NOTICE,
230 CFSTR("%@ load failed"),
231 bundleID);
232 return;
233 }
234
235 if (!CFBundleIsExecutableLoaded(bundle)) {
236 SCLog(TRUE,
237 LOG_NOTICE,
238 CFSTR("%@ executable not loaded"),
239 bundleID);
240 return;
241 }
242
243 /* bump the count of loaded bundles */
244 *nLoaded = *nLoaded + 1;
245
246 /* identify any exception handling functions */
247
248 func = CFBundleGetFunctionPointerForName(bundle, CFSTR("catch_exception_raise"));
249 if (func) {
250 catch_exception_raise_func = func;
251 }
252
253 func = CFBundleGetFunctionPointerForName(bundle, CFSTR("catch_exception_raise_state"));
254 if (func) {
255 catch_exception_raise_state_func = func;
256 }
257
258 func = CFBundleGetFunctionPointerForName(bundle, CFSTR("catch_exception_raise_identity"));
259 if (func) {
260 catch_exception_raise_identity_func = func;
261 }
262
263 /* if defined, call the bundles load() function */
264
265 load = CFBundleGetFunctionPointerForName(bundle, CFSTR("load"));
266 if (!load) {
267 return;
268 }
269
270 bundleVerbose = CFSetContainsValue(_plugins_verbose, bundleID);
271 if (!bundleVerbose) {
272 CFStringRef shortID = shortBundleIdentifier(bundleID);
273
274 if (shortID) {
275 bundleVerbose = CFSetContainsValue(_plugins_verbose, shortID);
276 CFRelease(shortID);
277 }
278 }
279
280 if (!bundleVerbose) {
281 dict = CFBundleGetInfoDictionary(bundle);
282 if (isA_CFDictionary(dict)) {
283 CFBooleanRef bVal;
284
285 bVal = CFDictionaryGetValue(dict, kSCBundleVerbose);
286 if (isA_CFBoolean(bVal) && CFBooleanGetValue(bVal)) {
287 bundleVerbose = TRUE;
288 }
289 }
290 }
291
292 (*load)(bundle, bundleVerbose);
293 return;
294 }
295
296
297 static void
298 startBundle(const void *value, void *context) {
299 CFBundleRef bundle = (CFBundleRef)value;
300 CFURLRef bundleURL;
301 char bundleName[MAXNAMLEN + 1];
302 char bundlePath[MAXPATHLEN];
303 char *cp;
304 CFDictionaryRef dict;
305 int len;
306 Boolean ok;
307 SCDynamicStoreBundleStartFunction start;
308
309 if (!CFBundleIsExecutableLoaded(bundle)) {
310 return;
311 }
312
313 start = CFBundleGetFunctionPointerForName(bundle, CFSTR("start"));
314 if (!start) {
315 return;
316 }
317
318 dict = isA_CFDictionary(CFBundleGetInfoDictionary(bundle));
319 if (!dict) {
320 return;
321 }
322
323 bundleURL = CFBundleCopyBundleURL(bundle);
324 if (!bundleURL) {
325 return;
326 }
327
328 ok = CFURLGetFileSystemRepresentation(bundleURL,
329 TRUE,
330 (UInt8 *)&bundlePath,
331 sizeof(bundlePath));
332 CFRelease(bundleURL);
333 if (!ok) {
334 return;
335 }
336
337 cp = strrchr(bundlePath, '/');
338 if (cp) {
339 cp++;
340 } else {
341 cp = bundlePath;
342 }
343
344 /* check if this directory entry is a valid bundle name */
345 len = strlen(cp);
346 if (len <= sizeof(BUNDLE_DIR_EXTENSION)) {
347 /* if entry name isn't long enough */
348 return;
349 }
350
351 len -= sizeof(BUNDLE_DIR_EXTENSION) - 1;
352 if (strcmp(&cp[len], BUNDLE_DIR_EXTENSION) != 0) {
353 /* if entry name doesn end with ".bundle" */
354 return;
355 }
356
357 /* get (just) the bundle's name */
358 bundleName[0] = '\0';
359 (void) strncat(bundleName, cp, len);
360
361 (*start)(bundleName, bundlePath);
362 return;
363 }
364
365
366 static void
367 primeBundle(const void *value, void *context) {
368 CFBundleRef bundle = (CFBundleRef)value;
369 SCDynamicStoreBundlePrimeFunction prime;
370
371 if (!CFBundleIsExecutableLoaded(bundle)) {
372 return;
373 }
374
375 prime = CFBundleGetFunctionPointerForName(bundle, CFSTR("prime"));
376 if (!prime) {
377 return;
378 }
379
380 (*prime)();
381 return;
382 }
383
384
385 #ifdef DEBUG
386
387 static void
388 timerCallback(CFRunLoopTimerRef timer, void *info)
389 {
390 SCLog(_configd_verbose,
391 LOG_INFO,
392 CFSTR("the CFRunLoop is waiting for something to happen...."));
393 return;
394 }
395
396 #endif /* DEBUG */
397
398
399 static void
400 sortBundles(CFMutableArrayRef orig)
401 {
402 CFMutableArrayRef new;
403
404 new = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
405 while (CFArrayGetCount(orig) > 0) {
406 int i;
407 Boolean inserted = FALSE;
408
409 for (i = 0; i < CFArrayGetCount(orig); i++) {
410 CFBundleRef bundle1 = (CFBundleRef)CFArrayGetValueAtIndex(orig, i);
411 CFStringRef bundleID1 = CFBundleGetIdentifier(bundle1);
412 int count;
413 CFDictionaryRef dict;
414 int j;
415 CFArrayRef requires = NULL;
416
417 dict = isA_CFDictionary(CFBundleGetInfoDictionary(bundle1));
418 if (dict) {
419 requires = CFDictionaryGetValue(dict, kSCBundleRequires);
420 requires = isA_CFArray(requires);
421 }
422 if (bundleID1 == NULL || requires == NULL) {
423 CFArrayInsertValueAtIndex(new, 0, bundle1);
424 CFArrayRemoveValueAtIndex(orig, i);
425 inserted = TRUE;
426 break;
427 }
428 count = CFArrayGetCount(requires);
429 for (j = 0; j < CFArrayGetCount(requires); j++) {
430 int k;
431 CFStringRef r = CFArrayGetValueAtIndex(requires, j);
432
433 for (k = 0; k < CFArrayGetCount(new); k++) {
434 CFBundleRef bundle2 = (CFBundleRef)CFArrayGetValueAtIndex(new, k);
435 CFStringRef bundleID2 = CFBundleGetIdentifier(bundle2);
436
437 if (bundleID2 && CFEqual(bundleID2, r)) {
438 count--;
439 }
440 }
441 }
442 if (count == 0) {
443 /* all dependencies are met, append */
444 CFArrayAppendValue(new, bundle1);
445 CFArrayRemoveValueAtIndex(orig, i);
446 inserted = TRUE;
447 break;
448 }
449 }
450
451 if (inserted == FALSE) {
452 SCLog(TRUE, LOG_NOTICE, CFSTR("Bundles have circular dependency!!!"));
453 break;
454 }
455 }
456 if (CFArrayGetCount(orig) > 0) {
457 /* we have a circular dependency, append remaining items on new array */
458 CFArrayAppendArray(new, orig, CFRangeMake(0, CFArrayGetCount(orig)));
459 }
460 else {
461 /* new one is a sorted version of original */
462 }
463
464 CFArrayRemoveAllValues(orig);
465 CFArrayAppendArray(orig, new, CFRangeMake(0, CFArrayGetCount(new)));
466 CFRelease(new);
467 return;
468 }
469
470
471 void *
472 plugin_exec(void *arg)
473 {
474 CFIndex nLoaded = 0;
475
476 /* keep track of bundles */
477 allBundles = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
478
479 /* allow plug-ins to exec child/helper processes */
480 _SCDPluginExecInit();
481
482 if (arg == NULL) {
483 char path[MAXPATHLEN];
484 NSSearchPathEnumerationState state;
485
486 /*
487 * identify and load all bundles
488 */
489 state = NSStartSearchPathEnumeration(NSLibraryDirectory,
490 NSLocalDomainMask|NSSystemDomainMask);
491 while ((state = NSGetNextSearchPathEnumeration(state, path))) {
492 CFArrayRef bundles;
493 CFURLRef url;
494
495 /* load any available bundle */
496 strcat(path, BUNDLE_DIRECTORY);
497 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("searching for bundles in \".\""));
498 url = CFURLCreateFromFileSystemRepresentation(NULL,
499 path,
500 strlen(path),
501 TRUE);
502 bundles = CFBundleCreateBundlesFromDirectory(NULL, url, CFSTR(".bundle"));
503 CFRelease(url);
504
505 if (bundles) {
506 CFArrayAppendArray(allBundles,
507 bundles,
508 CFRangeMake(0, CFArrayGetCount(bundles)));
509 CFRelease(bundles);
510 }
511 }
512
513 sortBundles(allBundles);
514 } else {
515 CFBundleRef bundle;
516 CFURLRef url;
517
518 /*
519 * load (only) the specified bundle
520 */
521 url = CFURLCreateFromFileSystemRepresentation(NULL,
522 (char *)arg,
523 strlen((char *)arg),
524 TRUE);
525 bundle = CFBundleCreate(NULL, url);
526 if (bundle) {
527 CFArrayAppendValue(allBundles, bundle);
528 CFRelease(bundle);
529 }
530 CFRelease(url);
531 }
532
533 /*
534 * load each bundle and, if defined, call its load() function. This
535 * function (or the start() function) should initialize any variables,
536 * open any sessions with "configd", and register any needed notifications.
537 *
538 * Note: Establishing initial information in the store should be
539 * deferred until the prime() initialization function so that
540 * any bundles which want to receive a notification that the
541 * data has changed will have an opportunity to install a
542 * notification handler.
543 */
544 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle load() functions"));
545 CFArrayApplyFunction(allBundles,
546 CFRangeMake(0, CFArrayGetCount(allBundles)),
547 loadBundle,
548 &nLoaded);
549
550 /*
551 * If defined, call each bundles start() function. This function is
552 * called after the bundle has been loaded and its load() function has
553 * been called. It should initialize any variables, open any sessions
554 * with "configd", and register any needed notifications.
555 *
556 * Note: Establishing initial information in the store should be
557 * deferred until the prime() initialization function so that
558 * any bundles which want to receive a notification that the
559 * data has changed will have an opportunity to install a
560 * notification handler.
561 */
562 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle start() functions"));
563 CFArrayApplyFunction(allBundles,
564 CFRangeMake(0, CFArrayGetCount(allBundles)),
565 startBundle,
566 NULL);
567
568 /*
569 * If defined, call each bundles prime() function. This function is
570 * called after the bundle has been loaded and its load() and start()
571 * functions have been called. It should initialize any configuration
572 * information and/or state in the store.
573 */
574 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle prime() functions"));
575 CFArrayApplyFunction(allBundles,
576 CFRangeMake(0, CFArrayGetCount(allBundles)),
577 primeBundle,
578 NULL);
579
580 #ifdef DEBUG
581 if (arg == NULL && (nLoaded > 0)) {
582 CFRunLoopTimerRef timer;
583
584 /* allocate a periodic event (to help show we're not blocking) */
585 timer = CFRunLoopTimerCreate(NULL, /* allocator */
586 CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */
587 60.0, /* interval */
588 0, /* flags */
589 0, /* order */
590 timerCallback, /* callout */
591 NULL); /* context */
592 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
593 CFRelease(timer);
594 }
595 #endif /* DEBUG */
596
597 if (_configd_fork) {
598 /* synchronize with parent process */
599 kill(getppid(), SIGTERM);
600 }
601
602 /*
603 * The assumption is that each loaded plugin will establish CFMachPortRef,
604 * CFSocketRef, and CFRunLoopTimerRef input sources to handle any events
605 * and register these sources with this threads run loop. If the plugin
606 * needs to wait and/or block at any time it should do so only in its a
607 * private thread.
608 */
609 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("starting plugin CFRunLoop"));
610 CFRunLoopRun();
611 SCLog(_configd_verbose, LOG_INFO, CFSTR("what, no more work for the \"configd\" bundles?"));
612 return NULL;
613 }
614
615
616 void
617 plugin_init()
618 {
619 pthread_attr_t tattr;
620 pthread_t tid;
621
622 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("Starting thread for plug-ins..."));
623 pthread_attr_init(&tattr);
624 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
625 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
626 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
627 pthread_create(&tid, &tattr, plugin_exec, NULL);
628 pthread_attr_destroy(&tattr);
629 SCLog(_configd_verbose, LOG_DEBUG, CFSTR(" thread id=0x%08x"), tid);
630
631 return;
632 }