2 * Copyright (c) 2000-2009, 2011, 2012 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)
64 #if !TARGET_OS_EMBEDDED
65 #define PLUGIN_MACOSX(p) CFSTR(p)
66 #define PLUGIN_IOS(p) NULL
67 #else // !TARGET_OS_EMBEDDED
68 #define PLUGIN_MACOSX(p) NULL
69 #define PLUGIN_IOS(p) CFSTR(p)
70 #endif // !TARGET_OS_EMBEDDED
72 // white-listed (ok-to-load) bundle identifiers
73 static const CFStringRef pluginWhitelist
[] = {
74 PLUGIN_MACOSX("com.apple.SystemConfiguration.Apple80211"),
75 PLUGIN_MACOSX("com.apple.SystemConfiguration.ApplicationFirewall"),
76 PLUGIN_MACOSX("com.apple.SystemConfiguration.Bluetooth"),
77 PLUGIN_ALL ("com.apple.SystemConfiguration.EAPOLController"),
78 PLUGIN_ALL ("com.apple.SystemConfiguration.IPConfiguration"),
79 PLUGIN_ALL ("com.apple.SystemConfiguration.IPMonitor"),
80 PLUGIN_ALL ("com.apple.SystemConfiguration.InterfaceNamer"),
81 PLUGIN_ALL ("com.apple.SystemConfiguration.KernelEventMonitor"),
82 PLUGIN_ALL ("com.apple.SystemConfiguration.LinkConfiguration"),
83 PLUGIN_ALL ("com.apple.SystemConfiguration.Logger"),
84 PLUGIN_ALL ("com.apple.SystemConfiguration.PPPController"),
85 PLUGIN_ALL ("com.apple.SystemConfiguration.PreferencesMonitor"),
86 #ifdef HAVE_REACHABILITY_SERVER
87 PLUGIN_ALL ("com.apple.SystemConfiguration.SCNetworkReachability"),
88 #endif // HAVE_REACHABILITY_SERVER
89 PLUGIN_MACOSX("com.apple.SystemConfiguration.wwanConfig"),
90 PLUGIN_MACOSX("com.apple.print.notification"),
92 #define N_PLUGIN_WHITELIST (sizeof(pluginWhitelist) / sizeof(pluginWhitelist[0]))
102 SCDynamicStoreBundleLoadFunction load
;
103 SCDynamicStoreBundleStartFunction start
;
104 SCDynamicStoreBundlePrimeFunction prime
;
105 SCDynamicStoreBundleStopFunction stop
;
109 // all loaded bundles
110 static CFMutableArrayRef allBundles
= NULL
;
113 static CFMutableDictionaryRef exiting
= NULL
;
115 // plugin CFRunLoopRef
117 CFRunLoopRef plugin_runLoop
= NULL
;
120 extern SCDynamicStoreBundleLoadFunction load_IPMonitor
;
121 extern SCDynamicStoreBundlePrimeFunction prime_IPMonitor
;
122 extern SCDynamicStoreBundleLoadFunction load_InterfaceNamer
;
123 extern SCDynamicStoreBundleLoadFunction load_KernelEventMonitor
;
124 extern SCDynamicStoreBundlePrimeFunction prime_KernelEventMonitor
;
125 extern SCDynamicStoreBundleLoadFunction load_LinkConfiguration
;
126 extern SCDynamicStoreBundleLoadFunction load_PreferencesMonitor
;
127 extern SCDynamicStoreBundlePrimeFunction prime_PreferencesMonitor
;
128 #ifdef HAVE_REACHABILITY_SERVER
129 extern SCDynamicStoreBundleLoadFunction load_SCNetworkReachability
;
130 #endif // HAVE_REACHABILITY_SERVER
134 const CFStringRef bundleID
;
135 const void *load
; // SCDynamicStoreBundleLoadFunction
136 const void *start
; // SCDynamicStoreBundleStartFunction
137 const void *prime
; // SCDynamicStoreBundlePrimeFunction
138 const void *stop
; // SCDynamicStoreBundleStopFunction
139 } builtin
, *builtinRef
;
142 static const builtin builtin_plugins
[] = {
144 CFSTR("com.apple.SystemConfiguration.IPMonitor"),
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 #ifdef HAVE_REACHABILITY_SERVER
180 CFSTR("com.apple.SystemConfiguration.SCNetworkReachability"),
181 &load_SCNetworkReachability
,
186 #endif // HAVE_REACHABILITY_SERVER
192 traceBundle(const char *op
, CFBundleRef bundle
)
194 if (_configd_trace
!= NULL
) {
195 if (bundle
!= NULL
) {
196 CFStringRef bundleID
= CFBundleGetIdentifier(bundle
);
198 SCTrace(TRUE
, _configd_trace
,
199 CFSTR("bundle : %s %@\n"),
203 SCTrace(TRUE
, _configd_trace
,
204 CFSTR("bundle : %s\n"),
215 addBundle(CFBundleRef bundle
, Boolean forceEnabled
)
217 CFDictionaryRef bundleDict
;
218 bundleInfoRef bundleInfo
;
220 bundleInfo
= CFAllocatorAllocate(NULL
, sizeof(*bundleInfo
), 0);
221 bundleInfo
->bundle
= (CFBundleRef
)CFRetain(bundle
);
222 bundleInfo
->loaded
= FALSE
;
223 bundleInfo
->builtin
= FALSE
;
224 bundleInfo
->enabled
= TRUE
;
225 bundleInfo
->forced
= forceEnabled
;
226 bundleInfo
->verbose
= FALSE
;
227 bundleInfo
->load
= NULL
;
228 bundleInfo
->start
= NULL
;
229 bundleInfo
->prime
= NULL
;
230 bundleInfo
->stop
= NULL
;
232 bundleDict
= CFBundleGetInfoDictionary(bundle
);
233 if (isA_CFDictionary(bundleDict
)) {
236 bVal
= CFDictionaryGetValue(bundleDict
, kSCBundleIsBuiltinKey
);
237 if (isA_CFBoolean(bVal
)) {
238 bundleInfo
->builtin
= CFBooleanGetValue(bVal
);
241 bVal
= CFDictionaryGetValue(bundleDict
, kSCBundleEnabledKey
);
242 if (isA_CFBoolean(bVal
)) {
243 bundleInfo
->enabled
= CFBooleanGetValue(bVal
);
246 bVal
= CFDictionaryGetValue(bundleDict
, kSCBundleVerboseKey
);
247 if (isA_CFBoolean(bVal
)) {
248 bundleInfo
->verbose
= CFBooleanGetValue(bVal
);
252 CFArrayAppendValue(allBundles
, bundleInfo
);
257 static CF_RETURNS_RETAINED CFStringRef
258 shortBundleIdentifier(CFStringRef bundleID
)
260 CFIndex len
= CFStringGetLength(bundleID
);
262 CFStringRef shortID
= NULL
;
264 if (CFStringFindWithOptions(bundleID
,
269 range
.location
= range
.location
+ range
.length
;
270 range
.length
= len
- range
.location
;
271 shortID
= CFStringCreateWithSubstring(NULL
, bundleID
, range
);
279 getBundleSymbol(CFBundleRef bundle
, CFStringRef functionName
, CFStringRef shortID
)
283 // search for load(), start(), prime(), stop(), ...
284 func
= CFBundleGetFunctionPointerForName(bundle
, functionName
);
289 if (shortID
!= NULL
) {
290 CFStringRef altFunctionName
;
292 // search for load_XXX(), ...
293 altFunctionName
= CFStringCreateWithFormat(NULL
,
298 func
= CFBundleGetFunctionPointerForName(bundle
, altFunctionName
);
299 CFRelease(altFunctionName
);
307 getBundleDirNameAndPath(CFBundleRef bundle
, char *buf
, size_t buf_len
)
314 url
= CFBundleCopyBundleURL(bundle
);
319 ok
= CFURLGetFileSystemRepresentation(url
, TRUE
, (UInt8
*)buf
, buf_len
);
325 cp
= strrchr(buf
, '/');
332 /* check if this directory entry is a valid bundle name */
334 if (len
<= (int)sizeof(BUNDLE_DIR_EXTENSION
)) {
335 /* if entry name isn't long enough */
339 len
-= sizeof(BUNDLE_DIR_EXTENSION
) - 1;
340 if (strcmp(&cp
[len
], BUNDLE_DIR_EXTENSION
) != 0) {
341 /* if entry name doesn't end with ".bundle" */
354 forkBundle_setup(pid_t pid
, void *setupContext
)
358 unsetenv("__LAUNCHD_FD");
359 setenv("__FORKED_PLUGIN__", "Yes", 1);
367 forkBundle(CFBundleRef bundle
, CFStringRef bundleID
)
369 char *argv
[] = { "configd", "-d", "-t", NULL
, NULL
};
371 char path
[MAXPATHLEN
];
374 // get the bundle's path
375 name
= getBundleDirNameAndPath(bundle
, path
, sizeof(path
));
377 SCLog(TRUE
, LOG_ERR
, CFSTR("skipped %@ (could not determine path)"), bundle
);
381 // fork and exec configd opting to load only this plugin
383 pid
= _SCDPluginExecCommand2(NULL
, NULL
, 0, 0, "/usr/libexec/configd", argv
, forkBundle_setup
, NULL
);
386 CFSTR("skipped %@ (could not exec child) : %s"),
392 SCLog(TRUE
, LOG_NOTICE
, CFSTR("forked %@, pid=%d"), bundleID
, pid
);
398 loadBundle(const void *value
, void *context
) {
399 CFStringRef bundleID
;
400 Boolean bundleAllowed
;
401 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
402 Boolean bundleExclude
;
403 CFIndex
*nLoaded
= (CFIndex
*)context
;
406 bundleID
= CFBundleGetIdentifier(bundleInfo
->bundle
);
407 if (bundleID
== NULL
) {
408 // sorry, no bundles without a bundle identifier
409 SCLog(TRUE
, LOG_NOTICE
, CFSTR("skipped %@ (no bundle ID)"), bundleInfo
->bundle
);
413 shortID
= shortBundleIdentifier(bundleID
);
415 bundleAllowed
= ((CFSetGetCount(_plugins_allowed
) == 0) || // if no white-listing
416 CFSetContainsValue(_plugins_allowed
, bundleID
) || // if [bundleID] white-listed
417 ((shortID
!= NULL
) &&
418 CFSetContainsValue(_plugins_allowed
, shortID
))|| // if [short bundleID] white-listed
419 bundleInfo
->forced
// if "testing" plugin
421 if (!bundleAllowed
) {
422 SCLog(TRUE
, LOG_WARNING
, CFSTR("skipped %@ (not allowed)"), bundleID
);
426 bundleExclude
= (CFSetContainsValue(_plugins_exclude
, bundleID
) || // if [bundleID] excluded
427 ((shortID
!= NULL
) &&
428 CFSetContainsValue(_plugins_exclude
, shortID
)) // if [short bundleID] excluded
431 // sorry, this bundle has been excluded
432 SCLog(TRUE
, LOG_NOTICE
, CFSTR("skipped %@ (excluded)"), bundleID
);
436 if (!bundleInfo
->enabled
&& !bundleInfo
->forced
) {
437 // sorry, this bundle has not been enabled
438 SCLog(TRUE
, LOG_INFO
, CFSTR("skipped %@ (disabled)"), bundleID
);
443 !_SC_CFEqual(bundleID
, CFSTR("com.apple.SystemConfiguration.SCNetworkReachability"))) {
444 forkBundle(bundleInfo
->bundle
, bundleID
);
448 if (!bundleInfo
->verbose
) {
449 bundleInfo
->verbose
= CFSetContainsValue(_plugins_verbose
, bundleID
);
450 if (!bundleInfo
->verbose
) {
451 if (shortID
!= NULL
) {
452 bundleInfo
->verbose
= CFSetContainsValue(_plugins_verbose
, shortID
);
457 if (bundleInfo
->builtin
) {
460 SCLog(TRUE
, LOG_DEBUG
, CFSTR("adding %@"), bundleID
);
462 for (i
= 0; i
< sizeof(builtin_plugins
)/sizeof(builtin_plugins
[0]); i
++) {
463 if (CFEqual(bundleID
, builtin_plugins
[i
].bundleID
)) {
464 bundleInfo
->load
= builtin_plugins
[i
].load
;
465 bundleInfo
->start
= builtin_plugins
[i
].start
;
466 bundleInfo
->prime
= builtin_plugins
[i
].prime
;
467 bundleInfo
->stop
= builtin_plugins
[i
].stop
;
472 if ((bundleInfo
->load
== NULL
) &&
473 (bundleInfo
->start
== NULL
) &&
474 (bundleInfo
->prime
== NULL
) &&
475 (bundleInfo
->stop
== NULL
)) {
476 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@ add failed"), bundleID
);
480 CFErrorRef error
= NULL
;
482 SCLog(TRUE
, LOG_DEBUG
, CFSTR("loading %@"), bundleID
);
485 traceBundle("loading", bundleInfo
->bundle
);
488 if (!CFBundleLoadExecutableAndReturnError(bundleInfo
->bundle
, &error
)) {
489 CFStringRef description
;
491 description
= CFErrorCopyDescription(error
);
492 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@ load failed"), bundleID
);
493 SCLog(TRUE
, LOG_NOTICE
, CFSTR(" %@"), description
);
494 CFRelease(description
);
499 // get bundle entry points
500 bundleInfo
->load
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("load" ), shortID
);
501 bundleInfo
->start
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("start"), shortID
);
502 bundleInfo
->prime
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("prime"), shortID
);
503 bundleInfo
->stop
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("stop" ), shortID
);
506 /* mark this bundle as having been loaded */
507 bundleInfo
->loaded
= TRUE
;
509 /* bump the count of loaded bundles */
510 *nLoaded
= *nLoaded
+ 1;
514 if (shortID
!= NULL
) CFRelease(shortID
);
520 callLoadFunction(const void *value
, void *context
) {
521 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
523 if (!bundleInfo
->loaded
) {
527 if (bundleInfo
->load
== NULL
) {
528 // if no load() function
533 traceBundle("calling load() for", bundleInfo
->bundle
);
536 (*bundleInfo
->load
)(bundleInfo
->bundle
, bundleInfo
->verbose
);
547 callStartFunction(const void *value
, void *context
) {
548 const char *bundleDirName
;
549 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
550 char bundleName
[MAXNAMLEN
+ 1];
551 char bundlePath
[MAXPATHLEN
];
554 if (!bundleInfo
->loaded
) {
558 if (bundleInfo
->start
== NULL
) {
559 // if no start() function
563 /* copy the bundle's path */
564 bundleDirName
= getBundleDirNameAndPath(bundleInfo
->bundle
, bundlePath
, sizeof(bundlePath
));
565 if (bundleDirName
== NULL
) {
566 // if we have a problem with the bundle's path
570 /* copy (just) the bundle's name */
571 if (strlcpy(bundleName
, bundleDirName
, sizeof(bundleName
)) > sizeof(bundleName
)) {
572 // if we have a problem with the bundle's name
575 len
= strlen(bundleName
) - (sizeof(BUNDLE_DIR_EXTENSION
) - 1);
576 bundleName
[len
] = '\0';
579 traceBundle("calling start() for", bundleInfo
->bundle
);
582 (*bundleInfo
->start
)(bundleName
, bundlePath
);
593 callPrimeFunction(const void *value
, void *context
) {
594 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
596 if (!bundleInfo
->loaded
) {
600 if (bundleInfo
->prime
== NULL
) {
601 // if no prime() function
606 traceBundle("calling prime() for", bundleInfo
->bundle
);
609 (*bundleInfo
->prime
)();
620 stopComplete(void *info
)
622 CFBundleRef bundle
= (CFBundleRef
)info
;
623 CFStringRef bundleID
= CFBundleGetIdentifier(bundle
);
624 CFRunLoopSourceRef stopRls
;
626 SCLog(TRUE
, LOG_DEBUG
, CFSTR("** %@ complete (%f)"), bundleID
, CFAbsoluteTimeGetCurrent());
628 stopRls
= (CFRunLoopSourceRef
)CFDictionaryGetValue(exiting
, bundle
);
629 if (stopRls
== NULL
) {
633 CFRunLoopSourceInvalidate(stopRls
);
635 CFDictionaryRemoveValue(exiting
, bundle
);
637 if (CFDictionaryGetCount(exiting
) == 0) {
640 // if all of the plugins are happy
641 status
= server_shutdown();
642 SCLog(TRUE
, LOG_DEBUG
, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent());
651 stopDelayed(CFRunLoopTimerRef timer
, void *info
)
658 SCLog(TRUE
, LOG_ERR
, CFSTR("server shutdown was delayed, unresponsive plugins:"));
661 * we've asked our plugins to shutdown but someone
664 n
= CFDictionaryGetCount(exiting
);
665 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
666 CFDictionaryGetKeysAndValues(exiting
, keys
, NULL
);
667 for (i
= 0; i
< n
; i
++) {
669 CFStringRef bundleID
;
671 bundle
= (CFBundleRef
)keys
[i
];
672 bundleID
= CFBundleGetIdentifier(bundle
);
673 SCLog(TRUE
, LOG_ERR
, CFSTR("** %@"), bundleID
);
675 CFAllocatorDeallocate(NULL
, keys
);
677 status
= server_shutdown();
682 stopRLSCopyDescription(const void *info
)
684 CFBundleRef bundle
= (CFBundleRef
)info
;
686 return CFStringCreateWithFormat(NULL
,
688 CFSTR("<stopRLS %p> {bundleID = %@}"),
690 CFBundleGetIdentifier(bundle
));
695 stopBundle(const void *value
, void *context
) {
696 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
697 CFRunLoopSourceRef stopRls
;
698 CFRunLoopSourceContext stopContext
= { 0 // version
699 , bundleInfo
->bundle
// info
701 , CFRelease
// release
702 , stopRLSCopyDescription
// copyDescription
707 , stopComplete
// perform
710 if (!bundleInfo
->loaded
) {
714 if (bundleInfo
->stop
== NULL
) {
715 // if no stop() function
719 stopRls
= CFRunLoopSourceCreate(NULL
, 0, &stopContext
);
720 CFRunLoopAddSource(CFRunLoopGetCurrent(), stopRls
, kCFRunLoopDefaultMode
);
721 CFDictionaryAddValue(exiting
, bundleInfo
->bundle
, stopRls
);
724 (*bundleInfo
->stop
)(stopRls
);
734 * If defined, call each bundles stop() function. This function is
735 * called when configd has been asked to shut down (via a SIGTERM). The
736 * function should signal the provided run loop source when it is "ready"
737 * for the shut down to proceeed.
739 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle stop() functions"));
740 CFArrayApplyFunction(allBundles
,
741 CFRangeMake(0, CFArrayGetCount(allBundles
)),
745 if (CFDictionaryGetCount(exiting
) == 0) {
748 // if all of the plugins are happy
749 status
= server_shutdown();
750 SCLog(TRUE
, LOG_DEBUG
, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent());
753 CFRunLoopTimerRef timer
;
756 * launchd will only wait 20 seconds before sending us a
757 * SIGKILL and because we want to know what's stuck before
758 * that time so set our own "we're not waiting any longer"
759 * timeout for 15 seconds.
761 timer
= CFRunLoopTimerCreate(NULL
, /* allocator */
762 CFAbsoluteTimeGetCurrent() + 15.0, /* fireDate (in 15 seconds) */
763 0.0, /* interval (== one-shot) */
766 stopDelayed
, /* callout */
768 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
781 termRLSCopyDescription(const void *info
)
783 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SIGTERM RLS>"));
789 plugin_term(int *status
)
791 CFRunLoopSourceContext termContext
= { 0 // version
795 , termRLSCopyDescription
// copyDescription
800 , stopBundles
// perform
802 CFRunLoopSourceRef termRls
;
804 if (plugin_runLoop
== NULL
) {
807 return FALSE
; // don't delay shutdown
810 if (exiting
!= NULL
) {
811 // if shutdown already active
815 SCLog(TRUE
, LOG_DEBUG
, CFSTR("starting server shutdown (%f)"), CFAbsoluteTimeGetCurrent());
817 exiting
= CFDictionaryCreateMutable(NULL
,
819 &kCFTypeDictionaryKeyCallBacks
,
820 &kCFTypeDictionaryValueCallBacks
);
822 termRls
= CFRunLoopSourceCreate(NULL
, 0, &termContext
);
823 CFRunLoopAddSource(plugin_runLoop
, termRls
, kCFRunLoopDefaultMode
);
824 CFRunLoopSourceSignal(termRls
);
826 CFRunLoopWakeUp(plugin_runLoop
);
833 #pragma mark initialization
838 timerCallback(CFRunLoopTimerRef timer
, void *info
)
843 if ((pass
> 120) && ((pass
% 60) != 0)) {
847 traceBundle("the [plugin] CFRunLoop is waiting...", NULL
);
854 sortBundles(CFMutableArrayRef orig
)
856 CFMutableArrayRef
new;
858 new = CFArrayCreateMutable(NULL
, 0, NULL
);
859 while (CFArrayGetCount(orig
) > 0) {
861 Boolean inserted
= FALSE
;
862 int nOrig
= CFArrayGetCount(orig
);
864 for (i
= 0; i
< nOrig
; i
++) {
865 bundleInfoRef bundleInfo1
= (bundleInfoRef
)CFArrayGetValueAtIndex(orig
, i
);
866 CFStringRef bundleID1
= CFBundleGetIdentifier(bundleInfo1
->bundle
);
868 CFDictionaryRef dict
;
871 CFArrayRef
requires = NULL
;
873 dict
= isA_CFDictionary(CFBundleGetInfoDictionary(bundleInfo1
->bundle
));
875 requires = CFDictionaryGetValue(dict
, kSCBundleRequiresKey
);
876 requires = isA_CFArray(requires);
878 if (bundleID1
== NULL
|| requires == NULL
) {
879 CFArrayInsertValueAtIndex(new, 0, bundleInfo1
);
880 CFArrayRemoveValueAtIndex(orig
, i
);
884 count
= nRequires
= CFArrayGetCount(requires);
885 for (j
= 0; j
< nRequires
; j
++) {
888 CFStringRef r
= CFArrayGetValueAtIndex(requires, j
);
890 nNew
= CFArrayGetCount(new);
891 for (k
= 0; k
< nNew
; k
++) {
892 bundleInfoRef bundleInfo2
= (bundleInfoRef
)CFArrayGetValueAtIndex(new, k
);
893 CFStringRef bundleID2
= CFBundleGetIdentifier(bundleInfo2
->bundle
);
895 if (bundleID2
&& CFEqual(bundleID2
, r
)) {
901 /* all dependencies are met, append */
902 CFArrayAppendValue(new, bundleInfo1
);
903 CFArrayRemoveValueAtIndex(orig
, i
);
909 if (inserted
== FALSE
) {
910 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Bundles have circular dependency!!!"));
914 if (CFArrayGetCount(orig
) > 0) {
915 /* we have a circular dependency, append remaining items on new array */
916 CFArrayAppendArray(new, orig
, CFRangeMake(0, CFArrayGetCount(orig
)));
919 /* new one is a sorted version of original */
922 CFArrayRemoveAllValues(orig
);
923 CFArrayAppendArray(orig
, new, CFRangeMake(0, CFArrayGetCount(new)));
932 * An alternate CFRelease() that we can use to fake out the
935 static __inline__
void
936 ALT_CFRelease(CFTypeRef cf
)
944 plugin_exec(void *arg
)
949 /* keep track of bundles */
950 allBundles
= CFArrayCreateMutable(NULL
, 0, NULL
);
952 /* add white-listed plugins to those we'll allow to be loaded */
953 for (i
= 0; i
< N_PLUGIN_WHITELIST
; i
++) {
954 if (pluginWhitelist
[i
] != NULL
) {
955 CFSetSetValue(_plugins_allowed
, pluginWhitelist
[i
]);
959 /* allow plug-ins to exec child/helper processes */
960 _SCDPluginExecInit();
963 char path
[MAXPATHLEN
];
964 NSSearchPathEnumerationState state
;
967 * identify and load all bundles
969 state
= NSStartSearchPathEnumeration(NSLibraryDirectory
,
971 while ((state
= NSGetNextSearchPathEnumeration(state
, path
))) {
975 /* load any available bundle */
976 strlcat(path
, BUNDLE_DIRECTORY
, sizeof(path
));
977 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("searching for bundles in \".\""));
978 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
982 bundles
= CFBundleCreateBundlesFromDirectory(NULL
, url
, CFSTR(".bundle"));
985 if (bundles
!= NULL
) {
989 n
= CFArrayGetCount(bundles
);
990 for (i
= 0; i
< n
; i
++) {
993 bundle
= (CFBundleRef
)CFArrayGetValueAtIndex(bundles
, i
);
994 addBundle(bundle
, FALSE
);
996 // The CFBundleCreateBundlesFromDirectory() API has
997 // a known/outstanding bug in that it over-retains the
998 // returned bundles. Since we do not expect this to
999 // be fixed we release the extra references.
1001 // See <rdar://problems/4912137&6078752> for more info.
1003 // Also, we use the hack below to keep the static
1005 ALT_CFRelease(bundle
);
1011 sortBundles(allBundles
);
1017 * load (only) the specified bundle
1019 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
1021 strlen((char *)arg
),
1023 bundle
= CFBundleCreate(NULL
, url
);
1024 if (bundle
!= NULL
) {
1025 addBundle(bundle
, TRUE
);
1032 * Look for the InterfaceNamer plugin, and move it to the start
1035 * Load the InterfaceNamer plugin (and thereby start its thread)
1036 * first in an attempt to minimize the amount of time that
1037 * opendirectoryd has to wait for the platform UUID to appear in
1040 * InterfaceNamer is responsible for creating the platform UUID on
1041 * platforms without a UUID in ROM. Until the platform UUID is created
1042 * and stashed in nvram, all calls to opendirectoryd to do things like
1043 * getpwuid() will block, because opendirectoryd will block while trying
1044 * to read the platform UUID from the kernel.
1046 * As an example, dlopen() causes XPC to do some intialization, and
1047 * part of that initialization involves communicating with xpcd.
1048 * Since xpcd calls getpwuid_r() during its initialization, it will
1049 * block until the platform UUID is available.
1051 for (int i
= 0; i
< CFArrayGetCount(allBundles
); i
++) {
1052 bundleInfoRef bi
= (bundleInfoRef
)CFArrayGetValueAtIndex(allBundles
, i
);
1053 CFStringRef bundleID
= CFBundleGetIdentifier(bi
->bundle
);
1055 if (_SC_CFEqual(bundleID
,
1056 CFSTR("com.apple.SystemConfiguration.InterfaceNamer")))
1058 CFArrayRemoveValueAtIndex(allBundles
, i
);
1059 CFArrayInsertValueAtIndex(allBundles
, 0, bi
);
1065 traceBundle("before loading any plugins", NULL
);
1071 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("loading bundles"));
1072 CFArrayApplyFunction(allBundles
,
1073 CFRangeMake(0, CFArrayGetCount(allBundles
)),
1078 * If defined, call each bundles load() function. This function (or
1079 * the start() function) should initialize any variables, open any
1080 * sessions with "configd", and register any needed notifications.
1082 * Note: Establishing initial information in the store should be
1083 * deferred until the prime() initialization function so that
1084 * any bundles which want to receive a notification that the
1085 * data has changed will have an opportunity to install a
1086 * notification handler.
1088 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle load() functions"));
1089 CFArrayApplyFunction(allBundles
,
1090 CFRangeMake(0, CFArrayGetCount(allBundles
)),
1095 // if no bundles loaded
1100 * If defined, call each bundles start() function. This function is
1101 * called after the bundle has been loaded and its load() function has
1102 * been called. It should initialize any variables, open any sessions
1103 * with "configd", and register any needed notifications.
1105 * Note: Establishing initial information in the store should be
1106 * deferred until the prime() initialization function so that
1107 * any bundles which want to receive a notification that the
1108 * data has changed will have an opportunity to install a
1109 * notification handler.
1111 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle start() functions"));
1112 CFArrayApplyFunction(allBundles
,
1113 CFRangeMake(0, CFArrayGetCount(allBundles
)),
1118 * If defined, call each bundles prime() function. This function is
1119 * called after the bundle has been loaded and its load() and start()
1120 * functions have been called. It should initialize any configuration
1121 * information and/or state in the store.
1123 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle prime() functions"));
1124 CFArrayApplyFunction(allBundles
,
1125 CFRangeMake(0, CFArrayGetCount(allBundles
)),
1130 if (arg
== NULL
&& (nLoaded
> 0)) {
1131 CFRunLoopTimerRef timer
;
1133 /* allocate a periodic event (to help show we're not blocking) */
1134 timer
= CFRunLoopTimerCreate(NULL
, /* allocator */
1135 CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */
1139 timerCallback
, /* callout */
1140 NULL
); /* context */
1141 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
1147 traceBundle("about to start plugin CFRunLoop", NULL
);
1151 * The assumption is that each loaded plugin will establish CFMachPortRef,
1152 * CFSocketRef, and CFRunLoopTimerRef input sources to handle any events
1153 * and register these sources with this threads run loop. If the plugin
1154 * needs to wait and/or block at any time it should do so only in its a
1157 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("starting plugin CFRunLoop"));
1158 plugin_runLoop
= CFRunLoopGetCurrent();
1159 pthread_setname_np("Main plugin thread");
1164 SCLog(_configd_verbose
, LOG_INFO
, CFSTR("No more work for the \"configd\" plugins"));
1165 plugin_runLoop
= NULL
;
1174 pthread_attr_t tattr
;
1177 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("Starting thread for plug-ins..."));
1178 pthread_attr_init(&tattr
);
1179 pthread_attr_setscope(&tattr
, PTHREAD_SCOPE_SYSTEM
);
1180 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
1181 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
1182 pthread_create(&tid
, &tattr
, plugin_exec
, NULL
);
1183 pthread_attr_destroy(&tattr
);
1184 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR(" thread id=0x%08x"), tid
);