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