this repo has no description
at fixPythonPipStalling 1045 lines 32 kB view raw
1/** 2 * StartupItems.c - Startup Item management routines 3 * Wilfredo Sanchez | wsanchez@opensource.apple.com 4 * Kevin Van Vechten | kevinvv@uclink4.berkeley.edu 5 * $Apple$ 6 ** 7 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. 8 * 9 * @APPLE_APACHE_LICENSE_HEADER_START@ 10 * 11 * Licensed under the Apache License, Version 2.0 (the "License"); 12 * you may not use this file except in compliance with the License. 13 * You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, software 18 * distributed under the License is distributed on an "AS IS" BASIS, 19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 * See the License for the specific language governing permissions and 21 * limitations under the License. 22 * 23 * @APPLE_APACHE_LICENSE_HEADER_END@ 24 **/ 25 26#include <unistd.h> 27#include <sys/types.h> 28#include <sys/stat.h> 29#include <sys/sysctl.h> 30#include <sys/mman.h> 31#include <stdlib.h> 32#include <fcntl.h> 33#include <dirent.h> 34#include <limits.h> 35#include <errno.h> 36#include <string.h> 37#include <sysexits.h> 38#include <syslog.h> 39#include <CoreFoundation/CoreFoundation.h> 40#include "StartupItems.h" 41 42#define kStartupItemsPath "/StartupItems" 43#define kParametersFile "StartupParameters.plist" 44#define kDisabledFile ".disabled" 45 46#define kRunSuccess CFSTR("success") 47#define kRunFailure CFSTR("failure") 48 49static const char *argumentForAction(Action anAction) 50{ 51 switch (anAction) { 52 case kActionStart: 53 return "start"; 54 case kActionStop: 55 return "stop"; 56 case kActionRestart: 57 return "restart"; 58 default: 59 return NULL; 60 } 61} 62 63#define checkTypeOfValue(aKey,aTypeID) \ 64 { \ 65 CFStringRef aProperty = CFDictionaryGetValue(aConfig, aKey); \ 66 if (aProperty && CFGetTypeID(aProperty) != aTypeID) \ 67 return FALSE; \ 68 } 69 70static int StartupItemValidate(CFDictionaryRef aConfig) 71{ 72 if (aConfig && CFGetTypeID(aConfig) == CFDictionaryGetTypeID()) { 73 checkTypeOfValue(kProvidesKey, CFArrayGetTypeID()); 74 checkTypeOfValue(kRequiresKey, CFArrayGetTypeID()); 75 76 return TRUE; 77 } 78 return FALSE; 79} 80 81/* 82 * remove item from waiting list 83 */ 84void RemoveItemFromWaitingList(StartupContext aStartupContext, CFMutableDictionaryRef anItem) 85{ 86 /* Remove the item from the waiting list. */ 87 if (aStartupContext && anItem && aStartupContext->aWaitingList) { 88 CFRange aRange = { 0, CFArrayGetCount(aStartupContext->aWaitingList) }; 89 CFIndex anIndex = CFArrayGetFirstIndexOfValue(aStartupContext->aWaitingList, aRange, anItem); 90 91 if (anIndex >= 0) { 92 CFArrayRemoveValueAtIndex(aStartupContext->aWaitingList, anIndex); 93 } 94 } 95} 96 97/* 98 * add item to failed list, create list if it doesn't exist 99 * return and fail quietly if it can't create list 100 */ 101void AddItemToFailedList(StartupContext aStartupContext, CFMutableDictionaryRef anItem) 102{ 103 if (aStartupContext && anItem) { 104 /* create the failed list if it doesn't exist */ 105 if (!aStartupContext->aFailedList) { 106 aStartupContext->aFailedList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 107 } 108 if (aStartupContext->aFailedList) { 109 CFArrayAppendValue(aStartupContext->aFailedList, anItem); 110 } 111 } 112} 113 114/** 115 * startupItemListCopyMatches returns an array of items which contain the string aService in the key aKey 116 **/ 117static CFMutableArrayRef startupItemListCopyMatches(CFArrayRef anItemList, CFStringRef aKey, CFStringRef aService) 118{ 119 CFMutableArrayRef aResult = NULL; 120 121 if (anItemList && aKey && aService) { 122 CFIndex anItemCount = CFArrayGetCount(anItemList); 123 CFIndex anItemIndex = 0; 124 125 aResult = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 126 127 for (anItemIndex = 0; anItemIndex < anItemCount; ++anItemIndex) { 128 CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(anItemList, anItemIndex); 129 CFArrayRef aList = CFDictionaryGetValue(anItem, aKey); 130 131 if (aList) { 132 if (CFArrayContainsValue(aList, CFRangeMake(0, CFArrayGetCount(aList)), aService) && 133 !CFArrayContainsValue(aResult, CFRangeMake(0, CFArrayGetCount(aResult)), anItem)) { 134 CFArrayAppendValue(aResult, anItem); 135 } 136 } 137 } 138 } 139 return aResult; 140} 141 142static void SpecialCasesStartupItemHandler(CFMutableDictionaryRef aConfig) 143{ 144 static const CFStringRef stubitems[] = { 145 CFSTR("Accounting"), 146 CFSTR("System Tuning"), 147 CFSTR("SecurityServer"), 148 CFSTR("Portmap"), 149 CFSTR("System Log"), 150 CFSTR("Resolver"), 151 CFSTR("LDAP"), 152 CFSTR("NetInfo"), 153 CFSTR("NetworkExtensions"), 154 CFSTR("DirectoryServices"), 155 CFSTR("Network Configuration"), 156 CFSTR("mDNSResponder"), 157 CFSTR("Cron"), 158 CFSTR("Core Graphics"), 159 CFSTR("Core Services"), 160 CFSTR("Network"), 161 CFSTR("TIM"), 162 CFSTR("Disks"), 163 CFSTR("NIS"), 164 NULL 165 }; 166 CFMutableArrayRef aList, aNewList; 167 CFIndex i, aCount; 168 CFStringRef ci, type = kRequiresKey; 169 const CFStringRef *c; 170 171 again: 172 aList = (CFMutableArrayRef) CFDictionaryGetValue(aConfig, type); 173 if (aList) { 174 aCount = CFArrayGetCount(aList); 175 176 aNewList = CFArrayCreateMutable(kCFAllocatorDefault, aCount, &kCFTypeArrayCallBacks); 177 178 for (i = 0; i < aCount; i++) { 179 ci = CFArrayGetValueAtIndex(aList, i); 180 CF_syslog(LOG_DEBUG, CFSTR("%@: Evaluating %@"), type, ci); 181 for (c = stubitems; *c; c++) { 182 if (CFEqual(*c, ci)) 183 break; 184 } 185 if (*c == NULL) { 186 CFArrayAppendValue(aNewList, ci); 187 CF_syslog(LOG_DEBUG, CFSTR("%@: Keeping %@"), type, ci); 188 } 189 } 190 191 CFDictionaryReplaceValue(aConfig, type, aNewList); 192 CFRelease(aNewList); 193 } 194 if (type == kUsesKey) 195 return; 196 type = kUsesKey; 197 goto again; 198} 199 200CFIndex StartupItemListCountServices(CFArrayRef anItemList) 201{ 202 CFIndex aResult = 0; 203 204 if (anItemList) { 205 CFIndex anItemCount = CFArrayGetCount(anItemList); 206 CFIndex anItemIndex = 0; 207 208 for (anItemIndex = 0; anItemIndex < anItemCount; ++anItemIndex) { 209 CFDictionaryRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex); 210 CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); 211 212 if (aProvidesList) 213 aResult += CFArrayGetCount(aProvidesList); 214 } 215 } 216 return aResult; 217} 218 219bool StartupItemSecurityCheck(const char *aPath) 220{ 221 static struct timeval boot_time; 222 struct stat aStatBuf; 223 bool r = true; 224 225 if (boot_time.tv_sec == 0) { 226 int mib[] = { CTL_KERN, KERN_BOOTTIME }; 227 size_t boot_time_sz = sizeof(boot_time); 228 int rv; 229 230 rv = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &boot_time, &boot_time_sz, NULL, 0); 231 232 assert(rv != -1); 233 assert(boot_time_sz == sizeof(boot_time)); 234 } 235 236 /* should use lstatx_np() on Tiger? */ 237 if (lstat(aPath, &aStatBuf) == -1) { 238 if (errno != ENOENT) 239 syslog(LOG_ERR, "lstat(\"%s\"): %m", aPath); 240 return false; 241 } 242 /* 243 * We check the boot time because of 5409386. 244 * We ignore the boot time if PPID != 1 because of 5503536. 245 */ 246 if ((aStatBuf.st_ctimespec.tv_sec > boot_time.tv_sec) && (getppid() == 1)) { 247 syslog(LOG_WARNING, "\"%s\" failed sanity check: path was created after boot up", aPath); 248 return false; 249 } 250 if (!(S_ISREG(aStatBuf.st_mode) || S_ISDIR(aStatBuf.st_mode))) { 251 syslog(LOG_WARNING, "\"%s\" failed security check: not a directory or regular file", aPath); 252 r = false; 253 } 254 if (aStatBuf.st_mode & S_IWOTH) { 255 syslog(LOG_WARNING, "\"%s\" failed security check: world writable", aPath); 256 r = false; 257 } 258 if (aStatBuf.st_mode & S_IWGRP) { 259 syslog(LOG_WARNING, "\"%s\" failed security check: group writable", aPath); 260 r = false; 261 } 262 if (aStatBuf.st_uid != 0) { 263 syslog(LOG_WARNING, "\"%s\" failed security check: not owned by UID 0", aPath); 264 r = false; 265 } 266 if (aStatBuf.st_gid != 0) { 267 syslog(LOG_WARNING, "\"%s\" failed security check: not owned by GID 0", aPath); 268 r = false; 269 } 270 if (r == false) { 271 mkdir(kFixerDir, ACCESSPERMS); 272 close(open(kFixerPath, O_RDWR|O_CREAT|O_NOCTTY, DEFFILEMODE)); 273 } 274 return r; 275} 276 277CFMutableArrayRef StartupItemListCreateWithMask(NSSearchPathDomainMask aMask) 278{ 279 CFMutableArrayRef anItemList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 280 281 char aPath[PATH_MAX]; 282 CFIndex aDomainIndex = 0; 283 284 NSSearchPathEnumerationState aState = NSStartSearchPathEnumeration(NSLibraryDirectory, aMask); 285 286 while ((aState = NSGetNextSearchPathEnumeration(aState, aPath))) { 287 DIR *aDirectory; 288 289 strlcat(aPath, kStartupItemsPath, sizeof(aPath)); 290 ++aDomainIndex; 291 292 /* 5485016 293 * 294 * Just in case... 295 */ 296 mkdir(aPath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); 297 298 if (!StartupItemSecurityCheck(aPath)) 299 continue; 300 301 if ((aDirectory = opendir(aPath))) { 302 struct dirent *aBundle; 303 304 while ((aBundle = readdir(aDirectory))) { 305 struct stat aStatBuf; 306 char *aBundleName = aBundle->d_name; 307 char aBundlePath[PATH_MAX]; 308 char aBundleExecutablePath[PATH_MAX]; 309 char aConfigFile[PATH_MAX]; 310 char aDisabledFile[PATH_MAX]; 311 312 if (aBundleName[0] == '.') 313 continue; 314 315 syslog(LOG_DEBUG, "Found item: %s", aBundleName); 316 317 sprintf(aBundlePath, "%s/%s", aPath, aBundleName); 318 sprintf(aBundleExecutablePath, "%s/%s", aBundlePath, aBundleName); 319 sprintf(aConfigFile, "%s/%s", aBundlePath, kParametersFile); 320 sprintf(aDisabledFile, "%s/%s", aBundlePath, kDisabledFile); 321 322 if (lstat(aDisabledFile, &aStatBuf) == 0) { 323 syslog(LOG_NOTICE, "Skipping disabled StartupItem: %s", aBundlePath); 324 continue; 325 } 326 if (!StartupItemSecurityCheck(aBundlePath)) 327 continue; 328 if (!StartupItemSecurityCheck(aBundleExecutablePath)) 329 continue; 330 if (!StartupItemSecurityCheck(aConfigFile)) 331 continue; 332 333 /* Stow away the plist data for each bundle */ 334 { 335 int aConfigFileDescriptor; 336 337 if ((aConfigFileDescriptor = open(aConfigFile, O_RDONLY|O_NOCTTY, (mode_t) 0)) != -1) { 338 struct stat aConfigFileStatBuffer; 339 340 if (stat(aConfigFile, &aConfigFileStatBuffer) != -1) { 341 off_t aConfigFileContentsSize = aConfigFileStatBuffer.st_size; 342 char *aConfigFileContentsBuffer; 343 344 if ((aConfigFileContentsBuffer = 345 mmap((caddr_t) 0, aConfigFileContentsSize, 346 PROT_READ, MAP_FILE | MAP_PRIVATE, 347 aConfigFileDescriptor, (off_t) 0)) != (caddr_t) - 1) { 348 CFDataRef aConfigData = NULL; 349 CFMutableDictionaryRef aConfig = NULL; 350 351 aConfigData = 352 CFDataCreateWithBytesNoCopy(NULL, 353 (const UInt8 *)aConfigFileContentsBuffer, 354 aConfigFileContentsSize, 355 kCFAllocatorNull); 356 357 if (aConfigData) { 358 aConfig = (CFMutableDictionaryRef) 359 CFPropertyListCreateFromXMLData(NULL, aConfigData, 360 kCFPropertyListMutableContainers, 361 NULL); 362 } 363 if (StartupItemValidate(aConfig)) { 364 CFStringRef aBundlePathString = 365 CFStringCreateWithCString(NULL, aBundlePath, 366 kCFStringEncodingUTF8); 367 368 CFNumberRef aDomainNumber = 369 CFNumberCreate(NULL, kCFNumberCFIndexType, 370 &aDomainIndex); 371 372 CFDictionarySetValue(aConfig, kBundlePathKey, 373 aBundlePathString); 374 CFDictionarySetValue(aConfig, kDomainKey, aDomainNumber); 375 CFRelease(aDomainNumber); 376 SpecialCasesStartupItemHandler(aConfig); 377 CFArrayAppendValue(anItemList, aConfig); 378 379 CFRelease(aBundlePathString); 380 } else { 381 syslog(LOG_ERR, "Malformatted parameters file: %s", 382 aConfigFile); 383 } 384 385 if (aConfig) 386 CFRelease(aConfig); 387 if (aConfigData) 388 CFRelease(aConfigData); 389 390 if (munmap(aConfigFileContentsBuffer, aConfigFileContentsSize) == 391 -1) { 392 syslog(LOG_WARNING, 393 "Unable to unmap parameters file %s for item %s: %m", 394 aConfigFile, aBundleName); 395 } 396 } else { 397 syslog(LOG_ERR, 398 "Unable to map parameters file %s for item %s: %m", 399 aConfigFile, aBundleName); 400 } 401 } else { 402 syslog(LOG_ERR, "Unable to stat parameters file %s for item %s: %m", 403 aConfigFile, aBundleName); 404 } 405 406 if (close(aConfigFileDescriptor) == -1) { 407 syslog(LOG_ERR, "Unable to close parameters file %s for item %s: %m", 408 aConfigFile, aBundleName); 409 } 410 } else { 411 syslog(LOG_ERR, "Unable to open parameters file %s for item %s: %m", aConfigFile, 412 aBundleName); 413 } 414 } 415 } 416 if (closedir(aDirectory) == -1) { 417 syslog(LOG_WARNING, "Unable to directory bundle %s: %m", aPath); 418 } 419 } else { 420 if (errno != ENOENT) { 421 syslog(LOG_WARNING, "Open on directory %s failed: %m", aPath); 422 return (NULL); 423 } 424 } 425 } 426 427 return anItemList; 428} 429 430CFMutableDictionaryRef StartupItemListGetProvider(CFArrayRef anItemList, CFStringRef aService) 431{ 432 CFMutableDictionaryRef aResult = NULL; 433 CFMutableArrayRef aList = startupItemListCopyMatches(anItemList, kProvidesKey, aService); 434 435 if (aList && CFArrayGetCount(aList) > 0) 436 aResult = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(aList, 0); 437 438 if (aList) CFRelease(aList); 439 440 return aResult; 441} 442 443CFArrayRef StartupItemListCreateFromRunning(CFArrayRef anItemList) 444{ 445 CFMutableArrayRef aResult = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 446 if (aResult) { 447 CFIndex anIndex, aCount = CFArrayGetCount(anItemList); 448 for (anIndex = 0; anIndex < aCount; ++anIndex) { 449 CFDictionaryRef anItem = CFArrayGetValueAtIndex(anItemList, anIndex); 450 if (anItem) { 451 CFNumberRef aPID = CFDictionaryGetValue(anItem, kPIDKey); 452 if (aPID) 453 CFArrayAppendValue(aResult, anItem); 454 } 455 } 456 } 457 return aResult; 458} 459 460/* 461 * Append items in anItemList to aDependents which depend on 462 * aParentItem. 463 * If anAction is kActionStart, dependent items are those which 464 * require any service provided by aParentItem. 465 * If anAction is kActionStop, dependent items are those which provide 466 * any service required by aParentItem. 467 */ 468static void appendDependents(CFMutableArrayRef aDependents, CFArrayRef anItemList, CFDictionaryRef aParentItem, Action anAction) 469{ 470 CFStringRef anInnerKey, anOuterKey; 471 CFArrayRef anOuterList; 472 473 /* Append the parent item to the list (avoiding duplicates) */ 474 if (!CFArrayContainsValue(aDependents, CFRangeMake(0, CFArrayGetCount(aDependents)), aParentItem)) 475 CFArrayAppendValue(aDependents, aParentItem); 476 477 /** 478 * Recursively append any children of the parent item for kStartAction and kStopAction. 479 * Do nothing for other actions. 480 **/ 481 switch (anAction) { 482 case kActionStart: 483 anInnerKey = kProvidesKey; 484 anOuterKey = kRequiresKey; 485 break; 486 case kActionStop: 487 anInnerKey = kRequiresKey; 488 anOuterKey = kProvidesKey; 489 break; 490 default: 491 return; 492 } 493 494 anOuterList = CFDictionaryGetValue(aParentItem, anOuterKey); 495 496 if (anOuterList) { 497 CFIndex anOuterCount = CFArrayGetCount(anOuterList); 498 CFIndex anOuterIndex; 499 500 for (anOuterIndex = 0; anOuterIndex < anOuterCount; anOuterIndex++) { 501 CFStringRef anOuterElement = CFArrayGetValueAtIndex(anOuterList, anOuterIndex); 502 CFIndex anItemCount = CFArrayGetCount(anItemList); 503 CFIndex anItemIndex; 504 505 for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) { 506 CFDictionaryRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex); 507 CFArrayRef anInnerList = CFDictionaryGetValue(anItem, anInnerKey); 508 509 if (anInnerList && 510 CFArrayContainsValue(anInnerList, CFRangeMake(0, CFArrayGetCount(anInnerList)), 511 anOuterElement) 512 && !CFArrayContainsValue(aDependents, CFRangeMake(0, CFArrayGetCount(aDependents)), anItem)) 513 appendDependents(aDependents, anItemList, anItem, anAction); 514 } 515 } 516 } 517} 518 519CFMutableArrayRef StartupItemListCreateDependentsList(CFMutableArrayRef anItemList, CFStringRef aService, Action anAction) 520{ 521 CFMutableArrayRef aDependents = NULL; 522 CFMutableDictionaryRef anItem = NULL; 523 524 if (anItemList && aService) 525 anItem = StartupItemListGetProvider(anItemList, aService); 526 527 if (anItem) { 528 switch (anAction) { 529 case kActionRestart: 530 case kActionStart: 531 case kActionStop: 532 aDependents = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 533 534 if (!aDependents) { 535 CF_syslog(LOG_EMERG, CFSTR("Failed to allocate dependancy list for item %@"), anItem); 536 return NULL; 537 } 538 appendDependents(aDependents, anItemList, anItem, anAction); 539 break; 540 541 default: 542 break; 543 } 544 } 545 return aDependents; 546} 547 548/** 549 * countUnmetRequirements counts the number of items in anItemList 550 * which are pending in aStatusDict. 551 **/ 552static int countUnmetRequirements(CFDictionaryRef aStatusDict, CFArrayRef anItemList) 553{ 554 int aCount = 0; 555 CFIndex anItemCount = CFArrayGetCount(anItemList); 556 CFIndex anItemIndex; 557 558 for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) { 559 CFStringRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex); 560 CFStringRef aStatus = CFDictionaryGetValue(aStatusDict, anItem); 561 562 if (!aStatus || !CFEqual(aStatus, kRunSuccess)) { 563 CF_syslog(LOG_DEBUG, CFSTR("\tFailed requirement/uses: %@"), anItem); 564 aCount++; 565 } 566 } 567 568 return aCount; 569} 570 571/** 572 * countDependantsPresent counts the number of items in aWaitingList 573 * which depend on items in anItemList. 574 **/ 575static int countDependantsPresent(CFArrayRef aWaitingList, CFArrayRef anItemList, CFStringRef aKey) 576{ 577 int aCount = 0; 578 CFIndex anItemCount = CFArrayGetCount(anItemList); 579 CFIndex anItemIndex; 580 581 for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) { 582 CFStringRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex); 583 CFArrayRef aMatchesList = startupItemListCopyMatches(aWaitingList, aKey, anItem); 584 585 if (aMatchesList) { 586 aCount = aCount + CFArrayGetCount(aMatchesList); 587 CFRelease(aMatchesList); 588 } 589 } 590 591 return aCount; 592} 593 594/** 595 * pendingAntecedents returns TRUE if any antecedents of this item 596 * are currently running, have not yet run, or none exist. 597 **/ 598static Boolean 599pendingAntecedents(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFArrayRef anAntecedentList, Action anAction) 600{ 601 int aPendingFlag = FALSE; 602 603 CFIndex anAntecedentCount = CFArrayGetCount(anAntecedentList); 604 CFIndex anAntecedentIndex; 605 606 for (anAntecedentIndex = 0; anAntecedentIndex < anAntecedentCount; ++anAntecedentIndex) { 607 CFStringRef anAntecedent = CFArrayGetValueAtIndex(anAntecedentList, anAntecedentIndex); 608 CFStringRef aKey = (anAction == kActionStart) ? kProvidesKey : kUsesKey; 609 CFArrayRef aMatchesList = startupItemListCopyMatches(aWaitingList, aKey, anAntecedent); 610 611 if (aMatchesList) { 612 CFIndex aMatchesListCount = CFArrayGetCount(aMatchesList); 613 CFIndex aMatchesListIndex; 614 615 for (aMatchesListIndex = 0; aMatchesListIndex < aMatchesListCount; ++aMatchesListIndex) { 616 CFDictionaryRef anItem = CFArrayGetValueAtIndex(aMatchesList, aMatchesListIndex); 617 618 if (!anItem || 619 !CFDictionaryGetValue(anItem, kPIDKey) || !CFDictionaryGetValue(aStatusDict, anAntecedent)) { 620 aPendingFlag = TRUE; 621 break; 622 } 623 } 624 625 CFRelease(aMatchesList); 626 627 if (aPendingFlag) 628 break; 629 } 630 } 631 return (aPendingFlag); 632} 633 634/** 635 * checkForDuplicates returns TRUE if an item provides the same service as a 636 * pending item, or an item that already succeeded. 637 **/ 638static Boolean checkForDuplicates(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFDictionaryRef anItem) 639{ 640 int aDuplicateFlag = FALSE; 641 642 CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); 643 CFIndex aProvidesCount = aProvidesList ? CFArrayGetCount(aProvidesList) : 0; 644 CFIndex aProvidesIndex; 645 646 for (aProvidesIndex = 0; aProvidesIndex < aProvidesCount; ++aProvidesIndex) { 647 CFStringRef aProvides = CFArrayGetValueAtIndex(aProvidesList, aProvidesIndex); 648 649 /* If the service succeeded, return true. */ 650 CFStringRef aStatus = CFDictionaryGetValue(aStatusDict, aProvides); 651 if (aStatus && CFEqual(aStatus, kRunSuccess)) { 652 aDuplicateFlag = TRUE; 653 break; 654 } 655 /* 656 * Otherwise test if any item is currently running which 657 * might provide that service. 658 */ 659 else { 660 CFArrayRef aMatchesList = startupItemListCopyMatches(aWaitingList, kProvidesKey, aProvides); 661 if (aMatchesList) { 662 CFIndex aMatchesListCount = CFArrayGetCount(aMatchesList); 663 CFIndex aMatchesListIndex; 664 665 for (aMatchesListIndex = 0; aMatchesListIndex < aMatchesListCount; ++aMatchesListIndex) { 666 CFDictionaryRef anDupItem = CFArrayGetValueAtIndex(aMatchesList, aMatchesListIndex); 667 if (anDupItem && CFDictionaryGetValue(anDupItem, kPIDKey)) { 668 /* 669 * Item is running, avoid 670 * race condition. 671 */ 672 aDuplicateFlag = TRUE; 673 break; 674 } else { 675 CFNumberRef anItemDomain = CFDictionaryGetValue(anItem, kDomainKey); 676 CFNumberRef anotherItemDomain = CFDictionaryGetValue(anDupItem, kDomainKey); 677 /* 678 * If anItem was found later 679 * than aDupItem, stall 680 * anItem until aDupItem 681 * runs. 682 */ 683 if (anItemDomain && 684 anotherItemDomain && 685 CFNumberCompare(anItemDomain, anotherItemDomain, 686 NULL) == kCFCompareGreaterThan) { 687 /* 688 * Item not running, 689 * but takes 690 * precedence. 691 */ 692 aDuplicateFlag = TRUE; 693 break; 694 } 695 } 696 } 697 698 CFRelease(aMatchesList); 699 if (aDuplicateFlag) 700 break; 701 } 702 } 703 } 704 return (aDuplicateFlag); 705} 706 707CFMutableDictionaryRef StartupItemListGetNext(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, Action anAction) 708{ 709 CFMutableDictionaryRef aNextItem = NULL; 710 CFIndex aWaitingCount = CFArrayGetCount(aWaitingList); 711 int aMinFailedAntecedents = INT_MAX; 712 CFIndex aWaitingIndex; 713 714 switch (anAction) { 715 case kActionStart: 716 break; 717 case kActionStop: 718 break; 719 case kActionRestart: 720 break; 721 default: 722 return NULL; 723 } 724 725 if (!aWaitingList || !aStatusDict || aWaitingCount <= 0) 726 return NULL; 727 728 /** 729 * Iterate through the items in aWaitingList and look for an optimally ready item. 730 **/ 731 for (aWaitingIndex = 0; aWaitingIndex < aWaitingCount; aWaitingIndex++) { 732 CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(aWaitingList, aWaitingIndex); 733 CFArrayRef anAntecedentList; 734 int aFailedAntecedentsCount = 0; /* Number of unmet soft 735 * depenancies */ 736 Boolean aBestPick = FALSE; /* Is this the best pick 737 * so far? */ 738 739 /* Filter out running items. */ 740 if (CFDictionaryGetValue(anItem, kPIDKey)) 741 continue; 742 743 /* 744 * Filter out dupilicate services; if someone has 745 * provided what we provide, we don't run. 746 */ 747 if (checkForDuplicates(aWaitingList, aStatusDict, anItem)) { 748 CF_syslog(LOG_DEBUG, CFSTR("Skipping %@ because of duplicate service."), 749 CFDictionaryGetValue(anItem, kDescriptionKey)); 750 continue; 751 } 752 /* 753 * Dependencies don't matter when restarting an item; 754 * stop here. 755 */ 756 if (anAction == kActionRestart) { 757 aNextItem = anItem; 758 break; 759 } 760 anAntecedentList = CFDictionaryGetValue(anItem, ((anAction == kActionStart) ? kRequiresKey : kProvidesKey)); 761 762 CF_syslog(LOG_DEBUG, CFSTR("Checking %@"), CFDictionaryGetValue(anItem, kDescriptionKey)); 763 764 if (anAntecedentList) 765 CF_syslog(LOG_DEBUG, CFSTR("Antecedents: %@"), anAntecedentList); 766 else 767 syslog(LOG_DEBUG, "No antecedents"); 768 769 /** 770 * Filter out the items which have unsatisfied antecedents. 771 **/ 772 if (anAntecedentList && 773 ((anAction == kActionStart) ? 774 countUnmetRequirements(aStatusDict, anAntecedentList) : 775 countDependantsPresent(aWaitingList, anAntecedentList, kRequiresKey))) 776 continue; 777 778 /** 779 * anItem has all hard dependancies met; check for soft dependancies. 780 * We'll favor the item with the fewest unmet soft dependancies here. 781 **/ 782 anAntecedentList = CFDictionaryGetValue(anItem, ((anAction == kActionStart) ? kUsesKey : kProvidesKey)); 783 784 if (anAntecedentList) 785 CF_syslog(LOG_DEBUG, CFSTR("Soft dependancies: %@"), anAntecedentList); 786 else 787 syslog(LOG_DEBUG, "No soft dependancies"); 788 789 if (anAntecedentList) { 790 aFailedAntecedentsCount = 791 ((anAction == kActionStart) ? 792 countUnmetRequirements(aStatusDict, anAntecedentList) : 793 countDependantsPresent(aWaitingList, anAntecedentList, kUsesKey)); 794 } else { 795 if (aMinFailedAntecedents > 0) 796 aBestPick = TRUE; 797 } 798 799 /* 800 * anItem has unmet dependencies that will 801 * likely be met in the future, so delay it 802 */ 803 if (aFailedAntecedentsCount > 0 && pendingAntecedents(aWaitingList, aStatusDict, anAntecedentList, anAction)) { 804 continue; 805 } 806 if (aFailedAntecedentsCount > 0) 807 syslog(LOG_DEBUG, "Total: %d", aFailedAntecedentsCount); 808 809 if (aFailedAntecedentsCount > aMinFailedAntecedents) 810 continue; /* Another item already won out */ 811 812 if (aFailedAntecedentsCount < aMinFailedAntecedents) 813 aBestPick = TRUE; 814 815 if (!aBestPick) 816 continue; 817 818 /* 819 * anItem has less unmet 820 * dependancies than any 821 * other item so far, so it 822 * wins. 823 */ 824 syslog(LOG_DEBUG, "Best pick so far, based on failed dependancies (%d->%d)", 825 aMinFailedAntecedents, aFailedAntecedentsCount); 826 827 /* 828 * We have a winner! Update success 829 * parameters to match anItem. 830 */ 831 aMinFailedAntecedents = aFailedAntecedentsCount; 832 aNextItem = anItem; 833 834 } /* End of waiting list loop. */ 835 836 return aNextItem; 837} 838 839CFStringRef StartupItemCreateDescription(CFMutableDictionaryRef anItem) 840{ 841 CFStringRef aString = NULL; 842 843 if (anItem) 844 aString = CFDictionaryGetValue(anItem, kDescriptionKey); 845 if (aString) 846 CFRetain(aString); 847 return aString; 848} 849 850pid_t StartupItemGetPID(CFDictionaryRef anItem) 851{ 852 CFIndex anItemPID = 0; 853 CFNumberRef aPIDNumber = anItem ? CFDictionaryGetValue(anItem, kPIDKey) : NULL; 854 if (aPIDNumber && CFNumberGetValue(aPIDNumber, kCFNumberCFIndexType, &anItemPID)) 855 return (pid_t) anItemPID; 856 else 857 return 0; 858} 859 860CFMutableDictionaryRef StartupItemWithPID(CFArrayRef anItemList, pid_t aPID) 861{ 862 CFIndex anItemCount = CFArrayGetCount(anItemList); 863 CFIndex anItemIndex; 864 865 for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) { 866 CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(anItemList, anItemIndex); 867 CFNumberRef aPIDNumber = CFDictionaryGetValue(anItem, kPIDKey); 868 CFIndex anItemPID; 869 870 if (aPIDNumber) { 871 CFNumberGetValue(aPIDNumber, kCFNumberCFIndexType, &anItemPID); 872 873 if ((pid_t) anItemPID == aPID) 874 return anItem; 875 } 876 } 877 878 return NULL; 879} 880 881int StartupItemRun(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, Action anAction) 882{ 883 int anError = -1; 884 CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); 885 static const CFStringRef stubitems[] = { 886 CFSTR("BootROMUpdater"), /* 3893064 */ 887 CFSTR("FCUUpdater"), /* 3893064 */ 888 CFSTR("AutoProtect Daemon"), /* 3965785 */ 889 CFSTR("Check For Missed Tasks"), /* 3965785 */ 890 CFSTR("Privacy"), /* 3933484 */ 891 CFSTR("Firmware Update Checking"), /* 4001504 */ 892 893 CFSTR("M-Audio FireWire Audio Support"), /* 3931757 */ 894 CFSTR("help for M-Audio Delta Family"), /* 3931757 */ 895 CFSTR("help for M-Audio Devices"), /* 3931757 */ 896 CFSTR("help for M-Audio Revo 5.1"), /* 3931757 */ 897 CFSTR("M-Audio USB Duo Configuration Service"), /* 3931757 */ 898 CFSTR("firmware loader for M-Audio devices"), /* 3931757 */ 899 CFSTR("M-Audio MobilePre USB Configuration Service"), /* 3931757 */ 900 CFSTR("M-Audio OmniStudio USB Configuration Service"), /* 3931757 */ 901 CFSTR("M-Audio Transit USB Configuration Service"), /* 3931757 */ 902 CFSTR("M-Audio Audiophile USB Configuration Service"), /* 3931757 */ 903 NULL 904 }; 905 const CFStringRef *c; 906 907 if (aProvidesList && anAction == kActionStop) { 908 CFIndex aProvidesCount = CFArrayGetCount(aProvidesList); 909 for (c = stubitems; *c; c++) { 910 if (CFArrayContainsValue(aProvidesList, CFRangeMake(0, aProvidesCount), *c)) { 911 CFIndex aPID = -1; 912 CFNumberRef aProcessNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID); 913 914 CFDictionarySetValue(anItem, kPIDKey, aProcessNumber); 915 CFRelease(aProcessNumber); 916 917 StartupItemExit(aStatusDict, anItem, TRUE); 918 return -1; 919 } 920 } 921 } 922 923 if (anAction == kActionNone) { 924 StartupItemExit(aStatusDict, anItem, TRUE); 925 anError = 0; 926 } else { 927 CFStringRef aBundlePathString = CFDictionaryGetValue(anItem, kBundlePathKey); 928 char aBundlePath[PATH_MAX]; 929 char anExecutable[PATH_MAX]; 930 char *tmp; 931 932 if (!CFStringGetCString(aBundlePathString, aBundlePath, sizeof(aBundlePath), kCFStringEncodingUTF8)) { 933 CF_syslog(LOG_EMERG, CFSTR("Internal error while running item %@"), aBundlePathString); 934 return (anError); 935 } 936 /* Compute path to excecutable */ 937 tmp = rindex(aBundlePath, '/'); 938 snprintf(anExecutable, sizeof(anExecutable), "%s%s", aBundlePath, tmp); 939 940 /** 941 * Run the bundle 942 **/ 943 944 if (access(anExecutable, X_OK)) { 945 /* 946 * Add PID key so that this item is marked as having 947 * been run. 948 */ 949 CFIndex aPID = -1; 950 CFNumberRef aProcessNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID); 951 952 CFDictionarySetValue(anItem, kPIDKey, aProcessNumber); 953 CFRelease(aProcessNumber); 954 955 CFDictionarySetValue(anItem, kErrorKey, kErrorPermissions); 956 StartupItemExit(aStatusDict, anItem, FALSE); 957 syslog(LOG_ERR, "No executable file %s", anExecutable); 958 } else { 959 pid_t aProccessID = fork(); 960 961 switch (aProccessID) { 962 case -1: /* SystemStarter (fork failed) */ 963 CFDictionarySetValue(anItem, kErrorKey, kErrorFork); 964 StartupItemExit(aStatusDict, anItem, FALSE); 965 966 CF_syslog(LOG_ERR, CFSTR("Failed to fork for item %@: %s"), aBundlePathString, strerror(errno)); 967 968 break; 969 970 default: /* SystemStarter (fork succeeded) */ 971 { 972 CFIndex aPID = (CFIndex) aProccessID; 973 CFNumberRef aProcessNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID); 974 975 CFDictionarySetValue(anItem, kPIDKey, aProcessNumber); 976 CFRelease(aProcessNumber); 977 978 syslog(LOG_DEBUG, "Running command (%d): %s %s", 979 aProccessID, anExecutable, argumentForAction(anAction)); 980 anError = 0; 981 } 982 break; 983 984 case 0: /* Child */ 985 { 986 if (setsid() == -1) 987 syslog(LOG_WARNING, "Unable to create session for item %s: %m", anExecutable); 988 989 anError = execl(anExecutable, anExecutable, argumentForAction(anAction), NULL); 990 991 /* We shouldn't get here. */ 992 993 syslog(LOG_ERR, "execl(\"%s\"): %m", anExecutable); 994 995 exit(anError); 996 } 997 } 998 } 999 } 1000 1001 return (anError); 1002} 1003 1004void 1005StartupItemSetStatus(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, CFStringRef aServiceName, 1006 Boolean aSuccess, Boolean aReplaceFlag) 1007{ 1008 void (*anAction) (CFMutableDictionaryRef, const void *, const void *) = aReplaceFlag ? 1009 CFDictionarySetValue : CFDictionaryAddValue; 1010 1011 if (aStatusDict && anItem) { 1012 CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey); 1013 if (aProvidesList) { 1014 CFIndex aProvidesCount = CFArrayGetCount(aProvidesList); 1015 CFIndex aProvidesIndex; 1016 1017 /* 1018 * If a service name was specified, and it is valid, 1019 * use only it. 1020 */ 1021 if (aServiceName && CFArrayContainsValue(aProvidesList, CFRangeMake(0, aProvidesCount), aServiceName)) { 1022 aProvidesList = CFArrayCreate(NULL, (const void **)&aServiceName, 1, &kCFTypeArrayCallBacks); 1023 aProvidesCount = 1; 1024 } else { 1025 CFRetain(aProvidesList); 1026 } 1027 1028 for (aProvidesIndex = 0; aProvidesIndex < aProvidesCount; aProvidesIndex++) { 1029 CFStringRef aService = CFArrayGetValueAtIndex(aProvidesList, aProvidesIndex); 1030 1031 if (aSuccess) 1032 anAction(aStatusDict, aService, kRunSuccess); 1033 else 1034 anAction(aStatusDict, aService, kRunFailure); 1035 } 1036 1037 CFRelease(aProvidesList); 1038 } 1039 } 1040} 1041 1042void StartupItemExit(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, Boolean aSuccess) 1043{ 1044 StartupItemSetStatus(aStatusDict, anItem, NULL, aSuccess, FALSE); 1045}