]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/plugin_support.c
configd-963.200.27.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 int status;
564
565 // if all of the plugins are happy
566 status = server_shutdown();
567 SC_log(LOG_INFO, "server shutdown complete (%f)", CFAbsoluteTimeGetCurrent());
568 exit (status);
569 }
570
571 return;
572 }
573
574
575 static void
576 stopDelayed(CFRunLoopTimerRef timer, void *info)
577 {
578 #pragma unused(timer)
579 #pragma unused(info)
580 const void **keys;
581 CFIndex i;
582 CFIndex n;
583 int status;
584
585 SC_log(LOG_INFO, "server shutdown was delayed, unresponsive plugins:");
586
587 /*
588 * we've asked our plugins to shutdown but someone
589 * isn't listening.
590 */
591 n = CFDictionaryGetCount(exiting);
592 keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
593 CFDictionaryGetKeysAndValues(exiting, keys, NULL);
594 for (i = 0; i < n; i++) {
595 CFBundleRef bundle;
596 CFStringRef bundleID;
597
598 bundle = (CFBundleRef)keys[i];
599 bundleID = CFBundleGetIdentifier(bundle);
600 SC_log(LOG_NOTICE, "** %@", bundleID);
601 }
602 CFAllocatorDeallocate(NULL, keys);
603
604 status = server_shutdown();
605 exit (status);
606 }
607
608 static CFStringRef
609 stopRLSCopyDescription(const void *info)
610 {
611 CFBundleRef bundle = (CFBundleRef)info;
612
613 return CFStringCreateWithFormat(NULL,
614 NULL,
615 CFSTR("<stopRLS %p> {bundleID = %@}"),
616 info,
617 CFBundleGetIdentifier(bundle));
618 }
619
620
621 static void
622 stopBundle(const void *value, void *context)
623 {
624 #pragma unused(context)
625 bundleInfoRef bundleInfo = (bundleInfoRef)value;
626 CFRunLoopSourceRef stopRls;
627 CFRunLoopSourceContext stopContext = { 0 // version
628 , bundleInfo->bundle // info
629 , CFRetain // retain
630 , CFRelease // release
631 , stopRLSCopyDescription // copyDescription
632 , CFEqual // equal
633 , CFHash // hash
634 , NULL // schedule
635 , NULL // cancel
636 , stopComplete // perform
637 };
638
639 if (!bundleInfo->loaded) {
640 return;
641 }
642
643 if (bundleInfo->stop == NULL) {
644 // if no stop() function
645 return;
646 }
647
648 stopRls = CFRunLoopSourceCreate(NULL, 0, &stopContext);
649 CFRunLoopAddSource(CFRunLoopGetCurrent(), stopRls, kCFRunLoopDefaultMode);
650 CFDictionaryAddValue(exiting, bundleInfo->bundle, stopRls);
651 CFRelease(stopRls);
652
653 (*bundleInfo->stop)(stopRls);
654
655 return;
656 }
657
658
659 static void
660 stopBundles()
661 {
662 /*
663 * If defined, call each bundles stop() function. This function is
664 * called when configd has been asked to shut down (via a SIGTERM). The
665 * function should signal the provided run loop source when it is "ready"
666 * for the shut down to proceeed.
667 */
668 SC_log(LOG_DEBUG, "calling bundle stop() functions");
669 CFArrayApplyFunction(allBundles,
670 CFRangeMake(0, CFArrayGetCount(allBundles)),
671 stopBundle,
672 NULL);
673
674 if (CFDictionaryGetCount(exiting) == 0) {
675 int status;
676
677 // if all of the plugins are happy
678 status = server_shutdown();
679 SC_log(LOG_INFO, "server shutdown complete (%f)", CFAbsoluteTimeGetCurrent());
680 exit (status);
681 } else {
682 CFRunLoopTimerRef timer;
683
684 /*
685 * launchd will only wait 20 seconds before sending us a
686 * SIGKILL and because we want to know what's stuck before
687 * that time so set our own "we're not waiting any longer"
688 * timeout for 15 seconds.
689 */
690 timer = CFRunLoopTimerCreate(NULL, /* allocator */
691 CFAbsoluteTimeGetCurrent() + 15.0, /* fireDate (in 15 seconds) */
692 0.0, /* interval (== one-shot) */
693 0, /* flags */
694 0, /* order */
695 stopDelayed, /* callout */
696 NULL); /* context */
697 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
698 CFRelease(timer);
699 }
700
701 return;
702 }
703
704
705 #pragma mark -
706 #pragma mark term
707
708
709 static CFStringRef
710 termRLSCopyDescription(const void *info)
711 {
712 #pragma unused(info)
713 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SIGTERM RLS>"));
714 }
715
716
717 __private_extern__
718 Boolean
719 plugin_term(int *status)
720 {
721 CFRunLoopSourceContext termContext = { 0 // version
722 , (void *)1 // info
723 , NULL // retain
724 , NULL // release
725 , termRLSCopyDescription // copyDescription
726 , NULL // equal
727 , NULL // hash
728 , NULL // schedule
729 , NULL // cancel
730 , stopBundles // perform
731 };
732 CFRunLoopSourceRef termRls;
733
734 if (plugin_runLoop == NULL) {
735 // if no plugins
736 *status = EX_OK;
737 return FALSE; // don't delay shutdown
738 }
739
740 if (exiting != NULL) {
741 // if shutdown already active
742 return TRUE;
743 }
744
745 SC_log(LOG_INFO, "starting server shutdown (%f)", CFAbsoluteTimeGetCurrent());
746
747 exiting = CFDictionaryCreateMutable(NULL,
748 0,
749 &kCFTypeDictionaryKeyCallBacks,
750 &kCFTypeDictionaryValueCallBacks);
751
752 termRls = CFRunLoopSourceCreate(NULL, 0, &termContext);
753 CFRunLoopAddSource(plugin_runLoop, termRls, kCFRunLoopDefaultMode);
754 CFRunLoopSourceSignal(termRls);
755 CFRelease(termRls);
756 CFRunLoopWakeUp(plugin_runLoop);
757
758 return TRUE;
759 }
760
761
762 #pragma mark -
763 #pragma mark initialization
764
765
766 static void
767 sortBundles(CFMutableArrayRef orig)
768 {
769 CFIndex i;
770 CFIndex n;
771 CFMutableArrayRef new;
772 CFMutableSetRef orig_bundleIDs;
773
774 orig_bundleIDs = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
775
776 n = CFArrayGetCount(orig);
777 for (i = 0; i < n; i++) {
778 bundleInfoRef bundleInfo = (bundleInfoRef)CFArrayGetValueAtIndex(orig, i);
779 CFStringRef bundleID = CFBundleGetIdentifier(bundleInfo->bundle);
780
781 if (bundleID != NULL) {
782 CFSetAddValue(orig_bundleIDs, bundleID);
783 }
784 }
785
786 new = CFArrayCreateMutable(NULL, 0, NULL);
787 while (n > 0) {
788 Boolean inserted = FALSE;
789
790 for (i = 0; i < n; i++) {
791 bundleInfoRef bundleInfo1 = (bundleInfoRef)CFArrayGetValueAtIndex(orig, i);
792 CFStringRef bundleID1 = CFBundleGetIdentifier(bundleInfo1->bundle);
793 CFIndex count;
794 CFDictionaryRef dict;
795 CFIndex j;
796 CFIndex nRequires;
797 CFArrayRef requires = NULL;
798
799 dict = isA_CFDictionary(CFBundleGetInfoDictionary(bundleInfo1->bundle));
800 if (dict) {
801 requires = CFDictionaryGetValue(dict, kSCBundleRequiresKey);
802 requires = isA_CFArray(requires);
803 }
804 if (bundleID1 == NULL || requires == NULL) {
805 CFArrayInsertValueAtIndex(new, 0, bundleInfo1);
806 CFArrayRemoveValueAtIndex(orig, i);
807 inserted = TRUE;
808 break;
809 }
810 count = nRequires = CFArrayGetCount(requires);
811 for (j = 0; j < nRequires; j++) {
812 CFIndex k;
813 CFIndex nNew;
814 CFStringRef r = CFArrayGetValueAtIndex(requires, j);
815
816 if (!CFSetContainsValue(orig_bundleIDs, r)) {
817 // if dependency not present
818 count--;
819 continue;
820 }
821
822 nNew = CFArrayGetCount(new);
823 for (k = 0; k < nNew; k++) {
824 bundleInfoRef bundleInfo2 = (bundleInfoRef)CFArrayGetValueAtIndex(new, k);
825 CFStringRef bundleID2 = CFBundleGetIdentifier(bundleInfo2->bundle);
826
827 if (bundleID2 && CFEqual(bundleID2, r)) {
828 count--;
829 }
830 }
831 }
832 if (count == 0) {
833 /* all dependencies are met, append */
834 CFArrayAppendValue(new, bundleInfo1);
835 CFArrayRemoveValueAtIndex(orig, i);
836 inserted = TRUE;
837 break;
838 }
839 }
840
841 if (!inserted) {
842 SC_log(LOG_NOTICE, "Bundles have circular dependency!!!");
843 break;
844 }
845
846 n = CFArrayGetCount(orig);
847 }
848 if (CFArrayGetCount(orig) > 0) {
849 /* we have a circular dependency, append remaining items on new array */
850 CFArrayAppendArray(new, orig, CFRangeMake(0, CFArrayGetCount(orig)));
851 }
852 else {
853 /* new one is a sorted version of original */
854 }
855
856 CFArrayRemoveAllValues(orig);
857 CFArrayAppendArray(orig, new, CFRangeMake(0, CFArrayGetCount(new)));
858 CFRelease(new);
859 CFRelease(orig_bundleIDs);
860 return;
861 }
862
863
864 /*
865 * ALT_CFRelease()
866 *
867 * An alternate CFRelease() that we can use to fake out the
868 * static analyzer.
869 */
870 static __inline__ void
871 ALT_CFRelease(CFTypeRef cf)
872 {
873 CFRelease(cf);
874 }
875
876
877 __private_extern__
878 void *
879 plugin_exec(void *arg)
880 {
881 CFIndex nLoaded = 0;
882
883 /* keep track of bundles */
884 allBundles = CFArrayCreateMutable(NULL, 0, NULL);
885
886 /* add white-listed plugins to those we'll allow to be loaded */
887 for (size_t i = 0; i < N_PLUGIN_WHITELIST; i++) {
888 if (pluginWhitelist[i] != NULL) {
889 CFSetSetValue(_plugins_allowed, pluginWhitelist[i]);
890 }
891 }
892
893 /* allow plug-ins to exec child/helper processes */
894 _SCDPluginExecInit();
895
896 if (arg == NULL) {
897 char path[MAXPATHLEN];
898 sysdir_search_path_enumeration_state state;
899
900 /*
901 * identify and load all bundles
902 */
903 state = sysdir_start_search_path_enumeration(SYSDIR_DIRECTORY_LIBRARY,
904 SYSDIR_DOMAIN_MASK_SYSTEM);
905 while ((state = sysdir_get_next_search_path_enumeration(state, path))) {
906 CFArrayRef bundles;
907 CFURLRef url;
908
909 #if TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC
910 const char *path_sim_prefix;
911
912 path_sim_prefix = getenv("IPHONE_SIMULATOR_ROOT");
913 if ((path_sim_prefix != NULL) && (strcmp(path_sim_prefix, ".") != 0)) {
914 char path_sim[MAXPATHLEN];
915
916 strlcpy(path_sim, path_sim_prefix, sizeof(path_sim));
917 strlcat(path_sim, path, sizeof(path_sim));
918 strlcpy(path, path_sim, sizeof(path));
919 } else {
920 path[0] = '\0';
921 }
922 #endif // TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC
923
924 /* load any available bundle */
925 strlcat(path, BUNDLE_DIRECTORY, sizeof(path));
926 SC_log(LOG_DEBUG, "searching for bundles in \"%s\"", path);
927 url = CFURLCreateFromFileSystemRepresentation(NULL,
928 (UInt8 *)path,
929 strlen(path),
930 TRUE);
931 bundles = CFBundleCreateBundlesFromDirectory(NULL, url, CFSTR(".bundle"));
932 CFRelease(url);
933
934 if (bundles != NULL) {
935 CFIndex i;
936 CFIndex n;
937
938 n = CFArrayGetCount(bundles);
939 for (i = 0; i < n; i++) {
940 CFBundleRef bundle;
941
942 bundle = (CFBundleRef)CFArrayGetValueAtIndex(bundles, i);
943 addBundle(bundle, FALSE);
944
945 // The CFBundleCreateBundlesFromDirectory() API has
946 // a known/outstanding bug in that it over-retains the
947 // returned bundles. Since we do not expect this to
948 // be fixed we release the extra references.
949 //
950 // See <rdar://problems/4912137&6078752> for more info.
951 //
952 // Also, we use the hack below to keep the static
953 // analyzer happy.
954 ALT_CFRelease(bundle);
955 }
956 CFRelease(bundles);
957 }
958 }
959
960 sortBundles(allBundles);
961 } else {
962 CFBundleRef bundle;
963 CFURLRef url;
964
965 /*
966 * load (only) the specified bundle
967 */
968 url = CFURLCreateFromFileSystemRepresentation(NULL,
969 (UInt8 *)arg,
970 strlen((char *)arg),
971 TRUE);
972 bundle = CFBundleCreate(NULL, url);
973 if (bundle != NULL) {
974 addBundle(bundle, TRUE);
975 CFRelease(bundle);
976 }
977 CFRelease(url);
978 }
979
980 /*
981 * Look for the InterfaceNamer plugin, and move it to the start
982 * of the list.
983 *
984 * Load the InterfaceNamer plugin (and thereby start its thread)
985 * first in an attempt to minimize the amount of time that
986 * opendirectoryd has to wait for the platform UUID to appear in
987 * nvram.
988 *
989 * InterfaceNamer is responsible for creating the platform UUID on
990 * platforms without a UUID in ROM. Until the platform UUID is created
991 * and stashed in nvram, all calls to opendirectoryd to do things like
992 * getpwuid() will block, because opendirectoryd will block while trying
993 * to read the platform UUID from the kernel.
994 *
995 * As an example, dlopen() causes XPC to do some intialization, and
996 * part of that initialization involves communicating with xpcd.
997 * Since xpcd calls getpwuid_r() during its initialization, it will
998 * block until the platform UUID is available.
999 */
1000 for (CFIndex i = 0; i < CFArrayGetCount(allBundles); i++) {
1001 bundleInfoRef bi = (bundleInfoRef)CFArrayGetValueAtIndex(allBundles, i);
1002 CFStringRef bundleID = CFBundleGetIdentifier(bi->bundle);
1003
1004 if (_SC_CFEqual(bundleID,
1005 CFSTR("com.apple.SystemConfiguration.InterfaceNamer")))
1006 {
1007 CFArrayRemoveValueAtIndex(allBundles, i);
1008 CFArrayInsertValueAtIndex(allBundles, 0, bi);
1009 break;
1010 }
1011 }
1012
1013 /*
1014 * load each bundle.
1015 */
1016 SC_log(LOG_DEBUG, "loading bundles");
1017 CFArrayApplyFunction(allBundles,
1018 CFRangeMake(0, CFArrayGetCount(allBundles)),
1019 loadBundle,
1020 &nLoaded);
1021
1022 /*
1023 * If defined, call each bundles load() function. This function (or
1024 * the start() function) should initialize any variables, open any
1025 * sessions with "configd", and register any needed notifications.
1026 *
1027 * Note: Establishing initial information in the store should be
1028 * deferred until the prime() initialization function so that
1029 * any bundles which want to receive a notification that the
1030 * data has changed will have an opportunity to install a
1031 * notification handler.
1032 */
1033 SC_log(LOG_DEBUG, "calling bundle load() functions");
1034 CFArrayApplyFunction(allBundles,
1035 CFRangeMake(0, CFArrayGetCount(allBundles)),
1036 callLoadFunction,
1037 NULL);
1038
1039 if (nLoaded == 0) {
1040 // if no bundles loaded
1041 goto done;
1042 }
1043
1044 /*
1045 * If defined, call each bundles start() function. This function is
1046 * called after the bundle has been loaded and its load() function has
1047 * been called. It should initialize any variables, open any sessions
1048 * with "configd", and register any needed notifications.
1049 *
1050 * Note: Establishing initial information in the store should be
1051 * deferred until the prime() initialization function so that
1052 * any bundles which want to receive a notification that the
1053 * data has changed will have an opportunity to install a
1054 * notification handler.
1055 */
1056 SC_log(LOG_DEBUG, "calling bundle start() functions");
1057 CFArrayApplyFunction(allBundles,
1058 CFRangeMake(0, CFArrayGetCount(allBundles)),
1059 callStartFunction,
1060 NULL);
1061
1062 /*
1063 * If defined, call each bundles prime() function. This function is
1064 * called after the bundle has been loaded and its load() and start()
1065 * functions have been called. It should initialize any configuration
1066 * information and/or state in the store.
1067 */
1068 SC_log(LOG_DEBUG, "calling bundle prime() functions");
1069 CFArrayApplyFunction(allBundles,
1070 CFRangeMake(0, CFArrayGetCount(allBundles)),
1071 callPrimeFunction,
1072 NULL);
1073
1074 /*
1075 * The assumption is that each loaded plugin will establish CFMachPortRef,
1076 * CFSocketRef, and CFRunLoopTimerRef input sources to handle any events
1077 * and register these sources with this threads run loop. If the plugin
1078 * needs to wait and/or block at any time it should do so only in its a
1079 * private thread.
1080 */
1081 SC_log(LOG_DEBUG, "starting plugin CFRunLoop");
1082 plugin_runLoop = CFRunLoopGetCurrent();
1083 pthread_setname_np("Main plugin thread");
1084 CFRunLoopRun();
1085
1086 done :
1087
1088 SC_log(LOG_INFO, "No more work for the \"configd\" plugin thread");
1089 plugin_runLoop = NULL;
1090 return NULL;
1091 }
1092
1093
1094 __private_extern__
1095 void
1096 plugin_init()
1097 {
1098 pthread_attr_t tattr;
1099 pthread_t tid;
1100
1101 SC_log(LOG_DEBUG, "Starting \"configd\" plugin thread");
1102 pthread_attr_init(&tattr);
1103 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
1104 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1105 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
1106 pthread_create(&tid, &tattr, plugin_exec, NULL);
1107 pthread_attr_destroy(&tattr);
1108 SC_log(LOG_DEBUG, " thread id=%p", tid);
1109
1110 return;
1111 }