this repo has no description
at fixPythonPipStalling 457 lines 13 kB view raw
1/** 2 * System Starter main 3 * Wilfredo Sanchez | wsanchez@opensource.apple.com 4 * $Apple$ 5 ** 6 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. 7 * 8 * @APPLE_APACHE_LICENSE_HEADER_START@ 9 * 10 * Licensed under the Apache License, Version 2.0 (the "License"); 11 * you may not use this file except in compliance with the License. 12 * You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, software 17 * distributed under the License is distributed on an "AS IS" BASIS, 18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 * See the License for the specific language governing permissions and 20 * limitations under the License. 21 * 22 * @APPLE_APACHE_LICENSE_HEADER_END@ 23 **/ 24 25#include <IOKit/IOKitLib.h> 26#include <sys/types.h> 27#include <sys/event.h> 28#include <sys/stat.h> 29#include <paths.h> 30#include <unistd.h> 31#include <crt_externs.h> 32#include <fcntl.h> 33#include <syslog.h> 34#include <assert.h> 35#include <CoreFoundation/CoreFoundation.h> 36#include <DiskArbitration/DiskArbitration.h> 37#include <DiskArbitration/DiskArbitrationPrivate.h> 38#include <NSSystemDirectories.h> 39#include "IPC.h" 40#include "StartupItems.h" 41#include "SystemStarter.h" 42#include "SystemStarterIPC.h" 43 44bool gDebugFlag = false; 45bool gVerboseFlag = false; 46bool gNoRunFlag = false; 47 48static void usage(void) __attribute__((noreturn)); 49static int system_starter(Action anAction, const char *aService); 50static void displayErrorMessages(StartupContext aStartupContext, Action anAction); 51static pid_t fwexec(const char *cmd, ...) __attribute__((sentinel)); 52static void autodiskmount(void); 53static void dummy_sig(int signo __attribute__((unused))) 54{ 55} 56 57int 58main(int argc, char *argv[]) 59{ 60 struct kevent kev; 61 Action anAction = kActionStart; 62 int ch, r, kq = kqueue(); 63 64 assert(kq != -1); 65 66 EV_SET(&kev, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); 67 r = kevent(kq, &kev, 1, NULL, 0, NULL); 68 assert(r != -1); 69 signal(SIGTERM, dummy_sig); 70 71 while ((ch = getopt(argc, argv, "gvxirdDqn?")) != -1) { 72 switch (ch) { 73 case 'v': 74 gVerboseFlag = true; 75 break; 76 case 'x': 77 case 'g': 78 case 'r': 79 case 'q': 80 break; 81 case 'd': 82 case 'D': 83 gDebugFlag = true; 84 break; 85 case 'n': 86 gNoRunFlag = true; 87 break; 88 case '?': 89 default: 90 usage(); 91 break; 92 } 93 } 94 argc -= optind; 95 argv += optind; 96 97 if (argc > 2) { 98 usage(); 99 } 100 101 openlog(getprogname(), LOG_PID|LOG_CONS|(gDebugFlag ? LOG_PERROR : 0), LOG_DAEMON); 102 if (gDebugFlag) { 103 setlogmask(LOG_UPTO(LOG_DEBUG)); 104 } else if (gVerboseFlag) { 105 setlogmask(LOG_UPTO(LOG_INFO)); 106 } else { 107 setlogmask(LOG_UPTO(LOG_NOTICE)); 108 } 109 110 if (!gNoRunFlag && (getuid() != 0)) { 111 syslog(LOG_ERR, "must be root to run"); 112 exit(EXIT_FAILURE); 113 } 114 115 if (argc > 0) { 116 if (strcmp(argv[0], "start") == 0) { 117 anAction = kActionStart; 118 } else if (strcmp(argv[0], "stop") == 0) { 119 anAction = kActionStop; 120 } else if (strcmp(argv[0], "restart") == 0) { 121 anAction = kActionRestart; 122 } else { 123 usage(); 124 } 125 } 126 127 if (argc == 2) { 128 exit(system_starter(anAction, argv[1])); 129 } 130 131 unlink(kFixerPath); 132 133 mach_timespec_t w = { 600, 0 }; 134 kern_return_t kr; 135 136 /* 137 * Too many old StartupItems had implicit dependancies on "Network" via 138 * other StartupItems that are now no-ops. 139 * 140 * SystemStarter is not on the critical path for boot up, so we'll 141 * stall here to deal with this legacy dependancy problem. 142 */ 143 144 if ((kr = IOKitWaitQuiet(kIOMasterPortDefault, &w)) != kIOReturnSuccess) { 145 syslog(LOG_NOTICE, "IOKitWaitQuiet: %d\n", kr); 146 } 147 148 fwexec("/usr/sbin/ipconfig", "waitall", NULL); 149 autodiskmount(); /* wait for Disk Arbitration to report idle */ 150 151 system_starter(kActionStart, NULL); 152 153 if (StartupItemSecurityCheck("/etc/rc.local")) { 154 fwexec(_PATH_BSHELL, "/etc/rc.local", NULL); 155 } 156 157 CFNotificationCenterPostNotificationWithOptions( 158 CFNotificationCenterGetDistributedCenter(), 159 CFSTR("com.apple.startupitems.completed"), 160 NULL, NULL, 161 kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions); 162 163 r = kevent(kq, NULL, 0, &kev, 1, NULL); 164 assert(r != -1); 165 assert(kev.filter == EVFILT_SIGNAL && kev.ident == SIGTERM); 166 167 if (StartupItemSecurityCheck("/etc/rc.shutdown.local")) { 168 fwexec(_PATH_BSHELL, "/etc/rc.shutdown.local", NULL); 169 } 170 171 system_starter(kActionStop, NULL); 172 173 exit(EXIT_SUCCESS); 174} 175 176 177/** 178 * checkForActivity checks to see if any items have completed since the last invokation. 179 * If not, a message is displayed showing what item(s) are being waited on. 180 **/ 181static void 182checkForActivity(StartupContext aStartupContext) 183{ 184 static CFIndex aLastStatusDictionaryCount = -1; 185 static CFStringRef aWaitingForString = NULL; 186 187 if (aStartupContext && aStartupContext->aStatusDict) { 188 CFIndex aCount = CFDictionaryGetCount(aStartupContext->aStatusDict); 189 190 if (!aWaitingForString) { 191 aWaitingForString = CFSTR("Waiting for %@"); 192 } 193 if (aLastStatusDictionaryCount == aCount) { 194 CFArrayRef aRunningList = StartupItemListCreateFromRunning(aStartupContext->aWaitingList); 195 if (aRunningList && CFArrayGetCount(aRunningList) > 0) { 196 CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(aRunningList, 0); 197 CFStringRef anItemDescription = StartupItemCreateDescription(anItem); 198 CFStringRef aString = aWaitingForString && anItemDescription ? 199 CFStringCreateWithFormat(NULL, NULL, aWaitingForString, anItemDescription) : NULL; 200 201 if (aString) { 202 CF_syslog(LOG_INFO, CFSTR("%@"), aString); 203 CFRelease(aString); 204 } 205 if (anItemDescription) 206 CFRelease(anItemDescription); 207 } 208 if (aRunningList) 209 CFRelease(aRunningList); 210 } 211 aLastStatusDictionaryCount = aCount; 212 } 213} 214 215/* 216 * print out any error messages to the log regarding non starting StartupItems 217 */ 218void 219displayErrorMessages(StartupContext aStartupContext, Action anAction) 220{ 221 if (aStartupContext->aFailedList && CFArrayGetCount(aStartupContext->aFailedList) > 0) { 222 CFIndex anItemCount = CFArrayGetCount(aStartupContext->aFailedList); 223 CFIndex anItemIndex; 224 225 226 syslog(LOG_WARNING, "The following StartupItems failed to %s properly:", (anAction == kActionStart) ? "start" : "stop"); 227 228 for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) { 229 CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(aStartupContext->aFailedList, anItemIndex); 230 CFStringRef anErrorDescription = CFDictionaryGetValue(anItem, kErrorKey); 231 CFStringRef anItemPath = CFDictionaryGetValue(anItem, kBundlePathKey); 232 233 if (anItemPath) { 234 CF_syslog(LOG_WARNING, CFSTR("%@"), anItemPath); 235 } 236 if (anErrorDescription) { 237 CF_syslog(LOG_WARNING, CFSTR(" - %@"), anErrorDescription); 238 } else { 239 CF_syslog(LOG_WARNING, CFSTR(" - %@"), kErrorInternal); 240 } 241 } 242 } 243 if (CFArrayGetCount(aStartupContext->aWaitingList) > 0) { 244 CFIndex anItemCount = CFArrayGetCount(aStartupContext->aWaitingList); 245 CFIndex anItemIndex; 246 247 syslog(LOG_WARNING, "The following StartupItems were not attempted due to failure of a required service:"); 248 249 for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) { 250 CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(aStartupContext->aWaitingList, anItemIndex); 251 CFStringRef anItemPath = CFDictionaryGetValue(anItem, kBundlePathKey); 252 if (anItemPath) { 253 CF_syslog(LOG_WARNING, CFSTR("%@"), anItemPath); 254 } 255 } 256 } 257} 258 259 260static int 261system_starter(Action anAction, const char *aService_cstr) 262{ 263 CFStringRef aService = NULL; 264 NSSearchPathDomainMask aMask; 265 266 if (aService_cstr) 267 aService = CFStringCreateWithCString(kCFAllocatorDefault, aService_cstr, kCFStringEncodingUTF8); 268 269 StartupContext aStartupContext = (StartupContext) malloc(sizeof(struct StartupContextStorage)); 270 if (!aStartupContext) { 271 syslog(LOG_ERR, "Not enough memory to allocate startup context"); 272 return (1); 273 } 274 if (gDebugFlag && gNoRunFlag) 275 sleep(1); 276 277 /** 278 * Get a list of Startup Items which are in /Local and /System. 279 * We can't search /Network yet because the network isn't up. 280 **/ 281 aMask = NSSystemDomainMask | NSLocalDomainMask; 282 283 aStartupContext->aWaitingList = StartupItemListCreateWithMask(aMask); 284 aStartupContext->aFailedList = NULL; 285 aStartupContext->aStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, 286 &kCFTypeDictionaryValueCallBacks); 287 aStartupContext->aServicesCount = 0; 288 aStartupContext->aRunningCount = 0; 289 290 if (aService) { 291 CFMutableArrayRef aDependentsList = StartupItemListCreateDependentsList(aStartupContext->aWaitingList, aService, anAction); 292 293 if (aDependentsList) { 294 CFRelease(aStartupContext->aWaitingList); 295 aStartupContext->aWaitingList = aDependentsList; 296 } else { 297 CF_syslog(LOG_ERR, CFSTR("Unknown service: %@"), aService); 298 return (1); 299 } 300 } 301 aStartupContext->aServicesCount = StartupItemListCountServices(aStartupContext->aWaitingList); 302 303 /** 304 * Do the run loop 305 **/ 306 while (1) { 307 CFMutableDictionaryRef anItem = StartupItemListGetNext(aStartupContext->aWaitingList, aStartupContext->aStatusDict, anAction); 308 309 if (anItem) { 310 int err = StartupItemRun(aStartupContext->aStatusDict, anItem, anAction); 311 if (!err) { 312 ++aStartupContext->aRunningCount; 313 MonitorStartupItem(aStartupContext, anItem); 314 } else { 315 /* add item to failed list */ 316 AddItemToFailedList(aStartupContext, anItem); 317 318 /* Remove the item from the waiting list. */ 319 RemoveItemFromWaitingList(aStartupContext, anItem); 320 } 321 } else { 322 /* 323 * If no item was selected to run, and if no items 324 * are running, startup is done. 325 */ 326 if (aStartupContext->aRunningCount == 0) { 327 syslog(LOG_DEBUG, "none left"); 328 break; 329 } 330 /* 331 * Process incoming IPC messages and item 332 * terminations 333 */ 334 switch (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 3.0, true)) { 335 case kCFRunLoopRunTimedOut: 336 checkForActivity(aStartupContext); 337 break; 338 case kCFRunLoopRunFinished: 339 break; 340 case kCFRunLoopRunStopped: 341 break; 342 case kCFRunLoopRunHandledSource: 343 break; 344 default: 345 /* unknown return value */ 346 break; 347 } 348 } 349 } 350 351 /** 352 * Good-bye. 353 **/ 354 displayErrorMessages(aStartupContext, anAction); 355 356 /* clean up */ 357 if (aStartupContext->aStatusDict) 358 CFRelease(aStartupContext->aStatusDict); 359 if (aStartupContext->aWaitingList) 360 CFRelease(aStartupContext->aWaitingList); 361 if (aStartupContext->aFailedList) 362 CFRelease(aStartupContext->aFailedList); 363 364 free(aStartupContext); 365 return (0); 366} 367 368void 369CF_syslog(int level, CFStringRef message,...) 370{ 371 char buf[8192]; 372 CFStringRef cooked_msg; 373 va_list ap; 374 375 va_start(ap, message); 376 cooked_msg = CFStringCreateWithFormatAndArguments(NULL, NULL, message, ap); 377 va_end(ap); 378 379 if (CFStringGetCString(cooked_msg, buf, sizeof(buf), kCFStringEncodingUTF8)) 380 syslog(level, "%s", buf); 381 382 CFRelease(cooked_msg); 383} 384 385static void 386usage(void) 387{ 388 fprintf(stderr, "usage: %s [-vdqn?] [ <action> [ <item> ] ]\n" 389 "\t<action>: action to take (start|stop|restart); default is start\n" 390 "\t<item> : name of item to act on; default is all items\n" 391 "options:\n" 392 "\t-v: verbose startup\n" 393 "\t-d: print debugging output\n" 394 "\t-q: be quiet (disable debugging output)\n" 395 "\t-n: don't actually perform action on items (pretend mode)\n" 396 "\t-?: show this help\n", 397 getprogname()); 398 exit(EXIT_FAILURE); 399} 400 401pid_t 402fwexec(const char *cmd, ...) 403{ 404 const char *argv[100] = { cmd }; 405 va_list ap; 406 int wstatus, i = 1; 407 pid_t p; 408 409 va_start(ap, cmd); 410 do { 411 argv[i] = va_arg(ap, char *); 412 } while (argv[i++]); 413 va_end(ap); 414 415 switch ((p = fork())) { 416 case -1: 417 return -1; 418 case 0: 419 execvp(argv[0], (char *const *)argv); 420 _exit(EXIT_FAILURE); 421 break; 422 default: 423 if (waitpid(p, &wstatus, 0) == -1) { 424 return -1; 425 } else if (WIFEXITED(wstatus)) { 426 if (WEXITSTATUS(wstatus) == 0) { 427 return 0; 428 } else { 429 syslog(LOG_WARNING, "%s exit status: %d", argv[0], WEXITSTATUS(wstatus)); 430 } 431 } else { 432 /* must have died due to signal */ 433 syslog(LOG_WARNING, "%s died: %s", argv[0], strsignal(WTERMSIG(wstatus))); 434 } 435 break; 436 } 437 438 return -1; 439} 440 441static void 442autodiskmount_idle(void* context __attribute__((unused))) 443{ 444 CFRunLoopStop(CFRunLoopGetCurrent()); 445} 446 447static void 448autodiskmount(void) 449{ 450 DASessionRef session = DASessionCreate(NULL); 451 if (session) { 452 DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 453 DARegisterIdleCallback(session, autodiskmount_idle, NULL); 454 CFRunLoopRun(); 455 CFRelease(session); 456 } 457}