Contents of /trunk/mkinitrd-magellan/busybox/init/init.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 28177 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 28177 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Mini init implementation for busybox |
4 | * |
5 | * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. |
6 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> |
7 | * Adjusted by so many folks, it's impossible to keep track. |
8 | * |
9 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
10 | */ |
11 | |
12 | #include "busybox.h" |
13 | #include <errno.h> |
14 | #include <paths.h> |
15 | #include <signal.h> |
16 | #include <sys/ioctl.h> |
17 | #include <sys/wait.h> |
18 | #include <sys/reboot.h> |
19 | |
20 | #include "init_shared.h" |
21 | |
22 | #if ENABLE_SYSLOGD |
23 | # include <sys/syslog.h> |
24 | #endif |
25 | |
26 | #define INIT_BUFFS_SIZE 256 |
27 | |
28 | /* From <linux/vt.h> */ |
29 | struct vt_stat { |
30 | unsigned short v_active; /* active vt */ |
31 | unsigned short v_signal; /* signal to send */ |
32 | unsigned short v_state; /* vt bitmask */ |
33 | }; |
34 | enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */ |
35 | |
36 | /* From <linux/serial.h> */ |
37 | struct serial_struct { |
38 | int type; |
39 | int line; |
40 | unsigned int port; |
41 | int irq; |
42 | int flags; |
43 | int xmit_fifo_size; |
44 | int custom_divisor; |
45 | int baud_base; |
46 | unsigned short close_delay; |
47 | char io_type; |
48 | char reserved_char[1]; |
49 | int hub6; |
50 | unsigned short closing_wait; /* time to wait before closing */ |
51 | unsigned short closing_wait2; /* no longer used... */ |
52 | unsigned char *iomem_base; |
53 | unsigned short iomem_reg_shift; |
54 | unsigned int port_high; |
55 | unsigned long iomap_base; /* cookie passed into ioremap */ |
56 | int reserved[1]; |
57 | }; |
58 | |
59 | #ifndef _PATH_STDPATH |
60 | #define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" |
61 | #endif |
62 | |
63 | #if ENABLE_FEATURE_INIT_COREDUMPS |
64 | /* |
65 | * When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called |
66 | * before processes are spawned to set core file size as unlimited. |
67 | * This is for debugging only. Don't use this is production, unless |
68 | * you want core dumps lying about.... |
69 | */ |
70 | #define CORE_ENABLE_FLAG_FILE "/.init_enable_core" |
71 | #include <sys/resource.h> |
72 | #endif |
73 | |
74 | #define INITTAB "/etc/inittab" /* inittab file location */ |
75 | #ifndef INIT_SCRIPT |
76 | #define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */ |
77 | #endif |
78 | |
79 | #define MAXENV 16 /* Number of env. vars */ |
80 | |
81 | #define CONSOLE_BUFF_SIZE 32 |
82 | |
83 | /* Allowed init action types */ |
84 | #define SYSINIT 0x001 |
85 | #define RESPAWN 0x002 |
86 | #define ASKFIRST 0x004 |
87 | #define WAIT 0x008 |
88 | #define ONCE 0x010 |
89 | #define CTRLALTDEL 0x020 |
90 | #define SHUTDOWN 0x040 |
91 | #define RESTART 0x080 |
92 | |
93 | /* A mapping between "inittab" action name strings and action type codes. */ |
94 | struct init_action_type { |
95 | const char *name; |
96 | int action; |
97 | }; |
98 | |
99 | static const struct init_action_type actions[] = { |
100 | {"sysinit", SYSINIT}, |
101 | {"respawn", RESPAWN}, |
102 | {"askfirst", ASKFIRST}, |
103 | {"wait", WAIT}, |
104 | {"once", ONCE}, |
105 | {"ctrlaltdel", CTRLALTDEL}, |
106 | {"shutdown", SHUTDOWN}, |
107 | {"restart", RESTART}, |
108 | {0, 0} |
109 | }; |
110 | |
111 | /* Set up a linked list of init_actions, to be read from inittab */ |
112 | struct init_action { |
113 | pid_t pid; |
114 | char command[INIT_BUFFS_SIZE]; |
115 | char terminal[CONSOLE_BUFF_SIZE]; |
116 | struct init_action *next; |
117 | int action; |
118 | }; |
119 | |
120 | /* Static variables */ |
121 | static struct init_action *init_action_list = NULL; |
122 | static char console[CONSOLE_BUFF_SIZE] = CONSOLE_DEV; |
123 | |
124 | #if !ENABLE_SYSLOGD |
125 | static char *log_console = VC_5; |
126 | #endif |
127 | #if !ENABLE_DEBUG_INIT |
128 | static sig_atomic_t got_cont = 0; |
129 | #endif |
130 | |
131 | enum { |
132 | LOG = 0x1, |
133 | CONSOLE = 0x2, |
134 | |
135 | #if ENABLE_FEATURE_EXTRA_QUIET |
136 | MAYBE_CONSOLE = 0x0, |
137 | #else |
138 | MAYBE_CONSOLE = CONSOLE, |
139 | #endif |
140 | |
141 | #ifndef RB_HALT_SYSTEM |
142 | RB_HALT_SYSTEM = 0xcdef0123, /* FIXME: this overflows enum */ |
143 | RB_ENABLE_CAD = 0x89abcdef, |
144 | RB_DISABLE_CAD = 0, |
145 | RB_POWER_OFF = 0x4321fedc, |
146 | RB_AUTOBOOT = 0x01234567, |
147 | #endif |
148 | }; |
149 | |
150 | static const char * const environment[] = { |
151 | "HOME=/", |
152 | "PATH=" _PATH_STDPATH, |
153 | "SHELL=/bin/sh", |
154 | "USER=root", |
155 | NULL |
156 | }; |
157 | |
158 | /* Function prototypes */ |
159 | static void delete_init_action(struct init_action *a); |
160 | static int waitfor(const struct init_action *a, pid_t pid); |
161 | #if !ENABLE_DEBUG_INIT |
162 | static void shutdown_signal(int sig); |
163 | #endif |
164 | |
165 | #if !ENABLE_DEBUG_INIT |
166 | static void loop_forever(void) |
167 | { |
168 | while (1) |
169 | sleep(1); |
170 | } |
171 | #endif |
172 | |
173 | /* Print a message to the specified device. |
174 | * Device may be bitwise-or'd from LOG | CONSOLE */ |
175 | #if ENABLE_DEBUG_INIT |
176 | #define messageD message |
177 | #else |
178 | #define messageD(...) do {} while (0) |
179 | #endif |
180 | static void message(int device, const char *fmt, ...) |
181 | __attribute__ ((format(printf, 2, 3))); |
182 | static void message(int device, const char *fmt, ...) |
183 | { |
184 | va_list arguments; |
185 | int l; |
186 | RESERVE_CONFIG_BUFFER(msg, 1024); |
187 | #if !ENABLE_SYSLOGD |
188 | static int log_fd = -1; |
189 | #endif |
190 | |
191 | msg[0] = '\r'; |
192 | va_start(arguments, fmt); |
193 | l = vsnprintf(msg + 1, 1024 - 2, fmt, arguments) + 1; |
194 | va_end(arguments); |
195 | |
196 | #if ENABLE_SYSLOGD |
197 | /* Log the message to syslogd */ |
198 | if (device & LOG) { |
199 | /* don`t out "\r\n" */ |
200 | openlog(applet_name, 0, LOG_DAEMON); |
201 | syslog(LOG_INFO, "%s", msg + 1); |
202 | closelog(); |
203 | } |
204 | |
205 | msg[l++] = '\n'; |
206 | msg[l] = 0; |
207 | #else |
208 | |
209 | msg[l++] = '\n'; |
210 | msg[l] = 0; |
211 | /* Take full control of the log tty, and never close it. |
212 | * It's mine, all mine! Muhahahaha! */ |
213 | if (log_fd < 0) { |
214 | if ((log_fd = device_open(log_console, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) { |
215 | log_fd = -2; |
216 | bb_error_msg("bummer, can't write to log on %s!", log_console); |
217 | device = CONSOLE; |
218 | } else { |
219 | fcntl(log_fd, F_SETFD, FD_CLOEXEC); |
220 | } |
221 | } |
222 | if ((device & LOG) && (log_fd >= 0)) { |
223 | full_write(log_fd, msg, l); |
224 | } |
225 | #endif |
226 | |
227 | if (device & CONSOLE) { |
228 | int fd = device_open(CONSOLE_DEV, |
229 | O_WRONLY | O_NOCTTY | O_NONBLOCK); |
230 | /* Always send console messages to /dev/console so people will see them. */ |
231 | if (fd >= 0) { |
232 | full_write(fd, msg, l); |
233 | close(fd); |
234 | #if ENABLE_DEBUG_INIT |
235 | /* all descriptors may be closed */ |
236 | } else { |
237 | bb_error_msg("bummer, can't print: "); |
238 | va_start(arguments, fmt); |
239 | vfprintf(stderr, fmt, arguments); |
240 | va_end(arguments); |
241 | #endif |
242 | } |
243 | } |
244 | RELEASE_CONFIG_BUFFER(msg); |
245 | } |
246 | |
247 | /* Set terminal settings to reasonable defaults */ |
248 | static void set_term(void) |
249 | { |
250 | struct termios tty; |
251 | |
252 | tcgetattr(STDIN_FILENO, &tty); |
253 | |
254 | /* set control chars */ |
255 | tty.c_cc[VINTR] = 3; /* C-c */ |
256 | tty.c_cc[VQUIT] = 28; /* C-\ */ |
257 | tty.c_cc[VERASE] = 127; /* C-? */ |
258 | tty.c_cc[VKILL] = 21; /* C-u */ |
259 | tty.c_cc[VEOF] = 4; /* C-d */ |
260 | tty.c_cc[VSTART] = 17; /* C-q */ |
261 | tty.c_cc[VSTOP] = 19; /* C-s */ |
262 | tty.c_cc[VSUSP] = 26; /* C-z */ |
263 | |
264 | /* use line dicipline 0 */ |
265 | tty.c_line = 0; |
266 | |
267 | /* Make it be sane */ |
268 | tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD; |
269 | tty.c_cflag |= CREAD | HUPCL | CLOCAL; |
270 | |
271 | |
272 | /* input modes */ |
273 | tty.c_iflag = ICRNL | IXON | IXOFF; |
274 | |
275 | /* output modes */ |
276 | tty.c_oflag = OPOST | ONLCR; |
277 | |
278 | /* local modes */ |
279 | tty.c_lflag = |
280 | ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; |
281 | |
282 | tcsetattr(STDIN_FILENO, TCSANOW, &tty); |
283 | } |
284 | |
285 | static void console_init(void) |
286 | { |
287 | int fd; |
288 | int tried = 0; |
289 | struct vt_stat vt; |
290 | struct serial_struct sr; |
291 | char *s; |
292 | |
293 | if ((s = getenv("CONSOLE")) != NULL || (s = getenv("console")) != NULL) { |
294 | safe_strncpy(console, s, sizeof(console)); |
295 | } else { |
296 | /* 2.2 kernels: identify the real console backend and try to use it */ |
297 | if (ioctl(0, TIOCGSERIAL, &sr) == 0) { |
298 | /* this is a serial console */ |
299 | snprintf(console, sizeof(console) - 1, SC_FORMAT, sr.line); |
300 | } else if (ioctl(0, VT_GETSTATE, &vt) == 0) { |
301 | /* this is linux virtual tty */ |
302 | snprintf(console, sizeof(console) - 1, VC_FORMAT, vt.v_active); |
303 | } else { |
304 | safe_strncpy(console, CONSOLE_DEV, sizeof(console)); |
305 | tried++; |
306 | } |
307 | } |
308 | |
309 | while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0 && tried < 2) { |
310 | /* Can't open selected console -- try |
311 | logical system console and VT_MASTER */ |
312 | safe_strncpy(console, (tried == 0 ? CONSOLE_DEV : CURRENT_VC), |
313 | sizeof(console)); |
314 | tried++; |
315 | } |
316 | if (fd < 0) { |
317 | /* Perhaps we should panic here? */ |
318 | #if !ENABLE_SYSLOGD |
319 | log_console = |
320 | #endif |
321 | safe_strncpy(console, bb_dev_null, sizeof(console)); |
322 | } else { |
323 | s = getenv("TERM"); |
324 | /* check for serial console */ |
325 | if (ioctl(fd, TIOCGSERIAL, &sr) == 0) { |
326 | /* Force the TERM setting to vt102 for serial console -- |
327 | * if TERM is set to linux (the default) */ |
328 | if (s == NULL || strcmp(s, "linux") == 0) |
329 | putenv("TERM=vt102"); |
330 | #if !ENABLE_SYSLOGD |
331 | log_console = console; |
332 | #endif |
333 | } else { |
334 | if (s == NULL) |
335 | putenv("TERM=linux"); |
336 | } |
337 | close(fd); |
338 | } |
339 | messageD(LOG, "console=%s", console); |
340 | } |
341 | |
342 | static void fixup_argv(int argc, char **argv, char *new_argv0) |
343 | { |
344 | int len; |
345 | |
346 | /* Fix up argv[0] to be certain we claim to be init */ |
347 | len = strlen(argv[0]); |
348 | memset(argv[0], 0, len); |
349 | safe_strncpy(argv[0], new_argv0, len + 1); |
350 | |
351 | /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */ |
352 | len = 1; |
353 | while (argc > len) { |
354 | memset(argv[len], 0, strlen(argv[len])); |
355 | len++; |
356 | } |
357 | } |
358 | |
359 | /* Open the new terminal device */ |
360 | static void open_new_terminal(const char * const device, const int fail) { |
361 | struct stat sb; |
362 | |
363 | if ((device_open(device, O_RDWR)) < 0) { |
364 | if (stat(device, &sb) != 0) { |
365 | message(LOG | CONSOLE, "device '%s' does not exist.", device); |
366 | } else { |
367 | message(LOG | CONSOLE, "Bummer, can't open %s", device); |
368 | } |
369 | if (fail) |
370 | _exit(1); |
371 | /* else */ |
372 | #if !ENABLE_DEBUG_INIT |
373 | shutdown_signal(SIGUSR1); |
374 | #else |
375 | _exit(2); |
376 | #endif |
377 | } |
378 | } |
379 | |
380 | static pid_t run(const struct init_action *a) |
381 | { |
382 | int i; |
383 | pid_t pid; |
384 | char *s, *tmpCmd, *cmd[INIT_BUFFS_SIZE], *cmdpath; |
385 | char buf[INIT_BUFFS_SIZE + 6]; /* INIT_BUFFS_SIZE+strlen("exec ")+1 */ |
386 | sigset_t nmask, omask; |
387 | static const char press_enter[] = |
388 | #ifdef CUSTOMIZED_BANNER |
389 | #include CUSTOMIZED_BANNER |
390 | #endif |
391 | "\nPlease press Enter to activate this console. "; |
392 | |
393 | /* Block sigchild while forking. */ |
394 | sigemptyset(&nmask); |
395 | sigaddset(&nmask, SIGCHLD); |
396 | sigprocmask(SIG_BLOCK, &nmask, &omask); |
397 | |
398 | if ((pid = fork()) == 0) { |
399 | |
400 | /* Clean up */ |
401 | close(0); |
402 | close(1); |
403 | close(2); |
404 | sigprocmask(SIG_SETMASK, &omask, NULL); |
405 | |
406 | /* Reset signal handlers that were set by the parent process */ |
407 | signal(SIGUSR1, SIG_DFL); |
408 | signal(SIGUSR2, SIG_DFL); |
409 | signal(SIGINT, SIG_DFL); |
410 | signal(SIGTERM, SIG_DFL); |
411 | signal(SIGHUP, SIG_DFL); |
412 | signal(SIGQUIT, SIG_DFL); |
413 | signal(SIGCONT, SIG_DFL); |
414 | signal(SIGSTOP, SIG_DFL); |
415 | signal(SIGTSTP, SIG_DFL); |
416 | |
417 | /* Create a new session and make ourself the process |
418 | * group leader */ |
419 | setsid(); |
420 | |
421 | /* Open the new terminal device */ |
422 | open_new_terminal(a->terminal, 1); |
423 | |
424 | /* Make sure the terminal will act fairly normal for us */ |
425 | set_term(); |
426 | /* Setup stdout, stderr for the new process so |
427 | * they point to the supplied terminal */ |
428 | dup(0); |
429 | dup(0); |
430 | |
431 | /* If the init Action requires us to wait, then force the |
432 | * supplied terminal to be the controlling tty. */ |
433 | if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { |
434 | |
435 | /* Now fork off another process to just hang around */ |
436 | if ((pid = fork()) < 0) { |
437 | message(LOG | CONSOLE, "Can't fork!"); |
438 | _exit(1); |
439 | } |
440 | |
441 | if (pid > 0) { |
442 | |
443 | /* We are the parent -- wait till the child is done */ |
444 | signal(SIGINT, SIG_IGN); |
445 | signal(SIGTSTP, SIG_IGN); |
446 | signal(SIGQUIT, SIG_IGN); |
447 | signal(SIGCHLD, SIG_DFL); |
448 | |
449 | waitfor(NULL, pid); |
450 | /* See if stealing the controlling tty back is necessary */ |
451 | if (tcgetpgrp(0) != getpid()) |
452 | _exit(0); |
453 | |
454 | /* Use a temporary process to steal the controlling tty. */ |
455 | if ((pid = fork()) < 0) { |
456 | message(LOG | CONSOLE, "Can't fork!"); |
457 | _exit(1); |
458 | } |
459 | if (pid == 0) { |
460 | setsid(); |
461 | ioctl(0, TIOCSCTTY, 1); |
462 | _exit(0); |
463 | } |
464 | waitfor(NULL, pid); |
465 | _exit(0); |
466 | } |
467 | |
468 | /* Now fall though to actually execute things */ |
469 | } |
470 | |
471 | /* See if any special /bin/sh requiring characters are present */ |
472 | if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) { |
473 | cmd[0] = (char *)DEFAULT_SHELL; |
474 | cmd[1] = "-c"; |
475 | cmd[2] = strcat(strcpy(buf, "exec "), a->command); |
476 | cmd[3] = NULL; |
477 | } else { |
478 | /* Convert command (char*) into cmd (char**, one word per string) */ |
479 | strcpy(buf, a->command); |
480 | s = buf; |
481 | for (tmpCmd = buf, i = 0; (tmpCmd = strsep(&s, " \t")) != NULL;) { |
482 | if (*tmpCmd != '\0') { |
483 | cmd[i] = tmpCmd; |
484 | i++; |
485 | } |
486 | } |
487 | cmd[i] = NULL; |
488 | } |
489 | |
490 | cmdpath = cmd[0]; |
491 | |
492 | /* |
493 | Interactive shells want to see a dash in argv[0]. This |
494 | typically is handled by login, argv will be setup this |
495 | way if a dash appears at the front of the command path |
496 | (like "-/bin/sh"). |
497 | */ |
498 | |
499 | if (*cmdpath == '-') { |
500 | |
501 | /* skip over the dash */ |
502 | ++cmdpath; |
503 | |
504 | /* find the last component in the command pathname */ |
505 | s = bb_get_last_path_component(cmdpath); |
506 | |
507 | /* make a new argv[0] */ |
508 | if ((cmd[0] = malloc(strlen(s) + 2)) == NULL) { |
509 | message(LOG | CONSOLE, bb_msg_memory_exhausted); |
510 | cmd[0] = cmdpath; |
511 | } else { |
512 | cmd[0][0] = '-'; |
513 | strcpy(cmd[0] + 1, s); |
514 | } |
515 | #if ENABLE_FEATURE_INIT_SCTTY |
516 | /* Establish this process as session leader and |
517 | * (attempt) to make the tty (if any) a controlling tty. |
518 | */ |
519 | (void) setsid(); |
520 | (void) ioctl(0, TIOCSCTTY, 0/*don't steal it*/); |
521 | #endif |
522 | } |
523 | |
524 | #if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) |
525 | if (a->action & ASKFIRST) { |
526 | char c; |
527 | /* |
528 | * Save memory by not exec-ing anything large (like a shell) |
529 | * before the user wants it. This is critical if swap is not |
530 | * enabled and the system has low memory. Generally this will |
531 | * be run on the second virtual console, and the first will |
532 | * be allowed to start a shell or whatever an init script |
533 | * specifies. |
534 | */ |
535 | messageD(LOG, "Waiting for enter to start '%s'" |
536 | "(pid %d, terminal %s)\n", |
537 | cmdpath, getpid(), a->terminal); |
538 | full_write(1, press_enter, sizeof(press_enter) - 1); |
539 | while (read(0, &c, 1) == 1 && c != '\n') |
540 | ; |
541 | } |
542 | #endif |
543 | |
544 | /* Log the process name and args */ |
545 | message(LOG, "Starting pid %d, console %s: '%s'", |
546 | getpid(), a->terminal, cmdpath); |
547 | |
548 | #if ENABLE_FEATURE_INIT_COREDUMPS |
549 | { |
550 | struct stat sb; |
551 | if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) { |
552 | struct rlimit limit; |
553 | |
554 | limit.rlim_cur = RLIM_INFINITY; |
555 | limit.rlim_max = RLIM_INFINITY; |
556 | setrlimit(RLIMIT_CORE, &limit); |
557 | } |
558 | } |
559 | #endif |
560 | |
561 | /* Now run it. The new program will take over this PID, |
562 | * so nothing further in init.c should be run. */ |
563 | execv(cmdpath, cmd); |
564 | |
565 | /* We're still here? Some error happened. */ |
566 | message(LOG | CONSOLE, "Bummer, cannot run '%s': %m", cmdpath); |
567 | _exit(-1); |
568 | } |
569 | sigprocmask(SIG_SETMASK, &omask, NULL); |
570 | return pid; |
571 | } |
572 | |
573 | static int waitfor(const struct init_action *a, pid_t pid) |
574 | { |
575 | int runpid; |
576 | int status, wpid; |
577 | |
578 | runpid = (NULL == a)? pid : run(a); |
579 | while (1) { |
580 | wpid = waitpid(runpid,&status,0); |
581 | if (wpid == runpid) |
582 | break; |
583 | if (wpid == -1 && errno == ECHILD) { |
584 | /* we missed its termination */ |
585 | break; |
586 | } |
587 | /* FIXME other errors should maybe trigger an error, but allow |
588 | * the program to continue */ |
589 | } |
590 | return wpid; |
591 | } |
592 | |
593 | /* Run all commands of a particular type */ |
594 | static void run_actions(int action) |
595 | { |
596 | struct init_action *a, *tmp; |
597 | |
598 | for (a = init_action_list; a; a = tmp) { |
599 | tmp = a->next; |
600 | if (a->action == action) { |
601 | if (access(a->terminal, R_OK | W_OK)) { |
602 | delete_init_action(a); |
603 | } else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { |
604 | waitfor(a, 0); |
605 | delete_init_action(a); |
606 | } else if (a->action & ONCE) { |
607 | run(a); |
608 | delete_init_action(a); |
609 | } else if (a->action & (RESPAWN | ASKFIRST)) { |
610 | /* Only run stuff with pid==0. If they have |
611 | * a pid, that means it is still running */ |
612 | if (a->pid == 0) { |
613 | a->pid = run(a); |
614 | } |
615 | } |
616 | } |
617 | } |
618 | } |
619 | |
620 | #if !ENABLE_DEBUG_INIT |
621 | static void init_reboot(unsigned long magic) |
622 | { |
623 | pid_t pid; |
624 | /* We have to fork here, since the kernel calls do_exit(0) in |
625 | * linux/kernel/sys.c, which can cause the machine to panic when |
626 | * the init process is killed.... */ |
627 | pid = vfork(); |
628 | if (pid == 0) { /* child */ |
629 | reboot(magic); |
630 | _exit(0); |
631 | } |
632 | waitpid(pid, NULL, 0); |
633 | } |
634 | |
635 | static void shutdown_system(void) |
636 | { |
637 | sigset_t block_signals; |
638 | |
639 | /* run everything to be run at "shutdown". This is done _prior_ |
640 | * to killing everything, in case people wish to use scripts to |
641 | * shut things down gracefully... */ |
642 | run_actions(SHUTDOWN); |
643 | |
644 | /* first disable all our signals */ |
645 | sigemptyset(&block_signals); |
646 | sigaddset(&block_signals, SIGHUP); |
647 | sigaddset(&block_signals, SIGQUIT); |
648 | sigaddset(&block_signals, SIGCHLD); |
649 | sigaddset(&block_signals, SIGUSR1); |
650 | sigaddset(&block_signals, SIGUSR2); |
651 | sigaddset(&block_signals, SIGINT); |
652 | sigaddset(&block_signals, SIGTERM); |
653 | sigaddset(&block_signals, SIGCONT); |
654 | sigaddset(&block_signals, SIGSTOP); |
655 | sigaddset(&block_signals, SIGTSTP); |
656 | sigprocmask(SIG_BLOCK, &block_signals, NULL); |
657 | |
658 | /* Allow Ctrl-Alt-Del to reboot system. */ |
659 | init_reboot(RB_ENABLE_CAD); |
660 | |
661 | message(CONSOLE | LOG, "The system is going down NOW !!"); |
662 | sync(); |
663 | |
664 | /* Send signals to every process _except_ pid 1 */ |
665 | message(CONSOLE | LOG, init_sending_format, "TERM"); |
666 | kill(-1, SIGTERM); |
667 | sleep(1); |
668 | sync(); |
669 | |
670 | message(CONSOLE | LOG, init_sending_format, "KILL"); |
671 | kill(-1, SIGKILL); |
672 | sleep(1); |
673 | |
674 | sync(); |
675 | } |
676 | |
677 | static void exec_signal(int sig ATTRIBUTE_UNUSED) |
678 | { |
679 | struct init_action *a, *tmp; |
680 | sigset_t unblock_signals; |
681 | |
682 | for (a = init_action_list; a; a = tmp) { |
683 | tmp = a->next; |
684 | if (a->action & RESTART) { |
685 | shutdown_system(); |
686 | |
687 | /* unblock all signals, blocked in shutdown_system() */ |
688 | sigemptyset(&unblock_signals); |
689 | sigaddset(&unblock_signals, SIGHUP); |
690 | sigaddset(&unblock_signals, SIGQUIT); |
691 | sigaddset(&unblock_signals, SIGCHLD); |
692 | sigaddset(&unblock_signals, SIGUSR1); |
693 | sigaddset(&unblock_signals, SIGUSR2); |
694 | sigaddset(&unblock_signals, SIGINT); |
695 | sigaddset(&unblock_signals, SIGTERM); |
696 | sigaddset(&unblock_signals, SIGCONT); |
697 | sigaddset(&unblock_signals, SIGSTOP); |
698 | sigaddset(&unblock_signals, SIGTSTP); |
699 | sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL); |
700 | |
701 | /* Close whatever files are open. */ |
702 | close(0); |
703 | close(1); |
704 | close(2); |
705 | |
706 | /* Open the new terminal device */ |
707 | open_new_terminal(a->terminal, 0); |
708 | |
709 | /* Make sure the terminal will act fairly normal for us */ |
710 | set_term(); |
711 | /* Setup stdout, stderr on the supplied terminal */ |
712 | dup(0); |
713 | dup(0); |
714 | |
715 | messageD(CONSOLE | LOG, "Trying to re-exec %s", a->command); |
716 | execl(a->command, a->command, NULL); |
717 | |
718 | message(CONSOLE | LOG, "exec of '%s' failed: %m", |
719 | a->command); |
720 | sync(); |
721 | sleep(2); |
722 | init_reboot(RB_HALT_SYSTEM); |
723 | loop_forever(); |
724 | } |
725 | } |
726 | } |
727 | |
728 | static void shutdown_signal(int sig) |
729 | { |
730 | char *m; |
731 | int rb; |
732 | |
733 | shutdown_system(); |
734 | |
735 | if (sig == SIGTERM) { |
736 | m = "reboot"; |
737 | rb = RB_AUTOBOOT; |
738 | } else if (sig == SIGUSR2) { |
739 | m = "poweroff"; |
740 | rb = RB_POWER_OFF; |
741 | } else { |
742 | m = "halt"; |
743 | rb = RB_HALT_SYSTEM; |
744 | } |
745 | message(CONSOLE | LOG, "Requesting system %s.", m); |
746 | sync(); |
747 | |
748 | /* allow time for last message to reach serial console */ |
749 | sleep(2); |
750 | |
751 | init_reboot(rb); |
752 | loop_forever(); |
753 | } |
754 | |
755 | static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED) |
756 | { |
757 | run_actions(CTRLALTDEL); |
758 | } |
759 | |
760 | /* The SIGSTOP & SIGTSTP handler */ |
761 | static void stop_handler(int sig ATTRIBUTE_UNUSED) |
762 | { |
763 | int saved_errno = errno; |
764 | |
765 | got_cont = 0; |
766 | while (!got_cont) |
767 | pause(); |
768 | got_cont = 0; |
769 | errno = saved_errno; |
770 | } |
771 | |
772 | /* The SIGCONT handler */ |
773 | static void cont_handler(int sig ATTRIBUTE_UNUSED) |
774 | { |
775 | got_cont = 1; |
776 | } |
777 | |
778 | #endif /* ! ENABLE_DEBUG_INIT */ |
779 | |
780 | static void new_init_action(int action, const char *command, const char *cons) |
781 | { |
782 | struct init_action *new_action, *a, *last; |
783 | |
784 | if (*cons == '\0') |
785 | cons = console; |
786 | |
787 | if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST)) |
788 | return; |
789 | |
790 | new_action = xzalloc(sizeof(struct init_action)); |
791 | |
792 | /* Append to the end of the list */ |
793 | for (a = last = init_action_list; a; a = a->next) { |
794 | /* don't enter action if it's already in the list, |
795 | * but do overwrite existing actions */ |
796 | if ((strcmp(a->command, command) == 0) && |
797 | (strcmp(a->terminal, cons) ==0)) { |
798 | a->action = action; |
799 | free(new_action); |
800 | return; |
801 | } |
802 | last = a; |
803 | } |
804 | if (last) { |
805 | last->next = new_action; |
806 | } else { |
807 | init_action_list = new_action; |
808 | } |
809 | strcpy(new_action->command, command); |
810 | new_action->action = action; |
811 | strcpy(new_action->terminal, cons); |
812 | messageD(LOG|CONSOLE, "command='%s' action='%d' terminal='%s'\n", |
813 | new_action->command, new_action->action, new_action->terminal); |
814 | } |
815 | |
816 | static void delete_init_action(struct init_action *action) |
817 | { |
818 | struct init_action *a, *b = NULL; |
819 | |
820 | for (a = init_action_list; a; b = a, a = a->next) { |
821 | if (a == action) { |
822 | if (b == NULL) { |
823 | init_action_list = a->next; |
824 | } else { |
825 | b->next = a->next; |
826 | } |
827 | free(a); |
828 | break; |
829 | } |
830 | } |
831 | } |
832 | |
833 | /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, |
834 | * then parse_inittab() simply adds in some default |
835 | * actions(i.e., runs INIT_SCRIPT and then starts a pair |
836 | * of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB |
837 | * _is_ defined, but /etc/inittab is missing, this |
838 | * results in the same set of default behaviors. |
839 | */ |
840 | static void parse_inittab(void) |
841 | { |
842 | #if ENABLE_FEATURE_USE_INITTAB |
843 | FILE *file; |
844 | char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE]; |
845 | char tmpConsole[CONSOLE_BUFF_SIZE]; |
846 | char *id, *runlev, *action, *command, *eol; |
847 | const struct init_action_type *a = actions; |
848 | |
849 | |
850 | file = fopen(INITTAB, "r"); |
851 | if (file == NULL) { |
852 | /* No inittab file -- set up some default behavior */ |
853 | #endif |
854 | /* Reboot on Ctrl-Alt-Del */ |
855 | new_init_action(CTRLALTDEL, "/sbin/reboot", ""); |
856 | /* Umount all filesystems on halt/reboot */ |
857 | new_init_action(SHUTDOWN, "/bin/umount -a -r", ""); |
858 | /* Swapoff on halt/reboot */ |
859 | if(ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "/sbin/swapoff -a", ""); |
860 | /* Prepare to restart init when a HUP is received */ |
861 | new_init_action(RESTART, "/sbin/init", ""); |
862 | /* Askfirst shell on tty1-4 */ |
863 | new_init_action(ASKFIRST, bb_default_login_shell, ""); |
864 | new_init_action(ASKFIRST, bb_default_login_shell, VC_2); |
865 | new_init_action(ASKFIRST, bb_default_login_shell, VC_3); |
866 | new_init_action(ASKFIRST, bb_default_login_shell, VC_4); |
867 | /* sysinit */ |
868 | new_init_action(SYSINIT, INIT_SCRIPT, ""); |
869 | |
870 | return; |
871 | #if ENABLE_FEATURE_USE_INITTAB |
872 | } |
873 | |
874 | while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) { |
875 | /* Skip leading spaces */ |
876 | for (id = buf; *id == ' ' || *id == '\t'; id++); |
877 | |
878 | /* Skip the line if it's a comment */ |
879 | if (*id == '#' || *id == '\n') |
880 | continue; |
881 | |
882 | /* Trim the trailing \n */ |
883 | eol = strrchr(id, '\n'); |
884 | if (eol != NULL) |
885 | *eol = '\0'; |
886 | |
887 | /* Keep a copy around for posterity's sake (and error msgs) */ |
888 | strcpy(lineAsRead, buf); |
889 | |
890 | /* Separate the ID field from the runlevels */ |
891 | runlev = strchr(id, ':'); |
892 | if (runlev == NULL || *(runlev + 1) == '\0') { |
893 | message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); |
894 | continue; |
895 | } else { |
896 | *runlev = '\0'; |
897 | ++runlev; |
898 | } |
899 | |
900 | /* Separate the runlevels from the action */ |
901 | action = strchr(runlev, ':'); |
902 | if (action == NULL || *(action + 1) == '\0') { |
903 | message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); |
904 | continue; |
905 | } else { |
906 | *action = '\0'; |
907 | ++action; |
908 | } |
909 | |
910 | /* Separate the action from the command */ |
911 | command = strchr(action, ':'); |
912 | if (command == NULL || *(command + 1) == '\0') { |
913 | message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); |
914 | continue; |
915 | } else { |
916 | *command = '\0'; |
917 | ++command; |
918 | } |
919 | |
920 | /* Ok, now process it */ |
921 | for (a = actions; a->name != 0; a++) { |
922 | if (strcmp(a->name, action) == 0) { |
923 | if (*id != '\0') { |
924 | if(strncmp(id, "/dev/", 5) == 0) |
925 | id += 5; |
926 | strcpy(tmpConsole, "/dev/"); |
927 | safe_strncpy(tmpConsole + 5, id, |
928 | CONSOLE_BUFF_SIZE - 5); |
929 | id = tmpConsole; |
930 | } |
931 | new_init_action(a->action, command, id); |
932 | break; |
933 | } |
934 | } |
935 | if (a->name == 0) { |
936 | /* Choke on an unknown action */ |
937 | message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); |
938 | } |
939 | } |
940 | fclose(file); |
941 | return; |
942 | #endif /* FEATURE_USE_INITTAB */ |
943 | } |
944 | |
945 | #if ENABLE_FEATURE_USE_INITTAB |
946 | static void reload_signal(int sig ATTRIBUTE_UNUSED) |
947 | { |
948 | struct init_action *a, *tmp; |
949 | |
950 | message(LOG, "Reloading /etc/inittab"); |
951 | |
952 | /* disable old entrys */ |
953 | for (a = init_action_list; a; a = a->next ) { |
954 | a->action = ONCE; |
955 | } |
956 | |
957 | parse_inittab(); |
958 | |
959 | /* remove unused entrys */ |
960 | for (a = init_action_list; a; a = tmp) { |
961 | tmp = a->next; |
962 | if (a->action & (ONCE | SYSINIT | WAIT ) && |
963 | a->pid == 0 ) { |
964 | delete_init_action(a); |
965 | } |
966 | } |
967 | run_actions(RESPAWN); |
968 | return; |
969 | } |
970 | #endif /* FEATURE_USE_INITTAB */ |
971 | |
972 | int init_main(int argc, char **argv) |
973 | { |
974 | struct init_action *a; |
975 | pid_t wpid; |
976 | |
977 | die_sleep = 30 * 24*60*60; /* if xmalloc will ever die... */ |
978 | |
979 | if (argc > 1 && !strcmp(argv[1], "-q")) { |
980 | return kill(1,SIGHUP); |
981 | } |
982 | #if !ENABLE_DEBUG_INIT |
983 | /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ |
984 | if (getpid() != 1 && |
985 | (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc"))) |
986 | { |
987 | bb_show_usage(); |
988 | } |
989 | /* Set up sig handlers -- be sure to |
990 | * clear all of these in run() */ |
991 | signal(SIGHUP, exec_signal); |
992 | signal(SIGQUIT, exec_signal); |
993 | signal(SIGUSR1, shutdown_signal); |
994 | signal(SIGUSR2, shutdown_signal); |
995 | signal(SIGINT, ctrlaltdel_signal); |
996 | signal(SIGTERM, shutdown_signal); |
997 | signal(SIGCONT, cont_handler); |
998 | signal(SIGSTOP, stop_handler); |
999 | signal(SIGTSTP, stop_handler); |
1000 | |
1001 | /* Turn off rebooting via CTL-ALT-DEL -- we get a |
1002 | * SIGINT on CAD so we can shut things down gracefully... */ |
1003 | init_reboot(RB_DISABLE_CAD); |
1004 | #endif |
1005 | |
1006 | /* Figure out where the default console should be */ |
1007 | console_init(); |
1008 | |
1009 | /* Close whatever files are open, and reset the console. */ |
1010 | close(0); |
1011 | close(1); |
1012 | close(2); |
1013 | |
1014 | if (device_open(console, O_RDWR | O_NOCTTY) == 0) { |
1015 | set_term(); |
1016 | close(0); |
1017 | } |
1018 | |
1019 | chdir("/"); |
1020 | setsid(); |
1021 | { |
1022 | const char * const *e; |
1023 | /* Make sure environs is set to something sane */ |
1024 | for (e = environment; *e; e++) |
1025 | putenv((char *) *e); |
1026 | } |
1027 | |
1028 | if (argc > 1) setenv("RUNLEVEL", argv[1], 1); |
1029 | |
1030 | /* Hello world */ |
1031 | message(MAYBE_CONSOLE | LOG, "init started: %s", bb_msg_full_version); |
1032 | |
1033 | /* Make sure there is enough memory to do something useful. */ |
1034 | if (ENABLE_SWAPONOFF) { |
1035 | struct sysinfo info; |
1036 | |
1037 | if (!sysinfo(&info) && |
1038 | (info.mem_unit ? : 1) * (long long)info.totalram < 1024*1024) |
1039 | { |
1040 | message(CONSOLE,"Low memory: forcing swapon."); |
1041 | /* swapon -a requires /proc typically */ |
1042 | new_init_action(SYSINIT, "/bin/mount -t proc proc /proc", ""); |
1043 | /* Try to turn on swap */ |
1044 | new_init_action(SYSINIT, "/sbin/swapon -a", ""); |
1045 | run_actions(SYSINIT); /* wait and removing */ |
1046 | } |
1047 | } |
1048 | |
1049 | /* Check if we are supposed to be in single user mode */ |
1050 | if (argc > 1 |
1051 | && (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1')) |
1052 | ) { |
1053 | /* Start a shell on console */ |
1054 | new_init_action(RESPAWN, bb_default_login_shell, ""); |
1055 | } else { |
1056 | /* Not in single user mode -- see what inittab says */ |
1057 | |
1058 | /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, |
1059 | * then parse_inittab() simply adds in some default |
1060 | * actions(i.e., runs INIT_SCRIPT and then starts a pair |
1061 | * of "askfirst" shells */ |
1062 | parse_inittab(); |
1063 | } |
1064 | |
1065 | #if ENABLE_SELINUX |
1066 | if (getenv("SELINUX_INIT") == NULL) { |
1067 | int enforce = 0; |
1068 | |
1069 | putenv("SELINUX_INIT=YES"); |
1070 | if (selinux_init_load_policy(&enforce) == 0) { |
1071 | execv(argv[0], argv); |
1072 | } else if (enforce > 0) { |
1073 | /* SELinux in enforcing mode but load_policy failed */ |
1074 | /* At this point, we probably can't open /dev/console, so log() won't work */ |
1075 | message(CONSOLE,"Unable to load SELinux Policy. Machine is in enforcing mode. Halting now."); |
1076 | exit(1); |
1077 | } |
1078 | } |
1079 | #endif /* CONFIG_SELINUX */ |
1080 | |
1081 | /* Make the command line just say "init" -- thats all, nothing else */ |
1082 | fixup_argv(argc, argv, "init"); |
1083 | |
1084 | /* Now run everything that needs to be run */ |
1085 | |
1086 | /* First run the sysinit command */ |
1087 | run_actions(SYSINIT); |
1088 | |
1089 | /* Next run anything that wants to block */ |
1090 | run_actions(WAIT); |
1091 | |
1092 | /* Next run anything to be run only once */ |
1093 | run_actions(ONCE); |
1094 | |
1095 | #if ENABLE_FEATURE_USE_INITTAB |
1096 | /* Redefine SIGHUP to reread /etc/inittab */ |
1097 | signal(SIGHUP, reload_signal); |
1098 | #else |
1099 | signal(SIGHUP, SIG_IGN); |
1100 | #endif /* FEATURE_USE_INITTAB */ |
1101 | |
1102 | |
1103 | /* Now run the looping stuff for the rest of forever */ |
1104 | while (1) { |
1105 | /* run the respawn stuff */ |
1106 | run_actions(RESPAWN); |
1107 | |
1108 | /* run the askfirst stuff */ |
1109 | run_actions(ASKFIRST); |
1110 | |
1111 | /* Don't consume all CPU time -- sleep a bit */ |
1112 | sleep(1); |
1113 | |
1114 | /* Wait for a child process to exit */ |
1115 | wpid = wait(NULL); |
1116 | while (wpid > 0) { |
1117 | /* Find out who died and clean up their corpse */ |
1118 | for (a = init_action_list; a; a = a->next) { |
1119 | if (a->pid == wpid) { |
1120 | /* Set the pid to 0 so that the process gets |
1121 | * restarted by run_actions() */ |
1122 | a->pid = 0; |
1123 | message(LOG, "Process '%s' (pid %d) exited. " |
1124 | "Scheduling it for restart.", |
1125 | a->command, wpid); |
1126 | } |
1127 | } |
1128 | /* see if anyone else is waiting to be reaped */ |
1129 | wpid = waitpid(-1, NULL, WNOHANG); |
1130 | } |
1131 | } |
1132 | } |