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