2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
24 * Modification History
26 * June 11, 2001 Allan Nathanson <ajn@apple.com>
27 * - start using CFBundle code
29 * June 1, 2001 Allan Nathanson <ajn@apple.com>
30 * - public API conversion
32 * May 26, 2000 Allan Nathanson <ajn@apple.com>
36 #include <mach-o/dyld.h>
37 #include <sys/types.h>
39 #include <sys/param.h>
42 #include <NSSystemDirectories.h>
45 #include <SystemConfiguration/SCDPlugin.h>
46 void _SCDPluginExecInit();
50 * path components, extensions, entry points, ...
52 #define BUNDLE_DIRECTORY "/SystemConfiguration" /* [/System/Library]/... */
53 #define BUNDLE_DIR_EXTENSION ".bundle"
56 static CFMutableArrayRef allBundles
= NULL
;
59 /* exception handling functions */
60 typedef kern_return_t (*cer_func_t
) (mach_port_t exception_port
,
63 exception_type_t exception
,
64 exception_data_t code
,
65 mach_msg_type_number_t codeCnt
);
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
,
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
);
77 typedef kern_return_t (*cer_identity_func_t
) (mach_port_t exception_port
,
80 exception_type_t exception
,
81 exception_data_t code
,
82 mach_msg_type_number_t codeCnt
,
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
);
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
;
94 catch_exception_raise(mach_port_t exception_port
,
97 exception_type_t exception
,
98 exception_data_t code
,
99 mach_msg_type_number_t codeCnt
)
102 if (catch_exception_raise_func
== NULL
) {
103 /* The user hasn't defined catch_exception_raise in their binary */
106 return (*catch_exception_raise_func
)(exception_port
,
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
,
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
)
126 if (catch_exception_raise_state_func
== 0) {
127 /* The user hasn't defined catch_exception_raise_state in their binary */
130 return (*catch_exception_raise_state_func
)(exception_port
,
143 catch_exception_raise_state_identity(mach_port_t exception_port
,
146 exception_type_t exception
,
147 exception_data_t code
,
148 mach_msg_type_number_t codeCnt
,
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
)
155 if (catch_exception_raise_identity_func
== 0) {
156 /* The user hasn't defined catch_exception_raise_identify in their binary */
159 return (*catch_exception_raise_identity_func
)(exception_port
,
174 shortBundleIdentifier(CFStringRef bundleID
)
176 CFIndex len
= CFStringGetLength(bundleID
);
178 CFStringRef shortID
= NULL
;
180 if (CFStringFindWithOptions(bundleID
,
185 range
.location
= range
.location
+ range
.length
;
186 range
.length
= len
- range
.location
;
187 shortID
= CFStringCreateWithSubstring(NULL
, bundleID
, range
);
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
;
202 SCDynamicStoreBundleLoadFunction load
;
204 CFIndex
*nLoaded
= (CFIndex
*)context
;
206 SCLog(TRUE
, LOG_DEBUG
, CFSTR("loading %@"), bundleID
);
208 bundleExclude
= CFSetContainsValue(_plugins_exclude
, bundleID
);
209 if (!bundleExclude
) {
210 CFStringRef shortID
= shortBundleIdentifier(bundleID
);
213 bundleExclude
= CFSetContainsValue(_plugins_exclude
, shortID
);
221 CFSTR("%@ load skipped"),
226 loaded
= CFBundleLoadExecutable(bundle
);
230 CFSTR("%@ load failed"),
235 if (!CFBundleIsExecutableLoaded(bundle
)) {
238 CFSTR("%@ executable not loaded"),
243 /* bump the count of loaded bundles */
244 *nLoaded
= *nLoaded
+ 1;
246 /* identify any exception handling functions */
248 func
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("catch_exception_raise"));
250 catch_exception_raise_func
= func
;
253 func
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("catch_exception_raise_state"));
255 catch_exception_raise_state_func
= func
;
258 func
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("catch_exception_raise_identity"));
260 catch_exception_raise_identity_func
= func
;
263 /* if defined, call the bundles load() function */
265 load
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("load"));
270 bundleVerbose
= CFSetContainsValue(_plugins_verbose
, bundleID
);
271 if (!bundleVerbose
) {
272 CFStringRef shortID
= shortBundleIdentifier(bundleID
);
275 bundleVerbose
= CFSetContainsValue(_plugins_verbose
, shortID
);
280 if (!bundleVerbose
) {
281 dict
= CFBundleGetInfoDictionary(bundle
);
282 if (isA_CFDictionary(dict
)) {
285 bVal
= CFDictionaryGetValue(dict
, kSCBundleVerbose
);
286 if (isA_CFBoolean(bVal
) && CFBooleanGetValue(bVal
)) {
287 bundleVerbose
= TRUE
;
292 (*load
)(bundle
, bundleVerbose
);
298 startBundle(const void *value
, void *context
) {
299 CFBundleRef bundle
= (CFBundleRef
)value
;
301 char bundleName
[MAXNAMLEN
+ 1];
302 char bundlePath
[MAXPATHLEN
];
304 CFDictionaryRef dict
;
307 SCDynamicStoreBundleStartFunction start
;
309 if (!CFBundleIsExecutableLoaded(bundle
)) {
313 start
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("start"));
318 dict
= isA_CFDictionary(CFBundleGetInfoDictionary(bundle
));
323 bundleURL
= CFBundleCopyBundleURL(bundle
);
328 ok
= CFURLGetFileSystemRepresentation(bundleURL
,
330 (UInt8
*)&bundlePath
,
332 CFRelease(bundleURL
);
337 cp
= strrchr(bundlePath
, '/');
344 /* check if this directory entry is a valid bundle name */
346 if (len
<= sizeof(BUNDLE_DIR_EXTENSION
)) {
347 /* if entry name isn't long enough */
351 len
-= sizeof(BUNDLE_DIR_EXTENSION
) - 1;
352 if (strcmp(&cp
[len
], BUNDLE_DIR_EXTENSION
) != 0) {
353 /* if entry name doesn end with ".bundle" */
357 /* get (just) the bundle's name */
358 bundleName
[0] = '\0';
359 (void) strncat(bundleName
, cp
, len
);
361 (*start
)(bundleName
, bundlePath
);
367 primeBundle(const void *value
, void *context
) {
368 CFBundleRef bundle
= (CFBundleRef
)value
;
369 SCDynamicStoreBundlePrimeFunction prime
;
371 if (!CFBundleIsExecutableLoaded(bundle
)) {
375 prime
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("prime"));
388 timerCallback(CFRunLoopTimerRef timer
, void *info
)
390 SCLog(_configd_verbose
,
392 CFSTR("the CFRunLoop is waiting for something to happen...."));
400 sortBundles(CFMutableArrayRef orig
)
402 CFMutableArrayRef
new;
404 new = CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
405 while (CFArrayGetCount(orig
) > 0) {
407 Boolean inserted
= FALSE
;
409 for (i
= 0; i
< CFArrayGetCount(orig
); i
++) {
410 CFBundleRef bundle1
= (CFBundleRef
)CFArrayGetValueAtIndex(orig
, i
);
411 CFStringRef bundleID1
= CFBundleGetIdentifier(bundle1
);
413 CFDictionaryRef dict
;
415 CFArrayRef
requires = NULL
;
417 dict
= isA_CFDictionary(CFBundleGetInfoDictionary(bundle1
));
419 requires = CFDictionaryGetValue(dict
, kSCBundleRequires
);
420 requires = isA_CFArray(requires);
422 if (bundleID1
== NULL
|| requires == NULL
) {
423 CFArrayInsertValueAtIndex(new, 0, bundle1
);
424 CFArrayRemoveValueAtIndex(orig
, i
);
428 count
= CFArrayGetCount(requires);
429 for (j
= 0; j
< CFArrayGetCount(requires); j
++) {
431 CFStringRef r
= CFArrayGetValueAtIndex(requires, j
);
433 for (k
= 0; k
< CFArrayGetCount(new); k
++) {
434 CFBundleRef bundle2
= (CFBundleRef
)CFArrayGetValueAtIndex(new, k
);
435 CFStringRef bundleID2
= CFBundleGetIdentifier(bundle2
);
437 if (bundleID2
&& CFEqual(bundleID2
, r
)) {
443 /* all dependencies are met, append */
444 CFArrayAppendValue(new, bundle1
);
445 CFArrayRemoveValueAtIndex(orig
, i
);
451 if (inserted
== FALSE
) {
452 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Bundles have circular dependency!!!"));
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
)));
461 /* new one is a sorted version of original */
464 CFArrayRemoveAllValues(orig
);
465 CFArrayAppendArray(orig
, new, CFRangeMake(0, CFArrayGetCount(new)));
472 plugin_exec(void *arg
)
476 /* keep track of bundles */
477 allBundles
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
479 /* allow plug-ins to exec child/helper processes */
480 _SCDPluginExecInit();
483 char path
[MAXPATHLEN
];
484 NSSearchPathEnumerationState state
;
487 * identify and load all bundles
489 state
= NSStartSearchPathEnumeration(NSLibraryDirectory
,
490 NSLocalDomainMask
|NSSystemDomainMask
);
491 while ((state
= NSGetNextSearchPathEnumeration(state
, path
))) {
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
,
502 bundles
= CFBundleCreateBundlesFromDirectory(NULL
, url
, CFSTR(".bundle"));
506 CFArrayAppendArray(allBundles
,
508 CFRangeMake(0, CFArrayGetCount(bundles
)));
513 sortBundles(allBundles
);
519 * load (only) the specified bundle
521 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
525 bundle
= CFBundleCreate(NULL
, url
);
527 CFArrayAppendValue(allBundles
, bundle
);
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.
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.
544 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle load() functions"));
545 CFArrayApplyFunction(allBundles
,
546 CFRangeMake(0, CFArrayGetCount(allBundles
)),
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.
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.
562 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle start() functions"));
563 CFArrayApplyFunction(allBundles
,
564 CFRangeMake(0, CFArrayGetCount(allBundles
)),
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.
574 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle prime() functions"));
575 CFArrayApplyFunction(allBundles
,
576 CFRangeMake(0, CFArrayGetCount(allBundles
)),
581 if (arg
== NULL
&& (nLoaded
> 0)) {
582 CFRunLoopTimerRef timer
;
584 /* allocate a periodic event (to help show we're not blocking) */
585 timer
= CFRunLoopTimerCreate(NULL
, /* allocator */
586 CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */
590 timerCallback
, /* callout */
592 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
598 /* synchronize with parent process */
599 kill(getppid(), SIGTERM
);
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
609 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("starting plugin CFRunLoop"));
611 SCLog(_configd_verbose
, LOG_INFO
, CFSTR("what, no more work for the \"configd\" bundles?"));
619 pthread_attr_t tattr
;
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
);