]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/plugin_support.c
configd-596.12.tar.gz
[apple/configd.git] / configd.tproj / plugin_support.c
1 /*
2 * Copyright (c) 2000-2009, 2011, 2013 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"
53 void _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
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"),
88 };
89 #define N_PLUGIN_WHITELIST (sizeof(pluginWhitelist) / sizeof(pluginWhitelist[0]))
90
91
92 typedef struct {
93 CFBundleRef bundle;
94 Boolean loaded;
95 Boolean builtin;
96 Boolean enabled;
97 Boolean forced;
98 Boolean verbose;
99 SCDynamicStoreBundleLoadFunction load;
100 SCDynamicStoreBundleStartFunction start;
101 SCDynamicStoreBundlePrimeFunction prime;
102 SCDynamicStoreBundleStopFunction stop;
103 } *bundleInfoRef;
104
105
106 // all loaded bundles
107 static CFMutableArrayRef allBundles = NULL;
108
109 // exiting bundles
110 static CFMutableDictionaryRef exiting = NULL;
111
112 // plugin CFRunLoopRef
113 __private_extern__
114 CFRunLoopRef plugin_runLoop = NULL;
115
116
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
130
131
132 typedef struct {
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;
139
140
141 static const builtin builtin_plugins[] = {
142 {
143 CFSTR("com.apple.SystemConfiguration.IPMonitor"),
144 &load_IPMonitor,
145 NULL,
146 &prime_IPMonitor,
147 NULL
148 },
149 #if !TARGET_IPHONE_SIMULATOR
150 {
151 CFSTR("com.apple.SystemConfiguration.InterfaceNamer"),
152 &load_InterfaceNamer,
153 NULL,
154 NULL,
155 NULL
156 },
157 {
158 CFSTR("com.apple.SystemConfiguration.KernelEventMonitor"),
159 &load_KernelEventMonitor,
160 NULL,
161 &prime_KernelEventMonitor,
162 NULL
163 },
164 {
165 CFSTR("com.apple.SystemConfiguration.LinkConfiguration"),
166 &load_LinkConfiguration,
167 NULL,
168 NULL,
169 NULL
170 },
171 {
172 CFSTR("com.apple.SystemConfiguration.PreferencesMonitor"),
173 &load_PreferencesMonitor,
174 NULL,
175 &prime_PreferencesMonitor,
176 NULL
177 },
178 #endif // !TARGET_IPHONE_SIMULATOR
179 #ifdef HAVE_REACHABILITY_SERVER
180 {
181 CFSTR("com.apple.SystemConfiguration.SCNetworkReachability"),
182 &load_SCNetworkReachability,
183 NULL,
184 NULL,
185 NULL
186 },
187 #endif // HAVE_REACHABILITY_SERVER
188 };
189
190
191 #ifdef DEBUG
192 static void
193 traceBundle(const char *op, CFBundleRef bundle)
194 {
195 if (_configd_trace != NULL) {
196 if (bundle != NULL) {
197 CFStringRef bundleID = CFBundleGetIdentifier(bundle);
198
199 SCTrace(TRUE, _configd_trace,
200 CFSTR("bundle : %s %@\n"),
201 op,
202 bundleID);
203 } else {
204 SCTrace(TRUE, _configd_trace,
205 CFSTR("bundle : %s\n"),
206 op);
207 }
208 }
209
210 return;
211 }
212 #endif /* DEBUG */
213
214
215 static void
216 addBundle(CFBundleRef bundle, Boolean forceEnabled)
217 {
218 CFDictionaryRef bundleDict;
219 bundleInfoRef bundleInfo;
220
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;
232
233 bundleDict = CFBundleGetInfoDictionary(bundle);
234 if (isA_CFDictionary(bundleDict)) {
235 CFBooleanRef bVal;
236
237 bVal = CFDictionaryGetValue(bundleDict, kSCBundleIsBuiltinKey);
238 if (isA_CFBoolean(bVal)) {
239 bundleInfo->builtin = CFBooleanGetValue(bVal);
240 }
241
242 bVal = CFDictionaryGetValue(bundleDict, kSCBundleEnabledKey);
243 if (isA_CFBoolean(bVal)) {
244 bundleInfo->enabled = CFBooleanGetValue(bVal);
245 }
246
247 bVal = CFDictionaryGetValue(bundleDict, kSCBundleVerboseKey);
248 if (isA_CFBoolean(bVal)) {
249 bundleInfo->verbose = CFBooleanGetValue(bVal);
250 }
251 }
252
253 CFArrayAppendValue(allBundles, bundleInfo);
254 return;
255 }
256
257
258 static CF_RETURNS_RETAINED CFStringRef
259 shortBundleIdentifier(CFStringRef bundleID)
260 {
261 CFIndex len = CFStringGetLength(bundleID);
262 CFRange range;
263 CFStringRef shortID = NULL;
264
265 if (CFStringFindWithOptions(bundleID,
266 CFSTR("."),
267 CFRangeMake(0, len),
268 kCFCompareBackwards,
269 &range)) {
270 range.location = range.location + range.length;
271 range.length = len - range.location;
272 shortID = CFStringCreateWithSubstring(NULL, bundleID, range);
273 }
274
275 return shortID;
276 }
277
278
279 static void *
280 getBundleSymbol(CFBundleRef bundle, CFStringRef functionName, CFStringRef shortID)
281 {
282 void *func;
283
284 // search for load(), start(), prime(), stop(), ...
285 func = CFBundleGetFunctionPointerForName(bundle, functionName);
286 if (func != NULL) {
287 return func;
288 }
289
290 if (shortID != NULL) {
291 CFStringRef altFunctionName;
292
293 // search for load_XXX(), ...
294 altFunctionName = CFStringCreateWithFormat(NULL,
295 NULL,
296 CFSTR("%@_%@"),
297 functionName,
298 shortID);
299 func = CFBundleGetFunctionPointerForName(bundle, altFunctionName);
300 CFRelease(altFunctionName);
301 }
302
303 return func;
304 }
305
306
307 static const char *
308 getBundleDirNameAndPath(CFBundleRef bundle, char *buf, size_t buf_len)
309 {
310 char *cp;
311 int len;
312 Boolean ok;
313 CFURLRef url;
314
315 url = CFBundleCopyBundleURL(bundle);
316 if (url == NULL) {
317 return NULL;
318 }
319
320 ok = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)buf, buf_len);
321 CFRelease(url);
322 if (!ok) {
323 return NULL;
324 }
325
326 cp = strrchr(buf, '/');
327 if (cp != NULL) {
328 cp++;
329 } else {
330 cp = buf;
331 }
332
333 /* check if this directory entry is a valid bundle name */
334 len = strlen(cp);
335 if (len <= (int)sizeof(BUNDLE_DIR_EXTENSION)) {
336 /* if entry name isn't long enough */
337 return NULL;
338 }
339
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" */
343 return NULL;
344 }
345
346 return cp;
347 }
348
349
350 #pragma mark -
351 #pragma mark load
352
353
354 static void
355 forkBundle_setup(pid_t pid, void *setupContext)
356 {
357 if (pid == 0) {
358 // if child
359 unsetenv("__LAUNCHD_FD");
360 setenv("__FORKED_PLUGIN__", "Yes", 1);
361 }
362
363 return;
364 }
365
366
367 static void
368 forkBundle(CFBundleRef bundle, CFStringRef bundleID)
369 {
370 char *argv[] = { "configd", "-d", "-t", NULL, NULL };
371 const char *name;
372 char path[MAXPATHLEN];
373 pid_t pid;
374
375 // get the bundle's path
376 name = getBundleDirNameAndPath(bundle, path, sizeof(path));
377 if (name == NULL) {
378 SCLog(TRUE, LOG_ERR, CFSTR("skipped %@ (could not determine path)"), bundle);
379 return;
380 }
381
382 // fork and exec configd opting to load only this plugin
383 argv[3] = path;
384 pid = _SCDPluginExecCommand2(NULL, NULL, 0, 0, "/usr/libexec/configd", argv, forkBundle_setup, NULL);
385 if (pid == -1) {
386 SCLog(TRUE, LOG_ERR,
387 CFSTR("skipped %@ (could not exec child) : %s"),
388 bundle,
389 strerror(errno));
390 return;
391 }
392
393 SCLog(TRUE, LOG_NOTICE, CFSTR("forked %@, pid=%d"), bundleID, pid);
394 return;
395 }
396
397
398 static void
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;
405 CFStringRef shortID;
406
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);
411 return;
412 }
413
414 shortID = shortBundleIdentifier(bundleID);
415
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
421 );
422 if (!bundleAllowed) {
423 SCLog(TRUE, LOG_WARNING, CFSTR("skipped %@ (not allowed)"), bundleID);
424 goto done;
425 }
426
427 bundleExclude = (CFSetContainsValue(_plugins_exclude, bundleID) || // if [bundleID] excluded
428 ((shortID != NULL) &&
429 CFSetContainsValue(_plugins_exclude, shortID)) // if [short bundleID] excluded
430 );
431 if (bundleExclude) {
432 // sorry, this bundle has been excluded
433 SCLog(TRUE, LOG_NOTICE, CFSTR("skipped %@ (excluded)"), bundleID);
434 goto done;
435 }
436
437 if (!bundleInfo->enabled && !bundleInfo->forced) {
438 // sorry, this bundle has not been enabled
439 SCLog(TRUE, LOG_INFO, CFSTR("skipped %@ (disabled)"), bundleID);
440 goto done;
441 }
442
443 if (_plugins_fork &&
444 !_SC_CFEqual(bundleID, CFSTR("com.apple.SystemConfiguration.SCNetworkReachability"))) {
445 forkBundle(bundleInfo->bundle, bundleID);
446 goto done;
447 }
448
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);
454 }
455 }
456 }
457
458 if (bundleInfo->builtin) {
459 int i;
460
461 SCLog(TRUE, LOG_DEBUG, CFSTR("adding %@"), bundleID);
462
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;
469 break;
470 }
471 }
472
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);
478 goto done;
479 }
480 } else {
481 CFErrorRef error = NULL;
482
483 SCLog(TRUE, LOG_DEBUG, CFSTR("loading %@"), bundleID);
484
485 #ifdef DEBUG
486 traceBundle("loading", bundleInfo->bundle);
487 #endif /* DEBUG */
488
489 if (!CFBundleLoadExecutableAndReturnError(bundleInfo->bundle, &error)) {
490 CFStringRef description;
491
492 description = CFErrorCopyDescription(error);
493 SCLog(TRUE, LOG_NOTICE, CFSTR("%@ load failed"), bundleID);
494 SCLog(TRUE, LOG_NOTICE, CFSTR(" %@"), description);
495 CFRelease(description);
496 CFRelease(error);
497 goto done;
498 }
499
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);
505 }
506
507 /* mark this bundle as having been loaded */
508 bundleInfo->loaded = TRUE;
509
510 /* bump the count of loaded bundles */
511 *nLoaded = *nLoaded + 1;
512
513 done :
514
515 if (shortID != NULL) CFRelease(shortID);
516 return;
517 }
518
519
520 void
521 callLoadFunction(const void *value, void *context) {
522 bundleInfoRef bundleInfo = (bundleInfoRef)value;
523
524 if (!bundleInfo->loaded) {
525 return;
526 }
527
528 if (bundleInfo->load == NULL) {
529 // if no load() function
530 return;
531 }
532
533 #ifdef DEBUG
534 traceBundle("calling load() for", bundleInfo->bundle);
535 #endif /* DEBUG */
536
537 (*bundleInfo->load)(bundleInfo->bundle, bundleInfo->verbose);
538
539 return;
540 }
541
542
543 #pragma mark -
544 #pragma mark start
545
546
547 void
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];
553 int len;
554
555 if (!bundleInfo->loaded) {
556 return;
557 }
558
559 if (bundleInfo->start == NULL) {
560 // if no start() function
561 return;
562 }
563
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
568 return;
569 }
570
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
574 return;
575 }
576 len = strlen(bundleName) - (sizeof(BUNDLE_DIR_EXTENSION) - 1);
577 bundleName[len] = '\0';
578
579 #ifdef DEBUG
580 traceBundle("calling start() for", bundleInfo->bundle);
581 #endif /* DEBUG */
582
583 (*bundleInfo->start)(bundleName, bundlePath);
584
585 return;
586 }
587
588
589 #pragma mark -
590 #pragma mark prime
591
592
593 void
594 callPrimeFunction(const void *value, void *context) {
595 bundleInfoRef bundleInfo = (bundleInfoRef)value;
596
597 if (!bundleInfo->loaded) {
598 return;
599 }
600
601 if (bundleInfo->prime == NULL) {
602 // if no prime() function
603 return;
604 }
605
606 #ifdef DEBUG
607 traceBundle("calling prime() for", bundleInfo->bundle);
608 #endif /* DEBUG */
609
610 (*bundleInfo->prime)();
611
612 return;
613 }
614
615
616 #pragma mark -
617 #pragma mark stop
618
619
620 static void
621 stopComplete(void *info)
622 {
623 CFBundleRef bundle = (CFBundleRef)info;
624 CFStringRef bundleID = CFBundleGetIdentifier(bundle);
625 CFRunLoopSourceRef stopRls;
626
627 SCLog(TRUE, LOG_DEBUG, CFSTR("** %@ complete (%f)"), bundleID, CFAbsoluteTimeGetCurrent());
628
629 stopRls = (CFRunLoopSourceRef)CFDictionaryGetValue(exiting, bundle);
630 if (stopRls == NULL) {
631 return;
632 }
633
634 CFRunLoopSourceInvalidate(stopRls);
635
636 CFDictionaryRemoveValue(exiting, bundle);
637
638 if (CFDictionaryGetCount(exiting) == 0) {
639 int status;
640
641 // if all of the plugins are happy
642 status = server_shutdown();
643 SCLog(TRUE, LOG_DEBUG, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent());
644 exit (status);
645 }
646
647 return;
648 }
649
650
651 static void
652 stopDelayed(CFRunLoopTimerRef timer, void *info)
653 {
654 const void **keys;
655 CFIndex i;
656 CFIndex n;
657 int status;
658
659 SCLog(TRUE, LOG_ERR, CFSTR("server shutdown was delayed, unresponsive plugins:"));
660
661 /*
662 * we've asked our plugins to shutdown but someone
663 * isn't listening.
664 */
665 n = CFDictionaryGetCount(exiting);
666 keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
667 CFDictionaryGetKeysAndValues(exiting, keys, NULL);
668 for (i = 0; i < n; i++) {
669 CFBundleRef bundle;
670 CFStringRef bundleID;
671
672 bundle = (CFBundleRef)keys[i];
673 bundleID = CFBundleGetIdentifier(bundle);
674 SCLog(TRUE, LOG_ERR, CFSTR("** %@"), bundleID);
675 }
676 CFAllocatorDeallocate(NULL, keys);
677
678 status = server_shutdown();
679 exit (status);
680 }
681
682 static CFStringRef
683 stopRLSCopyDescription(const void *info)
684 {
685 CFBundleRef bundle = (CFBundleRef)info;
686
687 return CFStringCreateWithFormat(NULL,
688 NULL,
689 CFSTR("<stopRLS %p> {bundleID = %@}"),
690 info,
691 CFBundleGetIdentifier(bundle));
692 }
693
694
695 static void
696 stopBundle(const void *value, void *context) {
697 bundleInfoRef bundleInfo = (bundleInfoRef)value;
698 CFRunLoopSourceRef stopRls;
699 CFRunLoopSourceContext stopContext = { 0 // version
700 , bundleInfo->bundle // info
701 , CFRetain // retain
702 , CFRelease // release
703 , stopRLSCopyDescription // copyDescription
704 , CFEqual // equal
705 , CFHash // hash
706 , NULL // schedule
707 , NULL // cancel
708 , stopComplete // perform
709 };
710
711 if (!bundleInfo->loaded) {
712 return;
713 }
714
715 if (bundleInfo->stop == NULL) {
716 // if no stop() function
717 return;
718 }
719
720 stopRls = CFRunLoopSourceCreate(NULL, 0, &stopContext);
721 CFRunLoopAddSource(CFRunLoopGetCurrent(), stopRls, kCFRunLoopDefaultMode);
722 CFDictionaryAddValue(exiting, bundleInfo->bundle, stopRls);
723 CFRelease(stopRls);
724
725 (*bundleInfo->stop)(stopRls);
726
727 return;
728 }
729
730
731 static void
732 stopBundles()
733 {
734 /*
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.
739 */
740 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle stop() functions"));
741 CFArrayApplyFunction(allBundles,
742 CFRangeMake(0, CFArrayGetCount(allBundles)),
743 stopBundle,
744 NULL);
745
746 if (CFDictionaryGetCount(exiting) == 0) {
747 int status;
748
749 // if all of the plugins are happy
750 status = server_shutdown();
751 SCLog(TRUE, LOG_DEBUG, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent());
752 exit (status);
753 } else {
754 CFRunLoopTimerRef timer;
755
756 /*
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.
761 */
762 timer = CFRunLoopTimerCreate(NULL, /* allocator */
763 CFAbsoluteTimeGetCurrent() + 15.0, /* fireDate (in 15 seconds) */
764 0.0, /* interval (== one-shot) */
765 0, /* flags */
766 0, /* order */
767 stopDelayed, /* callout */
768 NULL); /* context */
769 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
770 CFRelease(timer);
771 }
772
773 return;
774 }
775
776
777 #pragma mark -
778 #pragma mark term
779
780
781 static CFStringRef
782 termRLSCopyDescription(const void *info)
783 {
784 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SIGTERM RLS>"));
785 }
786
787
788 __private_extern__
789 Boolean
790 plugin_term(int *status)
791 {
792 CFRunLoopSourceContext termContext = { 0 // version
793 , (void *)1 // info
794 , NULL // retain
795 , NULL // release
796 , termRLSCopyDescription // copyDescription
797 , NULL // equal
798 , NULL // hash
799 , NULL // schedule
800 , NULL // cancel
801 , stopBundles // perform
802 };
803 CFRunLoopSourceRef termRls;
804
805 if (plugin_runLoop == NULL) {
806 // if no plugins
807 *status = EX_OK;
808 return FALSE; // don't delay shutdown
809 }
810
811 if (exiting != NULL) {
812 // if shutdown already active
813 return TRUE;
814 }
815
816 SCLog(TRUE, LOG_DEBUG, CFSTR("starting server shutdown (%f)"), CFAbsoluteTimeGetCurrent());
817
818 exiting = CFDictionaryCreateMutable(NULL,
819 0,
820 &kCFTypeDictionaryKeyCallBacks,
821 &kCFTypeDictionaryValueCallBacks);
822
823 termRls = CFRunLoopSourceCreate(NULL, 0, &termContext);
824 CFRunLoopAddSource(plugin_runLoop, termRls, kCFRunLoopDefaultMode);
825 CFRunLoopSourceSignal(termRls);
826 CFRelease(termRls);
827 CFRunLoopWakeUp(plugin_runLoop);
828
829 return TRUE;
830 }
831
832
833 #pragma mark -
834 #pragma mark initialization
835
836
837 #ifdef DEBUG
838 static void
839 timerCallback(CFRunLoopTimerRef timer, void *info)
840 {
841 static int pass = 0;
842
843 pass++;
844 if ((pass > 120) && ((pass % 60) != 0)) {
845 return;
846 }
847
848 traceBundle("the [plugin] CFRunLoop is waiting...", NULL);
849 return;
850 }
851 #endif /* DEBUG */
852
853
854 static void
855 sortBundles(CFMutableArrayRef orig)
856 {
857 CFIndex i;
858 CFIndex n;
859 CFMutableArrayRef new;
860 CFMutableSetRef orig_bundleIDs;
861
862 orig_bundleIDs = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
863
864 n = CFArrayGetCount(orig);
865 for (i = 0; i < n; i++) {
866 bundleInfoRef bundleInfo = (bundleInfoRef)CFArrayGetValueAtIndex(orig, i);
867 CFStringRef bundleID = CFBundleGetIdentifier(bundleInfo->bundle);
868
869 if (bundleID != NULL) {
870 CFSetAddValue(orig_bundleIDs, bundleID);
871 }
872 }
873
874 new = CFArrayCreateMutable(NULL, 0, NULL);
875 while (n > 0) {
876 Boolean inserted = FALSE;
877
878 for (i = 0; i < n; i++) {
879 bundleInfoRef bundleInfo1 = (bundleInfoRef)CFArrayGetValueAtIndex(orig, i);
880 CFStringRef bundleID1 = CFBundleGetIdentifier(bundleInfo1->bundle);
881 int count;
882 CFDictionaryRef dict;
883 int j;
884 int nRequires;
885 CFArrayRef requires = NULL;
886
887 dict = isA_CFDictionary(CFBundleGetInfoDictionary(bundleInfo1->bundle));
888 if (dict) {
889 requires = CFDictionaryGetValue(dict, kSCBundleRequiresKey);
890 requires = isA_CFArray(requires);
891 }
892 if (bundleID1 == NULL || requires == NULL) {
893 CFArrayInsertValueAtIndex(new, 0, bundleInfo1);
894 CFArrayRemoveValueAtIndex(orig, i);
895 inserted = TRUE;
896 break;
897 }
898 count = nRequires = CFArrayGetCount(requires);
899 for (j = 0; j < nRequires; j++) {
900 int k;
901 int nNew;
902 CFStringRef r = CFArrayGetValueAtIndex(requires, j);
903
904 if (!CFSetContainsValue(orig_bundleIDs, r)) {
905 // if dependency not present
906 count--;
907 continue;
908 }
909
910 nNew = CFArrayGetCount(new);
911 for (k = 0; k < nNew; k++) {
912 bundleInfoRef bundleInfo2 = (bundleInfoRef)CFArrayGetValueAtIndex(new, k);
913 CFStringRef bundleID2 = CFBundleGetIdentifier(bundleInfo2->bundle);
914
915 if (bundleID2 && CFEqual(bundleID2, r)) {
916 count--;
917 }
918 }
919 }
920 if (count == 0) {
921 /* all dependencies are met, append */
922 CFArrayAppendValue(new, bundleInfo1);
923 CFArrayRemoveValueAtIndex(orig, i);
924 inserted = TRUE;
925 break;
926 }
927 }
928
929 if (inserted == FALSE) {
930 SCLog(TRUE, LOG_NOTICE, CFSTR("Bundles have circular dependency!!!"));
931 break;
932 }
933
934 n = CFArrayGetCount(orig);
935 }
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)));
939 }
940 else {
941 /* new one is a sorted version of original */
942 }
943
944 CFArrayRemoveAllValues(orig);
945 CFArrayAppendArray(orig, new, CFRangeMake(0, CFArrayGetCount(new)));
946 CFRelease(new);
947 CFRelease(orig_bundleIDs);
948 return;
949 }
950
951
952 /*
953 * ALT_CFRelease()
954 *
955 * An alternate CFRelease() that we can use to fake out the
956 * static analyzer.
957 */
958 static __inline__ void
959 ALT_CFRelease(CFTypeRef cf)
960 {
961 CFRelease(cf);
962 }
963
964
965 __private_extern__
966 void *
967 plugin_exec(void *arg)
968 {
969 int i;
970 CFIndex nLoaded = 0;
971
972 /* keep track of bundles */
973 allBundles = CFArrayCreateMutable(NULL, 0, NULL);
974
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]);
979 }
980 }
981
982 /* allow plug-ins to exec child/helper processes */
983 _SCDPluginExecInit();
984
985 if (arg == NULL) {
986 char path[MAXPATHLEN];
987 NSSearchPathEnumerationState state;
988
989 /*
990 * identify and load all bundles
991 */
992 state = NSStartSearchPathEnumeration(NSLibraryDirectory,
993 NSSystemDomainMask);
994 while ((state = NSGetNextSearchPathEnumeration(state, path))) {
995 CFArrayRef bundles;
996 CFURLRef url;
997
998 #if TARGET_IPHONE_SIMULATOR
999 const char *path_sim_prefix;
1000
1001 path_sim_prefix = getenv("IPHONE_SIMULATOR_ROOT");
1002 if ((path_sim_prefix != NULL) && (strcmp(path_sim_prefix, ".") != 0)) {
1003 char path_sim[MAXPATHLEN];
1004
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));
1008 } else {
1009 path[0] = '\0';
1010 }
1011 #endif // TARGET_IPHONE_SIMULATOR
1012
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,
1017 (UInt8 *)path,
1018 strlen(path),
1019 TRUE);
1020 bundles = CFBundleCreateBundlesFromDirectory(NULL, url, CFSTR(".bundle"));
1021 CFRelease(url);
1022
1023 if (bundles != NULL) {
1024 CFIndex i;
1025 CFIndex n;
1026
1027 n = CFArrayGetCount(bundles);
1028 for (i = 0; i < n; i++) {
1029 CFBundleRef bundle;
1030
1031 bundle = (CFBundleRef)CFArrayGetValueAtIndex(bundles, i);
1032 addBundle(bundle, FALSE);
1033
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.
1038 //
1039 // See <rdar://problems/4912137&6078752> for more info.
1040 //
1041 // Also, we use the hack below to keep the static
1042 // analyzer happy.
1043 ALT_CFRelease(bundle);
1044 }
1045 CFRelease(bundles);
1046 }
1047 }
1048
1049 sortBundles(allBundles);
1050 } else {
1051 CFBundleRef bundle;
1052 CFURLRef url;
1053
1054 /*
1055 * load (only) the specified bundle
1056 */
1057 url = CFURLCreateFromFileSystemRepresentation(NULL,
1058 (UInt8 *)arg,
1059 strlen((char *)arg),
1060 TRUE);
1061 bundle = CFBundleCreate(NULL, url);
1062 if (bundle != NULL) {
1063 addBundle(bundle, TRUE);
1064 CFRelease(bundle);
1065 }
1066 CFRelease(url);
1067 }
1068
1069 /*
1070 * Look for the InterfaceNamer plugin, and move it to the start
1071 * of the list.
1072 *
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
1076 * nvram.
1077 *
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.
1083 *
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.
1088 */
1089 for (i = 0; i < CFArrayGetCount(allBundles); i++) {
1090 bundleInfoRef bi = (bundleInfoRef)CFArrayGetValueAtIndex(allBundles, i);
1091 CFStringRef bundleID = CFBundleGetIdentifier(bi->bundle);
1092
1093 if (_SC_CFEqual(bundleID,
1094 CFSTR("com.apple.SystemConfiguration.InterfaceNamer")))
1095 {
1096 CFArrayRemoveValueAtIndex(allBundles, i);
1097 CFArrayInsertValueAtIndex(allBundles, 0, bi);
1098 break;
1099 }
1100 }
1101
1102 #ifdef DEBUG
1103 traceBundle("before loading any plugins", NULL);
1104 #endif /* DEBUG */
1105
1106 /*
1107 * load each bundle.
1108 */
1109 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("loading bundles"));
1110 CFArrayApplyFunction(allBundles,
1111 CFRangeMake(0, CFArrayGetCount(allBundles)),
1112 loadBundle,
1113 &nLoaded);
1114
1115 /*
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.
1119 *
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.
1125 */
1126 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle load() functions"));
1127 CFArrayApplyFunction(allBundles,
1128 CFRangeMake(0, CFArrayGetCount(allBundles)),
1129 callLoadFunction,
1130 NULL);
1131
1132 if (nLoaded == 0) {
1133 // if no bundles loaded
1134 goto done;
1135 }
1136
1137 /*
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.
1142 *
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.
1148 */
1149 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle start() functions"));
1150 CFArrayApplyFunction(allBundles,
1151 CFRangeMake(0, CFArrayGetCount(allBundles)),
1152 callStartFunction,
1153 NULL);
1154
1155 /*
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.
1160 */
1161 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle prime() functions"));
1162 CFArrayApplyFunction(allBundles,
1163 CFRangeMake(0, CFArrayGetCount(allBundles)),
1164 callPrimeFunction,
1165 NULL);
1166
1167 #ifdef DEBUG
1168 if (arg == NULL && (nLoaded > 0)) {
1169 CFRunLoopTimerRef timer;
1170
1171 /* allocate a periodic event (to help show we're not blocking) */
1172 timer = CFRunLoopTimerCreate(NULL, /* allocator */
1173 CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */
1174 1.0, /* interval */
1175 0, /* flags */
1176 0, /* order */
1177 timerCallback, /* callout */
1178 NULL); /* context */
1179 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
1180 CFRelease(timer);
1181 }
1182 #endif /* DEBUG */
1183
1184 #ifdef DEBUG
1185 traceBundle("about to start plugin CFRunLoop", NULL);
1186 #endif /* DEBUG */
1187
1188 /*
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
1193 * private thread.
1194 */
1195 SCLog(_configd_verbose, LOG_DEBUG, CFSTR("starting plugin CFRunLoop"));
1196 plugin_runLoop = CFRunLoopGetCurrent();
1197 pthread_setname_np("Main plugin thread");
1198 CFRunLoopRun();
1199
1200 done :
1201
1202 SCLog(_configd_verbose, LOG_INFO, CFSTR("No more work for the \"configd\" plugins"));
1203 plugin_runLoop = NULL;
1204 return NULL;
1205 }
1206
1207
1208 __private_extern__
1209 void
1210 plugin_init()
1211 {
1212 pthread_attr_t tattr;
1213 pthread_t tid;
1214
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);
1223
1224 return;
1225 }