3 * Wilfredo Sanchez | wsanchez@opensource.apple.com
6 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
8 * @APPLE_APACHE_LICENSE_HEADER_START@
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
14 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 * @APPLE_APACHE_LICENSE_HEADER_END@
26 #include <crt_externs.h>
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <NSSystemDirectories.h>
32 #include "StartupItems.h"
33 #include "SystemStarter.h"
34 #include "SystemStarterIPC.h"
36 bool gDebugFlag
= false;
37 bool gVerboseFlag
= false;
38 bool gNoRunFlag
= false;
40 static void usage(void) __attribute__((noreturn
));
41 static int system_starter(Action anAction
, const char *aService
);
42 static void displayErrorMessages(StartupContext aStartupContext
);
43 static void doCFnote(void);
46 main(int argc
, char *argv
[])
48 Action anAction
= kActionStart
;
49 char *aService
= NULL
;
52 while ((ch
= getopt(argc
, argv
, "gvxirdDqn?")) != -1) {
81 openlog(getprogname(), LOG_PID
|LOG_CONS
|(gDebugFlag
? LOG_PERROR
: 0), LOG_DAEMON
);
82 setlogmask(LOG_UPTO(LOG_NOTICE
));
84 setlogmask(LOG_UPTO(LOG_INFO
));
86 setlogmask(LOG_UPTO(LOG_DEBUG
));
88 if (!gNoRunFlag
&& (getuid() != 0)) {
89 syslog(LOG_ERR
, "must be root to run");
94 if (strcmp(argv
[0], "start") == 0) {
95 anAction
= kActionStart
;
96 } else if (strcmp(argv
[0], "stop") == 0) {
97 anAction
= kActionStop
;
98 } else if (strcmp(argv
[0], "restart") == 0) {
99 anAction
= kActionRestart
;
111 } else if (!gDebugFlag
&& anAction
!= kActionStop
) {
115 setpriority(PRIO_PROCESS
, 0, 20);
117 /* Too many old StartupItems had implicit dependancies on
118 * "Network" via other StartupItems that are now no-ops.
120 * SystemStarter is not on the critical path for boot up,
121 * so we'll stall here to deal with this legacy dependancy
124 switch ((ipwa
= fork())) {
126 syslog(LOG_WARNING
, "fork(): %m");
129 execl("/usr/sbin/ipconfig", "ipconfig", "waitall", NULL
);
130 syslog(LOG_WARNING
, "execl(): %m");
133 if (waitpid(ipwa
, &status
, 0) == -1) {
134 syslog(LOG_WARNING
, "waitpid(): %m");
136 } else if (WIFEXITED(status
)) {
137 if (WEXITSTATUS(status
) == 0) {
140 syslog(LOG_WARNING
, "ipconfig waitall exit status: %d", WEXITSTATUS(status
));
143 /* must have died due to signal */
144 syslog(LOG_WARNING
, "ipconfig waitall: %s", strsignal(WTERMSIG(status
)));
150 exit(system_starter(anAction
, aService
));
155 * checkForActivity checks to see if any items have completed since the last invokation.
156 * If not, a message is displayed showing what item(s) are being waited on.
159 checkForActivity(StartupContext aStartupContext
)
161 static CFIndex aLastStatusDictionaryCount
= -1;
162 static CFStringRef aWaitingForString
= NULL
;
164 if (aStartupContext
&& aStartupContext
->aStatusDict
) {
165 CFIndex aCount
= CFDictionaryGetCount(aStartupContext
->aStatusDict
);
167 if (!aWaitingForString
) {
168 aWaitingForString
= CFSTR("Waiting for %@");
170 if (aLastStatusDictionaryCount
== aCount
) {
171 CFArrayRef aRunningList
= StartupItemListGetRunning(aStartupContext
->aWaitingList
);
172 if (aRunningList
&& CFArrayGetCount(aRunningList
) > 0) {
173 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aRunningList
, 0);
174 CFStringRef anItemDescription
= StartupItemGetDescription(anItem
);
175 CFStringRef aString
= aWaitingForString
&& anItemDescription
?
176 CFStringCreateWithFormat(NULL
, NULL
, aWaitingForString
, anItemDescription
) : NULL
;
179 CF_syslog(LOG_INFO
, CFSTR("%@"), aString
);
182 if (anItemDescription
)
183 CFRelease(anItemDescription
);
186 CFRelease(aRunningList
);
188 aLastStatusDictionaryCount
= aCount
;
193 * print out any error messages to the log regarding non starting StartupItems
196 displayErrorMessages(StartupContext aStartupContext
)
198 if (aStartupContext
->aFailedList
&& CFArrayGetCount(aStartupContext
->aFailedList
) > 0) {
199 CFIndex anItemCount
= CFArrayGetCount(aStartupContext
->aFailedList
);
203 syslog(LOG_WARNING
, "The following StartupItems failed to properly start:");
205 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
206 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aStartupContext
->aFailedList
, anItemIndex
);
207 CFStringRef anErrorDescription
= CFDictionaryGetValue(anItem
, kErrorKey
);
208 CFStringRef anItemPath
= CFDictionaryGetValue(anItem
, kBundlePathKey
);
211 CF_syslog(LOG_WARNING
, CFSTR("%@"), anItemPath
);
213 if (anErrorDescription
) {
214 CF_syslog(LOG_WARNING
, CFSTR(" - %@"), anErrorDescription
);
216 CF_syslog(LOG_WARNING
, CFSTR(" - %@"), kErrorInternal
);
220 if (CFArrayGetCount(aStartupContext
->aWaitingList
) > 0) {
221 CFIndex anItemCount
= CFArrayGetCount(aStartupContext
->aWaitingList
);
224 syslog(LOG_WARNING
, "The following StartupItems were not attempted due to failure of a required service:");
226 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
227 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aStartupContext
->aWaitingList
, anItemIndex
);
228 CFStringRef anItemPath
= CFDictionaryGetValue(anItem
, kBundlePathKey
);
230 CF_syslog(LOG_WARNING
, CFSTR("%@"), anItemPath
);
238 system_starter(Action anAction
, const char *aService_cstr
)
240 CFRunLoopSourceRef anIPCSource
= NULL
;
241 CFStringRef aService
= NULL
;
242 NSSearchPathDomainMask aMask
;
245 aService
= CFStringCreateWithCString(kCFAllocatorDefault
, aService_cstr
, kCFStringEncodingUTF8
);
247 StartupContext aStartupContext
= (StartupContext
) malloc(sizeof(struct StartupContextStorage
));
248 if (!aStartupContext
) {
249 syslog(LOG_ERR
, "Not enough memory to allocate startup context");
252 if (gDebugFlag
&& gNoRunFlag
)
256 * Create the IPC port
258 anIPCSource
= CreateIPCRunLoopSource(CFSTR(kSystemStarterMessagePort
), aStartupContext
);
260 CFRunLoopAddSource(CFRunLoopGetCurrent(), anIPCSource
, kCFRunLoopCommonModes
);
261 CFRelease(anIPCSource
);
263 syslog(LOG_ERR
, "Could not create IPC bootstrap port: %s", kSystemStarterMessagePort
);
268 * Get a list of Startup Items which are in /Local and /System.
269 * We can't search /Network yet because the network isn't up.
271 aMask
= NSSystemDomainMask
| NSLocalDomainMask
;
273 aStartupContext
->aWaitingList
= StartupItemListCreateWithMask(aMask
);
274 aStartupContext
->aFailedList
= NULL
;
275 aStartupContext
->aStatusDict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
276 &kCFTypeDictionaryValueCallBacks
);
277 aStartupContext
->aServicesCount
= 0;
278 aStartupContext
->aRunningCount
= 0;
281 CFMutableArrayRef aDependentsList
= StartupItemListCreateDependentsList(aStartupContext
->aWaitingList
, aService
, anAction
);
283 if (aDependentsList
) {
284 CFRelease(aStartupContext
->aWaitingList
);
285 aStartupContext
->aWaitingList
= aDependentsList
;
287 CF_syslog(LOG_ERR
, CFSTR("Unknown service: %@"), aService
);
291 aStartupContext
->aServicesCount
= StartupItemListCountServices(aStartupContext
->aWaitingList
);
297 CFMutableDictionaryRef anItem
= StartupItemListGetNext(aStartupContext
->aWaitingList
, aStartupContext
->aStatusDict
, anAction
);
300 int err
= StartupItemRun(aStartupContext
->aStatusDict
, anItem
, anAction
);
302 ++aStartupContext
->aRunningCount
;
303 MonitorStartupItem(aStartupContext
, anItem
);
305 /* add item to failed list */
306 AddItemToFailedList(aStartupContext
, anItem
);
308 /* Remove the item from the waiting list. */
309 RemoveItemFromWaitingList(aStartupContext
, anItem
);
313 * If no item was selected to run, and if no items
314 * are running, startup is done.
316 if (aStartupContext
->aRunningCount
== 0) {
317 syslog(LOG_DEBUG
, "none left");
321 * Process incoming IPC messages and item
324 switch (CFRunLoopRunInMode(kCFRunLoopDefaultMode
, 3.0, true)) {
325 case kCFRunLoopRunTimedOut
:
326 checkForActivity(aStartupContext
);
328 case kCFRunLoopRunFinished
:
330 case kCFRunLoopRunStopped
:
332 case kCFRunLoopRunHandledSource
:
335 /* unknown return value */
344 displayErrorMessages(aStartupContext
);
347 if (aStartupContext
->aStatusDict
)
348 CFRelease(aStartupContext
->aStatusDict
);
349 if (aStartupContext
->aWaitingList
)
350 CFRelease(aStartupContext
->aWaitingList
);
351 if (aStartupContext
->aFailedList
)
352 CFRelease(aStartupContext
->aFailedList
);
354 free(aStartupContext
);
359 CF_syslog(int level
, CFStringRef message
,...)
362 CFStringRef cooked_msg
;
365 va_start(ap
, message
);
366 cooked_msg
= CFStringCreateWithFormatAndArguments(NULL
, NULL
, message
, ap
);
369 if (CFStringGetCString(cooked_msg
, buf
, sizeof(buf
), kCFStringEncodingUTF8
))
372 CFRelease(cooked_msg
);
378 fprintf(stderr
, "usage: %s [-vdqn?] [ <action> [ <item> ] ]\n"
379 "\t<action>: action to take (start|stop|restart); default is start\n"
380 "\t<item> : name of item to act on; default is all items\n"
382 "\t-v: verbose startup\n"
383 "\t-d: print debugging output\n"
384 "\t-q: be quiet (disable debugging output)\n"
385 "\t-n: don't actually perform action on items (pretend mode)\n"
386 "\t-?: show this help\n",
391 static void doCFnote(void)
393 CFNotificationCenterPostNotificationWithOptions(
394 CFNotificationCenterGetDistributedCenter(),
395 CFSTR("com.apple.startupitems.completed"),
397 kCFNotificationDeliverImmediately
| kCFNotificationPostToAllSessions
);