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