2 * Copyright (c) 2000-2009, 2011, 2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * October 30, 2003 Allan Nathanson <ajn@apple.com>
28 * - add plugin "stop()" function support
30 * June 11, 2001 Allan Nathanson <ajn@apple.com>
31 * - start using CFBundle code
33 * June 1, 2001 Allan Nathanson <ajn@apple.com>
34 * - public API conversion
36 * May 26, 2000 Allan Nathanson <ajn@apple.com>
40 #include <sys/types.h>
42 #include <sys/param.h>
47 #include <NSSystemDirectories.h>
50 #include "configd_server.h"
51 #include <SystemConfiguration/SCDPlugin.h>
52 #include "SCNetworkReachabilityInternal.h"
53 void _SCDPluginExecInit();
57 * path components, extensions, entry points, ...
59 #define BUNDLE_DIRECTORY "/SystemConfiguration" /* [/System/Library]/... */
60 #define BUNDLE_DIR_EXTENSION ".bundle"
63 #define PLUGIN_ALL(p) CFSTR(p)
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
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_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.Logger"),
82 PLUGIN_ALL ("com.apple.SystemConfiguration.PPPController"),
83 PLUGIN_ALL ("com.apple.SystemConfiguration.PreferencesMonitor"),
84 #ifdef HAVE_REACHABILITY_SERVER
85 PLUGIN_ALL ("com.apple.SystemConfiguration.SCNetworkReachability"),
86 #endif // HAVE_REACHABILITY_SERVER
87 PLUGIN_MACOSX("com.apple.print.notification"),
89 #define N_PLUGIN_WHITELIST (sizeof(pluginWhitelist) / sizeof(pluginWhitelist[0]))
99 SCDynamicStoreBundleLoadFunction load
;
100 SCDynamicStoreBundleStartFunction start
;
101 SCDynamicStoreBundlePrimeFunction prime
;
102 SCDynamicStoreBundleStopFunction stop
;
106 // all loaded bundles
107 static CFMutableArrayRef allBundles
= NULL
;
110 static CFMutableDictionaryRef exiting
= NULL
;
112 // plugin CFRunLoopRef
114 CFRunLoopRef plugin_runLoop
= NULL
;
117 extern SCDynamicStoreBundleLoadFunction load_IPMonitor
;
118 extern SCDynamicStoreBundlePrimeFunction prime_IPMonitor
;
119 #if !TARGET_IPHONE_SIMULATOR
120 extern SCDynamicStoreBundleLoadFunction load_InterfaceNamer
;
121 extern SCDynamicStoreBundleLoadFunction load_KernelEventMonitor
;
122 extern SCDynamicStoreBundlePrimeFunction prime_KernelEventMonitor
;
123 extern SCDynamicStoreBundleLoadFunction load_LinkConfiguration
;
124 extern SCDynamicStoreBundleLoadFunction load_PreferencesMonitor
;
125 extern SCDynamicStoreBundlePrimeFunction prime_PreferencesMonitor
;
126 #endif // !TARGET_IPHONE_SIMULATOR
127 #ifdef HAVE_REACHABILITY_SERVER
128 extern SCDynamicStoreBundleLoadFunction load_SCNetworkReachability
;
129 #endif // HAVE_REACHABILITY_SERVER
133 const CFStringRef bundleID
;
134 const void *load
; // SCDynamicStoreBundleLoadFunction
135 const void *start
; // SCDynamicStoreBundleStartFunction
136 const void *prime
; // SCDynamicStoreBundlePrimeFunction
137 const void *stop
; // SCDynamicStoreBundleStopFunction
138 } builtin
, *builtinRef
;
141 static const builtin builtin_plugins
[] = {
143 CFSTR("com.apple.SystemConfiguration.IPMonitor"),
149 #if !TARGET_IPHONE_SIMULATOR
151 CFSTR("com.apple.SystemConfiguration.InterfaceNamer"),
152 &load_InterfaceNamer
,
158 CFSTR("com.apple.SystemConfiguration.KernelEventMonitor"),
159 &load_KernelEventMonitor
,
161 &prime_KernelEventMonitor
,
165 CFSTR("com.apple.SystemConfiguration.LinkConfiguration"),
166 &load_LinkConfiguration
,
172 CFSTR("com.apple.SystemConfiguration.PreferencesMonitor"),
173 &load_PreferencesMonitor
,
175 &prime_PreferencesMonitor
,
178 #endif // !TARGET_IPHONE_SIMULATOR
179 #ifdef HAVE_REACHABILITY_SERVER
181 CFSTR("com.apple.SystemConfiguration.SCNetworkReachability"),
182 &load_SCNetworkReachability
,
187 #endif // HAVE_REACHABILITY_SERVER
193 traceBundle(const char *op
, CFBundleRef bundle
)
195 if (_configd_trace
!= NULL
) {
196 if (bundle
!= NULL
) {
197 CFStringRef bundleID
= CFBundleGetIdentifier(bundle
);
199 SCTrace(TRUE
, _configd_trace
,
200 CFSTR("bundle : %s %@\n"),
204 SCTrace(TRUE
, _configd_trace
,
205 CFSTR("bundle : %s\n"),
216 addBundle(CFBundleRef bundle
, Boolean forceEnabled
)
218 CFDictionaryRef bundleDict
;
219 bundleInfoRef bundleInfo
;
221 bundleInfo
= CFAllocatorAllocate(NULL
, sizeof(*bundleInfo
), 0);
222 bundleInfo
->bundle
= (CFBundleRef
)CFRetain(bundle
);
223 bundleInfo
->loaded
= FALSE
;
224 bundleInfo
->builtin
= FALSE
;
225 bundleInfo
->enabled
= TRUE
;
226 bundleInfo
->forced
= forceEnabled
;
227 bundleInfo
->verbose
= FALSE
;
228 bundleInfo
->load
= NULL
;
229 bundleInfo
->start
= NULL
;
230 bundleInfo
->prime
= NULL
;
231 bundleInfo
->stop
= NULL
;
233 bundleDict
= CFBundleGetInfoDictionary(bundle
);
234 if (isA_CFDictionary(bundleDict
)) {
237 bVal
= CFDictionaryGetValue(bundleDict
, kSCBundleIsBuiltinKey
);
238 if (isA_CFBoolean(bVal
)) {
239 bundleInfo
->builtin
= CFBooleanGetValue(bVal
);
242 bVal
= CFDictionaryGetValue(bundleDict
, kSCBundleEnabledKey
);
243 if (isA_CFBoolean(bVal
)) {
244 bundleInfo
->enabled
= CFBooleanGetValue(bVal
);
247 bVal
= CFDictionaryGetValue(bundleDict
, kSCBundleVerboseKey
);
248 if (isA_CFBoolean(bVal
)) {
249 bundleInfo
->verbose
= CFBooleanGetValue(bVal
);
253 CFArrayAppendValue(allBundles
, bundleInfo
);
258 static CF_RETURNS_RETAINED CFStringRef
259 shortBundleIdentifier(CFStringRef bundleID
)
261 CFIndex len
= CFStringGetLength(bundleID
);
263 CFStringRef shortID
= NULL
;
265 if (CFStringFindWithOptions(bundleID
,
270 range
.location
= range
.location
+ range
.length
;
271 range
.length
= len
- range
.location
;
272 shortID
= CFStringCreateWithSubstring(NULL
, bundleID
, range
);
280 getBundleSymbol(CFBundleRef bundle
, CFStringRef functionName
, CFStringRef shortID
)
284 // search for load(), start(), prime(), stop(), ...
285 func
= CFBundleGetFunctionPointerForName(bundle
, functionName
);
290 if (shortID
!= NULL
) {
291 CFStringRef altFunctionName
;
293 // search for load_XXX(), ...
294 altFunctionName
= CFStringCreateWithFormat(NULL
,
299 func
= CFBundleGetFunctionPointerForName(bundle
, altFunctionName
);
300 CFRelease(altFunctionName
);
308 getBundleDirNameAndPath(CFBundleRef bundle
, char *buf
, size_t buf_len
)
315 url
= CFBundleCopyBundleURL(bundle
);
320 ok
= CFURLGetFileSystemRepresentation(url
, TRUE
, (UInt8
*)buf
, buf_len
);
326 cp
= strrchr(buf
, '/');
333 /* check if this directory entry is a valid bundle name */
335 if (len
<= (int)sizeof(BUNDLE_DIR_EXTENSION
)) {
336 /* if entry name isn't long enough */
340 len
-= sizeof(BUNDLE_DIR_EXTENSION
) - 1;
341 if (strcmp(&cp
[len
], BUNDLE_DIR_EXTENSION
) != 0) {
342 /* if entry name doesn't end with ".bundle" */
355 forkBundle_setup(pid_t pid
, void *setupContext
)
359 unsetenv("__LAUNCHD_FD");
360 setenv("__FORKED_PLUGIN__", "Yes", 1);
368 forkBundle(CFBundleRef bundle
, CFStringRef bundleID
)
370 char *argv
[] = { "configd", "-d", "-t", NULL
, NULL
};
372 char path
[MAXPATHLEN
];
375 // get the bundle's path
376 name
= getBundleDirNameAndPath(bundle
, path
, sizeof(path
));
378 SCLog(TRUE
, LOG_ERR
, CFSTR("skipped %@ (could not determine path)"), bundle
);
382 // fork and exec configd opting to load only this plugin
384 pid
= _SCDPluginExecCommand2(NULL
, NULL
, 0, 0, "/usr/libexec/configd", argv
, forkBundle_setup
, NULL
);
387 CFSTR("skipped %@ (could not exec child) : %s"),
393 SCLog(TRUE
, LOG_NOTICE
, CFSTR("forked %@, pid=%d"), bundleID
, pid
);
399 loadBundle(const void *value
, void *context
) {
400 CFStringRef bundleID
;
401 Boolean bundleAllowed
;
402 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
403 Boolean bundleExclude
;
404 CFIndex
*nLoaded
= (CFIndex
*)context
;
407 bundleID
= CFBundleGetIdentifier(bundleInfo
->bundle
);
408 if (bundleID
== NULL
) {
409 // sorry, no bundles without a bundle identifier
410 SCLog(TRUE
, LOG_NOTICE
, CFSTR("skipped %@ (no bundle ID)"), bundleInfo
->bundle
);
414 shortID
= shortBundleIdentifier(bundleID
);
416 bundleAllowed
= ((CFSetGetCount(_plugins_allowed
) == 0) || // if no white-listing
417 CFSetContainsValue(_plugins_allowed
, bundleID
) || // if [bundleID] white-listed
418 ((shortID
!= NULL
) &&
419 CFSetContainsValue(_plugins_allowed
, shortID
))|| // if [short bundleID] white-listed
420 bundleInfo
->forced
// if "testing" plugin
422 if (!bundleAllowed
) {
423 SCLog(TRUE
, LOG_WARNING
, CFSTR("skipped %@ (not allowed)"), bundleID
);
427 bundleExclude
= (CFSetContainsValue(_plugins_exclude
, bundleID
) || // if [bundleID] excluded
428 ((shortID
!= NULL
) &&
429 CFSetContainsValue(_plugins_exclude
, shortID
)) // if [short bundleID] excluded
432 // sorry, this bundle has been excluded
433 SCLog(TRUE
, LOG_NOTICE
, CFSTR("skipped %@ (excluded)"), bundleID
);
437 if (!bundleInfo
->enabled
&& !bundleInfo
->forced
) {
438 // sorry, this bundle has not been enabled
439 SCLog(TRUE
, LOG_INFO
, CFSTR("skipped %@ (disabled)"), bundleID
);
444 !_SC_CFEqual(bundleID
, CFSTR("com.apple.SystemConfiguration.SCNetworkReachability"))) {
445 forkBundle(bundleInfo
->bundle
, bundleID
);
449 if (!bundleInfo
->verbose
) {
450 bundleInfo
->verbose
= CFSetContainsValue(_plugins_verbose
, bundleID
);
451 if (!bundleInfo
->verbose
) {
452 if (shortID
!= NULL
) {
453 bundleInfo
->verbose
= CFSetContainsValue(_plugins_verbose
, shortID
);
458 if (bundleInfo
->builtin
) {
461 SCLog(TRUE
, LOG_DEBUG
, CFSTR("adding %@"), bundleID
);
463 for (i
= 0; i
< sizeof(builtin_plugins
)/sizeof(builtin_plugins
[0]); i
++) {
464 if (CFEqual(bundleID
, builtin_plugins
[i
].bundleID
)) {
465 bundleInfo
->load
= builtin_plugins
[i
].load
;
466 bundleInfo
->start
= builtin_plugins
[i
].start
;
467 bundleInfo
->prime
= builtin_plugins
[i
].prime
;
468 bundleInfo
->stop
= builtin_plugins
[i
].stop
;
473 if ((bundleInfo
->load
== NULL
) &&
474 (bundleInfo
->start
== NULL
) &&
475 (bundleInfo
->prime
== NULL
) &&
476 (bundleInfo
->stop
== NULL
)) {
477 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@ add failed"), bundleID
);
481 CFErrorRef error
= NULL
;
483 SCLog(TRUE
, LOG_DEBUG
, CFSTR("loading %@"), bundleID
);
486 traceBundle("loading", bundleInfo
->bundle
);
489 if (!CFBundleLoadExecutableAndReturnError(bundleInfo
->bundle
, &error
)) {
490 CFStringRef description
;
492 description
= CFErrorCopyDescription(error
);
493 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@ load failed"), bundleID
);
494 SCLog(TRUE
, LOG_NOTICE
, CFSTR(" %@"), description
);
495 CFRelease(description
);
500 // get bundle entry points
501 bundleInfo
->load
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("load" ), shortID
);
502 bundleInfo
->start
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("start"), shortID
);
503 bundleInfo
->prime
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("prime"), shortID
);
504 bundleInfo
->stop
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("stop" ), shortID
);
507 /* mark this bundle as having been loaded */
508 bundleInfo
->loaded
= TRUE
;
510 /* bump the count of loaded bundles */
511 *nLoaded
= *nLoaded
+ 1;
515 if (shortID
!= NULL
) CFRelease(shortID
);
521 callLoadFunction(const void *value
, void *context
) {
522 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
524 if (!bundleInfo
->loaded
) {
528 if (bundleInfo
->load
== NULL
) {
529 // if no load() function
534 traceBundle("calling load() for", bundleInfo
->bundle
);
537 (*bundleInfo
->load
)(bundleInfo
->bundle
, bundleInfo
->verbose
);
548 callStartFunction(const void *value
, void *context
) {
549 const char *bundleDirName
;
550 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
551 char bundleName
[MAXNAMLEN
+ 1];
552 char bundlePath
[MAXPATHLEN
];
555 if (!bundleInfo
->loaded
) {
559 if (bundleInfo
->start
== NULL
) {
560 // if no start() function
564 /* copy the bundle's path */
565 bundleDirName
= getBundleDirNameAndPath(bundleInfo
->bundle
, bundlePath
, sizeof(bundlePath
));
566 if (bundleDirName
== NULL
) {
567 // if we have a problem with the bundle's path
571 /* copy (just) the bundle's name */
572 if (strlcpy(bundleName
, bundleDirName
, sizeof(bundleName
)) > sizeof(bundleName
)) {
573 // if we have a problem with the bundle's name
576 len
= strlen(bundleName
) - (sizeof(BUNDLE_DIR_EXTENSION
) - 1);
577 bundleName
[len
] = '\0';
580 traceBundle("calling start() for", bundleInfo
->bundle
);
583 (*bundleInfo
->start
)(bundleName
, bundlePath
);
594 callPrimeFunction(const void *value
, void *context
) {
595 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
597 if (!bundleInfo
->loaded
) {
601 if (bundleInfo
->prime
== NULL
) {
602 // if no prime() function
607 traceBundle("calling prime() for", bundleInfo
->bundle
);
610 (*bundleInfo
->prime
)();
621 stopComplete(void *info
)
623 CFBundleRef bundle
= (CFBundleRef
)info
;
624 CFStringRef bundleID
= CFBundleGetIdentifier(bundle
);
625 CFRunLoopSourceRef stopRls
;
627 SCLog(TRUE
, LOG_DEBUG
, CFSTR("** %@ complete (%f)"), bundleID
, CFAbsoluteTimeGetCurrent());
629 stopRls
= (CFRunLoopSourceRef
)CFDictionaryGetValue(exiting
, bundle
);
630 if (stopRls
== NULL
) {
634 CFRunLoopSourceInvalidate(stopRls
);
636 CFDictionaryRemoveValue(exiting
, bundle
);
638 if (CFDictionaryGetCount(exiting
) == 0) {
641 // if all of the plugins are happy
642 status
= server_shutdown();
643 SCLog(TRUE
, LOG_DEBUG
, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent());
652 stopDelayed(CFRunLoopTimerRef timer
, void *info
)
659 SCLog(TRUE
, LOG_ERR
, CFSTR("server shutdown was delayed, unresponsive plugins:"));
662 * we've asked our plugins to shutdown but someone
665 n
= CFDictionaryGetCount(exiting
);
666 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
667 CFDictionaryGetKeysAndValues(exiting
, keys
, NULL
);
668 for (i
= 0; i
< n
; i
++) {
670 CFStringRef bundleID
;
672 bundle
= (CFBundleRef
)keys
[i
];
673 bundleID
= CFBundleGetIdentifier(bundle
);
674 SCLog(TRUE
, LOG_ERR
, CFSTR("** %@"), bundleID
);
676 CFAllocatorDeallocate(NULL
, keys
);
678 status
= server_shutdown();
683 stopRLSCopyDescription(const void *info
)
685 CFBundleRef bundle
= (CFBundleRef
)info
;
687 return CFStringCreateWithFormat(NULL
,
689 CFSTR("<stopRLS %p> {bundleID = %@}"),
691 CFBundleGetIdentifier(bundle
));
696 stopBundle(const void *value
, void *context
) {
697 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
698 CFRunLoopSourceRef stopRls
;
699 CFRunLoopSourceContext stopContext
= { 0 // version
700 , bundleInfo
->bundle
// info
702 , CFRelease
// release
703 , stopRLSCopyDescription
// copyDescription
708 , stopComplete
// perform
711 if (!bundleInfo
->loaded
) {
715 if (bundleInfo
->stop
== NULL
) {
716 // if no stop() function
720 stopRls
= CFRunLoopSourceCreate(NULL
, 0, &stopContext
);
721 CFRunLoopAddSource(CFRunLoopGetCurrent(), stopRls
, kCFRunLoopDefaultMode
);
722 CFDictionaryAddValue(exiting
, bundleInfo
->bundle
, stopRls
);
725 (*bundleInfo
->stop
)(stopRls
);
735 * If defined, call each bundles stop() function. This function is
736 * called when configd has been asked to shut down (via a SIGTERM). The
737 * function should signal the provided run loop source when it is "ready"
738 * for the shut down to proceeed.
740 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle stop() functions"));
741 CFArrayApplyFunction(allBundles
,
742 CFRangeMake(0, CFArrayGetCount(allBundles
)),
746 if (CFDictionaryGetCount(exiting
) == 0) {
749 // if all of the plugins are happy
750 status
= server_shutdown();
751 SCLog(TRUE
, LOG_DEBUG
, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent());
754 CFRunLoopTimerRef timer
;
757 * launchd will only wait 20 seconds before sending us a
758 * SIGKILL and because we want to know what's stuck before
759 * that time so set our own "we're not waiting any longer"
760 * timeout for 15 seconds.
762 timer
= CFRunLoopTimerCreate(NULL
, /* allocator */
763 CFAbsoluteTimeGetCurrent() + 15.0, /* fireDate (in 15 seconds) */
764 0.0, /* interval (== one-shot) */
767 stopDelayed
, /* callout */
769 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
782 termRLSCopyDescription(const void *info
)
784 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SIGTERM RLS>"));
790 plugin_term(int *status
)
792 CFRunLoopSourceContext termContext
= { 0 // version
796 , termRLSCopyDescription
// copyDescription
801 , stopBundles
// perform
803 CFRunLoopSourceRef termRls
;
805 if (plugin_runLoop
== NULL
) {
808 return FALSE
; // don't delay shutdown
811 if (exiting
!= NULL
) {
812 // if shutdown already active
816 SCLog(TRUE
, LOG_DEBUG
, CFSTR("starting server shutdown (%f)"), CFAbsoluteTimeGetCurrent());
818 exiting
= CFDictionaryCreateMutable(NULL
,
820 &kCFTypeDictionaryKeyCallBacks
,
821 &kCFTypeDictionaryValueCallBacks
);
823 termRls
= CFRunLoopSourceCreate(NULL
, 0, &termContext
);
824 CFRunLoopAddSource(plugin_runLoop
, termRls
, kCFRunLoopDefaultMode
);
825 CFRunLoopSourceSignal(termRls
);
827 CFRunLoopWakeUp(plugin_runLoop
);
834 #pragma mark initialization
839 timerCallback(CFRunLoopTimerRef timer
, void *info
)
844 if ((pass
> 120) && ((pass
% 60) != 0)) {
848 traceBundle("the [plugin] CFRunLoop is waiting...", NULL
);
855 sortBundles(CFMutableArrayRef orig
)
859 CFMutableArrayRef
new;
860 CFMutableSetRef orig_bundleIDs
;
862 orig_bundleIDs
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
864 n
= CFArrayGetCount(orig
);
865 for (i
= 0; i
< n
; i
++) {
866 bundleInfoRef bundleInfo
= (bundleInfoRef
)CFArrayGetValueAtIndex(orig
, i
);
867 CFStringRef bundleID
= CFBundleGetIdentifier(bundleInfo
->bundle
);
869 if (bundleID
!= NULL
) {
870 CFSetAddValue(orig_bundleIDs
, bundleID
);
874 new = CFArrayCreateMutable(NULL
, 0, NULL
);
876 Boolean inserted
= FALSE
;
878 for (i
= 0; i
< n
; i
++) {
879 bundleInfoRef bundleInfo1
= (bundleInfoRef
)CFArrayGetValueAtIndex(orig
, i
);
880 CFStringRef bundleID1
= CFBundleGetIdentifier(bundleInfo1
->bundle
);
882 CFDictionaryRef dict
;
885 CFArrayRef
requires = NULL
;
887 dict
= isA_CFDictionary(CFBundleGetInfoDictionary(bundleInfo1
->bundle
));
889 requires = CFDictionaryGetValue(dict
, kSCBundleRequiresKey
);
890 requires = isA_CFArray(requires);
892 if (bundleID1
== NULL
|| requires == NULL
) {
893 CFArrayInsertValueAtIndex(new, 0, bundleInfo1
);
894 CFArrayRemoveValueAtIndex(orig
, i
);
898 count
= nRequires
= CFArrayGetCount(requires);
899 for (j
= 0; j
< nRequires
; j
++) {
902 CFStringRef r
= CFArrayGetValueAtIndex(requires, j
);
904 if (!CFSetContainsValue(orig_bundleIDs
, r
)) {
905 // if dependency not present
910 nNew
= CFArrayGetCount(new);
911 for (k
= 0; k
< nNew
; k
++) {
912 bundleInfoRef bundleInfo2
= (bundleInfoRef
)CFArrayGetValueAtIndex(new, k
);
913 CFStringRef bundleID2
= CFBundleGetIdentifier(bundleInfo2
->bundle
);
915 if (bundleID2
&& CFEqual(bundleID2
, r
)) {
921 /* all dependencies are met, append */
922 CFArrayAppendValue(new, bundleInfo1
);
923 CFArrayRemoveValueAtIndex(orig
, i
);
929 if (inserted
== FALSE
) {
930 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Bundles have circular dependency!!!"));
934 n
= CFArrayGetCount(orig
);
936 if (CFArrayGetCount(orig
) > 0) {
937 /* we have a circular dependency, append remaining items on new array */
938 CFArrayAppendArray(new, orig
, CFRangeMake(0, CFArrayGetCount(orig
)));
941 /* new one is a sorted version of original */
944 CFArrayRemoveAllValues(orig
);
945 CFArrayAppendArray(orig
, new, CFRangeMake(0, CFArrayGetCount(new)));
947 CFRelease(orig_bundleIDs
);
955 * An alternate CFRelease() that we can use to fake out the
958 static __inline__
void
959 ALT_CFRelease(CFTypeRef cf
)
967 plugin_exec(void *arg
)
972 /* keep track of bundles */
973 allBundles
= CFArrayCreateMutable(NULL
, 0, NULL
);
975 /* add white-listed plugins to those we'll allow to be loaded */
976 for (i
= 0; i
< N_PLUGIN_WHITELIST
; i
++) {
977 if (pluginWhitelist
[i
] != NULL
) {
978 CFSetSetValue(_plugins_allowed
, pluginWhitelist
[i
]);
982 /* allow plug-ins to exec child/helper processes */
983 _SCDPluginExecInit();
986 char path
[MAXPATHLEN
];
987 NSSearchPathEnumerationState state
;
990 * identify and load all bundles
992 state
= NSStartSearchPathEnumeration(NSLibraryDirectory
,
994 while ((state
= NSGetNextSearchPathEnumeration(state
, path
))) {
998 #if TARGET_IPHONE_SIMULATOR
999 const char *path_sim_prefix
;
1001 path_sim_prefix
= getenv("IPHONE_SIMULATOR_ROOT");
1002 if ((path_sim_prefix
!= NULL
) && (strcmp(path_sim_prefix
, ".") != 0)) {
1003 char path_sim
[MAXPATHLEN
];
1005 strlcpy(path_sim
, path_sim_prefix
, sizeof(path_sim
));
1006 strlcat(path_sim
, path
, sizeof(path_sim
));
1007 strlcpy(path
, path_sim
, sizeof(path
));
1011 #endif // TARGET_IPHONE_SIMULATOR
1013 /* load any available bundle */
1014 strlcat(path
, BUNDLE_DIRECTORY
, sizeof(path
));
1015 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("searching for bundles in \"%s\""), path
);
1016 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
1020 bundles
= CFBundleCreateBundlesFromDirectory(NULL
, url
, CFSTR(".bundle"));
1023 if (bundles
!= NULL
) {
1027 n
= CFArrayGetCount(bundles
);
1028 for (i
= 0; i
< n
; i
++) {
1031 bundle
= (CFBundleRef
)CFArrayGetValueAtIndex(bundles
, i
);
1032 addBundle(bundle
, FALSE
);
1034 // The CFBundleCreateBundlesFromDirectory() API has
1035 // a known/outstanding bug in that it over-retains the
1036 // returned bundles. Since we do not expect this to
1037 // be fixed we release the extra references.
1039 // See <rdar://problems/4912137&6078752> for more info.
1041 // Also, we use the hack below to keep the static
1043 ALT_CFRelease(bundle
);
1049 sortBundles(allBundles
);
1055 * load (only) the specified bundle
1057 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
1059 strlen((char *)arg
),
1061 bundle
= CFBundleCreate(NULL
, url
);
1062 if (bundle
!= NULL
) {
1063 addBundle(bundle
, TRUE
);
1070 * Look for the InterfaceNamer plugin, and move it to the start
1073 * Load the InterfaceNamer plugin (and thereby start its thread)
1074 * first in an attempt to minimize the amount of time that
1075 * opendirectoryd has to wait for the platform UUID to appear in
1078 * InterfaceNamer is responsible for creating the platform UUID on
1079 * platforms without a UUID in ROM. Until the platform UUID is created
1080 * and stashed in nvram, all calls to opendirectoryd to do things like
1081 * getpwuid() will block, because opendirectoryd will block while trying
1082 * to read the platform UUID from the kernel.
1084 * As an example, dlopen() causes XPC to do some intialization, and
1085 * part of that initialization involves communicating with xpcd.
1086 * Since xpcd calls getpwuid_r() during its initialization, it will
1087 * block until the platform UUID is available.
1089 for (i
= 0; i
< CFArrayGetCount(allBundles
); i
++) {
1090 bundleInfoRef bi
= (bundleInfoRef
)CFArrayGetValueAtIndex(allBundles
, i
);
1091 CFStringRef bundleID
= CFBundleGetIdentifier(bi
->bundle
);
1093 if (_SC_CFEqual(bundleID
,
1094 CFSTR("com.apple.SystemConfiguration.InterfaceNamer")))
1096 CFArrayRemoveValueAtIndex(allBundles
, i
);
1097 CFArrayInsertValueAtIndex(allBundles
, 0, bi
);
1103 traceBundle("before loading any plugins", NULL
);
1109 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("loading bundles"));
1110 CFArrayApplyFunction(allBundles
,
1111 CFRangeMake(0, CFArrayGetCount(allBundles
)),
1116 * If defined, call each bundles load() function. This function (or
1117 * the start() function) should initialize any variables, open any
1118 * sessions with "configd", and register any needed notifications.
1120 * Note: Establishing initial information in the store should be
1121 * deferred until the prime() initialization function so that
1122 * any bundles which want to receive a notification that the
1123 * data has changed will have an opportunity to install a
1124 * notification handler.
1126 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle load() functions"));
1127 CFArrayApplyFunction(allBundles
,
1128 CFRangeMake(0, CFArrayGetCount(allBundles
)),
1133 // if no bundles loaded
1138 * If defined, call each bundles start() function. This function is
1139 * called after the bundle has been loaded and its load() function has
1140 * been called. It should initialize any variables, open any sessions
1141 * with "configd", and register any needed notifications.
1143 * Note: Establishing initial information in the store should be
1144 * deferred until the prime() initialization function so that
1145 * any bundles which want to receive a notification that the
1146 * data has changed will have an opportunity to install a
1147 * notification handler.
1149 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle start() functions"));
1150 CFArrayApplyFunction(allBundles
,
1151 CFRangeMake(0, CFArrayGetCount(allBundles
)),
1156 * If defined, call each bundles prime() function. This function is
1157 * called after the bundle has been loaded and its load() and start()
1158 * functions have been called. It should initialize any configuration
1159 * information and/or state in the store.
1161 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle prime() functions"));
1162 CFArrayApplyFunction(allBundles
,
1163 CFRangeMake(0, CFArrayGetCount(allBundles
)),
1168 if (arg
== NULL
&& (nLoaded
> 0)) {
1169 CFRunLoopTimerRef timer
;
1171 /* allocate a periodic event (to help show we're not blocking) */
1172 timer
= CFRunLoopTimerCreate(NULL
, /* allocator */
1173 CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */
1177 timerCallback
, /* callout */
1178 NULL
); /* context */
1179 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
1185 traceBundle("about to start plugin CFRunLoop", NULL
);
1189 * The assumption is that each loaded plugin will establish CFMachPortRef,
1190 * CFSocketRef, and CFRunLoopTimerRef input sources to handle any events
1191 * and register these sources with this threads run loop. If the plugin
1192 * needs to wait and/or block at any time it should do so only in its a
1195 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("starting plugin CFRunLoop"));
1196 plugin_runLoop
= CFRunLoopGetCurrent();
1197 pthread_setname_np("Main plugin thread");
1202 SCLog(_configd_verbose
, LOG_INFO
, CFSTR("No more work for the \"configd\" plugins"));
1203 plugin_runLoop
= NULL
;
1212 pthread_attr_t tattr
;
1215 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("Starting thread for plug-ins..."));
1216 pthread_attr_init(&tattr
);
1217 pthread_attr_setscope(&tattr
, PTHREAD_SCOPE_SYSTEM
);
1218 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
1219 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
1220 pthread_create(&tid
, &tattr
, plugin_exec
, NULL
);
1221 pthread_attr_destroy(&tattr
);
1222 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR(" thread id=0x%08x"), tid
);