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