3  * Wilfredo Sanchez | wsanchez@opensource.apple.com 
   6  * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. 
   8  * @APPLE_LICENSE_HEADER_START@ 
  10  * Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights 
  11  * Reserved.  This file contains Original Code and/or Modifications of 
  12  * Original Code as defined in and that are subject to the Apple Public 
  13  * Source License Version 1.1 (the "License").  You may not use this file 
  14  * except in compliance with the License.  Please obtain a copy of the 
  15  * License at http://www.apple.com/publicsource and read it before using 
  18  * The Original Code and all software distributed under the License are 
  19  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  22  * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT.  Please see the 
  23  * License for the specific language governing rights and limitations 
  26  * @APPLE_LICENSE_HEADER_END@ 
  30 #include <crt_externs.h> 
  33 #include <CoreFoundation/CoreFoundation.h> 
  34 #include <NSSystemDirectories.h> 
  36 #include "StartupItems.h" 
  37 #include "SystemStarter.h" 
  38 #include "SystemStarterIPC.h" 
  40 bool gDebugFlag 
= false; 
  41 bool gVerboseFlag 
= false; 
  42 bool gNoRunFlag 
= false; 
  44 static void     usage(void) __attribute__((noreturn
)); 
  45 static int      system_starter(Action anAction
, const char *aService
); 
  46 static void     doCFnote(void); 
  49 main(int argc
, char *argv
[]) 
  51         Action          anAction 
= kActionStart
; 
  52         char           *aService 
