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
);