2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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.
23 * @APPLE_LICENSE_HEADER_END@
27 * Modification History
29 * June 11, 2001 Allan Nathanson <ajn@apple.com>
30 * - start using CFBundle code
32 * June 1, 2001 Allan Nathanson <ajn@apple.com>
33 * - public API conversion
35 * May 26, 2000 Allan Nathanson <ajn@apple.com>
39 #include <mach-o/dyld.h>
40 #include <sys/types.h>
42 #include <sys/param.h>
45 #include <NSSystemDirectories.h>
48 #include <SystemConfiguration/SCDPlugin.h>
49 void _SCDPluginExecInit();
53 * path components, extensions, entry points, ...
55 #define BUNDLE_DIRECTORY "/SystemConfiguration" /* [/System/Library]/... */
56 #define BUNDLE_DIR_EXTENSION ".bundle"
59 static CFMutableArrayRef allBundles
= NULL
;
62 /* exception handling functions */
63 typedef kern_return_t (*cer_func_t
) (mach_port_t exception_port
,
66 exception_type_t exception
,
67 exception_data_t code
,
68 mach_msg_type_number_t codeCnt
);
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
,
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
);
80 typedef kern_return_t (*cer_identity_func_t
) (mach_port_t exception_port
,
83 exception_type_t exception
,
84 exception_data_t code
,
85 mach_msg_type_number_t codeCnt
,
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
);
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
;
97 catch_exception_raise(mach_port_t exception_port
,
100 exception_type_t exception
,
101 exception_data_t code
,
102 mach_msg_type_number_t codeCnt
)
105 if (catch_exception_raise_func
== NULL
) {
106 /* The user hasn't defined catch_exception_raise in their binary */
109 return (*catch_exception_raise_func
)(exception_port
,
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
,
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
)
129 if (catch_exception_raise_state_func
== 0) {
130 /* The user hasn't defined catch_exception_raise_state in their binary */
133 return (*catch_exception_raise_state_func
)(exception_port
,
146 catch_exception_raise_state_identity(mach_port_t exception_port
,
149 exception_type_t exception
,
150 exception_data_t code
,
151 mach_msg_type_number_t codeCnt
,
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
)
158 if (catch_exception_raise_identity_func
== 0) {
159 /* The user hasn't defined catch_exception_raise_identify in their binary */
162 return (*catch_exception_raise_identity_func
)(exception_port
,
177 shortBundleIdentifier(CFStringRef bundleID
)
179 CFIndex len
= CFStringGetLength(bundleID
);
181 CFStringRef shortID
= NULL
;
183 if (CFStringFindWithOptions(bundleID
,
188 range
.location
= range
.location
+ range
.length
;
189 range
.length
= len
- range
.location
;
190 shortID
= CFStringCreateWithSubstring(NULL
, bundleID
, range
);
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
;
205 SCDynamicStoreBundleLoadFunction load
;
207 CFIndex
*nLoaded
= (CFIndex
*)context
;
209 SCLog(TRUE
, LOG_DEBUG
, CFSTR("loading %@"), bundleID
);
211 bundleExclude
= CFSetContainsValue(_plugins_exclude
, bundleID
);
212 if (!bundleExclude
) {
213 CFStringRef shortID
= shortBundleIdentifier(bundleID
);
216 bundleExclude
= CFSetContainsValue(_plugins_exclude
, shortID
);
224 CFSTR("%@ load skipped"),
229 loaded
= CFBundleLoadExecutable(bundle
);
233 CFSTR("%@ load failed"),
238 if (!CFBundleIsExecutableLoaded(bundle
)) {
241 CFSTR("%@ executable not loaded"),
246 /* bump the count of loaded bundles */
247 *nLoaded
= *nLoaded
+ 1;
249 /* identify any exception handling functions */
251 func
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("catch_exception_raise"));
253 catch_exception_raise_func
= func
;
256 func
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("catch_exception_raise_state"));
258 catch_exception_raise_state_func
= func
;
261 func
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("catch_exception_raise_identity"));
263 catch_exception_raise_identity_func
= func
;
266 /* if defined, call the bundles load() function */
268 load
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("load"));
273 bundleVerbose
= CFSetContainsValue(_plugins_verbose
, bundleID
);
274 if (!bundleVerbose
) {
275 CFStringRef shortID
= shortBundleIdentifier(bundleID
);
278 bundleVerbose
= CFSetContainsValue(_plugins_verbose
, shortID
);
283 if (!bundleVerbose
) {
284 dict
= CFBundleGetInfoDictionary(bundle
);
285 if (isA_CFDictionary(dict
)) {
288 bVal
= CFDictionaryGetValue(dict
, kSCBundleVerbose
);
289 if (isA_CFBoolean(bVal
) && CFBooleanGetValue(bVal
)) {
290 bundleVerbose
= TRUE
;
295 (*load
)(bundle
, bundleVerbose
);
301 startBundle(const void *value
, void *context
) {
302 CFBundleRef bundle
= (CFBundleRef
)value
;
304 char bundleName
[MAXNAMLEN
+ 1];
305 char bundlePath
[MAXPATHLEN
];
307 CFDictionaryRef dict
;
310 SCDynamicStoreBundleStartFunction start
;
312 if (!CFBundleIsExecutableLoaded(bundle
)) {
316 start
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("start"));
321 dict
= isA_CFDictionary(CFBundleGetInfoDictionary(bundle
));
326 bundleURL
= CFBundleCopyBundleURL(bundle
);
331 ok
= CFURLGetFileSystemRepresentation(bundleURL
,
333 (UInt8
*)&bundlePath
,
335 CFRelease(bundleURL
);
340 cp
= strrchr(bundlePath
, '/');
347 /* check if this directory entry is a valid bundle name */
349 if (len
<= (int)sizeof(BUNDLE_DIR_EXTENSION
)) {
350 /* if entry name isn't long enough */
354 len
-= sizeof(BUNDLE_DIR_EXTENSION
) - 1;
355 if (strcmp(&cp
[len
], BUNDLE_DIR_EXTENSION
) != 0) {
356 /* if entry name doesn end with ".bundle" */
360 /* get (just) the bundle's name */
361 bundleName
[0] = '\0';
362 (void) strncat(bundleName
, cp
, len
);
364 (*start
)(bundleName
, bundlePath
);
370 primeBundle(const void *value
, void *context
) {
371 CFBundleRef bundle
= (CFBundleRef
)value
;
372 SCDynamicStoreBundlePrimeFunction prime
;
374 if (!CFBundleIsExecutableLoaded(bundle
)) {
378 prime
= CFBundleGetFunctionPointerForName(bundle
, CFSTR("prime"));
391 timerCallback(CFRunLoopTimerRef timer
, void *info
)
393 SCLog(_configd_verbose
,
395 CFSTR("the CFRunLoop is waiting for something to happen...."));
403 sortBundles(CFMutableArrayRef orig
)
405 CFMutableArrayRef
new;
407 new = CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
408 while (CFArrayGetCount(orig
) > 0) {
410 Boolean inserted
= FALSE
;
411 int nOrig
= CFArrayGetCount(orig
);
413 for (i
= 0; i
< nOrig
; i
++) {
414 CFBundleRef bundle1
= (CFBundleRef
)CFArrayGetValueAtIndex(orig
, i
);
415 CFStringRef bundleID1
= CFBundleGetIdentifier(bundle1
);
417 CFDictionaryRef dict
;
420 CFArrayRef
requires = NULL
;
422 dict
= isA_CFDictionary(CFBundleGetInfoDictionary(bundle1
));
424 requires = CFDictionaryGetValue(dict
, kSCBundleRequires
);
425 requires = isA_CFArray(requires);
427 if (bundleID1
== NULL
|| requires == NULL
) {
428 CFArrayInsertValueAtIndex(new, 0, bundle1
);
429 CFArrayRemoveValueAtIndex(orig
, i
);
433 count
= nRequires
= CFArrayGetCount(requires);
434 for (j
= 0; j
< nRequires
; j
++) {
437 CFStringRef r
= CFArrayGetValueAtIndex(requires, j
);
439 nNew
= CFArrayGetCount(new);
440 for (k
= 0; k
< nNew
; k
++) {
441 CFBundleRef bundle2
= (CFBundleRef
)CFArrayGetValueAtIndex(new, k
);
442 CFStringRef bundleID2
= CFBundleGetIdentifier(bundle2
);
444 if (bundleID2
&& CFEqual(bundleID2
, r
)) {
450 /* all dependencies are met, append */
451 CFArrayAppendValue(new, bundle1
);
452 CFArrayRemoveValueAtIndex(orig
, i
);
458 if (inserted
== FALSE
) {
459 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Bundles have circular dependency!!!"));
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
)));
468 /* new one is a sorted version of original */
471 CFArrayRemoveAllValues(orig
);
472 CFArrayAppendArray(orig
, new, CFRangeMake(0, CFArrayGetCount(new)));
480 plugin_exec(void *arg
)
484 /* keep track of bundles */
485 allBundles
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
487 /* allow plug-ins to exec child/helper processes */
488 _SCDPluginExecInit();
491 char path
[MAXPATHLEN
];
492 NSSearchPathEnumerationState state
;
495 * identify and load all bundles
497 state
= NSStartSearchPathEnumeration(NSLibraryDirectory
,
498 NSLocalDomainMask
|NSSystemDomainMask
);
499 while ((state
= NSGetNextSearchPathEnumeration(state
, path
))) {
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
,
510 bundles
= CFBundleCreateBundlesFromDirectory(NULL
, url
, CFSTR(".bundle"));
514 CFArrayAppendArray(allBundles
,
516 CFRangeMake(0, CFArrayGetCount(bundles
)));
521 sortBundles(allBundles
);
527 * load (only) the specified bundle
529 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
533 bundle
= CFBundleCreate(NULL
, url
);
535 CFArrayAppendValue(allBundles
, bundle
);
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.
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.
552 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle load() functions"));
553 CFArrayApplyFunction(allBundles
,
554 CFRangeMake(0, CFArrayGetCount(allBundles
)),
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.
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.
570 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle start() functions"));
571 CFArrayApplyFunction(allBundles
,
572 CFRangeMake(0, CFArrayGetCount(allBundles
)),
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.
582 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle prime() functions"));
583 CFArrayApplyFunction(allBundles
,
584 CFRangeMake(0, CFArrayGetCount(allBundles
)),
589 if (arg
== NULL
&& (nLoaded
> 0)) {
590 CFRunLoopTimerRef timer
;
592 /* allocate a periodic event (to help show we're not blocking) */
593 timer
= CFRunLoopTimerCreate(NULL
, /* allocator */
594 CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */
598 timerCallback
, /* callout */
600 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
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
612 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("starting plugin CFRunLoop"));
614 SCLog(_configd_verbose
, LOG_INFO
, CFSTR("what, no more work for the \"configd\" bundles?"));
623 pthread_attr_t tattr
;
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
);