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