= NULL
; 
  55         while ((ch 
= getopt(argc
, argv
, "gvxirdDqn?")) != -1) { 
  84         openlog(getprogname(), LOG_PID
|LOG_CONS
|(gDebugFlag 
? LOG_PERROR 
: 0), LOG_DAEMON
); 
  85         setlogmask(LOG_UPTO(LOG_NOTICE
)); 
  87                 setlogmask(LOG_UPTO(LOG_INFO
)); 
  89                 setlogmask(LOG_UPTO(LOG_DEBUG
)); 
  91         if (!gNoRunFlag 
&& (getuid() != 0)) { 
  92                 syslog(LOG_ERR
, "must be root to run"); 
  97                 if (strcmp(argv
[0], "start") == 0) { 
  98                         anAction 
= kActionStart
; 
  99                 } else if (strcmp(argv
[0], "stop") == 0) { 
 100                         anAction 
= kActionStop
; 
 101                 } else if (strcmp(argv
[0], "restart") == 0) { 
 102                         anAction 
= kActionRestart
; 
 114         } else if (!gDebugFlag 
&& anAction 
!= kActionStop
) { 
 118                 setpriority(PRIO_PROCESS
, 0, 20); 
 121                 /* Too many old StartupItems had implicit dependancies on 
 122                  * "Network" via other StartupItems that are now no-ops. 
 124                  * SystemStarter is not on the critical path for boot up, 
 125                  * so we'll stall here to deal with this legacy dependancy 
 128                 switch ((ipwa 
= fork())) { 
 130                         syslog(LOG_WARNING
, "fork(): %m"); 
 133                         execl("/usr/sbin/ipconfig", "ipconfig", "waitall", NULL
); 
 134                         syslog(LOG_WARNING
, "execl(): %m"); 
 137                         if (waitpid(ipwa
, &status
, 0) == -1) { 
 138                                 syslog(LOG_WARNING
, "waitpid(): %m"); 
 140                         } else if (WIFEXITED(status
)) { 
 141                                 if (WEXITSTATUS(status
) == 0) { 
 144                                         syslog(LOG_WARNING
, "ipconfig waitall exit status: %d", WEXITSTATUS(status
)); 
 147                                 /* must have died due to signal */ 
 148                                 syslog(LOG_WARNING
, "ipconfig waitall: %s", strsignal(WTERMSIG(status
))); 
 154         exit(system_starter(anAction
, aService
)); 
 159  * checkForActivity checks to see if any items have completed since the last invokation. 
 160  * If not, a message is displayed showing what item(s) are being waited on. 
 163 checkForActivity(StartupContext aStartupContext
) 
 165         static CFIndex  aLastStatusDictionaryCount 
= -1; 
 166         static CFStringRef aWaitingForString 
= NULL
; 
 168         if (aStartupContext 
&& aStartupContext
->aStatusDict
) { 
 169                 CFIndex         aCount 
= CFDictionaryGetCount(aStartupContext
->aStatusDict
); 
 171                 if (!aWaitingForString
) { 
 172                         aWaitingForString 
= CFSTR("Waiting for %@"); 
 174                 if (aLastStatusDictionaryCount 
== aCount
) { 
 175                         CFArrayRef      aRunningList 
= StartupItemListGetRunning(aStartupContext
->aWaitingList
); 
 176                         if (aRunningList 
&& CFArrayGetCount(aRunningList
) > 0) { 
 177                                 CFMutableDictionaryRef anItem 
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aRunningList
, 0); 
 178                                 CFStringRef     anItemDescription 
= StartupItemGetDescription(anItem
); 
 179                                 CFStringRef     aString 
= aWaitingForString 
&& anItemDescription 
? 
 180                                 CFStringCreateWithFormat(NULL
, NULL
, aWaitingForString
, anItemDescription
) : NULL
; 
 183                                         CF_syslog(LOG_INFO
, CFSTR("%@"), aString
); 
 186                                 if (anItemDescription
) 
 187                                         CFRelease(anItemDescription
); 
 190                                 CFRelease(aRunningList
); 
 192                 aLastStatusDictionaryCount 
= aCount
; 
 197  * print out any error messages to the log regarding non starting StartupItems 
 200 displayErrorMessages(StartupContext aStartupContext
) 
 202         if (aStartupContext
->aFailedList 
&& CFArrayGetCount(aStartupContext
->aFailedList
) > 0) { 
 203                 CFIndex         anItemCount 
= CFArrayGetCount(aStartupContext
->aFailedList
); 
 207                 syslog(LOG_WARNING
, "The following StartupItems failed to properly start:"); 
 209                 for (anItemIndex 
= 0; anItemIndex 
< anItemCount
; anItemIndex
++) { 
 210                         CFMutableDictionaryRef anItem 
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aStartupContext
->aFailedList
, anItemIndex
); 
 211                         CFStringRef     anErrorDescription 
= CFDictionaryGetValue(anItem
, kErrorKey
); 
 212                         CFStringRef     anItemPath 
= CFDictionaryGetValue(anItem
, kBundlePathKey
); 
 215                                 CF_syslog(LOG_WARNING
, CFSTR("%@"), anItemPath
); 
 217                         if (anErrorDescription
) { 
 218                                 CF_syslog(LOG_WARNING
, CFSTR(" - %@"), anErrorDescription
); 
 220                                 CF_syslog(LOG_WARNING
, CFSTR(" - %@"), kErrorInternal
); 
 224         if (CFArrayGetCount(aStartupContext
->aWaitingList
) > 0) { 
 225                 CFIndex         anItemCount 
= CFArrayGetCount(aStartupContext
->aWaitingList
); 
 228                 syslog(LOG_WARNING
, "The following StartupItems were not attempted due to failure of a required service:"); 
 230                 for (anItemIndex 
= 0; anItemIndex 
< anItemCount
; anItemIndex
++) { 
 231                         CFMutableDictionaryRef anItem 
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aStartupContext
->aWaitingList
, anItemIndex
); 
 232                         CFStringRef     anItemPath 
= CFDictionaryGetValue(anItem
, kBundlePathKey
); 
 234                                 CF_syslog(LOG_WARNING
, CFSTR("%@"), anItemPath
); 
 242 system_starter(Action anAction
, const char *aService_cstr
) 
 244         CFRunLoopSourceRef anIPCSource 
= NULL
; 
 245         CFStringRef     aService 
= NULL
; 
 246         NSSearchPathDomainMask aMask
; 
 249                 aService 
= CFStringCreateWithCString(kCFAllocatorDefault
, aService_cstr
, kCFStringEncodingUTF8
); 
 251         StartupContext  aStartupContext 
= (StartupContext
) malloc(sizeof(struct StartupContextStorage
)); 
 252         if (!aStartupContext
) { 
 253                 syslog(LOG_ERR
, "Not enough memory to allocate startup context"); 
 256         if (gDebugFlag 
&& gNoRunFlag
) 
 260          * Create the IPC port 
 262         anIPCSource 
= CreateIPCRunLoopSource(CFSTR(kSystemStarterMessagePort
), aStartupContext
); 
 264                 CFRunLoopAddSource(CFRunLoopGetCurrent(), anIPCSource
, kCFRunLoopCommonModes
); 
 265                 CFRelease(anIPCSource
); 
 267                 syslog(LOG_ERR
, "Could not create IPC bootstrap port: %s", kSystemStarterMessagePort
); 
 272          * Get a list of Startup Items which are in /Local and /System. 
 273          * We can't search /Network yet because the network isn't up. 
 275         aMask 
= NSSystemDomainMask 
| NSLocalDomainMask
; 
 277         aStartupContext
->aWaitingList 
= StartupItemListCreateWithMask(aMask
); 
 278         aStartupContext
->aFailedList 
= NULL
; 
 279         aStartupContext
->aStatusDict 
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, 
 280                                           &kCFTypeDictionaryValueCallBacks
); 
 281         aStartupContext
->aServicesCount 
= 0; 
 282         aStartupContext
->aRunningCount 
= 0; 
 285                 CFMutableArrayRef aDependentsList 
= StartupItemListCreateDependentsList(aStartupContext
->aWaitingList
, aService
, anAction
); 
 287                 if (aDependentsList
) { 
 288                         CFRelease(aStartupContext
->aWaitingList
); 
 289                         aStartupContext
->aWaitingList 
= aDependentsList
; 
 291                         CF_syslog(LOG_ERR
, CFSTR("Unknown service: %@"), aService
); 
 295         aStartupContext
->aServicesCount 
= StartupItemListCountServices(aStartupContext
->aWaitingList
); 
 301                 CFMutableDictionaryRef anItem 
= StartupItemListGetNext(aStartupContext
->aWaitingList
, aStartupContext
->aStatusDict
, anAction
); 
 304                         int             err 
= StartupItemRun(aStartupContext
->aStatusDict
, anItem
, anAction
); 
 306                                 ++aStartupContext
->aRunningCount
; 
 307                                 MonitorStartupItem(aStartupContext
, anItem
); 
 309                                 /* add item to failed list */ 
 310                                 AddItemToFailedList(aStartupContext
, anItem
); 
 312                                 /* Remove the item from the waiting list. */ 
 313                                 RemoveItemFromWaitingList(aStartupContext
, anItem
); 
 317                          * If no item was selected to run, and if no items 
 318                          * are running, startup is done. 
 320                         if (aStartupContext
->aRunningCount 
== 0) { 
 321                                 syslog(LOG_DEBUG
, "none left"); 
 325                          * Process incoming IPC messages and item 
 328                         switch (CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 3.0, true)) { 
 329                         case kCFRunLoopRunTimedOut
: 
 330                                 checkForActivity(aStartupContext
); 
 332                         case kCFRunLoopRunFinished
: 
 334                         case kCFRunLoopRunStopped
: 
 336                         case kCFRunLoopRunHandledSource
: 
 339                                 /* unknown return value */ 
 348         displayErrorMessages(aStartupContext
); 
 351         if (aStartupContext
->aStatusDict
) 
 352                 CFRelease(aStartupContext
->aStatusDict
); 
 353         if (aStartupContext
->aWaitingList
) 
 354                 CFRelease(aStartupContext
->aWaitingList
); 
 355         if (aStartupContext
->aFailedList
) 
 356                 CFRelease(aStartupContext
->aFailedList
); 
 358         free(aStartupContext
); 
 363 CF_syslog(int level
, CFStringRef message
,...) 
 366         CFStringRef     cooked_msg
; 
 369         va_start(ap
, message
); 
 370         cooked_msg 
= CFStringCreateWithFormatAndArguments(NULL
, NULL
, message
, ap
); 
 373         if (CFStringGetCString(cooked_msg
, buf
, sizeof(buf
), kCFStringEncodingUTF8
)) 
 376         CFRelease(cooked_msg
); 
 382         fprintf(stderr
, "usage: %s [-vdqn?] [ <action> [ <item> ] ]\n" 
 383         "\t<action>: action to take (start|stop|restart); default is start\n" 
 384                 "\t<item>  : name of item to act on; default is all items\n" 
 386                 "\t-v: verbose startup\n" 
 387                 "\t-d: print debugging output\n" 
 388                 "\t-q: be quiet (disable debugging output)\n" 
 389              "\t-n: don't actually perform action on items (pretend mode)\n" 
 390                 "\t-?: show this help\n", 
 395 static void doCFnote(void) 
 397         CFNotificationCenterPostNotificationWithOptions( 
 398                         CFNotificationCenterGetDistributedCenter(), 
 399                         CFSTR("com.apple.startupitems.completed"), 
 401                         kCFNotificationDeliverImmediately 
| kCFNotificationPostToAllSessions
);