13 |
#include <syslog.h> |
#include <syslog.h> |
14 |
#include <paths.h> |
#include <paths.h> |
15 |
#include <sys/reboot.h> |
#include <sys/reboot.h> |
16 |
|
#include <sys/resource.h> |
17 |
|
#include <linux/vt.h> |
18 |
|
|
19 |
|
|
20 |
/* Was a CONFIG_xxx option. A lot of people were building |
/* Was a CONFIG_xxx option. A lot of people were building |
21 |
* not fully functional init by switching it on! */ |
* not fully functional init by switching it on! */ |
22 |
#define DEBUG_INIT 0 |
#define DEBUG_INIT 0 |
23 |
|
|
24 |
#define COMMAND_SIZE 256 |
#define COMMAND_SIZE 256 |
25 |
#define CONSOLE_NAME_SIZE 32 |
#define CONSOLE_NAME_SIZE 32 |
|
#define MAXENV 16 /* Number of env. vars */ |
|
|
|
|
|
/* |
|
|
* When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called |
|
|
* before processes are spawned to set core file size as unlimited. |
|
|
* This is for debugging only. Don't use this is production, unless |
|
|
* you want core dumps lying about.... |
|
|
*/ |
|
|
#define CORE_ENABLE_FLAG_FILE "/.init_enable_core" |
|
|
#include <sys/resource.h> |
|
26 |
|
|
27 |
#define INITTAB "/etc/inittab" /* inittab file location */ |
/* Default sysinit script. */ |
28 |
#ifndef INIT_SCRIPT |
#ifndef INIT_SCRIPT |
29 |
#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */ |
#define INIT_SCRIPT "/etc/init.d/rcS" |
30 |
#endif |
#endif |
31 |
|
|
32 |
/* Allowed init action types */ |
/* Each type of actions can appear many times. They will be |
33 |
|
* handled in order. RESTART is an exception, only 1st is used. |
34 |
|
*/ |
35 |
|
/* Start these actions first and wait for completion */ |
36 |
#define SYSINIT 0x01 |
#define SYSINIT 0x01 |
37 |
#define RESPAWN 0x02 |
/* Start these after SYSINIT and wait for completion */ |
38 |
/* like respawn, but wait for <Enter> to be pressed on tty: */ |
#define WAIT 0x02 |
39 |
#define ASKFIRST 0x04 |
/* Start these after WAIT and *dont* wait for completion */ |
40 |
#define WAIT 0x08 |
#define ONCE 0x04 |
41 |
#define ONCE 0x10 |
/* |
42 |
|
* NB: while SYSINIT/WAIT/ONCE are being processed, |
43 |
|
* SIGHUP ("reread /etc/inittab") will be processed only after |
44 |
|
* each group of actions. If new inittab adds, say, a SYSINIT action, |
45 |
|
* it will not be run, since init is already "past SYSINIT stage". |
46 |
|
*/ |
47 |
|
/* Start these after ONCE are started, restart on exit */ |
48 |
|
#define RESPAWN 0x08 |
49 |
|
/* Like RESPAWN, but wait for <Enter> to be pressed on tty */ |
50 |
|
#define ASKFIRST 0x10 |
51 |
|
/* |
52 |
|
* Start these on SIGINT, and wait for completion. |
53 |
|
* Then go back to respawning RESPAWN and ASKFIRST actions. |
54 |
|
* NB: kernel sends SIGINT to us if Ctrl-Alt-Del was pressed. |
55 |
|
*/ |
56 |
#define CTRLALTDEL 0x20 |
#define CTRLALTDEL 0x20 |
57 |
|
/* |
58 |
|
* Start these before killing all processes in preparation for |
59 |
|
* running RESTART actions or doing low-level halt/reboot/poweroff |
60 |
|
* (initiated by SIGUSR1/SIGTERM/SIGUSR2). |
61 |
|
* Wait for completion before proceeding. |
62 |
|
*/ |
63 |
#define SHUTDOWN 0x40 |
#define SHUTDOWN 0x40 |
64 |
|
/* |
65 |
|
* exec() on SIGQUIT. SHUTDOWN actions are started and waited for, |
66 |
|
* then all processes are killed, then init exec's 1st RESTART action, |
67 |
|
* replacing itself by it. If no RESTART action specified, |
68 |
|
* SIGQUIT has no effect. |
69 |
|
*/ |
70 |
#define RESTART 0x80 |
#define RESTART 0x80 |
71 |
|
|
72 |
/* Set up a linked list of init_actions, to be read from inittab */ |
|
73 |
|
/* A linked list of init_actions, to be read from inittab */ |
74 |
struct init_action { |
struct init_action { |
75 |
struct init_action *next; |
struct init_action *next; |
76 |
pid_t pid; |
pid_t pid; |
79 |
char command[COMMAND_SIZE]; |
char command[COMMAND_SIZE]; |
80 |
}; |
}; |
81 |
|
|
|
/* Static variables */ |
|
82 |
static struct init_action *init_action_list = NULL; |
static struct init_action *init_action_list = NULL; |
83 |
|
|
84 |
static const char *log_console = VC_5; |
static const char *log_console = VC_5; |
86 |
enum { |
enum { |
87 |
L_LOG = 0x1, |
L_LOG = 0x1, |
88 |
L_CONSOLE = 0x2, |
L_CONSOLE = 0x2, |
|
|
|
|
#if ENABLE_FEATURE_EXTRA_QUIET |
|
|
MAYBE_CONSOLE = 0x0, |
|
|
#else |
|
|
MAYBE_CONSOLE = L_CONSOLE, |
|
|
#endif |
|
|
|
|
89 |
#ifndef RB_HALT_SYSTEM |
#ifndef RB_HALT_SYSTEM |
90 |
RB_HALT_SYSTEM = 0xcdef0123, /* FIXME: this overflows enum */ |
RB_HALT_SYSTEM = 0xcdef0123, /* FIXME: this overflows enum */ |
91 |
RB_ENABLE_CAD = 0x89abcdef, |
RB_ENABLE_CAD = 0x89abcdef, |
95 |
#endif |
#endif |
96 |
}; |
}; |
97 |
|
|
|
/* Function prototypes */ |
|
|
static void halt_reboot_pwoff(int sig) NORETURN; |
|
|
|
|
|
static void waitfor(pid_t pid) |
|
|
{ |
|
|
/* waitfor(run(x)): protect against failed fork inside run() */ |
|
|
if (pid <= 0) |
|
|
return; |
|
|
|
|
|
/* Wait for any child (prevent zombies from exiting orphaned processes) |
|
|
* but exit the loop only when specified one has exited. */ |
|
|
while (wait(NULL) != pid) |
|
|
continue; |
|
|
} |
|
|
|
|
|
static void loop_forever(void) NORETURN; |
|
|
static void loop_forever(void) |
|
|
{ |
|
|
while (1) |
|
|
sleep(1); |
|
|
} |
|
|
|
|
98 |
/* Print a message to the specified device. |
/* Print a message to the specified device. |
99 |
* "where" may be bitwise-or'd from L_LOG | L_CONSOLE |
* "where" may be bitwise-or'd from L_LOG | L_CONSOLE |
100 |
* NB: careful, we can be called after vfork! |
* NB: careful, we can be called after vfork! |
101 |
*/ |
*/ |
102 |
#define messageD(...) do { if (DEBUG_INIT) message(__VA_ARGS__); } while (0) |
#define dbg_message(...) do { if (DEBUG_INIT) message(__VA_ARGS__); } while (0) |
103 |
static void message(int where, const char *fmt, ...) |
static void message(int where, const char *fmt, ...) |
104 |
__attribute__ ((format(printf, 2, 3))); |
__attribute__ ((format(printf, 2, 3))); |
105 |
static void message(int where, const char *fmt, ...) |
static void message(int where, const char *fmt, ...) |
106 |
{ |
{ |
|
static int log_fd = -1; |
|
107 |
va_list arguments; |
va_list arguments; |
108 |
unsigned l; |
unsigned l; |
109 |
char msg[128]; |
char msg[128]; |
113 |
l = 1 + vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments); |
l = 1 + vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments); |
114 |
if (l > sizeof(msg) - 1) |
if (l > sizeof(msg) - 1) |
115 |
l = sizeof(msg) - 1; |
l = sizeof(msg) - 1; |
|
msg[l] = '\0'; |
|
116 |
va_end(arguments); |
va_end(arguments); |
117 |
|
|
118 |
if (ENABLE_FEATURE_INIT_SYSLOG) { |
#if ENABLE_FEATURE_INIT_SYSLOG |
119 |
if (where & L_LOG) { |
msg[l] = '\0'; |
120 |
/* Log the message to syslogd */ |
if (where & L_LOG) { |
121 |
openlog("init", 0, LOG_DAEMON); |
/* Log the message to syslogd */ |
122 |
/* don't print "\r" */ |
openlog(applet_name, 0, LOG_DAEMON); |
123 |
syslog(LOG_INFO, "%s", msg + 1); |
/* don't print "\r" */ |
124 |
closelog(); |
syslog(LOG_INFO, "%s", msg + 1); |
125 |
} |
closelog(); |
126 |
msg[l++] = '\n'; |
} |
127 |
msg[l] = '\0'; |
msg[l++] = '\n'; |
128 |
} else { |
msg[l] = '\0'; |
129 |
|
#else |
130 |
|
{ |
131 |
|
static int log_fd = -1; |
132 |
|
|
133 |
msg[l++] = '\n'; |
msg[l++] = '\n'; |
134 |
msg[l] = '\0'; |
msg[l] = '\0'; |
135 |
/* Take full control of the log tty, and never close it. |
/* Take full control of the log tty, and never close it. |
153 |
return; /* don't print dup messages */ |
return; /* don't print dup messages */ |
154 |
} |
} |
155 |
} |
} |
156 |
|
#endif |
157 |
|
|
158 |
if (where & L_CONSOLE) { |
if (where & L_CONSOLE) { |
159 |
/* Send console messages to console so people will see them. */ |
/* Send console messages to console so people will see them. */ |
161 |
} |
} |
162 |
} |
} |
163 |
|
|
|
/* From <linux/serial.h> */ |
|
|
struct serial_struct { |
|
|
int type; |
|
|
int line; |
|
|
unsigned int port; |
|
|
int irq; |
|
|
int flags; |
|
|
int xmit_fifo_size; |
|
|
int custom_divisor; |
|
|
int baud_base; |
|
|
unsigned short close_delay; |
|
|
char io_type; |
|
|
char reserved_char[1]; |
|
|
int hub6; |
|
|
unsigned short closing_wait; /* time to wait before closing */ |
|
|
unsigned short closing_wait2; /* no longer used... */ |
|
|
unsigned char *iomem_base; |
|
|
unsigned short iomem_reg_shift; |
|
|
unsigned int port_high; |
|
|
unsigned long iomap_base; /* cookie passed into ioremap */ |
|
|
int reserved[1]; |
|
|
/* Paranoia (imagine 64bit kernel overwriting 32bit userspace stack) */ |
|
|
uint32_t bbox_reserved[16]; |
|
|
}; |
|
164 |
static void console_init(void) |
static void console_init(void) |
165 |
{ |
{ |
166 |
struct serial_struct sr; |
int vtno; |
167 |
char *s; |
char *s; |
168 |
|
|
169 |
s = getenv("CONSOLE"); |
s = getenv("CONSOLE"); |
176 |
dup2(fd, STDOUT_FILENO); |
dup2(fd, STDOUT_FILENO); |
177 |
xmove_fd(fd, STDERR_FILENO); |
xmove_fd(fd, STDERR_FILENO); |
178 |
} |
} |
179 |
messageD(L_LOG, "console='%s'", s); |
dbg_message(L_LOG, "console='%s'", s); |
180 |
} else { |
} else { |
181 |
/* Make sure fd 0,1,2 are not closed |
/* Make sure fd 0,1,2 are not closed |
182 |
* (so that they won't be used by future opens) */ |
* (so that they won't be used by future opens) */ |
187 |
} |
} |
188 |
|
|
189 |
s = getenv("TERM"); |
s = getenv("TERM"); |
190 |
if (ioctl(STDIN_FILENO, TIOCGSERIAL, &sr) == 0) { |
if (ioctl(STDIN_FILENO, VT_OPENQRY, &vtno) != 0) { |
191 |
/* Force the TERM setting to vt102 for serial console |
/* Not a linux terminal, probably serial console. |
192 |
|
* Force the TERM setting to vt102 |
193 |
* if TERM is set to linux (the default) */ |
* if TERM is set to linux (the default) */ |
194 |
if (!s || strcmp(s, "linux") == 0) |
if (!s || strcmp(s, "linux") == 0) |
195 |
putenv((char*)"TERM=vt102"); |
putenv((char*)"TERM=vt102"); |
239 |
|
|
240 |
/* Open the new terminal device. |
/* Open the new terminal device. |
241 |
* NB: careful, we can be called after vfork! */ |
* NB: careful, we can be called after vfork! */ |
242 |
static void open_stdio_to_tty(const char* tty_name, int exit_on_failure) |
static int open_stdio_to_tty(const char* tty_name) |
243 |
{ |
{ |
244 |
/* empty tty_name means "use init's tty", else... */ |
/* empty tty_name means "use init's tty", else... */ |
245 |
if (tty_name[0]) { |
if (tty_name[0]) { |
246 |
int fd; |
int fd; |
247 |
|
|
248 |
close(STDIN_FILENO); |
close(STDIN_FILENO); |
249 |
/* fd can be only < 0 or 0: */ |
/* fd can be only < 0 or 0: */ |
250 |
fd = device_open(tty_name, O_RDWR); |
fd = device_open(tty_name, O_RDWR); |
251 |
if (fd) { |
if (fd) { |
252 |
message(L_LOG | L_CONSOLE, "can't open %s: %s", |
message(L_LOG | L_CONSOLE, "can't open %s: %s", |
253 |
tty_name, strerror(errno)); |
tty_name, strerror(errno)); |
254 |
if (exit_on_failure) |
return 0; /* failure */ |
|
_exit(EXIT_FAILURE); |
|
|
if (DEBUG_INIT) |
|
|
_exit(2); |
|
|
/* NB: we don't reach this if we were called after vfork. |
|
|
* Thus halt_reboot_pwoff() itself need not be vfork-safe. */ |
|
|
halt_reboot_pwoff(SIGUSR1); /* halt the system */ |
|
255 |
} |
} |
256 |
dup2(STDIN_FILENO, STDOUT_FILENO); |
dup2(STDIN_FILENO, STDOUT_FILENO); |
257 |
dup2(STDIN_FILENO, STDERR_FILENO); |
dup2(STDIN_FILENO, STDERR_FILENO); |
258 |
} |
} |
259 |
set_sane_term(); |
set_sane_term(); |
260 |
|
return 1; /* success */ |
261 |
|
} |
262 |
|
|
263 |
|
static void reset_sighandlers_and_unblock_sigs(void) |
264 |
|
{ |
265 |
|
bb_signals(0 |
266 |
|
+ (1 << SIGUSR1) |
267 |
|
+ (1 << SIGUSR2) |
268 |
|
+ (1 << SIGTERM) |
269 |
|
+ (1 << SIGQUIT) |
270 |
|
+ (1 << SIGINT) |
271 |
|
+ (1 << SIGHUP) |
272 |
|
+ (1 << SIGTSTP) |
273 |
|
+ (1 << SIGSTOP) |
274 |
|
, SIG_DFL); |
275 |
|
sigprocmask_allsigs(SIG_UNBLOCK); |
276 |
} |
} |
277 |
|
|
278 |
/* Wrapper around exec: |
/* Wrapper around exec: |
319 |
ioctl(STDIN_FILENO, TIOCSCTTY, 0 /*only try, don't steal*/); |
ioctl(STDIN_FILENO, TIOCSCTTY, 0 /*only try, don't steal*/); |
320 |
} |
} |
321 |
BB_EXECVP(cmd[0] + dash, cmd); |
BB_EXECVP(cmd[0] + dash, cmd); |
322 |
message(L_LOG | L_CONSOLE, "cannot run '%s': %s", cmd[0], strerror(errno)); |
message(L_LOG | L_CONSOLE, "can't run '%s': %s", cmd[0], strerror(errno)); |
323 |
/* returns if execvp fails */ |
/* returns if execvp fails */ |
324 |
} |
} |
325 |
|
|
327 |
static pid_t run(const struct init_action *a) |
static pid_t run(const struct init_action *a) |
328 |
{ |
{ |
329 |
pid_t pid; |
pid_t pid; |
|
sigset_t nmask, omask; |
|
330 |
|
|
331 |
/* Block sigchild while forking (why?) */ |
/* Careful: don't be affected by a signal in vforked child */ |
332 |
sigemptyset(&nmask); |
sigprocmask_allsigs(SIG_BLOCK); |
|
sigaddset(&nmask, SIGCHLD); |
|
|
sigprocmask(SIG_BLOCK, &nmask, &omask); |
|
333 |
if (BB_MMU && (a->action_type & ASKFIRST)) |
if (BB_MMU && (a->action_type & ASKFIRST)) |
334 |
pid = fork(); |
pid = fork(); |
335 |
else |
else |
336 |
pid = vfork(); |
pid = vfork(); |
|
sigprocmask(SIG_SETMASK, &omask, NULL); |
|
|
|
|
337 |
if (pid < 0) |
if (pid < 0) |
338 |
message(L_LOG | L_CONSOLE, "can't fork"); |
message(L_LOG | L_CONSOLE, "can't fork"); |
339 |
if (pid) |
if (pid) { |
340 |
return pid; |
sigprocmask_allsigs(SIG_UNBLOCK); |
341 |
|
return pid; /* Parent or error */ |
342 |
|
} |
343 |
|
|
344 |
/* Child */ |
/* Child */ |
345 |
|
|
346 |
/* Reset signal handlers that were set by the parent process */ |
/* Reset signal handlers that were set by the parent process */ |
347 |
bb_signals(0 |
reset_sighandlers_and_unblock_sigs(); |
|
+ (1 << SIGUSR1) |
|
|
+ (1 << SIGUSR2) |
|
|
+ (1 << SIGINT) |
|
|
+ (1 << SIGTERM) |
|
|
+ (1 << SIGHUP) |
|
|
+ (1 << SIGQUIT) |
|
|
+ (1 << SIGCONT) |
|
|
+ (1 << SIGSTOP) |
|
|
+ (1 << SIGTSTP) |
|
|
, SIG_DFL); |
|
348 |
|
|
349 |
/* Create a new session and make ourself the process |
/* Create a new session and make ourself the process group leader */ |
|
* group leader */ |
|
350 |
setsid(); |
setsid(); |
351 |
|
|
352 |
/* Open the new terminal device */ |
/* Open the new terminal device */ |
353 |
open_stdio_to_tty(a->terminal, 1 /* - exit if open fails */); |
if (!open_stdio_to_tty(a->terminal)) |
354 |
|
_exit(EXIT_FAILURE); |
|
// NB: do not enable unless you change vfork to fork above |
|
|
#ifdef BUT_RUN_ACTIONS_ALREADY_DOES_WAITING |
|
|
/* If the init Action requires us to wait, then force the |
|
|
* supplied terminal to be the controlling tty. */ |
|
|
if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { |
|
|
/* Now fork off another process to just hang around */ |
|
|
pid = fork(); |
|
|
if (pid < 0) { |
|
|
message(L_LOG | L_CONSOLE, "can't fork"); |
|
|
_exit(EXIT_FAILURE); |
|
|
} |
|
|
|
|
|
if (pid > 0) { |
|
|
/* Parent - wait till the child is done */ |
|
|
bb_signals(0 |
|
|
+ (1 << SIGINT) |
|
|
+ (1 << SIGTSTP) |
|
|
+ (1 << SIGQUIT) |
|
|
, SIG_IGN); |
|
|
signal(SIGCHLD, SIG_DFL); |
|
|
|
|
|
waitfor(pid); |
|
|
/* See if stealing the controlling tty back is necessary */ |
|
|
if (tcgetpgrp(0) != getpid()) |
|
|
_exit(EXIT_SUCCESS); |
|
|
|
|
|
/* Use a temporary process to steal the controlling tty. */ |
|
|
pid = fork(); |
|
|
if (pid < 0) { |
|
|
message(L_LOG | L_CONSOLE, "can't fork"); |
|
|
_exit(EXIT_FAILURE); |
|
|
} |
|
|
if (pid == 0) { |
|
|
setsid(); |
|
|
ioctl(0, TIOCSCTTY, 1); |
|
|
_exit(EXIT_SUCCESS); |
|
|
} |
|
|
waitfor(pid); |
|
|
_exit(EXIT_SUCCESS); |
|
|
} |
|
|
/* Child - fall though to actually execute things */ |
|
|
} |
|
|
#endif |
|
355 |
|
|
356 |
/* NB: on NOMMU we can't wait for input in child, so |
/* NB: on NOMMU we can't wait for input in child, so |
357 |
* "askfirst" will work the same as "respawn". */ |
* "askfirst" will work the same as "respawn". */ |
370 |
* be allowed to start a shell or whatever an init script |
* be allowed to start a shell or whatever an init script |
371 |
* specifies. |
* specifies. |
372 |
*/ |
*/ |
373 |
messageD(L_LOG, "waiting for enter to start '%s'" |
dbg_message(L_LOG, "waiting for enter to start '%s'" |
374 |
"(pid %d, tty '%s')\n", |
"(pid %d, tty '%s')\n", |
375 |
a->command, getpid(), a->terminal); |
a->command, getpid(), a->terminal); |
376 |
full_write(STDOUT_FILENO, press_enter, sizeof(press_enter) - 1); |
full_write(STDOUT_FILENO, press_enter, sizeof(press_enter) - 1); |
378 |
continue; |
continue; |
379 |
} |
} |
380 |
|
|
381 |
|
/* |
382 |
|
* When a file named /.init_enable_core exists, setrlimit is called |
383 |
|
* before processes are spawned to set core file size as unlimited. |
384 |
|
* This is for debugging only. Don't use this is production, unless |
385 |
|
* you want core dumps lying about.... |
386 |
|
*/ |
387 |
if (ENABLE_FEATURE_INIT_COREDUMPS) { |
if (ENABLE_FEATURE_INIT_COREDUMPS) { |
388 |
struct stat sb; |
if (access("/.init_enable_core", F_OK) == 0) { |
|
if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) { |
|
389 |
struct rlimit limit; |
struct rlimit limit; |
390 |
limit.rlim_cur = RLIM_INFINITY; |
limit.rlim_cur = RLIM_INFINITY; |
391 |
limit.rlim_max = RLIM_INFINITY; |
limit.rlim_max = RLIM_INFINITY; |
404 |
_exit(-1); |
_exit(-1); |
405 |
} |
} |
406 |
|
|
407 |
static void delete_init_action(struct init_action *action) |
static struct init_action *mark_terminated(pid_t pid) |
408 |
{ |
{ |
409 |
struct init_action *a, *b = NULL; |
struct init_action *a; |
|
|
|
|
for (a = init_action_list; a; b = a, a = a->next) { |
|
|
if (a == action) { |
|
|
if (b == NULL) { |
|
|
init_action_list = a->next; |
|
|
} else { |
|
|
b->next = a->next; |
|
|
} |
|
|
free(a); |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
/* Run all commands of a particular type */ |
|
|
static void run_actions(int action_type) |
|
|
{ |
|
|
struct init_action *a, *tmp; |
|
410 |
|
|
411 |
for (a = init_action_list; a; a = tmp) { |
if (pid > 0) { |
412 |
tmp = a->next; |
for (a = init_action_list; a; a = a->next) { |
413 |
if (a->action_type & action_type) { |
if (a->pid == pid) { |
414 |
// Pointless: run() will error out if open of device fails. |
a->pid = 0; |
415 |
///* a->terminal of "" means "init's console" */ |
return a; |
|
//if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) { |
|
|
// //message(L_LOG | L_CONSOLE, "Device %s cannot be opened in RW mode", a->terminal /*, strerror(errno)*/); |
|
|
// delete_init_action(a); |
|
|
//} else |
|
|
if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { |
|
|
waitfor(run(a)); |
|
|
delete_init_action(a); |
|
|
} else if (a->action_type & ONCE) { |
|
|
run(a); |
|
|
delete_init_action(a); |
|
|
} else if (a->action_type & (RESPAWN | ASKFIRST)) { |
|
|
/* Only run stuff with pid==0. If they have |
|
|
* a pid, that means it is still running */ |
|
|
if (a->pid == 0) { |
|
|
a->pid = run(a); |
|
|
} |
|
416 |
} |
} |
417 |
} |
} |
418 |
} |
} |
419 |
|
return NULL; |
420 |
} |
} |
421 |
|
|
422 |
static void init_reboot(unsigned long magic) |
static void waitfor(pid_t pid) |
|
{ |
|
|
pid_t pid; |
|
|
/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS) in |
|
|
* linux/kernel/sys.c, which can cause the machine to panic when |
|
|
* the init process is killed.... */ |
|
|
pid = vfork(); |
|
|
if (pid == 0) { /* child */ |
|
|
reboot(magic); |
|
|
_exit(EXIT_SUCCESS); |
|
|
} |
|
|
waitfor(pid); |
|
|
} |
|
|
|
|
|
static void kill_all_processes(void) |
|
|
{ |
|
|
/* run everything to be run at "shutdown". This is done _prior_ |
|
|
* to killing everything, in case people wish to use scripts to |
|
|
* shut things down gracefully... */ |
|
|
run_actions(SHUTDOWN); |
|
|
|
|
|
/* first disable all our signals */ |
|
|
sigprocmask_allsigs(SIG_BLOCK); |
|
|
|
|
|
message(L_CONSOLE | L_LOG, "The system is going down NOW!"); |
|
|
|
|
|
/* Allow Ctrl-Alt-Del to reboot system. */ |
|
|
init_reboot(RB_ENABLE_CAD); |
|
|
|
|
|
/* Send signals to every process _except_ pid 1 */ |
|
|
message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "TERM"); |
|
|
kill(-1, SIGTERM); |
|
|
sync(); |
|
|
sleep(1); |
|
|
|
|
|
message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "KILL"); |
|
|
kill(-1, SIGKILL); |
|
|
sync(); |
|
|
sleep(1); |
|
|
} |
|
|
|
|
|
static void halt_reboot_pwoff(int sig) |
|
423 |
{ |
{ |
424 |
const char *m = "halt"; |
/* waitfor(run(x)): protect against failed fork inside run() */ |
425 |
int rb; |
if (pid <= 0) |
426 |
|
return; |
|
kill_all_processes(); |
|
427 |
|
|
428 |
rb = RB_HALT_SYSTEM; |
/* Wait for any child (prevent zombies from exiting orphaned processes) |
429 |
if (sig == SIGTERM) { |
* but exit the loop only when specified one has exited. */ |
430 |
m = "reboot"; |
while (1) { |
431 |
rb = RB_AUTOBOOT; |
pid_t wpid = wait(NULL); |
432 |
} else if (sig == SIGUSR2) { |
mark_terminated(wpid); |
433 |
m = "poweroff"; |
/* Unsafe. SIGTSTP handler might have wait'ed it already */ |
434 |
rb = RB_POWER_OFF; |
/*if (wpid == pid) break;*/ |
435 |
|
/* More reliable: */ |
436 |
|
if (kill(pid, 0)) |
437 |
|
break; |
438 |
} |
} |
|
message(L_CONSOLE | L_LOG, "Requesting system %s", m); |
|
|
/* allow time for last message to reach serial console */ |
|
|
sleep(2); |
|
|
init_reboot(rb); |
|
|
loop_forever(); |
|
439 |
} |
} |
440 |
|
|
441 |
/* Handler for QUIT - exec "restart" action, |
/* Run all commands of a particular type */ |
442 |
* else (no such action defined) do nothing */ |
static void run_actions(int action_type) |
|
static void exec_restart_action(int sig UNUSED_PARAM) |
|
443 |
{ |
{ |
444 |
struct init_action *a; |
struct init_action *a; |
445 |
|
|
446 |
for (a = init_action_list; a; a = a->next) { |
for (a = init_action_list; a; a = a->next) { |
447 |
if (a->action_type & RESTART) { |
if (!(a->action_type & action_type)) |
448 |
kill_all_processes(); |
continue; |
|
|
|
|
/* unblock all signals (blocked in kill_all_processes()) */ |
|
|
sigprocmask_allsigs(SIG_UNBLOCK); |
|
|
|
|
|
/* Open the new terminal device */ |
|
|
open_stdio_to_tty(a->terminal, 0 /* - halt if open fails */); |
|
449 |
|
|
450 |
messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command); |
if (a->action_type & (SYSINIT | WAIT | ONCE | CTRLALTDEL | SHUTDOWN)) { |
451 |
init_exec(a->command); |
pid_t pid = run(a); |
452 |
sleep(2); |
if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN)) |
453 |
init_reboot(RB_HALT_SYSTEM); |
waitfor(pid); |
454 |
loop_forever(); |
} |
455 |
|
if (a->action_type & (RESPAWN | ASKFIRST)) { |
456 |
|
/* Only run stuff with pid == 0. If pid != 0, |
457 |
|
* it is already running |
458 |
|
*/ |
459 |
|
if (a->pid == 0) |
460 |
|
a->pid = run(a); |
461 |
} |
} |
462 |
} |
} |
463 |
} |
} |
464 |
|
|
|
static void ctrlaltdel_signal(int sig UNUSED_PARAM) |
|
|
{ |
|
|
run_actions(CTRLALTDEL); |
|
|
} |
|
|
|
|
|
/* The SIGCONT handler is set to record_signo(). |
|
|
* It just sets bb_got_signal = SIGCONT. */ |
|
|
|
|
|
/* The SIGSTOP & SIGTSTP handler */ |
|
|
static void stop_handler(int sig UNUSED_PARAM) |
|
|
{ |
|
|
int saved_errno = errno; |
|
|
|
|
|
bb_got_signal = 0; |
|
|
while (bb_got_signal == 0) |
|
|
pause(); |
|
|
|
|
|
errno = saved_errno; |
|
|
} |
|
|
|
|
465 |
static void new_init_action(uint8_t action_type, const char *command, const char *cons) |
static void new_init_action(uint8_t action_type, const char *command, const char *cons) |
466 |
{ |
{ |
467 |
struct init_action *a, *last; |
struct init_action *a, **nextp; |
|
|
|
|
// Why? |
|
|
// if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST)) |
|
|
// return; |
|
468 |
|
|
469 |
/* Append to the end of the list */ |
/* Scenario: |
470 |
for (a = last = init_action_list; a; a = a->next) { |
* old inittab: |
471 |
/* don't enter action if it's already in the list, |
* ::shutdown:umount -a -r |
472 |
* but do overwrite existing actions */ |
* ::shutdown:swapoff -a |
473 |
if ((strcmp(a->command, command) == 0) |
* new inittab: |
474 |
&& (strcmp(a->terminal, cons) == 0) |
* ::shutdown:swapoff -a |
475 |
|
* ::shutdown:umount -a -r |
476 |
|
* On reload, we must ensure entries end up in correct order. |
477 |
|
* To achieve that, if we find a matching entry, we move it |
478 |
|
* to the end. |
479 |
|
*/ |
480 |
|
nextp = &init_action_list; |
481 |
|
while ((a = *nextp) != NULL) { |
482 |
|
/* Don't enter action if it's already in the list, |
483 |
|
* This prevents losing running RESPAWNs. |
484 |
|
*/ |
485 |
|
if (strcmp(a->command, command) == 0 |
486 |
|
&& strcmp(a->terminal, cons) == 0 |
487 |
) { |
) { |
488 |
a->action_type = action_type; |
/* Remove from list */ |
489 |
return; |
*nextp = a->next; |
490 |
|
/* Find the end of the list */ |
491 |
|
while (*nextp != NULL) |
492 |
|
nextp = &(*nextp)->next; |
493 |
|
a->next = NULL; |
494 |
|
break; |
495 |
} |
} |
496 |
last = a; |
nextp = &a->next; |
497 |
} |
} |
498 |
|
|
499 |
a = xzalloc(sizeof(*a)); |
if (!a) |
500 |
if (last) { |
a = xzalloc(sizeof(*a)); |
501 |
last->next = a; |
/* Append to the end of the list */ |
502 |
} else { |
*nextp = a; |
|
init_action_list = a; |
|
|
} |
|
503 |
a->action_type = action_type; |
a->action_type = action_type; |
504 |
safe_strncpy(a->command, command, sizeof(a->command)); |
safe_strncpy(a->command, command, sizeof(a->command)); |
505 |
safe_strncpy(a->terminal, cons, sizeof(a->terminal)); |
safe_strncpy(a->terminal, cons, sizeof(a->terminal)); |
506 |
messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n", |
dbg_message(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n", |
507 |
a->command, a->action_type, a->terminal); |
a->command, a->action_type, a->terminal); |
508 |
} |
} |
509 |
|
|
523 |
if (parser == NULL) |
if (parser == NULL) |
524 |
#endif |
#endif |
525 |
{ |
{ |
526 |
/* No inittab file -- set up some default behavior */ |
/* No inittab file - set up some default behavior */ |
527 |
/* Reboot on Ctrl-Alt-Del */ |
/* Reboot on Ctrl-Alt-Del */ |
528 |
new_init_action(CTRLALTDEL, "reboot", ""); |
new_init_action(CTRLALTDEL, "reboot", ""); |
529 |
/* Umount all filesystems on halt/reboot */ |
/* Umount all filesystems on halt/reboot */ |
552 |
PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) { |
PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) { |
553 |
/* order must correspond to SYSINIT..RESTART constants */ |
/* order must correspond to SYSINIT..RESTART constants */ |
554 |
static const char actions[] ALIGN1 = |
static const char actions[] ALIGN1 = |
555 |
"sysinit\0""respawn\0""askfirst\0""wait\0""once\0" |
"sysinit\0""wait\0""once\0""respawn\0""askfirst\0" |
556 |
"ctrlaltdel\0""shutdown\0""restart\0"; |
"ctrlaltdel\0""shutdown\0""restart\0"; |
557 |
int action; |
int action; |
558 |
char *tty = token[0]; |
char *tty = token[0]; |
580 |
#endif |
#endif |
581 |
} |
} |
582 |
|
|
583 |
#if ENABLE_FEATURE_USE_INITTAB |
static void pause_and_low_level_reboot(unsigned magic) NORETURN; |
584 |
static void reload_signal(int sig UNUSED_PARAM) |
static void pause_and_low_level_reboot(unsigned magic) |
585 |
{ |
{ |
586 |
struct init_action *a, *tmp; |
pid_t pid; |
587 |
|
|
588 |
message(L_LOG, "reloading /etc/inittab"); |
/* Allow time for last message to reach serial console, etc */ |
589 |
|
sleep(1); |
590 |
|
|
591 |
|
/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS) |
592 |
|
* in linux/kernel/sys.c, which can cause the machine to panic when |
593 |
|
* the init process exits... */ |
594 |
|
pid = vfork(); |
595 |
|
if (pid == 0) { /* child */ |
596 |
|
reboot(magic); |
597 |
|
_exit(EXIT_SUCCESS); |
598 |
|
} |
599 |
|
while (1) |
600 |
|
sleep(1); |
601 |
|
} |
602 |
|
|
603 |
|
static void run_shutdown_and_kill_processes(void) |
604 |
|
{ |
605 |
|
/* Run everything to be run at "shutdown". This is done _prior_ |
606 |
|
* to killing everything, in case people wish to use scripts to |
607 |
|
* shut things down gracefully... */ |
608 |
|
run_actions(SHUTDOWN); |
609 |
|
|
610 |
|
message(L_CONSOLE | L_LOG, "The system is going down NOW!"); |
611 |
|
|
612 |
|
/* Send signals to every process _except_ pid 1 */ |
613 |
|
kill(-1, SIGTERM); |
614 |
|
message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM"); |
615 |
|
sync(); |
616 |
|
sleep(1); |
617 |
|
|
618 |
|
kill(-1, SIGKILL); |
619 |
|
message(L_CONSOLE, "Sent SIG%s to all processes", "KILL"); |
620 |
|
sync(); |
621 |
|
/*sleep(1); - callers take care about making a pause */ |
622 |
|
} |
623 |
|
|
624 |
|
/* Signal handling by init: |
625 |
|
* |
626 |
|
* For process with PID==1, on entry kernel sets all signals to SIG_DFL |
627 |
|
* and unmasks all signals. However, for process with PID==1, |
628 |
|
* default action (SIG_DFL) on any signal is to ignore it, |
629 |
|
* even for special signals SIGKILL and SIGCONT. |
630 |
|
* Also, any signal can be caught or blocked. |
631 |
|
* (but SIGSTOP is still handled specially, at least in 2.6.20) |
632 |
|
* |
633 |
|
* We install two kinds of handlers, "immediate" and "delayed". |
634 |
|
* |
635 |
|
* Immediate handlers execute at any time, even while, say, sysinit |
636 |
|
* is running. |
637 |
|
* |
638 |
|
* Delayed handlers just set a flag variable. The variable is checked |
639 |
|
* in the main loop and acted upon. |
640 |
|
* |
641 |
|
* halt/poweroff/reboot and restart have immediate handlers. |
642 |
|
* They only traverse linked list of struct action's, never modify it, |
643 |
|
* this should be safe to do even in signal handler. Also they |
644 |
|
* never return. |
645 |
|
* |
646 |
|
* SIGSTOP and SIGTSTP have immediate handlers. They just wait |
647 |
|
* for SIGCONT to happen. |
648 |
|
* |
649 |
|
* SIGHUP has a delayed handler, because modifying linked list |
650 |
|
* of struct action's from a signal handler while it is manipulated |
651 |
|
* by the program may be disastrous. |
652 |
|
* |
653 |
|
* Ctrl-Alt-Del has a delayed handler. Not a must, but allowing |
654 |
|
* it to happen even somewhere inside "sysinit" would be a bit awkward. |
655 |
|
* |
656 |
|
* There is a tiny probability that SIGHUP and Ctrl-Alt-Del will collide |
657 |
|
* and only one will be remembered and acted upon. |
658 |
|
*/ |
659 |
|
|
660 |
|
/* The SIGUSR[12]/SIGTERM handler */ |
661 |
|
static void halt_reboot_pwoff(int sig) NORETURN; |
662 |
|
static void halt_reboot_pwoff(int sig) |
663 |
|
{ |
664 |
|
const char *m; |
665 |
|
unsigned rb; |
666 |
|
|
667 |
|
/* We may call run() and it unmasks signals, |
668 |
|
* including the one masked inside this signal handler. |
669 |
|
* Testcase which would start multiple reboot scripts: |
670 |
|
* while true; do reboot; done |
671 |
|
* Preventing it: |
672 |
|
*/ |
673 |
|
reset_sighandlers_and_unblock_sigs(); |
674 |
|
|
675 |
|
run_shutdown_and_kill_processes(); |
676 |
|
|
677 |
|
m = "halt"; |
678 |
|
rb = RB_HALT_SYSTEM; |
679 |
|
if (sig == SIGTERM) { |
680 |
|
m = "reboot"; |
681 |
|
rb = RB_AUTOBOOT; |
682 |
|
} else if (sig == SIGUSR2) { |
683 |
|
m = "poweroff"; |
684 |
|
rb = RB_POWER_OFF; |
685 |
|
} |
686 |
|
message(L_CONSOLE, "Requesting system %s", m); |
687 |
|
pause_and_low_level_reboot(rb); |
688 |
|
/* not reached */ |
689 |
|
} |
690 |
|
|
691 |
|
/* Handler for QUIT - exec "restart" action, |
692 |
|
* else (no such action defined) do nothing */ |
693 |
|
static void restart_handler(int sig UNUSED_PARAM) |
694 |
|
{ |
695 |
|
struct init_action *a; |
696 |
|
|
|
/* disable old entrys */ |
|
697 |
for (a = init_action_list; a; a = a->next) { |
for (a = init_action_list; a; a = a->next) { |
698 |
a->action_type = ONCE; |
if (!(a->action_type & RESTART)) |
699 |
|
continue; |
700 |
|
|
701 |
|
/* Starting from here, we won't return. |
702 |
|
* Thus don't need to worry about preserving errno |
703 |
|
* and such. |
704 |
|
*/ |
705 |
|
|
706 |
|
reset_sighandlers_and_unblock_sigs(); |
707 |
|
|
708 |
|
run_shutdown_and_kill_processes(); |
709 |
|
|
710 |
|
/* Allow Ctrl-Alt-Del to reboot the system. |
711 |
|
* This is how kernel sets it up for init, we follow suit. |
712 |
|
*/ |
713 |
|
reboot(RB_ENABLE_CAD); /* misnomer */ |
714 |
|
|
715 |
|
if (open_stdio_to_tty(a->terminal)) { |
716 |
|
dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command); |
717 |
|
/* Theoretically should be safe. |
718 |
|
* But in practice, kernel bugs may leave |
719 |
|
* unkillable processes, and wait() may block forever. |
720 |
|
* Oh well. Hoping "new" init won't be too surprised |
721 |
|
* by having children it didn't create. |
722 |
|
*/ |
723 |
|
//while (wait(NULL) > 0) |
724 |
|
// continue; |
725 |
|
init_exec(a->command); |
726 |
|
} |
727 |
|
/* Open or exec failed */ |
728 |
|
pause_and_low_level_reboot(RB_HALT_SYSTEM); |
729 |
|
/* not reached */ |
730 |
|
} |
731 |
|
} |
732 |
|
|
733 |
|
/* The SIGSTOP/SIGTSTP handler |
734 |
|
* NB: inside it, all signals except SIGCONT are masked |
735 |
|
* via appropriate setup in sigaction(). |
736 |
|
*/ |
737 |
|
static void stop_handler(int sig UNUSED_PARAM) |
738 |
|
{ |
739 |
|
smallint saved_bb_got_signal; |
740 |
|
int saved_errno; |
741 |
|
|
742 |
|
saved_bb_got_signal = bb_got_signal; |
743 |
|
saved_errno = errno; |
744 |
|
signal(SIGCONT, record_signo); |
745 |
|
|
746 |
|
while (1) { |
747 |
|
pid_t wpid; |
748 |
|
|
749 |
|
if (bb_got_signal == SIGCONT) |
750 |
|
break; |
751 |
|
/* NB: this can accidentally wait() for a process |
752 |
|
* which we waitfor() elsewhere! waitfor() must have |
753 |
|
* code which is resilient against this. |
754 |
|
*/ |
755 |
|
wpid = wait_any_nohang(NULL); |
756 |
|
mark_terminated(wpid); |
757 |
|
sleep(1); |
758 |
} |
} |
759 |
|
|
760 |
|
signal(SIGCONT, SIG_DFL); |
761 |
|
errno = saved_errno; |
762 |
|
bb_got_signal = saved_bb_got_signal; |
763 |
|
} |
764 |
|
|
765 |
|
#if ENABLE_FEATURE_USE_INITTAB |
766 |
|
static void reload_inittab(void) |
767 |
|
{ |
768 |
|
struct init_action *a, **nextp; |
769 |
|
|
770 |
|
message(L_LOG, "reloading /etc/inittab"); |
771 |
|
|
772 |
|
/* Disable old entries */ |
773 |
|
for (a = init_action_list; a; a = a->next) |
774 |
|
a->action_type = 0; |
775 |
|
|
776 |
|
/* Append new entries, or modify existing entries |
777 |
|
* (incl. setting a->action_type) if cmd and device name |
778 |
|
* match new ones. End result: only entries with |
779 |
|
* a->action_type == 0 are stale. |
780 |
|
*/ |
781 |
parse_inittab(); |
parse_inittab(); |
782 |
|
|
783 |
if (ENABLE_FEATURE_KILL_REMOVED) { |
#if ENABLE_FEATURE_KILL_REMOVED |
784 |
/* Be nice and send SIGTERM first */ |
/* Kill stale entries */ |
785 |
for (a = init_action_list; a; a = a->next) { |
/* Be nice and send SIGTERM first */ |
786 |
pid_t pid = a->pid; |
for (a = init_action_list; a; a = a->next) |
787 |
if ((a->action_type & ONCE) && pid != 0) { |
if (a->action_type == 0 && a->pid != 0) |
788 |
kill(pid, SIGTERM); |
kill(a->pid, SIGTERM); |
789 |
} |
if (CONFIG_FEATURE_KILL_DELAY) { |
|
} |
|
|
#if CONFIG_FEATURE_KILL_DELAY |
|
790 |
/* NB: parent will wait in NOMMU case */ |
/* NB: parent will wait in NOMMU case */ |
791 |
if ((BB_MMU ? fork() : vfork()) == 0) { /* child */ |
if ((BB_MMU ? fork() : vfork()) == 0) { /* child */ |
792 |
sleep(CONFIG_FEATURE_KILL_DELAY); |
sleep(CONFIG_FEATURE_KILL_DELAY); |
793 |
for (a = init_action_list; a; a = a->next) { |
for (a = init_action_list; a; a = a->next) |
794 |
pid_t pid = a->pid; |
if (a->action_type == 0 && a->pid != 0) |
795 |
if ((a->action_type & ONCE) && pid != 0) { |
kill(a->pid, SIGKILL); |
|
kill(pid, SIGKILL); |
|
|
} |
|
|
} |
|
796 |
_exit(EXIT_SUCCESS); |
_exit(EXIT_SUCCESS); |
797 |
} |
} |
|
#endif |
|
798 |
} |
} |
799 |
|
#endif |
800 |
|
|
801 |
/* remove unused entrys */ |
/* Remove stale entries and SYSINIT entries. |
802 |
for (a = init_action_list; a; a = tmp) { |
* We never rerun SYSINIT entries anyway, |
803 |
tmp = a->next; |
* removing them too saves a few bytes */ |
804 |
if ((a->action_type & (ONCE | SYSINIT | WAIT)) && a->pid == 0) { |
nextp = &init_action_list; |
805 |
delete_init_action(a); |
while ((a = *nextp) != NULL) { |
806 |
|
if ((a->action_type & ~SYSINIT) == 0) { |
807 |
|
*nextp = a->next; |
808 |
|
free(a); |
809 |
|
} else { |
810 |
|
nextp = &a->next; |
811 |
} |
} |
812 |
} |
} |
813 |
run_actions(RESPAWN | ASKFIRST); |
|
814 |
|
/* Not needed: */ |
815 |
|
/* run_actions(RESPAWN | ASKFIRST); */ |
816 |
|
/* - we return to main loop, which does this automagically */ |
817 |
} |
} |
818 |
#endif |
#endif |
819 |
|
|
820 |
|
static int check_delayed_sigs(void) |
821 |
|
{ |
822 |
|
int sigs_seen = 0; |
823 |
|
|
824 |
|
while (1) { |
825 |
|
smallint sig = bb_got_signal; |
826 |
|
|
827 |
|
if (!sig) |
828 |
|
return sigs_seen; |
829 |
|
bb_got_signal = 0; |
830 |
|
sigs_seen = 1; |
831 |
|
#if ENABLE_FEATURE_USE_INITTAB |
832 |
|
if (sig == SIGHUP) |
833 |
|
reload_inittab(); |
834 |
|
#endif |
835 |
|
if (sig == SIGINT) |
836 |
|
run_actions(CTRLALTDEL); |
837 |
|
} |
838 |
|
} |
839 |
|
|
840 |
int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
841 |
int init_main(int argc UNUSED_PARAM, char **argv) |
int init_main(int argc UNUSED_PARAM, char **argv) |
842 |
{ |
{ |
843 |
struct init_action *a; |
die_sleep = 30 * 24*60*60; /* if xmalloc would ever die... */ |
|
pid_t wpid; |
|
844 |
|
|
845 |
die_sleep = 30 * 24*60*60; /* if xmalloc will ever die... */ |
if (argv[1] && strcmp(argv[1], "-q") == 0) { |
|
|
|
|
if (argv[1] && !strcmp(argv[1], "-q")) { |
|
846 |
return kill(1, SIGHUP); |
return kill(1, SIGHUP); |
847 |
} |
} |
848 |
|
|
853 |
) { |
) { |
854 |
bb_show_usage(); |
bb_show_usage(); |
855 |
} |
} |
856 |
/* Set up sig handlers -- be sure to |
/* Turn off rebooting via CTL-ALT-DEL - we get a |
|
* clear all of these in run() */ |
|
|
signal(SIGQUIT, exec_restart_action); |
|
|
bb_signals(0 |
|
|
+ (1 << SIGUSR1) /* halt */ |
|
|
+ (1 << SIGUSR2) /* poweroff */ |
|
|
+ (1 << SIGTERM) /* reboot */ |
|
|
, halt_reboot_pwoff); |
|
|
signal(SIGINT, ctrlaltdel_signal); |
|
|
signal(SIGCONT, record_signo); |
|
|
bb_signals(0 |
|
|
+ (1 << SIGSTOP) |
|
|
+ (1 << SIGTSTP) |
|
|
, stop_handler); |
|
|
|
|
|
/* Turn off rebooting via CTL-ALT-DEL -- we get a |
|
857 |
* SIGINT on CAD so we can shut things down gracefully... */ |
* SIGINT on CAD so we can shut things down gracefully... */ |
858 |
init_reboot(RB_DISABLE_CAD); |
reboot(RB_DISABLE_CAD); /* misnomer */ |
859 |
} |
} |
860 |
|
|
861 |
/* Figure out where the default console should be */ |
/* Figure out where the default console should be */ |
873 |
if (argv[1]) |
if (argv[1]) |
874 |
xsetenv("RUNLEVEL", argv[1]); |
xsetenv("RUNLEVEL", argv[1]); |
875 |
|
|
876 |
|
#if !ENABLE_FEATURE_EXTRA_QUIET |
877 |
/* Hello world */ |
/* Hello world */ |
878 |
message(MAYBE_CONSOLE | L_LOG, "init started: %s", bb_banner); |
message(L_CONSOLE | L_LOG, "init started: %s", bb_banner); |
879 |
|
#endif |
880 |
|
|
881 |
/* Make sure there is enough memory to do something useful. */ |
/* Make sure there is enough memory to do something useful. */ |
882 |
if (ENABLE_SWAPONOFF) { |
if (ENABLE_SWAPONOFF) { |
883 |
struct sysinfo info; |
struct sysinfo info; |
884 |
|
|
885 |
if (!sysinfo(&info) && |
if (sysinfo(&info) == 0 |
886 |
(info.mem_unit ? : 1) * (long long)info.totalram < 1024*1024) |
&& (info.mem_unit ? info.mem_unit : 1) * (long long)info.totalram < 1024*1024 |
887 |
{ |
) { |
888 |
message(L_CONSOLE, "Low memory, forcing swapon"); |
message(L_CONSOLE, "Low memory, forcing swapon"); |
889 |
/* swapon -a requires /proc typically */ |
/* swapon -a requires /proc typically */ |
890 |
new_init_action(SYSINIT, "mount -t proc proc /proc", ""); |
new_init_action(SYSINIT, "mount -t proc proc /proc", ""); |
896 |
|
|
897 |
/* Check if we are supposed to be in single user mode */ |
/* Check if we are supposed to be in single user mode */ |
898 |
if (argv[1] |
if (argv[1] |
899 |
&& (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1')) |
&& (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1')) |
900 |
) { |
) { |
901 |
/* ??? shouldn't we set RUNLEVEL="b" here? */ |
/* ??? shouldn't we set RUNLEVEL="b" here? */ |
902 |
/* Start a shell on console */ |
/* Start a shell on console */ |
903 |
new_init_action(RESPAWN, bb_default_login_shell, ""); |
new_init_action(RESPAWN, bb_default_login_shell, ""); |
904 |
} else { |
} else { |
905 |
/* Not in single user mode -- see what inittab says */ |
/* Not in single user mode - see what inittab says */ |
906 |
|
|
907 |
/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, |
/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, |
908 |
* then parse_inittab() simply adds in some default |
* then parse_inittab() simply adds in some default |
909 |
* actions(i.e., runs INIT_SCRIPT and then starts a pair |
* actions(i.e., INIT_SCRIPT and a pair |
910 |
* of "askfirst" shells */ |
* of "askfirst" shells */ |
911 |
parse_inittab(); |
parse_inittab(); |
912 |
} |
} |
920 |
BB_EXECVP(argv[0], argv); |
BB_EXECVP(argv[0], argv); |
921 |
} else if (enforce > 0) { |
} else if (enforce > 0) { |
922 |
/* SELinux in enforcing mode but load_policy failed */ |
/* SELinux in enforcing mode but load_policy failed */ |
923 |
message(L_CONSOLE, "cannot load SELinux Policy. " |
message(L_CONSOLE, "can't load SELinux Policy. " |
924 |
"Machine is in enforcing mode. Halting now."); |
"Machine is in enforcing mode. Halting now."); |
925 |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
926 |
} |
} |
927 |
} |
} |
928 |
#endif /* CONFIG_SELINUX */ |
#endif |
929 |
|
|
930 |
/* Make the command line just say "init" - thats all, nothing else */ |
/* Make the command line just say "init" - thats all, nothing else */ |
931 |
strncpy(argv[0], "init", strlen(argv[0])); |
strncpy(argv[0], "init", strlen(argv[0])); |
933 |
while (*++argv) |
while (*++argv) |
934 |
memset(*argv, 0, strlen(*argv)); |
memset(*argv, 0, strlen(*argv)); |
935 |
|
|
936 |
/* Now run everything that needs to be run */ |
/* Set up signal handlers */ |
937 |
|
if (!DEBUG_INIT) { |
938 |
|
struct sigaction sa; |
939 |
|
|
940 |
|
bb_signals(0 |
941 |
|
+ (1 << SIGUSR1) /* halt */ |
942 |
|
+ (1 << SIGTERM) /* reboot */ |
943 |
|
+ (1 << SIGUSR2) /* poweroff */ |
944 |
|
, halt_reboot_pwoff); |
945 |
|
signal(SIGQUIT, restart_handler); /* re-exec another init */ |
946 |
|
|
947 |
|
/* Stop handler must allow only SIGCONT inside itself */ |
948 |
|
memset(&sa, 0, sizeof(sa)); |
949 |
|
sigfillset(&sa.sa_mask); |
950 |
|
sigdelset(&sa.sa_mask, SIGCONT); |
951 |
|
sa.sa_handler = stop_handler; |
952 |
|
/* NB: sa_flags doesn't have SA_RESTART. |
953 |
|
* It must be able to interrupt wait(). |
954 |
|
*/ |
955 |
|
sigaction_set(SIGTSTP, &sa); /* pause */ |
956 |
|
/* Does not work as intended, at least in 2.6.20. |
957 |
|
* SIGSTOP is simply ignored by init: |
958 |
|
*/ |
959 |
|
sigaction_set(SIGSTOP, &sa); /* pause */ |
960 |
|
|
961 |
|
/* SIGINT (Ctrl-Alt-Del) must interrupt wait(), |
962 |
|
* setting handler without SA_RESTART flag. |
963 |
|
*/ |
964 |
|
bb_signals_recursive_norestart((1 << SIGINT), record_signo); |
965 |
|
} |
966 |
|
|
967 |
|
/* Set up "reread /etc/inittab" handler. |
968 |
|
* Handler is set up without SA_RESTART, it will interrupt syscalls. |
969 |
|
*/ |
970 |
|
if (!DEBUG_INIT && ENABLE_FEATURE_USE_INITTAB) |
971 |
|
bb_signals_recursive_norestart((1 << SIGHUP), record_signo); |
972 |
|
|
973 |
|
/* Now run everything that needs to be run */ |
974 |
/* First run the sysinit command */ |
/* First run the sysinit command */ |
975 |
run_actions(SYSINIT); |
run_actions(SYSINIT); |
976 |
|
check_delayed_sigs(); |
977 |
/* Next run anything that wants to block */ |
/* Next run anything that wants to block */ |
978 |
run_actions(WAIT); |
run_actions(WAIT); |
979 |
|
check_delayed_sigs(); |
980 |
/* Next run anything to be run only once */ |
/* Next run anything to be run only once */ |
981 |
run_actions(ONCE); |
run_actions(ONCE); |
982 |
|
|
983 |
/* Redefine SIGHUP to reread /etc/inittab */ |
/* Now run the looping stuff for the rest of forever. |
984 |
#if ENABLE_FEATURE_USE_INITTAB |
*/ |
|
signal(SIGHUP, reload_signal); |
|
|
#else |
|
|
signal(SIGHUP, SIG_IGN); |
|
|
#endif |
|
|
|
|
|
/* Now run the looping stuff for the rest of forever */ |
|
985 |
while (1) { |
while (1) { |
986 |
/* run the respawn/askfirst stuff */ |
int maybe_WNOHANG; |
987 |
|
|
988 |
|
maybe_WNOHANG = check_delayed_sigs(); |
989 |
|
|
990 |
|
/* (Re)run the respawn/askfirst stuff */ |
991 |
run_actions(RESPAWN | ASKFIRST); |
run_actions(RESPAWN | ASKFIRST); |
992 |
|
maybe_WNOHANG |= check_delayed_sigs(); |
993 |
|
|
994 |
/* Don't consume all CPU time -- sleep a bit */ |
/* Don't consume all CPU time - sleep a bit */ |
995 |
sleep(1); |
sleep(1); |
996 |
|
maybe_WNOHANG |= check_delayed_sigs(); |
997 |
|
|
998 |
/* Wait for any child process to exit */ |
/* Wait for any child process(es) to exit. |
999 |
wpid = wait(NULL); |
* |
1000 |
while (wpid > 0) { |
* If check_delayed_sigs above reported that a signal |
1001 |
/* Find out who died and clean up their corpse */ |
* was caught, wait will be nonblocking. This ensures |
1002 |
for (a = init_action_list; a; a = a->next) { |
* that if SIGHUP has reloaded inittab, respawn and askfirst |
1003 |
if (a->pid == wpid) { |
* actions will not be delayed until next child death. |
1004 |
/* Set the pid to 0 so that the process gets |
*/ |
1005 |
* restarted by run_actions() */ |
if (maybe_WNOHANG) |
1006 |
a->pid = 0; |
maybe_WNOHANG = WNOHANG; |
1007 |
message(L_LOG, "process '%s' (pid %d) exited. " |
while (1) { |
1008 |
"Scheduling for restart.", |
pid_t wpid; |
1009 |
a->command, wpid); |
struct init_action *a; |
1010 |
} |
|
1011 |
|
/* If signals happen _in_ the wait, they interrupt it, |
1012 |
|
* bb_signals_recursive_norestart set them up that way |
1013 |
|
*/ |
1014 |
|
wpid = waitpid(-1, NULL, maybe_WNOHANG); |
1015 |
|
if (wpid <= 0) |
1016 |
|
break; |
1017 |
|
|
1018 |
|
a = mark_terminated(wpid); |
1019 |
|
if (a) { |
1020 |
|
message(L_LOG, "process '%s' (pid %d) exited. " |
1021 |
|
"Scheduling for restart.", |
1022 |
|
a->command, wpid); |
1023 |
} |
} |
1024 |
/* see if anyone else is waiting to be reaped */ |
/* See if anyone else is waiting to be reaped */ |
1025 |
wpid = wait_any_nohang(NULL); |
maybe_WNOHANG = WNOHANG; |
1026 |
} |
} |
1027 |
} |
} /* while (1) */ |
1028 |
} |
} |