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