]>
Commit | Line | Data |
---|---|---|
5958d7c0 | 1 | /* |
d6c893b2 | 2 | * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. |
5958d7c0 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
009ee3c6 | 5 | * |
009ee3c6 A |
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 | |
11 | * file. | |
12 | * | |
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 | |
5958d7c0 A |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
009ee3c6 A |
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. | |
20 | * | |
5958d7c0 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
0fae82ee A |
24 | /* |
25 | * Modification History | |
26 | * | |
dbf6a266 A |
27 | * October 30, 2003 Allan Nathanson <ajn@apple.com> |
28 | * - add plugin "stop()" function support | |
29 | * | |
0fae82ee A |
30 | * June 11, 2001 Allan Nathanson <ajn@apple.com> |
31 | * - start using CFBundle code | |
32 | * | |
33 | * June 1, 2001 Allan Nathanson <ajn@apple.com> | |
34 | * - public API conversion | |
35 | * | |
36 | * May 26, 2000 Allan Nathanson <ajn@apple.com> | |
37 | * - initial revision | |
38 | */ | |
39 | ||
5958d7c0 A |
40 | #include <mach-o/dyld.h> |
41 | #include <sys/types.h> | |
42 | #include <sys/stat.h> | |
43 | #include <sys/param.h> | |
44 | #include <dirent.h> | |
dbf6a266 | 45 | #include <sysexits.h> |
5958d7c0 A |
46 | #include <unistd.h> |
47 | #include <NSSystemDirectories.h> | |
48 | ||
49 | #include "configd.h" | |
dbf6a266 | 50 | #include "configd_server.h" |
0fae82ee | 51 | #include <SystemConfiguration/SCDPlugin.h> |
a5f60add | 52 | void _SCDPluginExecInit(); |
5958d7c0 A |
53 | |
54 | ||
55 | /* | |
0fae82ee | 56 | * path components, extensions, entry points, ... |
5958d7c0 | 57 | */ |
0fae82ee A |
58 | #define BUNDLE_DIRECTORY "/SystemConfiguration" /* [/System/Library]/... */ |
59 | #define BUNDLE_DIR_EXTENSION ".bundle" | |
5958d7c0 | 60 | |
5958d7c0 | 61 | |
dbf6a266 A |
62 | typedef struct { |
63 | CFBundleRef bundle; | |
64 | Boolean loaded; | |
65 | Boolean builtin; | |
66 | Boolean verbose; | |
67 | SCDynamicStoreBundleLoadFunction load; | |
68 | SCDynamicStoreBundleStartFunction start; | |
69 | SCDynamicStoreBundlePrimeFunction prime; | |
70 | SCDynamicStoreBundleStopFunction stop; | |
71 | } *bundleInfoRef; | |
72 | ||
73 | ||
74 | // all loaded bundles | |
75 | static CFMutableArrayRef allBundles = NULL; | |
76 | ||
77 | // exiting bundles | |
78 | static CFMutableDictionaryRef exiting = NULL; | |
79 | ||
80 | // plugin CFRunLoopRef | |
81 | static CFRunLoopRef plugin_runLoop = NULL; | |
82 | ||
83 | ||
84 | #ifdef ppc | |
ba83da55 A |
85 | extern SCDynamicStoreBundleLoadFunction load_ATconfig; |
86 | extern SCDynamicStoreBundleStopFunction stop_ATconfig; | |
dbf6a266 A |
87 | #endif /* ppc */ |
88 | extern SCDynamicStoreBundleLoadFunction load_IPMonitor; | |
89 | extern SCDynamicStoreBundlePrimeFunction prime_IPMonitor; | |
90 | extern SCDynamicStoreBundleLoadFunction load_InterfaceNamer; | |
91 | extern SCDynamicStoreBundleLoadFunction load_KernelEventMonitor; | |
92 | extern SCDynamicStoreBundlePrimeFunction prime_KernelEventMonitor; | |
93 | extern SCDynamicStoreBundleLoadFunction load_Kicker; | |
94 | extern SCDynamicStoreBundleLoadFunction load_LinkConfiguration; | |
95 | extern SCDynamicStoreBundleLoadFunction load_PreferencesMonitor; | |
96 | extern SCDynamicStoreBundlePrimeFunction prime_PreferencesMonitor; | |
97 | extern SCDynamicStoreBundleStopFunction stop_PreferencesMonitor; | |
98 | ||
99 | ||
100 | typedef struct { | |
101 | const CFStringRef bundleID; | |
102 | const void *load; // SCDynamicStoreBundleLoadFunction | |
103 | const void *start; // SCDynamicStoreBundleStartFunction | |
104 | const void *prime; // SCDynamicStoreBundlePrimeFunction | |
105 | const void *stop; // SCDynamicStoreBundleStopFunction | |
106 | } builtin, *builtinRef; | |
107 | ||
108 | ||
109 | static const builtin builtin_plugins[] = { | |
110 | #ifdef ppc | |
ba83da55 A |
111 | { |
112 | CFSTR("com.apple.SystemConfiguration.ATconfig"), | |
113 | &load_ATconfig, | |
114 | NULL, | |
115 | NULL, | |
116 | &stop_ATconfig | |
117 | }, | |
dbf6a266 A |
118 | #endif /* ppc */ |
119 | { | |
120 | CFSTR("com.apple.SystemConfiguration.IPMonitor"), | |
121 | &load_IPMonitor, | |
122 | NULL, | |
123 | &prime_IPMonitor, | |
124 | NULL | |
125 | }, | |
126 | { | |
127 | CFSTR("com.apple.SystemConfiguration.InterfaceNamer"), | |
128 | &load_InterfaceNamer, | |
129 | NULL, | |
130 | NULL, | |
131 | NULL | |
132 | }, | |
133 | { | |
134 | CFSTR("com.apple.SystemConfiguration.KernelEventMonitor"), | |
135 | &load_KernelEventMonitor, | |
136 | NULL, | |
137 | &prime_KernelEventMonitor, | |
138 | NULL | |
139 | }, | |
140 | { | |
141 | CFSTR("com.apple.SystemConfiguration.Kicker"), | |
142 | &load_Kicker, | |
143 | NULL, | |
144 | NULL, | |
145 | NULL | |
146 | }, | |
147 | { | |
148 | CFSTR("com.apple.SystemConfiguration.LinkConfiguration"), | |
149 | &load_LinkConfiguration, | |
150 | NULL, | |
151 | NULL, | |
152 | NULL | |
153 | }, | |
154 | { | |
155 | CFSTR("com.apple.SystemConfiguration.PreferencesMonitor"), | |
156 | &load_PreferencesMonitor, | |
157 | NULL, | |
158 | &prime_PreferencesMonitor, | |
159 | &stop_PreferencesMonitor | |
5958d7c0 | 160 | } |
dbf6a266 | 161 | }; |
5958d7c0 A |
162 | |
163 | ||
dbf6a266 A |
164 | static void |
165 | addBundle(CFBundleRef bundle) | |
5958d7c0 | 166 | { |
dbf6a266 A |
167 | CFDictionaryRef bundleDict; |
168 | bundleInfoRef bundleInfo; | |
d6c893b2 | 169 | |
dbf6a266 A |
170 | bundleInfo = CFAllocatorAllocate(NULL, sizeof(*bundleInfo), 0); |
171 | bundleInfo->bundle = (CFBundleRef)CFRetain(bundle); | |
172 | bundleInfo->loaded = FALSE; | |
173 | bundleInfo->builtin = FALSE; | |
174 | bundleInfo->verbose = FALSE; | |
175 | bundleInfo->load = NULL; | |
176 | bundleInfo->start = NULL; | |
177 | bundleInfo->prime = NULL; | |
178 | bundleInfo->stop = NULL; | |
d6c893b2 | 179 | |
dbf6a266 A |
180 | bundleDict = CFBundleGetInfoDictionary(bundle); |
181 | if (isA_CFDictionary(bundleDict)) { | |
182 | CFBooleanRef bVal; | |
183 | ||
184 | bVal = CFDictionaryGetValue(bundleDict, kSCBundleIsBuiltinKey); | |
185 | if (isA_CFBoolean(bVal) && CFBooleanGetValue(bVal)) { | |
186 | bundleInfo->builtin = TRUE; | |
187 | } | |
5958d7c0 | 188 | |
dbf6a266 A |
189 | bVal = CFDictionaryGetValue(bundleDict, kSCBundleVerboseKey); |
190 | if (isA_CFBoolean(bVal) && CFBooleanGetValue(bVal)) { | |
191 | bundleInfo->verbose = TRUE; | |
192 | } | |
193 | } | |
5958d7c0 | 194 | |
dbf6a266 A |
195 | CFArrayAppendValue(allBundles, bundleInfo); |
196 | return; | |
5958d7c0 A |
197 | } |
198 | ||
199 | ||
0fae82ee A |
200 | static CFStringRef |
201 | shortBundleIdentifier(CFStringRef bundleID) | |
5958d7c0 | 202 | { |
dbf6a266 A |
203 | CFIndex len = CFStringGetLength(bundleID); |
204 | CFRange range; | |
0fae82ee A |
205 | CFStringRef shortID = NULL; |
206 | ||
207 | if (CFStringFindWithOptions(bundleID, | |
208 | CFSTR("."), | |
209 | CFRangeMake(0, len), | |
210 | kCFCompareBackwards, | |
211 | &range)) { | |
212 | range.location = range.location + range.length; | |
213 | range.length = len - range.location; | |
214 | shortID = CFStringCreateWithSubstring(NULL, bundleID, range); | |
215 | } | |
5958d7c0 | 216 | |
0fae82ee A |
217 | return shortID; |
218 | } | |
5958d7c0 | 219 | |
5958d7c0 | 220 | |
dbf6a266 A |
221 | static void * |
222 | getBundleSymbol(CFBundleRef bundle, CFStringRef functionName, CFStringRef shortID) | |
223 | { | |
224 | void *func; | |
0fae82ee | 225 | |
dbf6a266 A |
226 | // search for load(), start(), prime(), stop(), ... |
227 | func = CFBundleGetFunctionPointerForName(bundle, functionName); | |
228 | if (func != NULL) { | |
229 | return func; | |
5958d7c0 A |
230 | } |
231 | ||
dbf6a266 A |
232 | if (shortID != NULL) { |
233 | CFStringRef altFunctionName; | |
234 | ||
235 | // search for load_XXX(), ... | |
236 | altFunctionName = CFStringCreateWithFormat(NULL, | |
237 | NULL, | |
238 | CFSTR("%@_%@"), | |
239 | functionName, | |
240 | shortID); | |
241 | func = CFBundleGetFunctionPointerForName(bundle, altFunctionName); | |
242 | CFRelease(altFunctionName); | |
5958d7c0 A |
243 | } |
244 | ||
dbf6a266 A |
245 | return func; |
246 | } | |
5958d7c0 | 247 | |
dbf6a266 A |
248 | |
249 | static void | |
250 | loadBundle(const void *value, void *context) { | |
251 | CFStringRef bundleID; | |
252 | bundleInfoRef bundleInfo = (bundleInfoRef)value; | |
253 | Boolean bundleExclude; | |
254 | CFIndex *nLoaded = (CFIndex *)context; | |
255 | CFStringRef shortID; | |
256 | ||
257 | bundleID = CFBundleGetIdentifier(bundleInfo->bundle); | |
258 | if (bundleID == NULL) { | |
259 | // sorry, no bundles without a bundle identifier | |
260 | SCLog(TRUE, LOG_DEBUG, CFSTR("skipped %@"), bundleInfo->bundle); | |
0fae82ee A |
261 | return; |
262 | } | |
5958d7c0 | 263 | |
dbf6a266 | 264 | shortID = shortBundleIdentifier(bundleID); |
5958d7c0 | 265 | |
dbf6a266 A |
266 | bundleExclude = CFSetContainsValue(_plugins_exclude, bundleID); |
267 | if (bundleExclude) { | |
268 | if (shortID != NULL) { | |
269 | bundleExclude = CFSetContainsValue(_plugins_exclude, shortID); | |
270 | } | |
5958d7c0 A |
271 | } |
272 | ||
dbf6a266 A |
273 | if (bundleExclude) { |
274 | // sorry, this bundle has been excluded | |
275 | SCLog(TRUE, LOG_DEBUG, CFSTR("excluded %@"), bundleID); | |
276 | goto done; | |
5958d7c0 A |
277 | } |
278 | ||
dbf6a266 A |
279 | if (!bundleInfo->verbose) { |
280 | bundleInfo->verbose = CFSetContainsValue(_plugins_verbose, bundleID); | |
281 | if (!bundleInfo->verbose) { | |
282 | if (shortID != NULL) { | |
283 | bundleInfo->verbose = CFSetContainsValue(_plugins_verbose, shortID); | |
284 | } | |
285 | } | |
5958d7c0 A |
286 | } |
287 | ||
dbf6a266 A |
288 | if (bundleInfo->builtin) { |
289 | int i; | |
5958d7c0 | 290 | |
dbf6a266 | 291 | SCLog(TRUE, LOG_DEBUG, CFSTR("adding %@"), bundleID); |
5958d7c0 | 292 | |
dbf6a266 A |
293 | for (i = 0; i < sizeof(builtin_plugins)/sizeof(builtin_plugins[0]); i++) { |
294 | if (CFEqual(bundleID, builtin_plugins[i].bundleID)) { | |
295 | bundleInfo->load = builtin_plugins[i].load; | |
296 | bundleInfo->start = builtin_plugins[i].start; | |
297 | bundleInfo->prime = builtin_plugins[i].prime; | |
298 | bundleInfo->stop = builtin_plugins[i].stop; | |
299 | break; | |
300 | } | |
301 | } | |
302 | } else { | |
303 | SCLog(TRUE, LOG_DEBUG, CFSTR("loading %@"), bundleID); | |
5958d7c0 | 304 | |
dbf6a266 A |
305 | if (!CFBundleLoadExecutable(bundleInfo->bundle)) { |
306 | SCLog(TRUE, LOG_NOTICE, CFSTR("%@ load failed"), bundleID); | |
307 | goto done; | |
0fae82ee | 308 | } |
dbf6a266 A |
309 | |
310 | // get bundle entry points | |
311 | bundleInfo->load = getBundleSymbol(bundleInfo->bundle, CFSTR("load" ), shortID); | |
312 | bundleInfo->start = getBundleSymbol(bundleInfo->bundle, CFSTR("start"), shortID); | |
313 | bundleInfo->prime = getBundleSymbol(bundleInfo->bundle, CFSTR("prime"), shortID); | |
314 | bundleInfo->stop = getBundleSymbol(bundleInfo->bundle, CFSTR("stop" ), shortID); | |
5958d7c0 A |
315 | } |
316 | ||
dbf6a266 A |
317 | /* mark this bundle as having been loaded */ |
318 | bundleInfo->loaded = TRUE; | |
5958d7c0 | 319 | |
dbf6a266 A |
320 | /* bump the count of loaded bundles */ |
321 | *nLoaded = *nLoaded + 1; | |
322 | ||
323 | done : | |
5958d7c0 | 324 | |
dbf6a266 | 325 | if (shortID != NULL) CFRelease(shortID); |
0fae82ee | 326 | return; |
5958d7c0 A |
327 | } |
328 | ||
329 | ||
dbf6a266 A |
330 | void |
331 | callLoadFunction(const void *value, void *context) { | |
332 | bundleInfoRef bundleInfo = (bundleInfoRef)value; | |
0fae82ee | 333 | |
dbf6a266 | 334 | if (!bundleInfo->loaded) { |
0fae82ee | 335 | return; |
5958d7c0 | 336 | } |
5958d7c0 | 337 | |
dbf6a266 A |
338 | if (bundleInfo->load == NULL) { |
339 | // if no load() function | |
340 | return; | |
341 | } | |
342 | ||
343 | (*bundleInfo->load)(bundleInfo->bundle, bundleInfo->verbose); | |
344 | return; | |
345 | } | |
346 | ||
347 | ||
348 | void | |
349 | callStartFunction(const void *value, void *context) { | |
350 | bundleInfoRef bundleInfo = (bundleInfoRef)value; | |
351 | CFURLRef bundleURL; | |
352 | char bundleName[MAXNAMLEN + 1]; | |
353 | char bundlePath[MAXPATHLEN]; | |
354 | char *cp; | |
355 | int len; | |
356 | Boolean ok; | |
357 | ||
358 | if (!bundleInfo->loaded) { | |
0fae82ee A |
359 | return; |
360 | } | |
5958d7c0 | 361 | |
dbf6a266 A |
362 | if (bundleInfo->start == NULL) { |
363 | // if no start() function | |
0fae82ee A |
364 | return; |
365 | } | |
5958d7c0 | 366 | |
dbf6a266 A |
367 | bundleURL = CFBundleCopyBundleURL(bundleInfo->bundle); |
368 | if (bundleURL == NULL) { | |
0fae82ee | 369 | return; |
5958d7c0 | 370 | } |
5958d7c0 | 371 | |
0fae82ee A |
372 | ok = CFURLGetFileSystemRepresentation(bundleURL, |
373 | TRUE, | |
374 | (UInt8 *)&bundlePath, | |
375 | sizeof(bundlePath)); | |
376 | CFRelease(bundleURL); | |
377 | if (!ok) { | |
378 | return; | |
379 | } | |
5958d7c0 | 380 | |
0fae82ee A |
381 | cp = strrchr(bundlePath, '/'); |
382 | if (cp) { | |
383 | cp++; | |
384 | } else { | |
385 | cp = bundlePath; | |
386 | } | |
5958d7c0 A |
387 | |
388 | /* check if this directory entry is a valid bundle name */ | |
0fae82ee | 389 | len = strlen(cp); |
009ee3c6 | 390 | if (len <= (int)sizeof(BUNDLE_DIR_EXTENSION)) { |
5958d7c0 A |
391 | /* if entry name isn't long enough */ |
392 | return; | |
393 | } | |
394 | ||
395 | len -= sizeof(BUNDLE_DIR_EXTENSION) - 1; | |
0fae82ee | 396 | if (strcmp(&cp[len], BUNDLE_DIR_EXTENSION) != 0) { |
5958d7c0 A |
397 | /* if entry name doesn end with ".bundle" */ |
398 | return; | |
399 | } | |
400 | ||
5958d7c0 | 401 | /* get (just) the bundle's name */ |
0fae82ee A |
402 | bundleName[0] = '\0'; |
403 | (void) strncat(bundleName, cp, len); | |
5958d7c0 | 404 | |
dbf6a266 A |
405 | (*bundleInfo->start)(bundleName, bundlePath); |
406 | return; | |
407 | } | |
408 | ||
409 | ||
410 | void | |
411 | callPrimeFunction(const void *value, void *context) { | |
412 | bundleInfoRef bundleInfo = (bundleInfoRef)value; | |
413 | ||
414 | if (!bundleInfo->loaded) { | |
415 | return; | |
416 | } | |
417 | ||
418 | if (bundleInfo->prime == NULL) { | |
419 | // if no prime() function | |
420 | return; | |
421 | } | |
422 | ||
423 | (*bundleInfo->prime)(); | |
5958d7c0 A |
424 | return; |
425 | } | |
426 | ||
427 | ||
428 | static void | |
dbf6a266 A |
429 | stopComplete(void *info) |
430 | { | |
431 | CFBundleRef bundle = (CFBundleRef)info; | |
432 | CFStringRef bundleID = CFBundleGetIdentifier(bundle); | |
433 | CFRunLoopSourceRef stopRls; | |
434 | ||
435 | SCLog(TRUE, LOG_DEBUG, CFSTR("** %@ complete (%f)"), bundleID, CFAbsoluteTimeGetCurrent()); | |
436 | ||
437 | stopRls = (CFRunLoopSourceRef)CFDictionaryGetValue(exiting, bundle); | |
438 | CFRunLoopSourceInvalidate(stopRls); | |
439 | ||
440 | CFDictionaryRemoveValue(exiting, bundle); | |
441 | ||
442 | if (CFDictionaryGetCount(exiting) == 0) { | |
443 | int status; | |
444 | ||
445 | // if all of the plugins are happy | |
446 | status = server_shutdown(); | |
447 | SCLog(TRUE, LOG_DEBUG, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent()); | |
448 | exit (status); | |
449 | } | |
5958d7c0 | 450 | |
dbf6a266 A |
451 | return; |
452 | } | |
453 | ||
454 | ||
455 | static void | |
456 | stopDelayed(CFRunLoopTimerRef timer, void *info) | |
457 | { | |
458 | const void **keys; | |
459 | CFIndex i; | |
460 | CFIndex n; | |
461 | int status; | |
462 | ||
463 | SCLog(TRUE, LOG_ERR, CFSTR("server shutdown was delayed, unresponsive plugins:")); | |
464 | ||
465 | /* | |
466 | * we've asked our plugins to shutdown but someone | |
467 | * isn't listening. | |
468 | */ | |
469 | n = CFDictionaryGetCount(exiting); | |
470 | keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0); | |
471 | CFDictionaryGetKeysAndValues(exiting, keys, NULL); | |
472 | for (i = 0; i < n; i++) { | |
473 | CFBundleRef bundle; | |
474 | CFStringRef bundleID; | |
475 | ||
476 | bundle = (CFBundleRef)keys[i]; | |
477 | bundleID = CFBundleGetIdentifier(bundle); | |
478 | SCLog(TRUE, LOG_ERR, CFSTR("** %@"), bundleID); | |
479 | } | |
480 | CFAllocatorDeallocate(NULL, keys); | |
481 | ||
482 | status = server_shutdown(); | |
483 | exit (status); | |
484 | } | |
485 | ||
486 | static void | |
487 | stopBundle(const void *value, void *context) { | |
488 | bundleInfoRef bundleInfo = (bundleInfoRef)value; | |
489 | CFRunLoopSourceRef stopRls; | |
490 | CFRunLoopSourceContext stopContext = { 0 // version | |
491 | , bundleInfo->bundle // info | |
492 | , CFRetain // retain | |
493 | , CFRelease // release | |
494 | , CFCopyDescription // copyDescription | |
495 | , CFEqual // equal | |
496 | , CFHash // hash | |
497 | , NULL // schedule | |
498 | , NULL // cancel | |
499 | , stopComplete // perform | |
500 | }; | |
501 | ||
502 | if (!bundleInfo->loaded) { | |
5958d7c0 A |
503 | return; |
504 | } | |
505 | ||
dbf6a266 A |
506 | if (bundleInfo->stop == NULL) { |
507 | // if no stop() function | |
0fae82ee | 508 | return; |
5958d7c0 A |
509 | } |
510 | ||
dbf6a266 A |
511 | stopRls = CFRunLoopSourceCreate(NULL, 0, &stopContext); |
512 | CFRunLoopAddSource(CFRunLoopGetCurrent(), stopRls, kCFRunLoopDefaultMode); | |
513 | CFDictionaryAddValue(exiting, bundleInfo->bundle, stopRls); | |
514 | CFRelease(stopRls); | |
515 | ||
516 | (*bundleInfo->stop)(stopRls); | |
517 | ||
518 | return; | |
519 | } | |
520 | ||
521 | ||
522 | static void | |
523 | stopBundles() | |
524 | { | |
525 | /* | |
526 | * If defined, call each bundles stop() function. This function is | |
527 | * called when configd has been asked to shut down (via a SIGTERM). The | |
528 | * function should signal the provided run loop source when it is "ready" | |
529 | * for the shut down to proceeed. | |
530 | */ | |
531 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle stop() functions")); | |
532 | CFArrayApplyFunction(allBundles, | |
533 | CFRangeMake(0, CFArrayGetCount(allBundles)), | |
534 | stopBundle, | |
535 | NULL); | |
536 | ||
537 | if (CFDictionaryGetCount(exiting) == 0) { | |
538 | int status; | |
539 | ||
540 | // if all of the plugins are happy | |
541 | status = server_shutdown(); | |
542 | SCLog(TRUE, LOG_DEBUG, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent()); | |
543 | exit (status); | |
544 | } else { | |
545 | CFRunLoopTimerRef timer; | |
546 | ||
547 | /* sorry, we're not going to wait longer than 20 seconds */ | |
548 | timer = CFRunLoopTimerCreate(NULL, /* allocator */ | |
549 | CFAbsoluteTimeGetCurrent() + 20.0, /* fireDate (in 20 seconds) */ | |
550 | 0.0, /* interval (== one-shot) */ | |
551 | 0, /* flags */ | |
552 | 0, /* order */ | |
553 | stopDelayed, /* callout */ | |
554 | NULL); /* context */ | |
555 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); | |
556 | CFRelease(timer); | |
557 | } | |
558 | ||
5958d7c0 A |
559 | return; |
560 | } | |
561 | ||
562 | ||
dbf6a266 A |
563 | __private_extern__ |
564 | Boolean | |
565 | plugin_term(int *status) | |
566 | { | |
567 | CFRunLoopSourceRef stopRls; | |
568 | CFRunLoopSourceContext stopContext = { 0 // version | |
569 | , NULL // info | |
570 | , NULL // retain | |
571 | , NULL // release | |
572 | , NULL // copyDescription | |
573 | , NULL // equal | |
574 | , NULL // hash | |
575 | , NULL // schedule | |
576 | , NULL // cancel | |
577 | , stopBundles // perform | |
578 | }; | |
579 | ||
580 | if (plugin_runLoop == NULL) { | |
581 | // if no plugins | |
582 | *status = EX_OK; | |
583 | return FALSE; // don't delay shutdown | |
584 | } | |
585 | ||
586 | if (exiting != NULL) { | |
587 | // if shutdown already active | |
588 | return TRUE; | |
589 | } | |
590 | ||
591 | SCLog(TRUE, LOG_DEBUG, CFSTR("starting server shutdown (%f)"), CFAbsoluteTimeGetCurrent()); | |
592 | ||
593 | exiting = CFDictionaryCreateMutable(NULL, | |
594 | 0, | |
595 | &kCFTypeDictionaryKeyCallBacks, | |
596 | &kCFTypeDictionaryValueCallBacks); | |
597 | ||
598 | stopRls = CFRunLoopSourceCreate(NULL, 0, &stopContext); | |
599 | CFRunLoopAddSource(plugin_runLoop, stopRls, kCFRunLoopDefaultMode); | |
600 | CFRunLoopSourceSignal(stopRls); | |
601 | CFRelease(stopRls); | |
602 | CFRunLoopWakeUp(plugin_runLoop); | |
603 | ||
604 | return TRUE; | |
605 | } | |
606 | ||
607 | ||
a5f60add A |
608 | #ifdef DEBUG |
609 | ||
0fae82ee | 610 | static void |
5958d7c0 A |
611 | timerCallback(CFRunLoopTimerRef timer, void *info) |
612 | { | |
0fae82ee A |
613 | SCLog(_configd_verbose, |
614 | LOG_INFO, | |
615 | CFSTR("the CFRunLoop is waiting for something to happen....")); | |
616 | return; | |
617 | } | |
618 | ||
a5f60add A |
619 | #endif /* DEBUG */ |
620 | ||
0fae82ee A |
621 | |
622 | static void | |
623 | sortBundles(CFMutableArrayRef orig) | |
624 | { | |
dbf6a266 | 625 | CFMutableArrayRef new; |
0fae82ee | 626 | |
dbf6a266 | 627 | new = CFArrayCreateMutable(NULL, 0, NULL); |
0fae82ee A |
628 | while (CFArrayGetCount(orig) > 0) { |
629 | int i; | |
009ee3c6 A |
630 | Boolean inserted = FALSE; |
631 | int nOrig = CFArrayGetCount(orig); | |
0fae82ee | 632 | |
009ee3c6 | 633 | for (i = 0; i < nOrig; i++) { |
dbf6a266 A |
634 | bundleInfoRef bundleInfo1 = (bundleInfoRef)CFArrayGetValueAtIndex(orig, i); |
635 | CFStringRef bundleID1 = CFBundleGetIdentifier(bundleInfo1->bundle); | |
0fae82ee A |
636 | int count; |
637 | CFDictionaryRef dict; | |
638 | int j; | |
009ee3c6 | 639 | int nRequires; |
0fae82ee A |
640 | CFArrayRef requires = NULL; |
641 | ||
dbf6a266 | 642 | dict = isA_CFDictionary(CFBundleGetInfoDictionary(bundleInfo1->bundle)); |
0fae82ee | 643 | if (dict) { |
dbf6a266 | 644 | requires = CFDictionaryGetValue(dict, kSCBundleRequiresKey); |
0fae82ee A |
645 | requires = isA_CFArray(requires); |
646 | } | |
647 | if (bundleID1 == NULL || requires == NULL) { | |
dbf6a266 | 648 | CFArrayInsertValueAtIndex(new, 0, bundleInfo1); |
0fae82ee A |
649 | CFArrayRemoveValueAtIndex(orig, i); |
650 | inserted = TRUE; | |
651 | break; | |
652 | } | |
009ee3c6 A |
653 | count = nRequires = CFArrayGetCount(requires); |
654 | for (j = 0; j < nRequires; j++) { | |
0fae82ee | 655 | int k; |
009ee3c6 | 656 | int nNew; |
0fae82ee A |
657 | CFStringRef r = CFArrayGetValueAtIndex(requires, j); |
658 | ||
009ee3c6 A |
659 | nNew = CFArrayGetCount(new); |
660 | for (k = 0; k < nNew; k++) { | |
dbf6a266 A |
661 | bundleInfoRef bundleInfo2 = (bundleInfoRef)CFArrayGetValueAtIndex(new, k); |
662 | CFStringRef bundleID2 = CFBundleGetIdentifier(bundleInfo2->bundle); | |
0fae82ee A |
663 | |
664 | if (bundleID2 && CFEqual(bundleID2, r)) { | |
665 | count--; | |
666 | } | |
667 | } | |
668 | } | |
669 | if (count == 0) { | |
670 | /* all dependencies are met, append */ | |
dbf6a266 | 671 | CFArrayAppendValue(new, bundleInfo1); |
0fae82ee A |
672 | CFArrayRemoveValueAtIndex(orig, i); |
673 | inserted = TRUE; | |
674 | break; | |
675 | } | |
676 | } | |
677 | ||
678 | if (inserted == FALSE) { | |
679 | SCLog(TRUE, LOG_NOTICE, CFSTR("Bundles have circular dependency!!!")); | |
680 | break; | |
681 | } | |
682 | } | |
683 | if (CFArrayGetCount(orig) > 0) { | |
684 | /* we have a circular dependency, append remaining items on new array */ | |
685 | CFArrayAppendArray(new, orig, CFRangeMake(0, CFArrayGetCount(orig))); | |
686 | } | |
687 | else { | |
688 | /* new one is a sorted version of original */ | |
689 | } | |
690 | ||
691 | CFArrayRemoveAllValues(orig); | |
692 | CFArrayAppendArray(orig, new, CFRangeMake(0, CFArrayGetCount(new))); | |
693 | CFRelease(new); | |
5958d7c0 A |
694 | return; |
695 | } | |
696 | ||
697 | ||
009ee3c6 | 698 | __private_extern__ |
5958d7c0 A |
699 | void * |
700 | plugin_exec(void *arg) | |
701 | { | |
dbf6a266 | 702 | CFIndex nLoaded = 0; |
5958d7c0 | 703 | |
0fae82ee | 704 | /* keep track of bundles */ |
dbf6a266 | 705 | allBundles = CFArrayCreateMutable(NULL, 0, NULL); |
5958d7c0 | 706 | |
009ee3c6 A |
707 | /* allow plug-ins to exec child/helper processes */ |
708 | _SCDPluginExecInit(); | |
a5f60add | 709 | |
5958d7c0 | 710 | if (arg == NULL) { |
0fae82ee A |
711 | char path[MAXPATHLEN]; |
712 | NSSearchPathEnumerationState state; | |
713 | ||
5958d7c0 | 714 | /* |
0fae82ee | 715 | * identify and load all bundles |
5958d7c0 A |
716 | */ |
717 | state = NSStartSearchPathEnumeration(NSLibraryDirectory, | |
dbf6a266 | 718 | NSSystemDomainMask); |
5958d7c0 | 719 | while ((state = NSGetNextSearchPathEnumeration(state, path))) { |
0fae82ee A |
720 | CFArrayRef bundles; |
721 | CFURLRef url; | |
722 | ||
723 | /* load any available bundle */ | |
5958d7c0 | 724 | strcat(path, BUNDLE_DIRECTORY); |
0fae82ee A |
725 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("searching for bundles in \".\"")); |
726 | url = CFURLCreateFromFileSystemRepresentation(NULL, | |
d6c893b2 | 727 | (UInt8 *)path, |
0fae82ee A |
728 | strlen(path), |
729 | TRUE); | |
730 | bundles = CFBundleCreateBundlesFromDirectory(NULL, url, CFSTR(".bundle")); | |
731 | CFRelease(url); | |
732 | ||
dbf6a266 A |
733 | if (bundles != NULL) { |
734 | CFIndex i; | |
735 | CFIndex n; | |
736 | ||
737 | n = CFArrayGetCount(bundles); | |
738 | for (i = 0; i < n; i++) { | |
739 | CFBundleRef bundle; | |
d6c893b2 | 740 | |
dbf6a266 A |
741 | bundle = (CFBundleRef)CFArrayGetValueAtIndex(bundles, i); |
742 | addBundle(bundle); | |
743 | } | |
0fae82ee A |
744 | CFRelease(bundles); |
745 | } | |
5958d7c0 A |
746 | } |
747 | ||
0fae82ee | 748 | sortBundles(allBundles); |
5958d7c0 | 749 | } else { |
0fae82ee A |
750 | CFBundleRef bundle; |
751 | CFURLRef url; | |
752 | ||
5958d7c0 | 753 | /* |
0fae82ee | 754 | * load (only) the specified bundle |
5958d7c0 | 755 | */ |
0fae82ee | 756 | url = CFURLCreateFromFileSystemRepresentation(NULL, |
d6c893b2 | 757 | (UInt8 *)arg, |
0fae82ee A |
758 | strlen((char *)arg), |
759 | TRUE); | |
760 | bundle = CFBundleCreate(NULL, url); | |
dbf6a266 A |
761 | if (bundle != NULL) { |
762 | addBundle(bundle); | |
0fae82ee A |
763 | CFRelease(bundle); |
764 | } | |
765 | CFRelease(url); | |
766 | } | |
5958d7c0 | 767 | |
0fae82ee | 768 | /* |
dbf6a266 A |
769 | * load each bundle. |
770 | */ | |
771 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("loading bundles")); | |
772 | CFArrayApplyFunction(allBundles, | |
773 | CFRangeMake(0, CFArrayGetCount(allBundles)), | |
774 | loadBundle, | |
775 | &nLoaded); | |
776 | ||
777 | /* | |
778 | * If defined, call each bundles load() function. This function (or | |
779 | * the start() function) should initialize any variables, open any | |
780 | * sessions with "configd", and register any needed notifications. | |
0fae82ee A |
781 | * |
782 | * Note: Establishing initial information in the store should be | |
783 | * deferred until the prime() initialization function so that | |
784 | * any bundles which want to receive a notification that the | |
785 | * data has changed will have an opportunity to install a | |
786 | * notification handler. | |
787 | */ | |
788 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle load() functions")); | |
789 | CFArrayApplyFunction(allBundles, | |
790 | CFRangeMake(0, CFArrayGetCount(allBundles)), | |
dbf6a266 A |
791 | callLoadFunction, |
792 | NULL); | |
5958d7c0 | 793 | |
0fae82ee A |
794 | /* |
795 | * If defined, call each bundles start() function. This function is | |
796 | * called after the bundle has been loaded and its load() function has | |
797 | * been called. It should initialize any variables, open any sessions | |
798 | * with "configd", and register any needed notifications. | |
799 | * | |
800 | * Note: Establishing initial information in the store should be | |
801 | * deferred until the prime() initialization function so that | |
802 | * any bundles which want to receive a notification that the | |
803 | * data has changed will have an opportunity to install a | |
804 | * notification handler. | |
805 | */ | |
806 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle start() functions")); | |
807 | CFArrayApplyFunction(allBundles, | |
808 | CFRangeMake(0, CFArrayGetCount(allBundles)), | |
dbf6a266 | 809 | callStartFunction, |
0fae82ee | 810 | NULL); |
5958d7c0 | 811 | |
0fae82ee A |
812 | /* |
813 | * If defined, call each bundles prime() function. This function is | |
814 | * called after the bundle has been loaded and its load() and start() | |
815 | * functions have been called. It should initialize any configuration | |
816 | * information and/or state in the store. | |
817 | */ | |
818 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle prime() functions")); | |
819 | CFArrayApplyFunction(allBundles, | |
820 | CFRangeMake(0, CFArrayGetCount(allBundles)), | |
dbf6a266 | 821 | callPrimeFunction, |
0fae82ee | 822 | NULL); |
5958d7c0 | 823 | |
a5f60add | 824 | #ifdef DEBUG |
0fae82ee A |
825 | if (arg == NULL && (nLoaded > 0)) { |
826 | CFRunLoopTimerRef timer; | |
5958d7c0 A |
827 | |
828 | /* allocate a periodic event (to help show we're not blocking) */ | |
a5f60add A |
829 | timer = CFRunLoopTimerCreate(NULL, /* allocator */ |
830 | CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */ | |
831 | 60.0, /* interval */ | |
832 | 0, /* flags */ | |
833 | 0, /* order */ | |
834 | timerCallback, /* callout */ | |
835 | NULL); /* context */ | |
836 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); | |
837 | CFRelease(timer); | |
838 | } | |
839 | #endif /* DEBUG */ | |
5958d7c0 | 840 | |
5958d7c0 A |
841 | /* |
842 | * The assumption is that each loaded plugin will establish CFMachPortRef, | |
843 | * CFSocketRef, and CFRunLoopTimerRef input sources to handle any events | |
844 | * and register these sources with this threads run loop. If the plugin | |
845 | * needs to wait and/or block at any time it should do so only in its a | |
846 | * private thread. | |
847 | */ | |
0fae82ee | 848 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("starting plugin CFRunLoop")); |
dbf6a266 | 849 | plugin_runLoop = CFRunLoopGetCurrent(); |
5958d7c0 | 850 | CFRunLoopRun(); |
dbf6a266 A |
851 | |
852 | SCLog(_configd_verbose, LOG_INFO, CFSTR("No more work for the \"configd\" plugins")); | |
853 | plugin_runLoop = NULL; | |
5958d7c0 A |
854 | return NULL; |
855 | } | |
856 | ||
857 | ||
009ee3c6 | 858 | __private_extern__ |
5958d7c0 A |
859 | void |
860 | plugin_init() | |
861 | { | |
862 | pthread_attr_t tattr; | |
863 | pthread_t tid; | |
864 | ||
0fae82ee | 865 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("Starting thread for plug-ins...")); |
5958d7c0 A |
866 | pthread_attr_init(&tattr); |
867 | pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM); | |
868 | pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); | |
869 | // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack | |
870 | pthread_create(&tid, &tattr, plugin_exec, NULL); | |
871 | pthread_attr_destroy(&tattr); | |
0fae82ee | 872 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR(" thread id=0x%08x"), tid); |
5958d7c0 A |
873 | |
874 | return; | |
875 | } |