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