aboutsummaryrefslogtreecommitdiff
path: root/init
diff options
context:
space:
mode:
Diffstat (limited to 'init')
-rw-r--r--init/Config.in84
-rw-r--r--init/Kbuild12
-rw-r--r--init/halt.c58
-rw-r--r--init/init.c1131
-rw-r--r--init/init_shared.c63
-rw-r--r--init/init_shared.h10
-rw-r--r--init/mesg.c47
7 files changed, 1405 insertions, 0 deletions
diff --git a/init/Config.in b/init/Config.in
new file mode 100644
index 000000000..c0ad5263d
--- /dev/null
+++ b/init/Config.in
@@ -0,0 +1,84 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Init Utilities"
7
8config INIT
9 bool "init"
10 default n
11 select FEATURE_SYSLOG
12 help
13 init is the first program run when the system boots.
14
15config DEBUG_INIT
16 bool "debugging aid"
17 default n
18 depends on INIT
19 help
20 Turn this on to disable all the dangerous
21 rebooting stuff when debugging.
22
23config FEATURE_USE_INITTAB
24 bool "Support reading an inittab file"
25 default y
26 depends on INIT
27 help
28 Allow init to read an inittab file when the system boot.
29
30config FEATURE_INIT_SCTTY
31 bool "Support running commands with a controlling-tty"
32 default n
33 depends on INIT
34 help
35 If this option is enabled a command starting with hyphen (-)
36 is run in its own session (setsid(2)) and possibly with a
37 controlling tty (TIOCSCTTY). This is not the traditional init
38 behavour, but is often what you want in an embedded system where
39 the console is only accessed during development or for maintenance.
40
41config FEATURE_EXTRA_QUIET
42 bool "Be _extra_ quiet on boot"
43 default y
44 depends on INIT
45 help
46 Prevent init from logging some messages to the console during boot.
47
48config FEATURE_INIT_COREDUMPS
49 bool "Support dumping core for child processes (debugging only)"
50 default n
51 depends on INIT
52 help
53 If this option is enabled and the file /.init_enable_core
54 exists, then init will call setrlimit() to allow unlimited
55 core file sizes. If this option is disabled, processes
56 will not generate any core files.
57
58
59
60config FEATURE_INITRD
61 bool "Support running init from within an initrd (not initramfs)"
62 default y
63 depends on INIT
64 help
65 Legacy support for running init under the old-style initrd. Allows
66 the name linuxrc to act as init, and it doesn't assume init is PID 1.
67
68 This does not apply to initramfs, which runs /init as PID 1 and
69 requires no special support.
70
71config HALT
72 bool "poweroff, halt, and reboot"
73 default y
74 help
75 Stop all processes and either halt, reboot, or power off the system.
76
77config MESG
78 bool "mesg"
79 default y
80 help
81 Mesg controls access to your terminal by others. It is typically
82 used to allow or disallow other users to write to your terminal
83
84endmenu
diff --git a/init/Kbuild b/init/Kbuild
new file mode 100644
index 000000000..e99360241
--- /dev/null
+++ b/init/Kbuild
@@ -0,0 +1,12 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
4#
5# Licensed under the GPL v2, see the file LICENSE in this tarball.
6
7lib-y:=
8lib-$(CONFIG_HALT) += halt.o
9lib-$(CONFIG_INIT) += init.o
10lib-$(CONFIG_MESG) += mesg.o
11lib-$(CONFIG_INIT) += init_shared.o
12lib-$(CONFIG_HALT) += init_shared.o
diff --git a/init/halt.c b/init/halt.c
new file mode 100644
index 000000000..a6cf48bbe
--- /dev/null
+++ b/init/halt.c
@@ -0,0 +1,58 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Poweroff reboot and halt, oh my.
4 *
5 * Copyright 2006 by Rob Landley <rob@landley.net>
6 *
7 * Licensed under GPL version 2, see file LICENSE in this tarball for details.
8 */
9
10#include "busybox.h"
11#include <sys/reboot.h>
12
13int halt_main(int argc, char *argv[])
14{
15 static const int magic[] = {
16#ifdef RB_HALT_SYSTEM
17RB_HALT_SYSTEM,
18#elif defined RB_HALT
19RB_HALT,
20#endif
21#ifdef RB_POWER_OFF
22RB_POWER_OFF,
23#elif defined RB_POWERDOWN
24RB_POWERDOWN,
25#endif
26RB_AUTOBOOT
27 };
28 static const int signals[] = { SIGUSR1, SIGUSR2, SIGTERM };
29
30 char *delay;
31 int which, flags, rc = 1;
32
33 /* Figure out which applet we're running */
34 for (which = 0; "hpr"[which] != *applet_name; which++);
35
36 /* Parse and handle arguments */
37 flags = getopt32(argc, argv, "d:nf", &delay);
38 if (flags & 1) sleep(xatou(delay));
39 if (!(flags & 2)) sync();
40
41 /* Perform action. */
42 if (ENABLE_INIT && !(flags & 4)) {
43 if (ENABLE_FEATURE_INITRD) {
44 pid_t *pidlist = find_pid_by_name("linuxrc");
45 if (pidlist[0] > 0)
46 rc = kill(pidlist[0], signals[which]);
47 if (ENABLE_FEATURE_CLEAN_UP)
48 free(pidlist);
49 }
50 if (rc)
51 rc = kill(1, signals[which]);
52 } else
53 rc = reboot(magic[which]);
54
55 if (rc)
56 bb_error_msg("no");
57 return rc;
58}
diff --git a/init/init.c b/init/init.c
new file mode 100644
index 000000000..213a5c149
--- /dev/null
+++ b/init/init.c
@@ -0,0 +1,1131 @@
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#ifdef CONFIG_SYSLOGD
23# include <sys/syslog.h>
24#endif
25
26#define INIT_BUFFS_SIZE 256
27
28/* From <linux/vt.h> */
29struct 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};
34enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */
35
36/* From <linux/serial.h> */
37struct 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 defined CONFIG_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. */
94struct init_action_type {
95 const char *name;
96 int action;
97};
98
99static 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 */
112struct 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 */
121static struct init_action *init_action_list = NULL;
122static char console[CONSOLE_BUFF_SIZE] = CONSOLE_DEV;
123
124#ifndef CONFIG_SYSLOGD
125static char *log_console = VC_5;
126#endif
127#if !ENABLE_DEBUG_INIT
128static sig_atomic_t got_cont = 0;
129#endif
130
131enum {
132 LOG = 0x1,
133 CONSOLE = 0x2,
134
135#if defined CONFIG_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
150static const char * const environment[] = {
151 "HOME=/",
152 "PATH=" _PATH_STDPATH,
153 "SHELL=/bin/sh",
154 "USER=root",
155 NULL
156};
157
158/* Function prototypes */
159static void delete_init_action(struct init_action *a);
160static int waitfor(const struct init_action *a, pid_t pid);
161#if !ENABLE_DEBUG_INIT
162static void shutdown_signal(int sig);
163#endif
164
165static void loop_forever(void)
166{
167 while (1)
168 sleep(1);
169}
170
171/* Print a message to the specified device.
172 * Device may be bitwise-or'd from LOG | CONSOLE */
173#if ENABLE_DEBUG_INIT
174#define messageD message
175#else
176#define messageD(...) do {;} while(0);
177#endif
178static void message(int device, const char *fmt, ...)
179 __attribute__ ((format(printf, 2, 3)));
180static void message(int device, const char *fmt, ...)
181{
182 va_list arguments;
183 int l;
184 RESERVE_CONFIG_BUFFER(msg, 1024);
185#ifndef CONFIG_SYSLOGD
186 static int log_fd = -1;
187#endif
188
189 msg[0] = '\r';
190 va_start(arguments, fmt);
191 l = vsnprintf(msg + 1, 1024 - 2, fmt, arguments) + 1;
192 va_end(arguments);
193
194#ifdef CONFIG_SYSLOGD
195 /* Log the message to syslogd */
196 if (device & LOG) {
197 /* don`t out "\r\n" */
198 openlog(applet_name, 0, LOG_DAEMON);
199 syslog(LOG_INFO, "%s", msg + 1);
200 closelog();
201 }
202
203 msg[l++] = '\n';
204 msg[l] = 0;
205#else
206
207 msg[l++] = '\n';
208 msg[l] = 0;
209 /* Take full control of the log tty, and never close it.
210 * It's mine, all mine! Muhahahaha! */
211 if (log_fd < 0) {
212 if ((log_fd = device_open(log_console, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) {
213 log_fd = -2;
214 bb_error_msg("bummer, can't write to log on %s!", log_console);
215 device = CONSOLE;
216 } else {
217 fcntl(log_fd, F_SETFD, FD_CLOEXEC);
218 }
219 }
220 if ((device & LOG) && (log_fd >= 0)) {
221 full_write(log_fd, msg, l);
222 }
223#endif
224
225 if (device & CONSOLE) {
226 int fd = device_open(CONSOLE_DEV,
227 O_WRONLY | O_NOCTTY | O_NONBLOCK);
228 /* Always send console messages to /dev/console so people will see them. */
229 if (fd >= 0) {
230 full_write(fd, msg, l);
231 close(fd);
232#if ENABLE_DEBUG_INIT
233 /* all descriptors may be closed */
234 } else {
235 bb_error_msg("bummer, can't print: ");
236 va_start(arguments, fmt);
237 vfprintf(stderr, fmt, arguments);
238 va_end(arguments);
239#endif
240 }
241 }
242 RELEASE_CONFIG_BUFFER(msg);
243}
244
245/* Set terminal settings to reasonable defaults */
246static void set_term(void)
247{
248 struct termios tty;
249
250 tcgetattr(STDIN_FILENO, &tty);
251
252 /* set control chars */
253 tty.c_cc[VINTR] = 3; /* C-c */
254 tty.c_cc[VQUIT] = 28; /* C-\ */
255 tty.c_cc[VERASE] = 127; /* C-? */
256 tty.c_cc[VKILL] = 21; /* C-u */
257 tty.c_cc[VEOF] = 4; /* C-d */
258 tty.c_cc[VSTART] = 17; /* C-q */
259 tty.c_cc[VSTOP] = 19; /* C-s */
260 tty.c_cc[VSUSP] = 26; /* C-z */
261
262 /* use line dicipline 0 */
263 tty.c_line = 0;
264
265 /* Make it be sane */
266 tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
267 tty.c_cflag |= CREAD | HUPCL | CLOCAL;
268
269
270 /* input modes */
271 tty.c_iflag = ICRNL | IXON | IXOFF;
272
273 /* output modes */
274 tty.c_oflag = OPOST | ONLCR;
275
276 /* local modes */
277 tty.c_lflag =
278 ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
279
280 tcsetattr(STDIN_FILENO, TCSANOW, &tty);
281}
282
283static void console_init(void)
284{
285 int fd;
286 int tried = 0;
287 struct vt_stat vt;
288 struct serial_struct sr;
289 char *s;
290
291 if ((s = getenv("CONSOLE")) != NULL || (s = getenv("console")) != NULL) {
292 safe_strncpy(console, s, sizeof(console));
293 } else {
294 /* 2.2 kernels: identify the real console backend and try to use it */
295 if (ioctl(0, TIOCGSERIAL, &sr) == 0) {
296 /* this is a serial console */
297 snprintf(console, sizeof(console) - 1, SC_FORMAT, sr.line);
298 } else if (ioctl(0, VT_GETSTATE, &vt) == 0) {
299 /* this is linux virtual tty */
300 snprintf(console, sizeof(console) - 1, VC_FORMAT, vt.v_active);
301 } else {
302 safe_strncpy(console, CONSOLE_DEV, sizeof(console));
303 tried++;
304 }
305 }
306
307 while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0 && tried < 2) {
308 /* Can't open selected console -- try
309 logical system console and VT_MASTER */
310 safe_strncpy(console, (tried == 0 ? CONSOLE_DEV : CURRENT_VC),
311 sizeof(console));
312 tried++;
313 }
314 if (fd < 0) {
315 /* Perhaps we should panic here? */
316#ifndef CONFIG_SYSLOGD
317 log_console =
318#endif
319 safe_strncpy(console, bb_dev_null, sizeof(console));
320 } else {
321 s = getenv("TERM");
322 /* check for serial console */
323 if (ioctl(fd, TIOCGSERIAL, &sr) == 0) {
324 /* Force the TERM setting to vt102 for serial console --
325 * if TERM is set to linux (the default) */
326 if (s == NULL || strcmp(s, "linux") == 0)
327 putenv("TERM=vt102");
328#ifndef CONFIG_SYSLOGD
329 log_console = console;
330#endif
331 } else {
332 if (s == NULL)
333 putenv("TERM=linux");
334 }
335 close(fd);
336 }
337 messageD(LOG, "console=%s", console);
338}
339
340static void fixup_argv(int argc, char **argv, char *new_argv0)
341{
342 int len;
343
344 /* Fix up argv[0] to be certain we claim to be init */
345 len = strlen(argv[0]);
346 memset(argv[0], 0, len);
347 safe_strncpy(argv[0], new_argv0, len + 1);
348
349 /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
350 len = 1;
351 while (argc > len) {
352 memset(argv[len], 0, strlen(argv[len]));
353 len++;
354 }
355}
356
357/* Open the new terminal device */
358static void open_new_terminal(const char * const device, const int fail) {
359 struct stat sb;
360
361 if ((device_open(device, O_RDWR)) < 0) {
362 if (stat(device, &sb) != 0) {
363 message(LOG | CONSOLE, "device '%s' does not exist.", device);
364 } else {
365 message(LOG | CONSOLE, "Bummer, can't open %s", device);
366 }
367 if (fail)
368 _exit(1);
369 /* else */
370#if !ENABLE_DEBUG_INIT
371 shutdown_signal(SIGUSR1);
372#else
373 _exit(2);
374#endif
375 }
376}
377
378static pid_t run(const struct init_action *a)
379{
380 int i;
381 pid_t pid;
382 char *s, *tmpCmd, *cmd[INIT_BUFFS_SIZE], *cmdpath;
383 char buf[INIT_BUFFS_SIZE + 6]; /* INIT_BUFFS_SIZE+strlen("exec ")+1 */
384 sigset_t nmask, omask;
385 static const char press_enter[] =
386#ifdef CUSTOMIZED_BANNER
387#include CUSTOMIZED_BANNER
388#endif
389 "\nPlease press Enter to activate this console. ";
390
391 /* Block sigchild while forking. */
392 sigemptyset(&nmask);
393 sigaddset(&nmask, SIGCHLD);
394 sigprocmask(SIG_BLOCK, &nmask, &omask);
395
396 if ((pid = fork()) == 0) {
397
398 /* Clean up */
399 close(0);
400 close(1);
401 close(2);
402 sigprocmask(SIG_SETMASK, &omask, NULL);
403
404 /* Reset signal handlers that were set by the parent process */
405 signal(SIGUSR1, SIG_DFL);
406 signal(SIGUSR2, SIG_DFL);
407 signal(SIGINT, SIG_DFL);
408 signal(SIGTERM, SIG_DFL);
409 signal(SIGHUP, SIG_DFL);
410 signal(SIGQUIT, SIG_DFL);
411 signal(SIGCONT, SIG_DFL);
412 signal(SIGSTOP, SIG_DFL);
413 signal(SIGTSTP, SIG_DFL);
414
415 /* Create a new session and make ourself the process
416 * group leader */
417 setsid();
418
419 /* Open the new terminal device */
420 open_new_terminal(a->terminal, 1);
421
422 /* Make sure the terminal will act fairly normal for us */
423 set_term();
424 /* Setup stdout, stderr for the new process so
425 * they point to the supplied terminal */
426 dup(0);
427 dup(0);
428
429 /* If the init Action requires us to wait, then force the
430 * supplied terminal to be the controlling tty. */
431 if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
432
433 /* Now fork off another process to just hang around */
434 if ((pid = fork()) < 0) {
435 message(LOG | CONSOLE, "Can't fork!");
436 _exit(1);
437 }
438
439 if (pid > 0) {
440
441 /* We are the parent -- wait till the child is done */
442 signal(SIGINT, SIG_IGN);
443 signal(SIGTSTP, SIG_IGN);
444 signal(SIGQUIT, SIG_IGN);
445 signal(SIGCHLD, SIG_DFL);
446
447 waitfor(NULL, pid);
448 /* See if stealing the controlling tty back is necessary */
449 if (tcgetpgrp(0) != getpid())
450 _exit(0);
451
452 /* Use a temporary process to steal the controlling tty. */
453 if ((pid = fork()) < 0) {
454 message(LOG | CONSOLE, "Can't fork!");
455 _exit(1);
456 }
457 if (pid == 0) {
458 setsid();
459 ioctl(0, TIOCSCTTY, 1);
460 _exit(0);
461 }
462 waitfor(NULL, pid);
463 _exit(0);
464 }
465
466 /* Now fall though to actually execute things */
467 }
468
469 /* See if any special /bin/sh requiring characters are present */
470 if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
471 cmd[0] = (char *)DEFAULT_SHELL;
472 cmd[1] = "-c";
473 cmd[2] = strcat(strcpy(buf, "exec "), a->command);
474 cmd[3] = NULL;
475 } else {
476 /* Convert command (char*) into cmd (char**, one word per string) */
477 strcpy(buf, a->command);
478 s = buf;
479 for (tmpCmd = buf, i = 0; (tmpCmd = strsep(&s, " \t")) != NULL;) {
480 if (*tmpCmd != '\0') {
481 cmd[i] = tmpCmd;
482 i++;
483 }
484 }
485 cmd[i] = NULL;
486 }
487
488 cmdpath = cmd[0];
489
490 /*
491 Interactive shells want to see a dash in argv[0]. This
492 typically is handled by login, argv will be setup this
493 way if a dash appears at the front of the command path
494 (like "-/bin/sh").
495 */
496
497 if (*cmdpath == '-') {
498
499 /* skip over the dash */
500 ++cmdpath;
501
502 /* find the last component in the command pathname */
503 s = bb_get_last_path_component(cmdpath);
504
505 /* make a new argv[0] */
506 if ((cmd[0] = malloc(strlen(s) + 2)) == NULL) {
507 message(LOG | CONSOLE, bb_msg_memory_exhausted);
508 cmd[0] = cmdpath;
509 } else {
510 cmd[0][0] = '-';
511 strcpy(cmd[0] + 1, s);
512 }
513#ifdef CONFIG_FEATURE_INIT_SCTTY
514 /* Establish this process as session leader and
515 * (attempt) to make the tty (if any) a controlling tty.
516 */
517 (void) setsid();
518 (void) ioctl(0, TIOCSCTTY, 0/*don't steal it*/);
519#endif
520 }
521
522#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
523 if (a->action & ASKFIRST) {
524 char c;
525 /*
526 * Save memory by not exec-ing anything large (like a shell)
527 * before the user wants it. This is critical if swap is not
528 * enabled and the system has low memory. Generally this will
529 * be run on the second virtual console, and the first will
530 * be allowed to start a shell or whatever an init script
531 * specifies.
532 */
533 messageD(LOG, "Waiting for enter to start '%s'"
534 "(pid %d, terminal %s)\n",
535 cmdpath, getpid(), a->terminal);
536 full_write(1, press_enter, sizeof(press_enter) - 1);
537 while(read(0, &c, 1) == 1 && c != '\n')
538 ;
539 }
540#endif
541
542 /* Log the process name and args */
543 message(LOG, "Starting pid %d, console %s: '%s'",
544 getpid(), a->terminal, cmdpath);
545
546#if defined CONFIG_FEATURE_INIT_COREDUMPS
547 {
548 struct stat sb;
549 if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) {
550 struct rlimit limit;
551
552 limit.rlim_cur = RLIM_INFINITY;
553 limit.rlim_max = RLIM_INFINITY;
554 setrlimit(RLIMIT_CORE, &limit);
555 }
556 }
557#endif
558
559 /* Now run it. The new program will take over this PID,
560 * so nothing further in init.c should be run. */
561 execv(cmdpath, cmd);
562
563 /* We're still here? Some error happened. */
564 message(LOG | CONSOLE, "Bummer, cannot run '%s': %m", cmdpath);
565 _exit(-1);
566 }
567 sigprocmask(SIG_SETMASK, &omask, NULL);
568 return pid;
569}
570
571static int waitfor(const struct init_action *a, pid_t pid)
572{
573 int runpid;
574 int status, wpid;
575
576 runpid = (NULL == a)? pid : run(a);
577 while (1) {
578 wpid = waitpid(runpid,&status,0);
579 if (wpid == runpid)
580 break;
581 if (wpid == -1 && errno == ECHILD) {
582 /* we missed its termination */
583 break;
584 }
585 /* FIXME other errors should maybe trigger an error, but allow
586 * the program to continue */
587 }
588 return wpid;
589}
590
591/* Run all commands of a particular type */
592static void run_actions(int action)
593{
594 struct init_action *a, *tmp;
595
596 for (a = init_action_list; a; a = tmp) {
597 tmp = a->next;
598 if (a->action == action) {
599 if (access(a->terminal, R_OK | W_OK)) {
600 delete_init_action(a);
601 } else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
602 waitfor(a, 0);
603 delete_init_action(a);
604 } else if (a->action & ONCE) {
605 run(a);
606 delete_init_action(a);
607 } else if (a->action & (RESPAWN | ASKFIRST)) {
608 /* Only run stuff with pid==0. If they have
609 * a pid, that means it is still running */
610 if (a->pid == 0) {
611 a->pid = run(a);
612 }
613 }
614 }
615 }
616}
617
618#if !ENABLE_DEBUG_INIT
619static void init_reboot(unsigned long magic)
620{
621 pid_t pid;
622 /* We have to fork here, since the kernel calls do_exit(0) in
623 * linux/kernel/sys.c, which can cause the machine to panic when
624 * the init process is killed.... */
625 if ((pid = fork()) == 0) {
626 reboot(magic);
627 _exit(0);
628 }
629 waitpid (pid, NULL, 0);
630}
631
632static void shutdown_system(void)
633{
634 sigset_t block_signals;
635
636 /* run everything to be run at "shutdown". This is done _prior_
637 * to killing everything, in case people wish to use scripts to
638 * shut things down gracefully... */
639 run_actions(SHUTDOWN);
640
641 /* first disable all our signals */
642 sigemptyset(&block_signals);
643 sigaddset(&block_signals, SIGHUP);
644 sigaddset(&block_signals, SIGQUIT);
645 sigaddset(&block_signals, SIGCHLD);
646 sigaddset(&block_signals, SIGUSR1);
647 sigaddset(&block_signals, SIGUSR2);
648 sigaddset(&block_signals, SIGINT);
649 sigaddset(&block_signals, SIGTERM);
650 sigaddset(&block_signals, SIGCONT);
651 sigaddset(&block_signals, SIGSTOP);
652 sigaddset(&block_signals, SIGTSTP);
653 sigprocmask(SIG_BLOCK, &block_signals, NULL);
654
655 /* Allow Ctrl-Alt-Del to reboot system. */
656 init_reboot(RB_ENABLE_CAD);
657
658 message(CONSOLE | LOG, "The system is going down NOW !!");
659 sync();
660
661 /* Send signals to every process _except_ pid 1 */
662 message(CONSOLE | LOG, init_sending_format, "TERM");
663 kill(-1, SIGTERM);
664 sleep(1);
665 sync();
666
667 message(CONSOLE | LOG, init_sending_format, "KILL");
668 kill(-1, SIGKILL);
669 sleep(1);
670
671 sync();
672}
673
674static void exec_signal(int sig ATTRIBUTE_UNUSED)
675{
676 struct init_action *a, *tmp;
677 sigset_t unblock_signals;
678
679 for (a = init_action_list; a; a = tmp) {
680 tmp = a->next;
681 if (a->action & RESTART) {
682 shutdown_system();
683
684 /* unblock all signals, blocked in shutdown_system() */
685 sigemptyset(&unblock_signals);
686 sigaddset(&unblock_signals, SIGHUP);
687 sigaddset(&unblock_signals, SIGQUIT);
688 sigaddset(&unblock_signals, SIGCHLD);
689 sigaddset(&unblock_signals, SIGUSR1);
690 sigaddset(&unblock_signals, SIGUSR2);
691 sigaddset(&unblock_signals, SIGINT);
692 sigaddset(&unblock_signals, SIGTERM);
693 sigaddset(&unblock_signals, SIGCONT);
694 sigaddset(&unblock_signals, SIGSTOP);
695 sigaddset(&unblock_signals, SIGTSTP);
696 sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL);
697
698 /* Close whatever files are open. */
699 close(0);
700 close(1);
701 close(2);
702
703 /* Open the new terminal device */
704 open_new_terminal(a->terminal, 0);
705
706 /* Make sure the terminal will act fairly normal for us */
707 set_term();
708 /* Setup stdout, stderr on the supplied terminal */
709 dup(0);
710 dup(0);
711
712 messageD(CONSOLE | LOG, "Trying to re-exec %s", a->command);
713 execl(a->command, a->command, NULL);
714
715 message(CONSOLE | LOG, "exec of '%s' failed: %m",
716 a->command);
717 sync();
718 sleep(2);
719 init_reboot(RB_HALT_SYSTEM);
720 loop_forever();
721 }
722 }
723}
724
725static void shutdown_signal(int sig)
726{
727 char *m;
728 int rb;
729
730 shutdown_system();
731
732 if (sig == SIGTERM) {
733 m = "reboot";
734 rb = RB_AUTOBOOT;
735 } else if (sig == SIGUSR2) {
736 m = "poweroff";
737 rb = RB_POWER_OFF;
738 } else {
739 m = "halt";
740 rb = RB_HALT_SYSTEM;
741 }
742 message(CONSOLE | LOG, "Requesting system %s.", m);
743 sync();
744
745 /* allow time for last message to reach serial console */
746 sleep(2);
747
748 init_reboot(rb);
749
750 loop_forever();
751}
752
753static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)
754{
755 run_actions(CTRLALTDEL);
756}
757
758/* The SIGSTOP & SIGTSTP handler */
759static void stop_handler(int sig ATTRIBUTE_UNUSED)
760{
761 int saved_errno = errno;
762
763 got_cont = 0;
764 while (!got_cont)
765 pause();
766 got_cont = 0;
767 errno = saved_errno;
768}
769
770/* The SIGCONT handler */
771static void cont_handler(int sig ATTRIBUTE_UNUSED)
772{
773 got_cont = 1;
774}
775
776#endif /* ! ENABLE_DEBUG_INIT */
777
778static void new_init_action(int action, const char *command, const char *cons)
779{
780 struct init_action *new_action, *a, *last;
781
782 if (*cons == '\0')
783 cons = console;
784
785 if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST))
786 return;
787
788 new_action = calloc((size_t) (1), sizeof(struct init_action));
789 if (!new_action) {
790 message(LOG | CONSOLE, "Memory allocation failure");
791 loop_forever();
792 }
793
794 /* Append to the end of the list */
795 for (a = last = init_action_list; a; a = a->next) {
796 /* don't enter action if it's already in the list,
797 * but do overwrite existing actions */
798 if ((strcmp(a->command, command) == 0) &&
799 (strcmp(a->terminal, cons) ==0)) {
800 a->action = action;
801 free(new_action);
802 return;
803 }
804 last = a;
805 }
806 if (last) {
807 last->next = new_action;
808 } else {
809 init_action_list = new_action;
810 }
811 strcpy(new_action->command, command);
812 new_action->action = action;
813 strcpy(new_action->terminal, cons);
814 messageD(LOG|CONSOLE, "command='%s' action='%d' terminal='%s'\n",
815 new_action->command, new_action->action, new_action->terminal);
816}
817
818static void delete_init_action(struct init_action *action)
819{
820 struct init_action *a, *b = NULL;
821
822 for (a = init_action_list; a; b = a, a = a->next) {
823 if (a == action) {
824 if (b == NULL) {
825 init_action_list = a->next;
826 } else {
827 b->next = a->next;
828 }
829 free(a);
830 break;
831 }
832 }
833}
834
835/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
836 * then parse_inittab() simply adds in some default
837 * actions(i.e., runs INIT_SCRIPT and then starts a pair
838 * of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB
839 * _is_ defined, but /etc/inittab is missing, this
840 * results in the same set of default behaviors.
841 */
842static void parse_inittab(void)
843{
844#ifdef CONFIG_FEATURE_USE_INITTAB
845 FILE *file;
846 char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE];
847 char tmpConsole[CONSOLE_BUFF_SIZE];
848 char *id, *runlev, *action, *command, *eol;
849 const struct init_action_type *a = actions;
850
851
852 file = fopen(INITTAB, "r");
853 if (file == NULL) {
854 /* No inittab file -- set up some default behavior */
855#endif
856 /* Reboot on Ctrl-Alt-Del */
857 new_init_action(CTRLALTDEL, "/sbin/reboot", "");
858 /* Umount all filesystems on halt/reboot */
859 new_init_action(SHUTDOWN, "/bin/umount -a -r", "");
860 /* Swapoff on halt/reboot */
861 if(ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "/sbin/swapoff -a", "");
862 /* Prepare to restart init when a HUP is received */
863 new_init_action(RESTART, "/sbin/init", "");
864 /* Askfirst shell on tty1-4 */
865 new_init_action(ASKFIRST, bb_default_login_shell, "");
866 new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
867 new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
868 new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
869 /* sysinit */
870 new_init_action(SYSINIT, INIT_SCRIPT, "");
871
872 return;
873#ifdef CONFIG_FEATURE_USE_INITTAB
874 }
875
876 while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) {
877 /* Skip leading spaces */
878 for (id = buf; *id == ' ' || *id == '\t'; id++);
879
880 /* Skip the line if it's a comment */
881 if (*id == '#' || *id == '\n')
882 continue;
883
884 /* Trim the trailing \n */
885 eol = strrchr(id, '\n');
886 if (eol != NULL)
887 *eol = '\0';
888
889 /* Keep a copy around for posterity's sake (and error msgs) */
890 strcpy(lineAsRead, buf);
891
892 /* Separate the ID field from the runlevels */
893 runlev = strchr(id, ':');
894 if (runlev == NULL || *(runlev + 1) == '\0') {
895 message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
896 continue;
897 } else {
898 *runlev = '\0';
899 ++runlev;
900 }
901
902 /* Separate the runlevels from the action */
903 action = strchr(runlev, ':');
904 if (action == NULL || *(action + 1) == '\0') {
905 message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
906 continue;
907 } else {
908 *action = '\0';
909 ++action;
910 }
911
912 /* Separate the action from the command */
913 command = strchr(action, ':');
914 if (command == NULL || *(command + 1) == '\0') {
915 message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
916 continue;
917 } else {
918 *command = '\0';
919 ++command;
920 }
921
922 /* Ok, now process it */
923 for (a = actions; a->name != 0; a++) {
924 if (strcmp(a->name, action) == 0) {
925 if (*id != '\0') {
926 if(strncmp(id, "/dev/", 5) == 0)
927 id += 5;
928 strcpy(tmpConsole, "/dev/");
929 safe_strncpy(tmpConsole + 5, id,
930 CONSOLE_BUFF_SIZE - 5);
931 id = tmpConsole;
932 }
933 new_init_action(a->action, command, id);
934 break;
935 }
936 }
937 if (a->name == 0) {
938 /* Choke on an unknown action */
939 message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
940 }
941 }
942 fclose(file);
943 return;
944#endif /* CONFIG_FEATURE_USE_INITTAB */
945}
946
947#ifdef CONFIG_FEATURE_USE_INITTAB
948static void reload_signal(int sig ATTRIBUTE_UNUSED)
949{
950 struct init_action *a, *tmp;
951
952 message(LOG, "Reloading /etc/inittab");
953
954 /* disable old entrys */
955 for (a = init_action_list; a; a = a->next ) {
956 a->action = ONCE;
957 }
958
959 parse_inittab();
960
961 /* remove unused entrys */
962 for (a = init_action_list; a; a = tmp) {
963 tmp = a->next;
964 if (a->action & (ONCE | SYSINIT | WAIT ) &&
965 a->pid == 0 ) {
966 delete_init_action(a);
967 }
968 }
969 run_actions(RESPAWN);
970 return;
971}
972#endif /* CONFIG_FEATURE_USE_INITTAB */
973
974int init_main(int argc, char **argv)
975{
976 struct init_action *a;
977 pid_t wpid;
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 && (!strcmp(argv[1], "single") ||
1051 !strcmp(argv[1], "-s") || !strcmp(argv[1], "1"))) {
1052 /* Start a shell on console */
1053 new_init_action(RESPAWN, bb_default_login_shell, "");
1054 } else {
1055 /* Not in single user mode -- see what inittab says */
1056
1057 /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
1058 * then parse_inittab() simply adds in some default
1059 * actions(i.e., runs INIT_SCRIPT and then starts a pair
1060 * of "askfirst" shells */
1061 parse_inittab();
1062 }
1063
1064#ifdef CONFIG_SELINUX
1065 if (getenv("SELINUX_INIT") == NULL) {
1066 int enforce = 0;
1067
1068 putenv("SELINUX_INIT=YES");
1069 if (selinux_init_load_policy(&enforce) == 0) {
1070 execv(argv[0], argv);
1071 } else if (enforce > 0) {
1072 /* SELinux in enforcing mode but load_policy failed */
1073 /* At this point, we probably can't open /dev/console, so log() won't work */
1074 message(CONSOLE,"Unable to load SELinux Policy. Machine is in enforcing mode. Halting now.");
1075 exit(1);
1076 }
1077 }
1078#endif /* CONFIG_SELINUX */
1079
1080 /* Make the command line just say "init" -- thats all, nothing else */
1081 fixup_argv(argc, argv, "init");
1082
1083 /* Now run everything that needs to be run */
1084
1085 /* First run the sysinit command */
1086 run_actions(SYSINIT);
1087
1088 /* Next run anything that wants to block */
1089 run_actions(WAIT);
1090
1091 /* Next run anything to be run only once */
1092 run_actions(ONCE);
1093
1094#ifdef CONFIG_FEATURE_USE_INITTAB
1095 /* Redefine SIGHUP to reread /etc/inittab */
1096 signal(SIGHUP, reload_signal);
1097#else
1098 signal(SIGHUP, SIG_IGN);
1099#endif /* CONFIG_FEATURE_USE_INITTAB */
1100
1101
1102 /* Now run the looping stuff for the rest of forever */
1103 while (1) {
1104 /* run the respawn stuff */
1105 run_actions(RESPAWN);
1106
1107 /* run the askfirst stuff */
1108 run_actions(ASKFIRST);
1109
1110 /* Don't consume all CPU time -- sleep a bit */
1111 sleep(1);
1112
1113 /* Wait for a child process to exit */
1114 wpid = wait(NULL);
1115 while (wpid > 0) {
1116 /* Find out who died and clean up their corpse */
1117 for (a = init_action_list; a; a = a->next) {
1118 if (a->pid == wpid) {
1119 /* Set the pid to 0 so that the process gets
1120 * restarted by run_actions() */
1121 a->pid = 0;
1122 message(LOG, "Process '%s' (pid %d) exited. "
1123 "Scheduling it for restart.",
1124 a->command, wpid);
1125 }
1126 }
1127 /* see if anyone else is waiting to be reaped */
1128 wpid = waitpid(-1, NULL, WNOHANG);
1129 }
1130 }
1131}
diff --git a/init/init_shared.c b/init/init_shared.c
new file mode 100644
index 000000000..47480fc21
--- /dev/null
+++ b/init/init_shared.c
@@ -0,0 +1,63 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Stuff shared between init, reboot, halt, and poweroff
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Licensed under GPL version 2, see file LICENSE in this tarball for details.
8 */
9
10#include "busybox.h"
11#include <sys/reboot.h>
12#include <sys/syslog.h>
13#include "init_shared.h"
14
15const char * const init_sending_format = "Sending SIG%s to all processes.";
16#ifndef CONFIG_INIT
17const char * const bb_shutdown_format = "\r%s\n";
18int bb_shutdown_system(unsigned long magic)
19{
20 int pri = LOG_KERN|LOG_NOTICE|LOG_FACMASK;
21 const char *message;
22
23 /* Don't kill ourself */
24 signal(SIGTERM,SIG_IGN);
25 signal(SIGHUP,SIG_IGN);
26 bb_setpgrp;
27
28 /* Allow Ctrl-Alt-Del to reboot system. */
29#ifndef RB_ENABLE_CAD
30#define RB_ENABLE_CAD 0x89abcdef
31#endif
32 reboot(RB_ENABLE_CAD);
33
34 openlog(applet_name, 0, pri);
35
36 message = "\nThe system is going down NOW !!";
37 syslog(pri, "%s", message);
38 printf(bb_shutdown_format, message);
39
40 sync();
41
42 /* Send signals to every process _except_ pid 1 */
43 message = "TERM";
44 syslog(pri, init_sending_format, message);
45 printf(bb_shutdown_format, message);
46
47 kill(-1, SIGTERM);
48 sleep(1);
49 sync();
50
51 message = "KILL";
52 syslog(pri, init_sending_format, message);
53 printf(bb_shutdown_format, message);
54
55 kill(-1, SIGKILL);
56 sleep(1);
57
58 sync();
59
60 reboot(magic);
61 return 0; /* Shrug */
62}
63#endif
diff --git a/init/init_shared.h b/init/init_shared.h
new file mode 100644
index 000000000..6df8de484
--- /dev/null
+++ b/init/init_shared.h
@@ -0,0 +1,10 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Helper functions shared by init et al.
4 *
5 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
6 */
7extern int kill_init(int sig);
8extern int bb_shutdown_system(unsigned long magic);
9extern const char * const init_sending_format;
10
diff --git a/init/mesg.c b/init/mesg.c
new file mode 100644
index 000000000..7e47644c3
--- /dev/null
+++ b/init/mesg.c
@@ -0,0 +1,47 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * mesg implementation for busybox
4 *
5 * Copyright (c) 2002 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
8 */
9
10#include "busybox.h"
11#include <unistd.h>
12#include <stdlib.h>
13
14#ifdef USE_TTY_GROUP
15#define S_IWGRP_OR_S_IWOTH S_IWGRP
16#else
17#define S_IWGRP_OR_S_IWOTH (S_IWGRP | S_IWOTH)
18#endif
19
20int mesg_main(int argc, char *argv[])
21{
22 struct stat sb;
23 char *tty;
24 char c = 0;
25
26 if ((--argc == 0)
27 || ((argc == 1) && (((c = **++argv) == 'y') || (c == 'n')))) {
28 if ((tty = ttyname(STDERR_FILENO)) == NULL) {
29 tty = "ttyname";
30 } else if (stat(tty, &sb) == 0) {
31 if (argc == 0) {
32 puts(((sb.st_mode & (S_IWGRP | S_IWOTH)) ==
33 0) ? "is n" : "is y");
34 return EXIT_SUCCESS;
35 }
36 if (chmod
37 (tty,
38 (c ==
39 'y') ? sb.st_mode | (S_IWGRP_OR_S_IWOTH) : sb.
40 st_mode & ~(S_IWGRP | S_IWOTH)) == 0) {
41 return EXIT_SUCCESS;
42 }
43 }
44 bb_perror_msg_and_die("%s", tty);
45 }
46 bb_show_usage();
47}