2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * June 11, 2001 Allan Nathanson <ajn@apple.com>
28 * - start using CFBundle code
30 * June 1, 2001 Allan Nathanson <ajn@apple.com>
31 * - public API conversion
33 * May 26, 2000 Allan Nathanson <ajn@apple.com>
37 #include <mach-o/dyld.h>
38 #include <sys/types.h>
40 #include <sys/param.h>
43 #include <NSSystemDirectories.h>
46 #include <SystemConfiguration/SCDPlugin.h>
47 void _SCDPluginExecInit();
51 * path components, extensions, entry points, ...
53 #define BUNDLE_DIRECTORY "/SystemConfiguration" /* [/System/Library]/... */
54 #define BUNDLE_DIR_EXTENSION ".bundle"
57 static CFMutableArrayRef allBundles
= NULL
;
60 /* exception handling functions */
61 typedef kern_return_t (*cer_func_t
) (mach_port_t exception_port
,
64 exception_type_t exception
,
65 exception_data_t code
,
66 mach_msg_type_number_t codeCnt
);
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
,
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
);
78 typedef kern_return_t (*cer_identity_func_t
) (mach_port_t exception_port
,
81 exception_type_t exception
,
82 exception_data_t code
,
83 mach_msg_type_number_t codeCnt
,
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
);
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
;
95 catch_exception_raise(mach_port_t exception_port
,
98 exception_type_t exception
,
99 exception_data_t code
,
100 mach_msg_type_number_t codeCnt
)
103 if (catch_exception_raise_func
== NULL
) {
104 /* The user hasn't defined catch_exception_raise in their binary */
107 return (*catch_exception_raise_func
)(exception_port
,
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
,
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
)
127 if (catch_exception_raise_state_func
== 0) {
128 /* The user hasn't defined catch_exception_raise_state in their binary */
131 return (*catch_exception_raise_state_func
)(exception_port
,
144 catch_exception_raise_state_identity(mach_port_t exception_port
,
147 exception_type_t exception
,
148 exception_data_t code
,
149 mach_msg_type_number_t codeCnt
,
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
)
156 if (catch_exception_raise_identity_func
== 0) {
157 /* The user hasn't defined catch_exception_raise_identify in their binary */
160 return (*catch_exception_raise_identity_func
)(exception_port
,
175 shortBundleIdentifier(CFStringRef bundleID
)
177 CFIndex len
= CFStringGetLength(bundleID
);
179 CFStringRef shortID
= NULL
;
181 if (CFStringFindWithOptions(bundleID
,
186 range
.location
= range
.location
+ range
.length
;
187 range
.length
= len
- range
.location
;
188 shortID
= CFStringCreateWithSubstring(NULL
, bundleID
, range
);
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
;
203 SCDynamicStoreBundleLoadFunction load
;
205 CFIndex
*nLoaded
= (CFIndex
*)context
;
207 SCLog(TRUE
, LOG_DEBUG
, CFSTR("loading %@"), bundleID
);
209 bundleExclude
= CFSetContainsValue(_plugins_exclude
, bundleID
);
210 if (!bundleExclude
) {
211 CFStringRef shortID
= shortBundleIdentifier(bundleID
);
214 bundleExclude
= CFSetContainsValue(_plugins_exclude
, shortID
);
222 CFSTR("%@ load skipped"),
227 loaded
= CFBundleLoadExecutable(bundle
);
231 CFSTR("%@ load failed"),
236 if (!CFBundleIsExecutableLoaded(bundle
)) {
239 CFSTR("%@ executable not loaded"),
244 /* bump the count of loaded bundles */
245 *nLoaded
= *nLoaded
+ 1;
247 /* identify any exception handling functions */
249 func
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("catch_exception_raise"));
251 catch_exception_raise_func
= func
;
254 func
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("catch_exception_raise_state"));
256 catch_exception_raise_state_func
= func
;
259 func
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("catch_exception_raise_identity"));
261 catch_exception_raise_identity_func
= func
;
264 /* if defined, call the bundles load() function */
266 load
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("load"));
271 bundleVerbose
= CFSetContainsValue(_plugins_verbose
, bundleID
);
272 if (!bundleVerbose
) {
273 CFStringRef shortID
= shortBundleIdentifier(bundleID
);
276 bundleVerbose
= CFSetContainsValue(_plugins_verbose
, shortID
);
281 if (!bundleVerbose
) {
282 dict
= CFBundleGetInfoDictionary(bundle
);
283 if (isA_CFDictionary(dict
)) {
286 bVal
= CFDictionaryGetValue(dict
, kSCBundleVerbose
);
287 if (isA_CFBoolean(bVal
) && CFBooleanGetValue(bVal
)) {
288 bundleVerbose
= TRUE
;
293 (*load
)(bundle
, bundleVerbose
);
299 startBundle(const void *value
, void *context
) {
300 CFBundleRef bundle
= (CFBundleRef
)value
;
302 char bundleName
[MAXNAMLEN
+ 1];
303 char bundlePath
[MAXPATHLEN
];
305 CFDictionaryRef dict
;
308 SCDynamicStoreBundleStartFunction start
;
310 if (!CFBundleIsExecutableLoaded(bundle
)) {
314 start
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("start"));
319 dict
= isA_CFDictionary(CFBundleGetInfoDictionary(bundle
));
324 bundleURL
= CFBundleCopyBundleURL(bundle
);
329 ok
= CFURLGetFileSystemRepresentation(bundleURL
,
331 (UInt8
*)&bundlePath
,
333 CFRelease(bundleURL
);
338 cp
= strrchr(bundlePath
, '/');
345 /* check if this directory entry is a valid bundle name */
347 if (len
<= (int)sizeof(BUNDLE_DIR_EXTENSION
)) {
348 /* if entry name isn't long enough */
352 len
-= sizeof(BUNDLE_DIR_EXTENSION
) - 1;
353 if (strcmp(&cp
[len
], BUNDLE_DIR_EXTENSION
) != 0) {
354 /* if entry name doesn end with ".bundle" */
358 /* get (just) the bundle's name */
359 bundleName
[0] = '\0';
360 (void) strncat(bundleName
, cp
, len
);
362 (*start
)(bundleName
, bundlePath
);
368 primeBundle(const void *value
, void *context
) {
369 CFBundleRef bundle
= (CFBundleRef
)value
;
370 SCDynamicStoreBundlePrimeFunction prime
;
372 if (!CFBundleIsExecutableLoaded(bundle
)) {
376 prime
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("prime"));
389 timerCallback(CFRunLoopTimerRef timer
, void *info
)
391 SCLog(_configd_verbose
,
393 CFSTR("the CFRunLoop is waiting for something to happen...."));
401 sortBundles(CFMutableArrayRef orig
)
403 CFMutableArrayRef
new;
405 new = CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
406 while (CFArrayGetCount(orig
) > 0) {
408 Boolean inserted
= FALSE
;
409 int nOrig
= CFArrayGetCount(orig
);
411 for (i
= 0; i
< nOrig
; i
++) {
412 CFBundleRef bundle1
= (CFBundleRef
)CFArrayGetValueAtIndex(orig
, i
);
413 CFStringRef bundleID1
= CFBundleGetIdentifier(bundle1
);
415 CFDictionaryRef dict
;
418 CFArrayRef
requires = NULL
;
420 dict
= isA_CFDictionary(CFBundleGetInfoDictionary(bundle1
));
422 requires = CFDictionaryGetValue(dict
, kSCBundleRequires
);
423 requires = isA_CFArray(requires);
425 if (bundleID1
== NULL
|| requires == NULL
) {
426 CFArrayInsertValueAtIndex(new, 0, bundle1
);
427 CFArrayRemoveValueAtIndex(orig
, i
);
431 count
= nRequires
= CFArrayGetCount(requires);
432 for (j
= 0; j
< nRequires
; j
++) {
435 CFStringRef r
= CFArrayGetValueAtIndex(requires, j
);
437 nNew
= CFArrayGetCount(new);
438 for (k
= 0; k
< nNew
; k
++) {
439 CFBundleRef bundle2
= (CFBundleRef
)CFArrayGetValueAtIndex(new, k
);
440 CFStringRef bundleID2
= CFBundleGetIdentifier(bundle2
);
442 if (bundleID2
&& CFEqual(bundleID2
, r
)) {
448 /* all dependencies are met, append */
449 CFArrayAppendValue(new, bundle1
);
450 CFArrayRemoveValueAtIndex(orig
, i
);
456 if (inserted
== FALSE
) {
457 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Bundles have circular dependency!!!"));
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
)));
466 /* new one is a sorted version of original */
469 CFArrayRemoveAllValues(orig
);
470 CFArrayAppendArray(orig
, new, CFRangeMake(0, CFArrayGetCount(new)));
478 plugin_exec(void *arg
)
482 /* keep track of bundles */
483 allBundles
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
485 /* allow plug-ins to exec child/helper processes */
486 _SCDPluginExecInit();
489 char path
[MAXPATHLEN
];
490 NSSearchPathEnumerationState state
;
493 * identify and load all bundles
495 state
= NSStartSearchPathEnumeration(NSLibraryDirectory
,
496 NSLocalDomainMask
|NSSystemDomainMask
);
497 while ((state
= NSGetNextSearchPathEnumeration(state
, path
))) {
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
,
508 bundles
= CFBundleCreateBundlesFromDirectory(NULL
, url
, CFSTR(".bundle"));
512 CFArrayAppendArray(allBundles
,
514 CFRangeMake(0, CFArrayGetCount(bundles
)));
519 sortBundles(allBundles
);
525 * load (only) the specified bundle
527 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
531 bundle
= CFBundleCreate(NULL
, url
);
533 CFArrayAppendValue(allBundles
, bundle
);
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.
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.
550 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle load() functions"));
551 CFArrayApplyFunction(allBundles
,
552 CFRangeMake(0, CFArrayGetCount(allBundles
)),
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.
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.
568 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle start() functions"));
569 CFArrayApplyFunction(allBundles
,
570 CFRangeMake(0, CFArrayGetCount(allBundles
)),
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.
580 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle prime() functions"));
581 CFArrayApplyFunction(allBundles
,
582 CFRangeMake(0, CFArrayGetCount(allBundles
)),
587 if (arg
== NULL
&& (nLoaded
> 0)) {
588 CFRunLoopTimerRef timer
;
590 /* allocate a periodic event (to help show we're not blocking) */
591 timer
= CFRunLoopTimerCreate(NULL
, /* allocator */
592 CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */
596 timerCallback
, /* callout */
598 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
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
610 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("starting plugin CFRunLoop"));
612 SCLog(_configd_verbose
, LOG_INFO
, CFSTR("what, no more work for the \"configd\" bundles?"));
621 pthread_attr_t tattr
;
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
);