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