]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/plugin_support.c
configd-24.tar.gz
[apple/configd.git] / configd.tproj / plugin_support.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <mach-o/dyld.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/param.h>
27 #include <dirent.h>
28 #include <unistd.h>
29 #include <NSSystemDirectories.h>
30
31 #include "configd.h"
32
33
34 /*
35 * Information maintained for each to-be-kicked registration.
36 */
37 typedef struct {
38 /*
39 * bundle paths
40 */
41 char bundle[MAXNAMLEN + 1]; /* bundle name */
42 char path [MAXPATHLEN]; /* bundle path */
43
44 /*
45 * entry points for initialization code.
46 */
47 SCDBundleStartRoutine_t start; /* address of start() routine */
48 SCDBundlePrimeRoutine_t prime; /* address of prime() routine */
49
50 } plugin, *pluginRef;
51
52 CFMutableArrayRef plugins;
53
54
55 /* exception handling functions */
56 typedef kern_return_t (*cer_func_t) (mach_port_t exception_port,
57 mach_port_t thread,
58 mach_port_t task,
59 exception_type_t exception,
60 exception_data_t code,
61 mach_msg_type_number_t codeCnt);
62
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,
67 int *flavor,
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);
72
73 typedef kern_return_t (*cer_identity_func_t) (mach_port_t exception_port,
74 mach_port_t thread,
75 mach_port_t task,
76 exception_type_t exception,
77 exception_data_t code,
78 mach_msg_type_number_t codeCnt,
79 int *flavor,
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);
84
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;
88
89 kern_return_t
90 catch_exception_raise(mach_port_t exception_port,
91 mach_port_t thread,
92 mach_port_t task,
93 exception_type_t exception,
94 exception_data_t code,
95 mach_msg_type_number_t codeCnt)
96 {
97
98 if (catch_exception_raise_func == NULL) {
99 /* The user hasn't defined catch_exception_raise in their binary */
100 abort();
101 }
102 return (*catch_exception_raise_func)(exception_port,
103 thread,
104 task,
105 exception,
106 code,
107 codeCnt);
108 }
109
110
111 kern_return_t
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,
116 int *flavor,
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)
121 {
122 if (catch_exception_raise_state_func == 0) {
123 /* The user hasn't defined catch_exception_raise_state in their binary */
124 abort();
125 }
126 return (*catch_exception_raise_state_func)(exception_port,
127 exception,
128 code,
129 codeCnt,
130 flavor,
131 old_state,
132 old_stateCnt,
133 new_state,
134 new_stateCnt);
135 }
136
137
138 kern_return_t
139 catch_exception_raise_state_identity(mach_port_t exception_port,
140 mach_port_t thread,
141 mach_port_t task,
142 exception_type_t exception,
143 exception_data_t code,
144 mach_msg_type_number_t codeCnt,
145 int *flavor,
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)
150 {
151 if (catch_exception_raise_identity_func == 0) {
152 /* The user hasn't defined catch_exception_raise_identify in their binary */
153 abort();
154 }
155 return (*catch_exception_raise_identity_func)(exception_port,
156 thread,
157 task,
158 exception,
159 code,
160 codeCnt,
161 flavor,
162 old_state,
163 old_stateCnt,
164 new_state,
165 new_stateCnt);
166 }
167
168
169 static boolean_t
170 bundleLoad(pluginRef info)
171 {
172 int len;
173 NSObjectFileImage image;
174 NSObjectFileImageReturnCode status;
175 NSModule module;
176 NSSymbol symbol;
177 unsigned long options;
178 char *bundleExe; /* full path of bundle executable */
179 struct stat sb;
180
181 /*
182 * allocate enough space for the bundle directory path, a "/" separator,
183 * the bundle name, and the (optional) "_debug" extension.
184 */
185
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);
191
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) {
197 goto load;
198 }
199
200 /* check for the "_debug" version */
201 strcat(bundleExe, BUNDLE_DEBUG_EXTENSION);
202 if (stat(bundleExe, &sb) == 0) {
203 goto load;
204 }
205
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) {
211 goto load;
212 }
213
214 /* check for the "_debug" version */
215 strcat(bundleExe, BUNDLE_DEBUG_EXTENSION);
216 if (stat(bundleExe, &sb) == 0) {
217 goto load;
218 }
219
220 SCDLog(LOG_ERR,
221 CFSTR("bundleLoad() failed, no executable for %s in %s"),
222 info->bundle,
223 info->path);
224 CFAllocatorDeallocate(NULL, bundleExe);
225 return FALSE;
226
227 load :
228
229 /* load the bundle */
230 SCDLog(LOG_DEBUG, CFSTR("loading %s"), bundleExe);
231 status = NSCreateObjectFileImageFromFile(bundleExe, &image);
232 if (status != NSObjectFileImageSuccess) {
233 char *err;
234
235 switch (status) {
236 case NSObjectFileImageFailure :
237 err = "NSObjectFileImageFailure";
238 break;
239 case NSObjectFileImageInappropriateFile :
240 err = "NSObjectFileImageInappropriateFile";
241 break;
242 case NSObjectFileImageArch :
243 err = "NSObjectFileImageArch";
244 break;
245 case NSObjectFileImageFormat :
246 err = "NSObjectFileImageFormat";
247 break;
248 case NSObjectFileImageAccess :
249 err = "NSObjectFileImageAccess";
250 break;
251 default :
252 err = "Unknown";
253 break;
254 }
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);
259 return FALSE;
260 }
261
262 options = NSLINKMODULE_OPTION_BINDNOW;
263 options |= NSLINKMODULE_OPTION_PRIVATE;
264 options |= NSLINKMODULE_OPTION_RETURN_ON_ERROR;
265 module = NSLinkModule(image, bundleExe, options);
266
267 if (module == NULL) {
268 NSLinkEditErrors c;
269 int errorNumber;
270 const char *fileName;
271 const char *errorString;
272
273 SCDLog(LOG_ERR, CFSTR("NSLinkModule() failed"));
274 SCDLog(LOG_ERR, CFSTR(" executable path = %s"), bundleExe);
275
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);
284
285 CFAllocatorDeallocate(NULL, bundleExe);
286 return FALSE;
287 }
288
289 CFAllocatorDeallocate(NULL, bundleExe);
290
291 /* identify the initialization functions */
292
293 symbol = NSLookupSymbolInModule(module, BUNDLE_ENTRY_POINT);
294 if (symbol) {
295 info->start = NSAddressOfSymbol(symbol);
296 }
297
298 symbol = NSLookupSymbolInModule(module, BUNDLE_ENTRY_POINT2);
299 if (symbol) {
300 info->prime = NSAddressOfSymbol(symbol);
301 }
302
303 if ((info->start == NULL) && (info->prime == NULL)) {
304 SCDLog(LOG_DEBUG, CFSTR(" no entry points"));
305 return FALSE;
306 }
307
308 /* identify any exception handling functions */
309
310 symbol = NSLookupSymbolInModule(module, "_catch_exception_raise");
311 if (symbol) {
312 catch_exception_raise_func = NSAddressOfSymbol(symbol);
313 }
314
315 symbol = NSLookupSymbolInModule(module, "_catch_exception_raise_state");
316 if (symbol) {
317 catch_exception_raise_state_func = NSAddressOfSymbol(symbol);
318 }
319
320 symbol = NSLookupSymbolInModule(module, "_catch_exception_raise_identity");
321 if (symbol) {
322 catch_exception_raise_identity_func = NSAddressOfSymbol(symbol);
323 }
324
325 return TRUE;
326 }
327
328
329 static void
330 bundleStart(const void *value, void *context)
331 {
332 CFDataRef data = (CFDataRef)value;
333 pluginRef info;
334
335 info = (pluginRef)CFDataGetBytePtr(data);
336 if (info->start) {
337 (*info->start)(info->bundle, info->path);
338 }
339 }
340
341
342 static void
343 bundlePrime(const void *value, void *context)
344 {
345 CFDataRef data = (CFDataRef)value;
346 pluginRef info;
347
348 info = (pluginRef)CFDataGetBytePtr(data);
349 if (info->prime) {
350 (*info->prime)(info->bundle, info->path);
351 }
352 }
353
354
355 static void
356 loadOne(const char *bundleDir, const char *bundleName)
357 {
358 CFMutableDataRef info;
359 pluginRef pluginInfo;
360 int len;
361
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 */
366 return;
367 }
368
369 len -= sizeof(BUNDLE_DIR_EXTENSION) - 1;
370 if (strcmp(&bundleName[len], BUNDLE_DIR_EXTENSION) != 0) {
371 /* if entry name doesn end with ".bundle" */
372 return;
373 }
374
375 info = CFDataCreateMutable(NULL, sizeof(plugin));
376 pluginInfo = (pluginRef)CFDataGetBytePtr(info);
377 pluginInfo->start = NULL;
378 pluginInfo->prime = NULL;
379
380 /* get (just) the bundle's name */
381 pluginInfo->bundle[0] = '\0';
382 (void) strncat(pluginInfo->bundle, bundleName, len);
383
384 /* get the bundle directory path */
385 (void) sprintf(pluginInfo->path, "%s/%s", bundleDir, bundleName);
386
387 /* load the bundle */
388 if (bundleLoad(pluginInfo)) {
389 SCDLog(LOG_INFO, CFSTR("%s loaded"), bundleName);
390 CFArrayAppendValue(plugins, info);
391 } else {
392 SCDLog(LOG_ERR, CFSTR("load of \"%s\" failed"), bundleName);
393 }
394 CFRelease(info);
395
396 return;
397 }
398
399
400 static void
401 loadAll(const char *bundleDir)
402 {
403 DIR *dirp;
404 struct dirent *dp;
405
406 dirp = opendir(bundleDir);
407 if (dirp == NULL) {
408 /* if no plugin directory */
409 return;
410 }
411
412 while ((dp = readdir(dirp)) != NULL) {
413 loadOne(bundleDir, dp->d_name);
414 }
415
416 closedir(dirp);
417 return;
418 }
419
420
421 void
422 timerCallback(CFRunLoopTimerRef timer, void *info)
423 {
424 SCDLog(LOG_INFO, CFSTR("the CFRunLoop is waiting for something to happen...."));
425 return;
426 }
427
428
429 void *
430 plugin_exec(void *arg)
431 {
432 NSSearchPathEnumerationState state;
433 char path[MAXPATHLEN];
434
435 /* keep track of loaded plugins */
436 plugins = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
437
438 if (arg == NULL) {
439 /*
440 * identify and load all plugins
441 */
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);
448 loadAll(path);
449 }
450
451 if (SCDOptionGet(NULL, kSCDOptionDebug)) {
452 SCDLog(LOG_DEBUG, CFSTR("searching for plugins in \".\""));
453 loadAll(".");
454 }
455 } else {
456 /*
457 * load the plugin specified on the command line
458 */
459 char *bn, *bd;
460
461 if ((bn = strrchr((char *)arg, '/')) != NULL) {
462 int len;
463
464 /* plug-in directory */
465 len = bn - (char *)arg;
466 if (len == 0)
467 len++; /* if plugin is in the root directory */
468
469 bd = CFAllocatorAllocate(NULL, len + 1, 0);
470 bd[0] = '\0';
471 (void) strncat(bd, (char *)arg, len);
472
473 /* plug-in name */
474 bn++; /* name starts just after trailing path separator */
475 } else {
476 /* plug-in (in current) directory */
477 bd = CFAllocatorAllocate(NULL, sizeof("."), 0);
478 (void) strcpy(bd, ".");
479
480 /* plug-in name */
481 bn = (char *)arg; /* no path separators */
482 }
483
484 loadOne(bd, bn);
485
486 CFAllocatorDeallocate(NULL, bd);
487
488 /* allocate a periodic event (to help show we're not blocking) */
489 if (CFArrayGetCount(plugins)) {
490 CFRunLoopTimerRef timer;
491
492 timer = CFRunLoopTimerCreate(NULL, /* allocator */
493 CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */
494 60.0, /* interval */
495 0, /* flags */
496 0, /* order */
497 timerCallback, /* callout */
498 NULL); /* context */
499 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
500 CFRelease(timer);
501 }
502 }
503
504 /*
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.
511 */
512 SCDLog(LOG_DEBUG, CFSTR("calling plugin start() functions"));
513 CFArrayApplyFunction(plugins,
514 CFRangeMake(0, CFArrayGetCount(plugins)),
515 bundleStart,
516 NULL);
517
518 /*
519 * execute each plugins prime() function which should initialize any
520 * configuration information and/or state in the cache.
521 */
522 SCDLog(LOG_DEBUG, CFSTR("calling plugin prime() functions"));
523 CFArrayApplyFunction(plugins,
524 CFRangeMake(0, CFArrayGetCount(plugins)),
525 bundlePrime,
526 NULL);
527
528 /*
529 * all plugins have been loaded and started.
530 */
531 CFRelease(plugins);
532
533 if (!SCDOptionGet(NULL, kSCDOptionDebug) && (arg == NULL)) {
534 /* synchronize with parent process */
535 kill(getppid(), SIGTERM);
536 }
537
538 /*
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
543 * private thread.
544 */
545 SCDLog(LOG_DEBUG, CFSTR("starting plugin CFRunLoop"));
546 CFRunLoopRun();
547 SCDLog(LOG_INFO, CFSTR("what, no more work for the \"configd\" plugins?"));
548 return NULL;
549 }
550
551
552 void
553 plugin_init()
554 {
555 pthread_attr_t tattr;
556 pthread_t tid;
557
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);
566
567 return;
568 }