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