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