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