Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/openlaunchd
Path: blob/master/SystemStarter/SystemStarter.c
374 views
1
/**
2
* System Starter main
3
* Wilfredo Sanchez | [email protected]
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
44
bool gDebugFlag = false;
45
bool gVerboseFlag = false;
46
bool gNoRunFlag = false;
47
48
static void usage(void) __attribute__((noreturn));
49
static int system_starter(Action anAction, const char *aService);
50
static void displayErrorMessages(StartupContext aStartupContext, Action anAction);
51
static pid_t fwexec(const char *cmd, ...) __attribute__((sentinel));
52
static void autodiskmount(void);
53
static void dummy_sig(int signo __attribute__((unused)))
54
{
55
}
56
57
int
58
main(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
**/
181
static void
182
checkForActivity(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
*/
218
void
219
displayErrorMessages(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
260
static int
261
system_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
368
void
369
CF_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
385
static void
386
usage(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
401
pid_t
402
fwexec(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
441
static void
442
autodiskmount_idle(void* context __attribute__((unused)))
443
{
444
CFRunLoopStop(CFRunLoopGetCurrent());
445
}
446
447
static void
448
autodiskmount(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
}
458
459