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