2 * Copyright (c) 2000 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@
23 #include <mach-o/dyld.h>
24 #include <sys/types.h>
26 #include <sys/param.h>
29 #include <NSSystemDirectories.h>
35 * Information maintained for each to-be-kicked registration.
41 char bundle
[MAXNAMLEN
+ 1]; /* bundle name */
42 char path
[MAXPATHLEN
]; /* bundle path */
45 * entry points for initialization code.
47 SCDBundleStartRoutine_t start
; /* address of start() routine */
48 SCDBundlePrimeRoutine_t prime
; /* address of prime() routine */
52 CFMutableArrayRef plugins
;
55 /* exception handling functions */
56 typedef kern_return_t (*cer_func_t
) (mach_port_t exception_port
,
59 exception_type_t exception
,
60 exception_data_t code
,
61 mach_msg_type_number_t codeCnt
);
63 typedef kern_return_t (*cer_state_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 thread_state_t old_state
,
69 mach_msg_type_number_t old_stateCnt
,
70 thread_state_t new_state
,
71 mach_msg_type_number_t
*new_stateCnt
);
73 typedef kern_return_t (*cer_identity_func_t
) (mach_port_t exception_port
,
76 exception_type_t exception
,
77 exception_data_t code
,
78 mach_msg_type_number_t codeCnt
,
80 thread_state_t old_state
,
81 mach_msg_type_number_t old_stateCnt
,
82 thread_state_t new_state
,
83 mach_msg_type_number_t
*new_stateCnt
);
85 static cer_func_t catch_exception_raise_func
= NULL
;
86 static cer_state_func_t catch_exception_raise_state_func
= NULL
;
87 static cer_identity_func_t catch_exception_raise_identity_func
= NULL
;
90 catch_exception_raise(mach_port_t exception_port
,
93 exception_type_t exception
,
94 exception_data_t code
,
95 mach_msg_type_number_t codeCnt
)
98 if (catch_exception_raise_func
== NULL
) {
99 /* The user hasn't defined catch_exception_raise in their binary */
102 return (*catch_exception_raise_func
)(exception_port
,
112 catch_exception_raise_state(mach_port_t exception_port
,
113 exception_type_t exception
,
114 exception_data_t code
,
115 mach_msg_type_number_t codeCnt
,
117 thread_state_t old_state
,
118 mach_msg_type_number_t old_stateCnt
,
119 thread_state_t new_state
,
120 mach_msg_type_number_t
*new_stateCnt
)
122 if (catch_exception_raise_state_func
== 0) {
123 /* The user hasn't defined catch_exception_raise_state in their binary */
126 return (*catch_exception_raise_state_func
)(exception_port
,
139 catch_exception_raise_state_identity(mach_port_t exception_port
,
142 exception_type_t exception
,
143 exception_data_t code
,
144 mach_msg_type_number_t codeCnt
,
146 thread_state_t old_state
,
147 mach_msg_type_number_t old_stateCnt
,
148 thread_state_t new_state
,
149 mach_msg_type_number_t
*new_stateCnt
)
151 if (catch_exception_raise_identity_func
== 0) {
152 /* The user hasn't defined catch_exception_raise_identify in their binary */
155 return (*catch_exception_raise_identity_func
)(exception_port
,
170 bundleLoad(pluginRef info
)
173 NSObjectFileImage image
;
174 NSObjectFileImageReturnCode status
;
177 unsigned long options
;
178 char *bundleExe
; /* full path of bundle executable */
182 * allocate enough space for the bundle directory path, a "/" separator,
183 * the bundle name, and the (optional) "_debug" extension.
186 len
= strlen(info
->path
); /* path */
187 len
+= sizeof(BUNDLE_NEW_SUBDIR
) - 1; /* "/" or "/Contents/MacOS/" */
188 len
+= strlen(info
->bundle
); /* bundle name */
189 len
+= sizeof(BUNDLE_DEBUG_EXTENSION
); /* "_debug" (and NUL) */
190 bundleExe
= CFAllocatorAllocate(NULL
, len
, 0);
192 /* check for the (old layout) bundle executable path */
193 strcpy(bundleExe
, info
->path
);
194 strcat(bundleExe
, BUNDLE_OLD_SUBDIR
);
195 strcat(bundleExe
, info
->bundle
);
196 if (stat(bundleExe
, &sb
) == 0) {
200 /* check for the "_debug" version */
201 strcat(bundleExe
, BUNDLE_DEBUG_EXTENSION
);
202 if (stat(bundleExe
, &sb
) == 0) {
206 /* check for the (new layout) bundle executable path */
207 strcpy(bundleExe
, info
->path
);
208 strcat(bundleExe
, BUNDLE_NEW_SUBDIR
);
209 strcat(bundleExe
, info
->bundle
);
210 if (stat(bundleExe
, &sb
) == 0) {
214 /* check for the "_debug" version */
215 strcat(bundleExe
, BUNDLE_DEBUG_EXTENSION
);
216 if (stat(bundleExe
, &sb
) == 0) {
221 CFSTR("bundleLoad() failed, no executable for %s in %s"),
224 CFAllocatorDeallocate(NULL
, bundleExe
);
229 /* load the bundle */
230 SCDLog(LOG_DEBUG
, CFSTR("loading %s"), bundleExe
);
231 status
= NSCreateObjectFileImageFromFile(bundleExe
, &image
);
232 if (status
!= NSObjectFileImageSuccess
) {
236 case NSObjectFileImageFailure
:
237 err
= "NSObjectFileImageFailure";
239 case NSObjectFileImageInappropriateFile
:
240 err
= "NSObjectFileImageInappropriateFile";
242 case NSObjectFileImageArch
:
243 err
= "NSObjectFileImageArch";
245 case NSObjectFileImageFormat
:
246 err
= "NSObjectFileImageFormat";
248 case NSObjectFileImageAccess
:
249 err
= "NSObjectFileImageAccess";
255 SCDLog(LOG_ERR
, CFSTR("NSCreateObjectFileImageFromFile() failed"));
256 SCDLog(LOG_ERR
, CFSTR(" executable path = %s"), bundleExe
);
257 SCDLog(LOG_ERR
, CFSTR(" error status = %s"), err
);
258 CFAllocatorDeallocate(NULL
, bundleExe
);
262 options
= NSLINKMODULE_OPTION_BINDNOW
;
263 options
|= NSLINKMODULE_OPTION_PRIVATE
;
264 options
|= NSLINKMODULE_OPTION_RETURN_ON_ERROR
;
265 module = NSLinkModule(image
, bundleExe
, options
);
267 if (module == NULL
) {
270 const char *fileName
;
271 const char *errorString
;
273 SCDLog(LOG_ERR
, CFSTR("NSLinkModule() failed"));
274 SCDLog(LOG_ERR
, CFSTR(" executable path = %s"), bundleExe
);
276 /* collect and report the details */
277 NSLinkEditError(&c
, &errorNumber
, &fileName
, &errorString
);
278 SCDLog(LOG_ERR
, CFSTR(" NSLinkEditErrors = %d"), (int)c
);
279 SCDLog(LOG_ERR
, CFSTR(" errorNumber = %d"), errorNumber
);
280 if((fileName
!= NULL
) && (*fileName
!= '\0'))
281 SCDLog(LOG_ERR
, CFSTR(" fileName = %s"), fileName
);
282 if((errorString
!= NULL
) && (*errorString
!= '\0'))
283 SCDLog(LOG_ERR
, CFSTR(" errorString = %s"), errorString
);
285 CFAllocatorDeallocate(NULL
, bundleExe
);
289 CFAllocatorDeallocate(NULL
, bundleExe
);
291 /* identify the initialization functions */
293 symbol
= NSLookupSymbolInModule(module, BUNDLE_ENTRY_POINT
);
295 info
->start
= NSAddressOfSymbol(symbol
);
298 symbol
= NSLookupSymbolInModule(module, BUNDLE_ENTRY_POINT2
);
300 info
->prime
= NSAddressOfSymbol(symbol
);
303 if ((info
->start
== NULL
) && (info
->prime
== NULL
)) {
304 SCDLog(LOG_DEBUG
, CFSTR(" no entry points"));
308 /* identify any exception handling functions */
310 symbol
= NSLookupSymbolInModule(module, "_catch_exception_raise");
312 catch_exception_raise_func
= NSAddressOfSymbol(symbol
);
315 symbol
= NSLookupSymbolInModule(module, "_catch_exception_raise_state");
317 catch_exception_raise_state_func
= NSAddressOfSymbol(symbol
);
320 symbol
= NSLookupSymbolInModule(module, "_catch_exception_raise_identity");
322 catch_exception_raise_identity_func
= NSAddressOfSymbol(symbol
);
330 bundleStart(const void *value
, void *context
)
332 CFDataRef data
= (CFDataRef
)value
;
335 info
= (pluginRef
)CFDataGetBytePtr(data
);
337 (*info
->start
)(info
->bundle
, info
->path
);
343 bundlePrime(const void *value
, void *context
)
345 CFDataRef data
= (CFDataRef
)value
;
348 info
= (pluginRef
)CFDataGetBytePtr(data
);
350 (*info
->prime
)(info
->bundle
, info
->path
);
356 loadOne(const char *bundleDir
, const char *bundleName
)
358 CFMutableDataRef info
;
359 pluginRef pluginInfo
;
362 /* check if this directory entry is a valid bundle name */
363 len
= strlen(bundleName
);
364 if (len
<= sizeof(BUNDLE_DIR_EXTENSION
)) {
365 /* if entry name isn't long enough */
369 len
-= sizeof(BUNDLE_DIR_EXTENSION
) - 1;
370 if (strcmp(&bundleName
[len
], BUNDLE_DIR_EXTENSION
) != 0) {
371 /* if entry name doesn end with ".bundle" */
375 info
= CFDataCreateMutable(NULL
, sizeof(plugin
));
376 pluginInfo
= (pluginRef
)CFDataGetBytePtr(info
);
377 pluginInfo
->start
= NULL
;
378 pluginInfo
->prime
= NULL
;
380 /* get (just) the bundle's name */
381 pluginInfo
->bundle
[0] = '\0';
382 (void) strncat(pluginInfo
->bundle
, bundleName
, len
);
384 /* get the bundle directory path */
385 (void) sprintf(pluginInfo
->path
, "%s/%s", bundleDir
, bundleName
);
387 /* load the bundle */
388 if (bundleLoad(pluginInfo
)) {
389 SCDLog(LOG_INFO
, CFSTR("%s loaded"), bundleName
);
390 CFArrayAppendValue(plugins
, info
);
392 SCDLog(LOG_ERR
, CFSTR("load of \"%s\" failed"), bundleName
);
401 loadAll(const char *bundleDir
)
406 dirp
= opendir(bundleDir
);
408 /* if no plugin directory */
412 while ((dp
= readdir(dirp
)) != NULL
) {
413 loadOne(bundleDir
, dp
->d_name
);
422 timerCallback(CFRunLoopTimerRef timer
, void *info
)
424 SCDLog(LOG_INFO
, CFSTR("the CFRunLoop is waiting for something to happen...."));
430 plugin_exec(void *arg
)
432 NSSearchPathEnumerationState state
;
433 char path
[MAXPATHLEN
];
435 /* keep track of loaded plugins */
436 plugins
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
440 * identify and load all plugins
442 state
= NSStartSearchPathEnumeration(NSLibraryDirectory
,
443 NSLocalDomainMask
|NSSystemDomainMask
);
444 while ((state
= NSGetNextSearchPathEnumeration(state
, path
))) {
445 /* load any available plugins */
446 strcat(path
, BUNDLE_DIRECTORY
);
447 SCDLog(LOG_DEBUG
, CFSTR("searching for plugins in \"%s\""), path
);
451 if (SCDOptionGet(NULL
, kSCDOptionDebug
)) {
452 SCDLog(LOG_DEBUG
, CFSTR("searching for plugins in \".\""));
457 * load the plugin specified on the command line
461 if ((bn
= strrchr((char *)arg
, '/')) != NULL
) {
464 /* plug-in directory */
465 len
= bn
- (char *)arg
;
467 len
++; /* if plugin is in the root directory */
469 bd
= CFAllocatorAllocate(NULL
, len
+ 1, 0);
471 (void) strncat(bd
, (char *)arg
, len
);
474 bn
++; /* name starts just after trailing path separator */
476 /* plug-in (in current) directory */
477 bd
= CFAllocatorAllocate(NULL
, sizeof("."), 0);
478 (void) strcpy(bd
, ".");
481 bn
= (char *)arg
; /* no path separators */
486 CFAllocatorDeallocate(NULL
, bd
);
488 /* allocate a periodic event (to help show we're not blocking) */
489 if (CFArrayGetCount(plugins
)) {
490 CFRunLoopTimerRef timer
;
492 timer
= CFRunLoopTimerCreate(NULL
, /* allocator */
493 CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */
497 timerCallback
, /* callout */
499 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
505 * execute each plugins start() function which should initialize any
506 * variables, open any sessions with "configd", and register any needed
507 * notifications. Establishing initial information in the cache should
508 * be deferred until the prime() initialization function so that any
509 * plug-ins which want to receive a notification that the data has
510 * changed will have an opportunity to install a notification handler.
512 SCDLog(LOG_DEBUG
, CFSTR("calling plugin start() functions"));
513 CFArrayApplyFunction(plugins
,
514 CFRangeMake(0, CFArrayGetCount(plugins
)),
519 * execute each plugins prime() function which should initialize any
520 * configuration information and/or state in the cache.
522 SCDLog(LOG_DEBUG
, CFSTR("calling plugin prime() functions"));
523 CFArrayApplyFunction(plugins
,
524 CFRangeMake(0, CFArrayGetCount(plugins
)),
529 * all plugins have been loaded and started.
533 if (!SCDOptionGet(NULL
, kSCDOptionDebug
) && (arg
== NULL
)) {
534 /* synchronize with parent process */
535 kill(getppid(), SIGTERM
);
539 * The assumption is that each loaded plugin will establish CFMachPortRef,
540 * CFSocketRef, and CFRunLoopTimerRef input sources to handle any events
541 * and register these sources with this threads run loop. If the plugin
542 * needs to wait and/or block at any time it should do so only in its a
545 SCDLog(LOG_DEBUG
, CFSTR("starting plugin CFRunLoop"));
547 SCDLog(LOG_INFO
, CFSTR("what, no more work for the \"configd\" plugins?"));
555 pthread_attr_t tattr
;
558 SCDLog(LOG_DEBUG
, CFSTR("Starting thread for plug-ins..."));
559 pthread_attr_init(&tattr
);
560 pthread_attr_setscope(&tattr
, PTHREAD_SCOPE_SYSTEM
);
561 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
562 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
563 pthread_create(&tid
, &tattr
, plugin_exec
, NULL
);
564 pthread_attr_destroy(&tattr
);
565 SCDLog(LOG_DEBUG
, CFSTR(" thread id=0x%08x"), tid
);