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