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