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