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