9 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
10 |
*/ |
*/ |
11 |
|
|
12 |
#include "busybox.h" |
#include "libbb.h" |
13 |
#include <errno.h> |
#include <syslog.h> |
14 |
#include <paths.h> |
#include <paths.h> |
|
#include <signal.h> |
|
|
#include <sys/ioctl.h> |
|
|
#include <sys/wait.h> |
|
15 |
#include <sys/reboot.h> |
#include <sys/reboot.h> |
16 |
|
|
17 |
#include "init_shared.h" |
/* Was a CONFIG_xxx option. A lot of people were building |
18 |
|
* not fully functional init by switching it on! */ |
19 |
|
#define DEBUG_INIT 0 |
20 |
|
|
21 |
#if ENABLE_SYSLOGD |
#define COMMAND_SIZE 256 |
22 |
# include <sys/syslog.h> |
#define CONSOLE_NAME_SIZE 32 |
23 |
#endif |
#define MAXENV 16 /* Number of env. vars */ |
|
|
|
|
#define INIT_BUFFS_SIZE 256 |
|
|
|
|
|
/* From <linux/vt.h> */ |
|
|
struct vt_stat { |
|
|
unsigned short v_active; /* active vt */ |
|
|
unsigned short v_signal; /* signal to send */ |
|
|
unsigned short v_state; /* vt bitmask */ |
|
|
}; |
|
|
enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */ |
|
|
|
|
|
/* 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]; |
|
|
}; |
|
|
|
|
|
#ifndef _PATH_STDPATH |
|
|
#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" |
|
|
#endif |
|
24 |
|
|
|
#if ENABLE_FEATURE_INIT_COREDUMPS |
|
25 |
/* |
/* |
26 |
* When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called |
* When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called |
27 |
* before processes are spawned to set core file size as unlimited. |
* before processes are spawned to set core file size as unlimited. |
30 |
*/ |
*/ |
31 |
#define CORE_ENABLE_FLAG_FILE "/.init_enable_core" |
#define CORE_ENABLE_FLAG_FILE "/.init_enable_core" |
32 |
#include <sys/resource.h> |
#include <sys/resource.h> |
|
#endif |
|
33 |
|
|
34 |
#define INITTAB "/etc/inittab" /* inittab file location */ |
#define INITTAB "/etc/inittab" /* inittab file location */ |
35 |
#ifndef INIT_SCRIPT |
#ifndef INIT_SCRIPT |
36 |
#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */ |
#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */ |
37 |
#endif |
#endif |
38 |
|
|
|
#define MAXENV 16 /* Number of env. vars */ |
|
|
|
|
|
#define CONSOLE_BUFF_SIZE 32 |
|
|
|
|
39 |
/* Allowed init action types */ |
/* Allowed init action types */ |
40 |
#define SYSINIT 0x001 |
#define SYSINIT 0x01 |
41 |
#define RESPAWN 0x002 |
#define RESPAWN 0x02 |
42 |
#define ASKFIRST 0x004 |
/* like respawn, but wait for <Enter> to be pressed on tty: */ |
43 |
#define WAIT 0x008 |
#define ASKFIRST 0x04 |
44 |
#define ONCE 0x010 |
#define WAIT 0x08 |
45 |
#define CTRLALTDEL 0x020 |
#define ONCE 0x10 |
46 |
#define SHUTDOWN 0x040 |
#define CTRLALTDEL 0x20 |
47 |
#define RESTART 0x080 |
#define SHUTDOWN 0x40 |
48 |
|
#define RESTART 0x80 |
|
/* A mapping between "inittab" action name strings and action type codes. */ |
|
|
struct init_action_type { |
|
|
const char *name; |
|
|
int action; |
|
|
}; |
|
|
|
|
|
static const struct init_action_type actions[] = { |
|
|
{"sysinit", SYSINIT}, |
|
|
{"respawn", RESPAWN}, |
|
|
{"askfirst", ASKFIRST}, |
|
|
{"wait", WAIT}, |
|
|
{"once", ONCE}, |
|
|
{"ctrlaltdel", CTRLALTDEL}, |
|
|
{"shutdown", SHUTDOWN}, |
|
|
{"restart", RESTART}, |
|
|
{0, 0} |
|
|
}; |
|
49 |
|
|
50 |
/* Set up a linked list of init_actions, to be read from inittab */ |
/* Set up a linked list of init_actions, to be read from inittab */ |
51 |
struct init_action { |
struct init_action { |
|
pid_t pid; |
|
|
char command[INIT_BUFFS_SIZE]; |
|
|
char terminal[CONSOLE_BUFF_SIZE]; |
|
52 |
struct init_action *next; |
struct init_action *next; |
53 |
int action; |
pid_t pid; |
54 |
|
uint8_t action_type; |
55 |
|
char terminal[CONSOLE_NAME_SIZE]; |
56 |
|
char command[COMMAND_SIZE]; |
57 |
}; |
}; |
58 |
|
|
59 |
/* Static variables */ |
/* Static variables */ |
60 |
static struct init_action *init_action_list = NULL; |
static struct init_action *init_action_list = NULL; |
|
static char console[CONSOLE_BUFF_SIZE] = CONSOLE_DEV; |
|
61 |
|
|
62 |
#if !ENABLE_SYSLOGD |
static const char *log_console = VC_5; |
|
static char *log_console = VC_5; |
|
|
#endif |
|
|
#if !ENABLE_DEBUG_INIT |
|
|
static sig_atomic_t got_cont = 0; |
|
|
#endif |
|
63 |
|
|
64 |
enum { |
enum { |
65 |
LOG = 0x1, |
L_LOG = 0x1, |
66 |
CONSOLE = 0x2, |
L_CONSOLE = 0x2, |
67 |
|
|
68 |
#if ENABLE_FEATURE_EXTRA_QUIET |
#if ENABLE_FEATURE_EXTRA_QUIET |
69 |
MAYBE_CONSOLE = 0x0, |
MAYBE_CONSOLE = 0x0, |
70 |
#else |
#else |
71 |
MAYBE_CONSOLE = CONSOLE, |
MAYBE_CONSOLE = L_CONSOLE, |
72 |
#endif |
#endif |
73 |
|
|
74 |
#ifndef RB_HALT_SYSTEM |
#ifndef RB_HALT_SYSTEM |
80 |
#endif |
#endif |
81 |
}; |
}; |
82 |
|
|
|
static const char * const environment[] = { |
|
|
"HOME=/", |
|
|
"PATH=" _PATH_STDPATH, |
|
|
"SHELL=/bin/sh", |
|
|
"USER=root", |
|
|
NULL |
|
|
}; |
|
|
|
|
83 |
/* Function prototypes */ |
/* Function prototypes */ |
84 |
static void delete_init_action(struct init_action *a); |
static void halt_reboot_pwoff(int sig) NORETURN; |
|
static int waitfor(const struct init_action *a, pid_t pid); |
|
|
#if !ENABLE_DEBUG_INIT |
|
|
static void shutdown_signal(int sig); |
|
|
#endif |
|
85 |
|
|
86 |
#if !ENABLE_DEBUG_INIT |
static void waitfor(pid_t pid) |
87 |
|
{ |
88 |
|
/* waitfor(run(x)): protect against failed fork inside run() */ |
89 |
|
if (pid <= 0) |
90 |
|
return; |
91 |
|
|
92 |
|
/* Wait for any child (prevent zombies from exiting orphaned processes) |
93 |
|
* but exit the loop only when specified one has exited. */ |
94 |
|
while (wait(NULL) != pid) |
95 |
|
continue; |
96 |
|
} |
97 |
|
|
98 |
|
static void loop_forever(void) NORETURN; |
99 |
static void loop_forever(void) |
static void loop_forever(void) |
100 |
{ |
{ |
101 |
while (1) |
while (1) |
102 |
sleep(1); |
sleep(1); |
103 |
} |
} |
|
#endif |
|
104 |
|
|
105 |
/* Print a message to the specified device. |
/* Print a message to the specified device. |
106 |
* Device may be bitwise-or'd from LOG | CONSOLE */ |
* "where" may be bitwise-or'd from L_LOG | L_CONSOLE |
107 |
#if ENABLE_DEBUG_INIT |
* NB: careful, we can be called after vfork! |
108 |
#define messageD message |
*/ |
109 |
#else |
#define messageD(...) do { if (DEBUG_INIT) message(__VA_ARGS__); } while (0) |
110 |
#define messageD(...) do {} while (0) |
static void message(int where, const char *fmt, ...) |
|
#endif |
|
|
static void message(int device, const char *fmt, ...) |
|
111 |
__attribute__ ((format(printf, 2, 3))); |
__attribute__ ((format(printf, 2, 3))); |
112 |
static void message(int device, const char *fmt, ...) |
static void message(int where, const char *fmt, ...) |
113 |
{ |
{ |
|
va_list arguments; |
|
|
int l; |
|
|
RESERVE_CONFIG_BUFFER(msg, 1024); |
|
|
#if !ENABLE_SYSLOGD |
|
114 |
static int log_fd = -1; |
static int log_fd = -1; |
115 |
#endif |
va_list arguments; |
116 |
|
unsigned l; |
117 |
|
char msg[128]; |
118 |
|
|
119 |
msg[0] = '\r'; |
msg[0] = '\r'; |
120 |
va_start(arguments, fmt); |
va_start(arguments, fmt); |
121 |
l = vsnprintf(msg + 1, 1024 - 2, fmt, arguments) + 1; |
l = 1 + vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments); |
122 |
va_end(arguments); |
if (l > sizeof(msg) - 1) |
123 |
|
l = sizeof(msg) - 1; |
124 |
#if ENABLE_SYSLOGD |
msg[l] = '\0'; |
125 |
/* Log the message to syslogd */ |
va_end(arguments); |
126 |
if (device & LOG) { |
|
127 |
/* don`t out "\r\n" */ |
if (ENABLE_FEATURE_INIT_SYSLOG) { |
128 |
openlog(applet_name, 0, LOG_DAEMON); |
if (where & L_LOG) { |
129 |
syslog(LOG_INFO, "%s", msg + 1); |
/* Log the message to syslogd */ |
130 |
closelog(); |
openlog("init", 0, LOG_DAEMON); |
131 |
} |
/* don't print "\r" */ |
132 |
|
syslog(LOG_INFO, "%s", msg + 1); |
133 |
msg[l++] = '\n'; |
closelog(); |
134 |
msg[l] = 0; |
} |
135 |
#else |
msg[l++] = '\n'; |
136 |
|
msg[l] = '\0'; |
137 |
msg[l++] = '\n'; |
} else { |
138 |
msg[l] = 0; |
msg[l++] = '\n'; |
139 |
/* Take full control of the log tty, and never close it. |
msg[l] = '\0'; |
140 |
* It's mine, all mine! Muhahahaha! */ |
/* Take full control of the log tty, and never close it. |
141 |
if (log_fd < 0) { |
* It's mine, all mine! Muhahahaha! */ |
142 |
if ((log_fd = device_open(log_console, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) { |
if (log_fd < 0) { |
143 |
log_fd = -2; |
if (!log_console) { |
144 |
bb_error_msg("bummer, can't write to log on %s!", log_console); |
log_fd = STDERR_FILENO; |
145 |
device = CONSOLE; |
} else { |
146 |
} else { |
log_fd = device_open(log_console, O_WRONLY | O_NONBLOCK | O_NOCTTY); |
147 |
fcntl(log_fd, F_SETFD, FD_CLOEXEC); |
if (log_fd < 0) { |
148 |
|
bb_error_msg("can't log to %s", log_console); |
149 |
|
where = L_CONSOLE; |
150 |
|
} else { |
151 |
|
close_on_exec_on(log_fd); |
152 |
|
} |
153 |
|
} |
154 |
|
} |
155 |
|
if (where & L_LOG) { |
156 |
|
full_write(log_fd, msg, l); |
157 |
|
if (log_fd == STDERR_FILENO) |
158 |
|
return; /* don't print dup messages */ |
159 |
} |
} |
160 |
} |
} |
|
if ((device & LOG) && (log_fd >= 0)) { |
|
|
full_write(log_fd, msg, l); |
|
|
} |
|
|
#endif |
|
161 |
|
|
162 |
if (device & CONSOLE) { |
if (where & L_CONSOLE) { |
163 |
int fd = device_open(CONSOLE_DEV, |
/* Send console messages to console so people will see them. */ |
164 |
O_WRONLY | O_NOCTTY | O_NONBLOCK); |
full_write(STDERR_FILENO, msg, l); |
|
/* Always send console messages to /dev/console so people will see them. */ |
|
|
if (fd >= 0) { |
|
|
full_write(fd, msg, l); |
|
|
close(fd); |
|
|
#if ENABLE_DEBUG_INIT |
|
|
/* all descriptors may be closed */ |
|
|
} else { |
|
|
bb_error_msg("bummer, can't print: "); |
|
|
va_start(arguments, fmt); |
|
|
vfprintf(stderr, fmt, arguments); |
|
|
va_end(arguments); |
|
|
#endif |
|
|
} |
|
165 |
} |
} |
|
RELEASE_CONFIG_BUFFER(msg); |
|
166 |
} |
} |
167 |
|
|
168 |
/* Set terminal settings to reasonable defaults */ |
/* From <linux/serial.h> */ |
169 |
static void set_term(void) |
struct serial_struct { |
170 |
|
int type; |
171 |
|
int line; |
172 |
|
unsigned int port; |
173 |
|
int irq; |
174 |
|
int flags; |
175 |
|
int xmit_fifo_size; |
176 |
|
int custom_divisor; |
177 |
|
int baud_base; |
178 |
|
unsigned short close_delay; |
179 |
|
char io_type; |
180 |
|
char reserved_char[1]; |
181 |
|
int hub6; |
182 |
|
unsigned short closing_wait; /* time to wait before closing */ |
183 |
|
unsigned short closing_wait2; /* no longer used... */ |
184 |
|
unsigned char *iomem_base; |
185 |
|
unsigned short iomem_reg_shift; |
186 |
|
unsigned int port_high; |
187 |
|
unsigned long iomap_base; /* cookie passed into ioremap */ |
188 |
|
int reserved[1]; |
189 |
|
/* Paranoia (imagine 64bit kernel overwriting 32bit userspace stack) */ |
190 |
|
uint32_t bbox_reserved[16]; |
191 |
|
}; |
192 |
|
static void console_init(void) |
193 |
|
{ |
194 |
|
struct serial_struct sr; |
195 |
|
char *s; |
196 |
|
|
197 |
|
s = getenv("CONSOLE"); |
198 |
|
if (!s) |
199 |
|
s = getenv("console"); |
200 |
|
if (s) { |
201 |
|
int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY); |
202 |
|
if (fd >= 0) { |
203 |
|
dup2(fd, STDIN_FILENO); |
204 |
|
dup2(fd, STDOUT_FILENO); |
205 |
|
xmove_fd(fd, STDERR_FILENO); |
206 |
|
} |
207 |
|
messageD(L_LOG, "console='%s'", s); |
208 |
|
} else { |
209 |
|
/* Make sure fd 0,1,2 are not closed |
210 |
|
* (so that they won't be used by future opens) */ |
211 |
|
bb_sanitize_stdio(); |
212 |
|
// Users report problems |
213 |
|
// /* Make sure init can't be blocked by writing to stderr */ |
214 |
|
// fcntl(STDERR_FILENO, F_SETFL, fcntl(STDERR_FILENO, F_GETFL) | O_NONBLOCK); |
215 |
|
} |
216 |
|
|
217 |
|
s = getenv("TERM"); |
218 |
|
if (ioctl(STDIN_FILENO, TIOCGSERIAL, &sr) == 0) { |
219 |
|
/* Force the TERM setting to vt102 for serial console |
220 |
|
* if TERM is set to linux (the default) */ |
221 |
|
if (!s || strcmp(s, "linux") == 0) |
222 |
|
putenv((char*)"TERM=vt102"); |
223 |
|
if (!ENABLE_FEATURE_INIT_SYSLOG) |
224 |
|
log_console = NULL; |
225 |
|
} else if (!s) |
226 |
|
putenv((char*)"TERM=linux"); |
227 |
|
} |
228 |
|
|
229 |
|
/* Set terminal settings to reasonable defaults. |
230 |
|
* NB: careful, we can be called after vfork! */ |
231 |
|
static void set_sane_term(void) |
232 |
{ |
{ |
233 |
struct termios tty; |
struct termios tty; |
234 |
|
|
244 |
tty.c_cc[VSTOP] = 19; /* C-s */ |
tty.c_cc[VSTOP] = 19; /* C-s */ |
245 |
tty.c_cc[VSUSP] = 26; /* C-z */ |
tty.c_cc[VSUSP] = 26; /* C-z */ |
246 |
|
|
247 |
/* use line dicipline 0 */ |
/* use line discipline 0 */ |
248 |
tty.c_line = 0; |
tty.c_line = 0; |
249 |
|
|
250 |
/* Make it be sane */ |
/* Make it be sane */ |
251 |
tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD; |
tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD; |
252 |
tty.c_cflag |= CREAD | HUPCL | CLOCAL; |
tty.c_cflag |= CREAD | HUPCL | CLOCAL; |
253 |
|
|
|
|
|
254 |
/* input modes */ |
/* input modes */ |
255 |
tty.c_iflag = ICRNL | IXON | IXOFF; |
tty.c_iflag = ICRNL | IXON | IXOFF; |
256 |
|
|
261 |
tty.c_lflag = |
tty.c_lflag = |
262 |
ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; |
ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; |
263 |
|
|
264 |
tcsetattr(STDIN_FILENO, TCSANOW, &tty); |
tcsetattr_stdin_TCSANOW(&tty); |
265 |
} |
} |
266 |
|
|
267 |
static void console_init(void) |
/* Open the new terminal device. |
268 |
|
* NB: careful, we can be called after vfork! */ |
269 |
|
static void open_stdio_to_tty(const char* tty_name, int exit_on_failure) |
270 |
|
{ |
271 |
|
/* empty tty_name means "use init's tty", else... */ |
272 |
|
if (tty_name[0]) { |
273 |
|
int fd; |
274 |
|
close(STDIN_FILENO); |
275 |
|
/* fd can be only < 0 or 0: */ |
276 |
|
fd = device_open(tty_name, O_RDWR); |
277 |
|
if (fd) { |
278 |
|
message(L_LOG | L_CONSOLE, "can't open %s: %s", |
279 |
|
tty_name, strerror(errno)); |
280 |
|
if (exit_on_failure) |
281 |
|
_exit(EXIT_FAILURE); |
282 |
|
if (DEBUG_INIT) |
283 |
|
_exit(2); |
284 |
|
/* NB: we don't reach this if we were called after vfork. |
285 |
|
* Thus halt_reboot_pwoff() itself need not be vfork-safe. */ |
286 |
|
halt_reboot_pwoff(SIGUSR1); /* halt the system */ |
287 |
|
} |
288 |
|
dup2(STDIN_FILENO, STDOUT_FILENO); |
289 |
|
dup2(STDIN_FILENO, STDERR_FILENO); |
290 |
|
} |
291 |
|
set_sane_term(); |
292 |
|
} |
293 |
|
|
294 |
|
/* Wrapper around exec: |
295 |
|
* Takes string (max COMMAND_SIZE chars). |
296 |
|
* If chars like '>' detected, execs '[-]/bin/sh -c "exec ......."'. |
297 |
|
* Otherwise splits words on whitespace, deals with leading dash, |
298 |
|
* and uses plain exec(). |
299 |
|
* NB: careful, we can be called after vfork! |
300 |
|
*/ |
301 |
|
static void init_exec(const char *command) |
302 |
{ |
{ |
303 |
int fd; |
char *cmd[COMMAND_SIZE / 2]; |
304 |
int tried = 0; |
char buf[COMMAND_SIZE + 6]; /* COMMAND_SIZE+strlen("exec ")+1 */ |
305 |
struct vt_stat vt; |
int dash = (command[0] == '-' /* maybe? && command[1] == '/' */); |
306 |
struct serial_struct sr; |
|
307 |
char *s; |
/* See if any special /bin/sh requiring characters are present */ |
308 |
|
if (strpbrk(command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) { |
309 |
if ((s = getenv("CONSOLE")) != NULL || (s = getenv("console")) != NULL) { |
strcpy(buf, "exec "); |
310 |
safe_strncpy(console, s, sizeof(console)); |
strcpy(buf + 5, command + dash); /* excluding "-" */ |
311 |
} else { |
/* NB: LIBBB_DEFAULT_LOGIN_SHELL define has leading dash */ |
312 |
/* 2.2 kernels: identify the real console backend and try to use it */ |
cmd[0] = (char*)(LIBBB_DEFAULT_LOGIN_SHELL + !dash); |
313 |
if (ioctl(0, TIOCGSERIAL, &sr) == 0) { |
cmd[1] = (char*)"-c"; |
314 |
/* this is a serial console */ |
cmd[2] = buf; |
315 |
snprintf(console, sizeof(console) - 1, SC_FORMAT, sr.line); |
cmd[3] = NULL; |
|
} else if (ioctl(0, VT_GETSTATE, &vt) == 0) { |
|
|
/* this is linux virtual tty */ |
|
|
snprintf(console, sizeof(console) - 1, VC_FORMAT, vt.v_active); |
|
|
} else { |
|
|
safe_strncpy(console, CONSOLE_DEV, sizeof(console)); |
|
|
tried++; |
|
|
} |
|
|
} |
|
|
|
|
|
while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0 && tried < 2) { |
|
|
/* Can't open selected console -- try |
|
|
logical system console and VT_MASTER */ |
|
|
safe_strncpy(console, (tried == 0 ? CONSOLE_DEV : CURRENT_VC), |
|
|
sizeof(console)); |
|
|
tried++; |
|
|
} |
|
|
if (fd < 0) { |
|
|
/* Perhaps we should panic here? */ |
|
|
#if !ENABLE_SYSLOGD |
|
|
log_console = |
|
|
#endif |
|
|
safe_strncpy(console, bb_dev_null, sizeof(console)); |
|
316 |
} else { |
} else { |
317 |
s = getenv("TERM"); |
/* Convert command (char*) into cmd (char**, one word per string) */ |
318 |
/* check for serial console */ |
char *word, *next; |
319 |
if (ioctl(fd, TIOCGSERIAL, &sr) == 0) { |
int i = 0; |
320 |
/* Force the TERM setting to vt102 for serial console -- |
next = strcpy(buf, command); /* including "-" */ |
321 |
* if TERM is set to linux (the default) */ |
while ((word = strsep(&next, " \t")) != NULL) { |
322 |
if (s == NULL || strcmp(s, "linux") == 0) |
if (*word != '\0') { /* not two spaces/tabs together? */ |
323 |
putenv("TERM=vt102"); |
cmd[i] = word; |
324 |
#if !ENABLE_SYSLOGD |
i++; |
325 |
log_console = console; |
} |
|
#endif |
|
|
} else { |
|
|
if (s == NULL) |
|
|
putenv("TERM=linux"); |
|
326 |
} |
} |
327 |
close(fd); |
cmd[i] = NULL; |
|
} |
|
|
messageD(LOG, "console=%s", console); |
|
|
} |
|
|
|
|
|
static void fixup_argv(int argc, char **argv, char *new_argv0) |
|
|
{ |
|
|
int len; |
|
|
|
|
|
/* Fix up argv[0] to be certain we claim to be init */ |
|
|
len = strlen(argv[0]); |
|
|
memset(argv[0], 0, len); |
|
|
safe_strncpy(argv[0], new_argv0, len + 1); |
|
|
|
|
|
/* Wipe argv[1]-argv[N] so they don't clutter the ps listing */ |
|
|
len = 1; |
|
|
while (argc > len) { |
|
|
memset(argv[len], 0, strlen(argv[len])); |
|
|
len++; |
|
|
} |
|
|
} |
|
|
|
|
|
/* Open the new terminal device */ |
|
|
static void open_new_terminal(const char * const device, const int fail) { |
|
|
struct stat sb; |
|
|
|
|
|
if ((device_open(device, O_RDWR)) < 0) { |
|
|
if (stat(device, &sb) != 0) { |
|
|
message(LOG | CONSOLE, "device '%s' does not exist.", device); |
|
|
} else { |
|
|
message(LOG | CONSOLE, "Bummer, can't open %s", device); |
|
|
} |
|
|
if (fail) |
|
|
_exit(1); |
|
|
/* else */ |
|
|
#if !ENABLE_DEBUG_INIT |
|
|
shutdown_signal(SIGUSR1); |
|
|
#else |
|
|
_exit(2); |
|
|
#endif |
|
328 |
} |
} |
329 |
|
/* If we saw leading "-", it is interactive shell. |
330 |
|
* Try harder to give it a controlling tty. |
331 |
|
* And skip "-" in actual exec call. */ |
332 |
|
if (dash) { |
333 |
|
/* _Attempt_ to make stdin a controlling tty. */ |
334 |
|
if (ENABLE_FEATURE_INIT_SCTTY) |
335 |
|
ioctl(STDIN_FILENO, TIOCSCTTY, 0 /*only try, don't steal*/); |
336 |
|
} |
337 |
|
BB_EXECVP(cmd[0] + dash, cmd); |
338 |
|
message(L_LOG | L_CONSOLE, "cannot run '%s': %s", cmd[0], strerror(errno)); |
339 |
|
/* returns if execvp fails */ |
340 |
} |
} |
341 |
|
|
342 |
|
/* Used only by run_actions */ |
343 |
static pid_t run(const struct init_action *a) |
static pid_t run(const struct init_action *a) |
344 |
{ |
{ |
|
int i; |
|
345 |
pid_t pid; |
pid_t pid; |
|
char *s, *tmpCmd, *cmd[INIT_BUFFS_SIZE], *cmdpath; |
|
|
char buf[INIT_BUFFS_SIZE + 6]; /* INIT_BUFFS_SIZE+strlen("exec ")+1 */ |
|
346 |
sigset_t nmask, omask; |
sigset_t nmask, omask; |
|
static const char press_enter[] = |
|
|
#ifdef CUSTOMIZED_BANNER |
|
|
#include CUSTOMIZED_BANNER |
|
|
#endif |
|
|
"\nPlease press Enter to activate this console. "; |
|
347 |
|
|
348 |
/* Block sigchild while forking. */ |
/* Block sigchild while forking (why?) */ |
349 |
sigemptyset(&nmask); |
sigemptyset(&nmask); |
350 |
sigaddset(&nmask, SIGCHLD); |
sigaddset(&nmask, SIGCHLD); |
351 |
sigprocmask(SIG_BLOCK, &nmask, &omask); |
sigprocmask(SIG_BLOCK, &nmask, &omask); |
352 |
|
if (BB_MMU && (a->action_type & ASKFIRST)) |
353 |
|
pid = fork(); |
354 |
|
else |
355 |
|
pid = vfork(); |
356 |
|
sigprocmask(SIG_SETMASK, &omask, NULL); |
357 |
|
|
358 |
if ((pid = fork()) == 0) { |
if (pid < 0) |
359 |
|
message(L_LOG | L_CONSOLE, "can't fork"); |
360 |
|
if (pid) |
361 |
|
return pid; |
362 |
|
|
363 |
|
/* Child */ |
364 |
|
|
365 |
|
/* Reset signal handlers that were set by the parent process */ |
366 |
|
bb_signals(0 |
367 |
|
+ (1 << SIGUSR1) |
368 |
|
+ (1 << SIGUSR2) |
369 |
|
+ (1 << SIGINT) |
370 |
|
+ (1 << SIGTERM) |
371 |
|
+ (1 << SIGHUP) |
372 |
|
+ (1 << SIGQUIT) |
373 |
|
+ (1 << SIGCONT) |
374 |
|
+ (1 << SIGSTOP) |
375 |
|
+ (1 << SIGTSTP) |
376 |
|
, SIG_DFL); |
377 |
|
|
378 |
/* Clean up */ |
/* Create a new session and make ourself the process |
379 |
close(0); |
* group leader */ |
380 |
close(1); |
setsid(); |
|
close(2); |
|
|
sigprocmask(SIG_SETMASK, &omask, NULL); |
|
|
|
|
|
/* Reset signal handlers that were set by the parent process */ |
|
|
signal(SIGUSR1, SIG_DFL); |
|
|
signal(SIGUSR2, SIG_DFL); |
|
|
signal(SIGINT, SIG_DFL); |
|
|
signal(SIGTERM, SIG_DFL); |
|
|
signal(SIGHUP, SIG_DFL); |
|
|
signal(SIGQUIT, SIG_DFL); |
|
|
signal(SIGCONT, SIG_DFL); |
|
|
signal(SIGSTOP, SIG_DFL); |
|
|
signal(SIGTSTP, SIG_DFL); |
|
|
|
|
|
/* Create a new session and make ourself the process |
|
|
* group leader */ |
|
|
setsid(); |
|
|
|
|
|
/* Open the new terminal device */ |
|
|
open_new_terminal(a->terminal, 1); |
|
|
|
|
|
/* Make sure the terminal will act fairly normal for us */ |
|
|
set_term(); |
|
|
/* Setup stdout, stderr for the new process so |
|
|
* they point to the supplied terminal */ |
|
|
dup(0); |
|
|
dup(0); |
|
|
|
|
|
/* If the init Action requires us to wait, then force the |
|
|
* supplied terminal to be the controlling tty. */ |
|
|
if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { |
|
|
|
|
|
/* Now fork off another process to just hang around */ |
|
|
if ((pid = fork()) < 0) { |
|
|
message(LOG | CONSOLE, "Can't fork!"); |
|
|
_exit(1); |
|
|
} |
|
381 |
|
|
382 |
if (pid > 0) { |
/* Open the new terminal device */ |
383 |
|
open_stdio_to_tty(a->terminal, 1 /* - exit if open fails */); |
384 |
|
|
385 |
/* We are the parent -- wait till the child is done */ |
// NB: do not enable unless you change vfork to fork above |
386 |
signal(SIGINT, SIG_IGN); |
#ifdef BUT_RUN_ACTIONS_ALREADY_DOES_WAITING |
387 |
signal(SIGTSTP, SIG_IGN); |
/* If the init Action requires us to wait, then force the |
388 |
signal(SIGQUIT, SIG_IGN); |
* supplied terminal to be the controlling tty. */ |
389 |
signal(SIGCHLD, SIG_DFL); |
if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { |
390 |
|
/* Now fork off another process to just hang around */ |
391 |
waitfor(NULL, pid); |
pid = fork(); |
392 |
/* See if stealing the controlling tty back is necessary */ |
if (pid < 0) { |
393 |
if (tcgetpgrp(0) != getpid()) |
message(L_LOG | L_CONSOLE, "can't fork"); |
394 |
_exit(0); |
_exit(EXIT_FAILURE); |
395 |
|
} |
396 |
/* Use a temporary process to steal the controlling tty. */ |
|
397 |
if ((pid = fork()) < 0) { |
if (pid > 0) { |
398 |
message(LOG | CONSOLE, "Can't fork!"); |
/* Parent - wait till the child is done */ |
399 |
_exit(1); |
bb_signals(0 |
400 |
} |
+ (1 << SIGINT) |
401 |
if (pid == 0) { |
+ (1 << SIGTSTP) |
402 |
setsid(); |
+ (1 << SIGQUIT) |
403 |
ioctl(0, TIOCSCTTY, 1); |
, SIG_IGN); |
404 |
_exit(0); |
signal(SIGCHLD, SIG_DFL); |
405 |
} |
|
406 |
waitfor(NULL, pid); |
waitfor(pid); |
407 |
_exit(0); |
/* See if stealing the controlling tty back is necessary */ |
408 |
|
if (tcgetpgrp(0) != getpid()) |
409 |
|
_exit(EXIT_SUCCESS); |
410 |
|
|
411 |
|
/* Use a temporary process to steal the controlling tty. */ |
412 |
|
pid = fork(); |
413 |
|
if (pid < 0) { |
414 |
|
message(L_LOG | L_CONSOLE, "can't fork"); |
415 |
|
_exit(EXIT_FAILURE); |
416 |
} |
} |
417 |
|
if (pid == 0) { |
418 |
/* Now fall though to actually execute things */ |
setsid(); |
419 |
} |
ioctl(0, TIOCSCTTY, 1); |
420 |
|
_exit(EXIT_SUCCESS); |
|
/* See if any special /bin/sh requiring characters are present */ |
|
|
if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) { |
|
|
cmd[0] = (char *)DEFAULT_SHELL; |
|
|
cmd[1] = "-c"; |
|
|
cmd[2] = strcat(strcpy(buf, "exec "), a->command); |
|
|
cmd[3] = NULL; |
|
|
} else { |
|
|
/* Convert command (char*) into cmd (char**, one word per string) */ |
|
|
strcpy(buf, a->command); |
|
|
s = buf; |
|
|
for (tmpCmd = buf, i = 0; (tmpCmd = strsep(&s, " \t")) != NULL;) { |
|
|
if (*tmpCmd != '\0') { |
|
|
cmd[i] = tmpCmd; |
|
|
i++; |
|
|
} |
|
421 |
} |
} |
422 |
cmd[i] = NULL; |
waitfor(pid); |
423 |
|
_exit(EXIT_SUCCESS); |
424 |
} |
} |
425 |
|
/* Child - fall though to actually execute things */ |
426 |
cmdpath = cmd[0]; |
} |
|
|
|
|
/* |
|
|
Interactive shells want to see a dash in argv[0]. This |
|
|
typically is handled by login, argv will be setup this |
|
|
way if a dash appears at the front of the command path |
|
|
(like "-/bin/sh"). |
|
|
*/ |
|
|
|
|
|
if (*cmdpath == '-') { |
|
|
|
|
|
/* skip over the dash */ |
|
|
++cmdpath; |
|
|
|
|
|
/* find the last component in the command pathname */ |
|
|
s = bb_get_last_path_component(cmdpath); |
|
|
|
|
|
/* make a new argv[0] */ |
|
|
if ((cmd[0] = malloc(strlen(s) + 2)) == NULL) { |
|
|
message(LOG | CONSOLE, bb_msg_memory_exhausted); |
|
|
cmd[0] = cmdpath; |
|
|
} else { |
|
|
cmd[0][0] = '-'; |
|
|
strcpy(cmd[0] + 1, s); |
|
|
} |
|
|
#if ENABLE_FEATURE_INIT_SCTTY |
|
|
/* Establish this process as session leader and |
|
|
* (attempt) to make the tty (if any) a controlling tty. |
|
|
*/ |
|
|
(void) setsid(); |
|
|
(void) ioctl(0, TIOCSCTTY, 0/*don't steal it*/); |
|
427 |
#endif |
#endif |
|
} |
|
428 |
|
|
429 |
#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) |
/* NB: on NOMMU we can't wait for input in child, so |
430 |
if (a->action & ASKFIRST) { |
* "askfirst" will work the same as "respawn". */ |
431 |
char c; |
if (BB_MMU && (a->action_type & ASKFIRST)) { |
432 |
/* |
static const char press_enter[] ALIGN1 = |
433 |
* Save memory by not exec-ing anything large (like a shell) |
#ifdef CUSTOMIZED_BANNER |
434 |
* before the user wants it. This is critical if swap is not |
#include CUSTOMIZED_BANNER |
|
* enabled and the system has low memory. Generally this will |
|
|
* be run on the second virtual console, and the first will |
|
|
* be allowed to start a shell or whatever an init script |
|
|
* specifies. |
|
|
*/ |
|
|
messageD(LOG, "Waiting for enter to start '%s'" |
|
|
"(pid %d, terminal %s)\n", |
|
|
cmdpath, getpid(), a->terminal); |
|
|
full_write(1, press_enter, sizeof(press_enter) - 1); |
|
|
while (read(0, &c, 1) == 1 && c != '\n') |
|
|
; |
|
|
} |
|
435 |
#endif |
#endif |
436 |
|
"\nPlease press Enter to activate this console. "; |
437 |
|
char c; |
438 |
|
/* |
439 |
|
* Save memory by not exec-ing anything large (like a shell) |
440 |
|
* before the user wants it. This is critical if swap is not |
441 |
|
* enabled and the system has low memory. Generally this will |
442 |
|
* be run on the second virtual console, and the first will |
443 |
|
* be allowed to start a shell or whatever an init script |
444 |
|
* specifies. |
445 |
|
*/ |
446 |
|
messageD(L_LOG, "waiting for enter to start '%s'" |
447 |
|
"(pid %d, tty '%s')\n", |
448 |
|
a->command, getpid(), a->terminal); |
449 |
|
full_write(STDOUT_FILENO, press_enter, sizeof(press_enter) - 1); |
450 |
|
while (safe_read(STDIN_FILENO, &c, 1) == 1 && c != '\n') |
451 |
|
continue; |
452 |
|
} |
453 |
|
|
454 |
/* Log the process name and args */ |
if (ENABLE_FEATURE_INIT_COREDUMPS) { |
455 |
message(LOG, "Starting pid %d, console %s: '%s'", |
struct stat sb; |
456 |
getpid(), a->terminal, cmdpath); |
if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) { |
457 |
|
struct rlimit limit; |
458 |
#if ENABLE_FEATURE_INIT_COREDUMPS |
limit.rlim_cur = RLIM_INFINITY; |
459 |
{ |
limit.rlim_max = RLIM_INFINITY; |
460 |
struct stat sb; |
setrlimit(RLIMIT_CORE, &limit); |
|
if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) { |
|
|
struct rlimit limit; |
|
|
|
|
|
limit.rlim_cur = RLIM_INFINITY; |
|
|
limit.rlim_max = RLIM_INFINITY; |
|
|
setrlimit(RLIMIT_CORE, &limit); |
|
|
} |
|
461 |
} |
} |
|
#endif |
|
|
|
|
|
/* Now run it. The new program will take over this PID, |
|
|
* so nothing further in init.c should be run. */ |
|
|
execv(cmdpath, cmd); |
|
|
|
|
|
/* We're still here? Some error happened. */ |
|
|
message(LOG | CONSOLE, "Bummer, cannot run '%s': %m", cmdpath); |
|
|
_exit(-1); |
|
462 |
} |
} |
463 |
sigprocmask(SIG_SETMASK, &omask, NULL); |
|
464 |
return pid; |
/* Log the process name and args */ |
465 |
|
message(L_LOG, "starting pid %d, tty '%s': '%s'", |
466 |
|
getpid(), a->terminal, a->command); |
467 |
|
|
468 |
|
/* Now run it. The new program will take over this PID, |
469 |
|
* so nothing further in init.c should be run. */ |
470 |
|
init_exec(a->command); |
471 |
|
/* We're still here? Some error happened. */ |
472 |
|
_exit(-1); |
473 |
} |
} |
474 |
|
|
475 |
static int waitfor(const struct init_action *a, pid_t pid) |
static void delete_init_action(struct init_action *action) |
476 |
{ |
{ |
477 |
int runpid; |
struct init_action *a, *b = NULL; |
|
int status, wpid; |
|
478 |
|
|
479 |
runpid = (NULL == a)? pid : run(a); |
for (a = init_action_list; a; b = a, a = a->next) { |
480 |
while (1) { |
if (a == action) { |
481 |
wpid = waitpid(runpid,&status,0); |
if (b == NULL) { |
482 |
if (wpid == runpid) |
init_action_list = a->next; |
483 |
break; |
} else { |
484 |
if (wpid == -1 && errno == ECHILD) { |
b->next = a->next; |
485 |
/* we missed its termination */ |
} |
486 |
|
free(a); |
487 |
break; |
break; |
488 |
} |
} |
|
/* FIXME other errors should maybe trigger an error, but allow |
|
|
* the program to continue */ |
|
489 |
} |
} |
|
return wpid; |
|
490 |
} |
} |
491 |
|
|
492 |
/* Run all commands of a particular type */ |
/* Run all commands of a particular type */ |
493 |
static void run_actions(int action) |
static void run_actions(int action_type) |
494 |
{ |
{ |
495 |
struct init_action *a, *tmp; |
struct init_action *a, *tmp; |
496 |
|
|
497 |
for (a = init_action_list; a; a = tmp) { |
for (a = init_action_list; a; a = tmp) { |
498 |
tmp = a->next; |
tmp = a->next; |
499 |
if (a->action == action) { |
if (a->action_type & action_type) { |
500 |
if (access(a->terminal, R_OK | W_OK)) { |
// Pointless: run() will error out if open of device fails. |
501 |
delete_init_action(a); |
///* a->terminal of "" means "init's console" */ |
502 |
} else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { |
//if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) { |
503 |
waitfor(a, 0); |
// //message(L_LOG | L_CONSOLE, "Device %s cannot be opened in RW mode", a->terminal /*, strerror(errno)*/); |
504 |
|
// delete_init_action(a); |
505 |
|
//} else |
506 |
|
if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { |
507 |
|
waitfor(run(a)); |
508 |
delete_init_action(a); |
delete_init_action(a); |
509 |
} else if (a->action & ONCE) { |
} else if (a->action_type & ONCE) { |
510 |
run(a); |
run(a); |
511 |
delete_init_action(a); |
delete_init_action(a); |
512 |
} else if (a->action & (RESPAWN | ASKFIRST)) { |
} else if (a->action_type & (RESPAWN | ASKFIRST)) { |
513 |
/* Only run stuff with pid==0. If they have |
/* Only run stuff with pid==0. If they have |
514 |
* a pid, that means it is still running */ |
* a pid, that means it is still running */ |
515 |
if (a->pid == 0) { |
if (a->pid == 0) { |
520 |
} |
} |
521 |
} |
} |
522 |
|
|
|
#if !ENABLE_DEBUG_INIT |
|
523 |
static void init_reboot(unsigned long magic) |
static void init_reboot(unsigned long magic) |
524 |
{ |
{ |
525 |
pid_t pid; |
pid_t pid; |
526 |
/* We have to fork here, since the kernel calls do_exit(0) in |
/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS) in |
527 |
* linux/kernel/sys.c, which can cause the machine to panic when |
* linux/kernel/sys.c, which can cause the machine to panic when |
528 |
* the init process is killed.... */ |
* the init process is killed.... */ |
529 |
pid = vfork(); |
pid = vfork(); |
530 |
if (pid == 0) { /* child */ |
if (pid == 0) { /* child */ |
531 |
reboot(magic); |
reboot(magic); |
532 |
_exit(0); |
_exit(EXIT_SUCCESS); |
533 |
} |
} |
534 |
waitpid(pid, NULL, 0); |
waitfor(pid); |
535 |
} |
} |
536 |
|
|
537 |
static void shutdown_system(void) |
static void kill_all_processes(void) |
538 |
{ |
{ |
|
sigset_t block_signals; |
|
|
|
|
539 |
/* run everything to be run at "shutdown". This is done _prior_ |
/* run everything to be run at "shutdown". This is done _prior_ |
540 |
* to killing everything, in case people wish to use scripts to |
* to killing everything, in case people wish to use scripts to |
541 |
* shut things down gracefully... */ |
* shut things down gracefully... */ |
542 |
run_actions(SHUTDOWN); |
run_actions(SHUTDOWN); |
543 |
|
|
544 |
/* first disable all our signals */ |
/* first disable all our signals */ |
545 |
sigemptyset(&block_signals); |
sigprocmask_allsigs(SIG_BLOCK); |
546 |
sigaddset(&block_signals, SIGHUP); |
|
547 |
sigaddset(&block_signals, SIGQUIT); |
message(L_CONSOLE | L_LOG, "The system is going down NOW!"); |
|
sigaddset(&block_signals, SIGCHLD); |
|
|
sigaddset(&block_signals, SIGUSR1); |
|
|
sigaddset(&block_signals, SIGUSR2); |
|
|
sigaddset(&block_signals, SIGINT); |
|
|
sigaddset(&block_signals, SIGTERM); |
|
|
sigaddset(&block_signals, SIGCONT); |
|
|
sigaddset(&block_signals, SIGSTOP); |
|
|
sigaddset(&block_signals, SIGTSTP); |
|
|
sigprocmask(SIG_BLOCK, &block_signals, NULL); |
|
548 |
|
|
549 |
/* Allow Ctrl-Alt-Del to reboot system. */ |
/* Allow Ctrl-Alt-Del to reboot system. */ |
550 |
init_reboot(RB_ENABLE_CAD); |
init_reboot(RB_ENABLE_CAD); |
551 |
|
|
|
message(CONSOLE | LOG, "The system is going down NOW !!"); |
|
|
sync(); |
|
|
|
|
552 |
/* Send signals to every process _except_ pid 1 */ |
/* Send signals to every process _except_ pid 1 */ |
553 |
message(CONSOLE | LOG, init_sending_format, "TERM"); |
message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "TERM"); |
554 |
kill(-1, SIGTERM); |
kill(-1, SIGTERM); |
|
sleep(1); |
|
555 |
sync(); |
sync(); |
|
|
|
|
message(CONSOLE | LOG, init_sending_format, "KILL"); |
|
|
kill(-1, SIGKILL); |
|
556 |
sleep(1); |
sleep(1); |
557 |
|
|
558 |
|
message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "KILL"); |
559 |
|
kill(-1, SIGKILL); |
560 |
sync(); |
sync(); |
561 |
|
sleep(1); |
562 |
} |
} |
563 |
|
|
564 |
static void exec_signal(int sig ATTRIBUTE_UNUSED) |
static void halt_reboot_pwoff(int sig) |
|
{ |
|
|
struct init_action *a, *tmp; |
|
|
sigset_t unblock_signals; |
|
|
|
|
|
for (a = init_action_list; a; a = tmp) { |
|
|
tmp = a->next; |
|
|
if (a->action & RESTART) { |
|
|
shutdown_system(); |
|
|
|
|
|
/* unblock all signals, blocked in shutdown_system() */ |
|
|
sigemptyset(&unblock_signals); |
|
|
sigaddset(&unblock_signals, SIGHUP); |
|
|
sigaddset(&unblock_signals, SIGQUIT); |
|
|
sigaddset(&unblock_signals, SIGCHLD); |
|
|
sigaddset(&unblock_signals, SIGUSR1); |
|
|
sigaddset(&unblock_signals, SIGUSR2); |
|
|
sigaddset(&unblock_signals, SIGINT); |
|
|
sigaddset(&unblock_signals, SIGTERM); |
|
|
sigaddset(&unblock_signals, SIGCONT); |
|
|
sigaddset(&unblock_signals, SIGSTOP); |
|
|
sigaddset(&unblock_signals, SIGTSTP); |
|
|
sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL); |
|
|
|
|
|
/* Close whatever files are open. */ |
|
|
close(0); |
|
|
close(1); |
|
|
close(2); |
|
|
|
|
|
/* Open the new terminal device */ |
|
|
open_new_terminal(a->terminal, 0); |
|
|
|
|
|
/* Make sure the terminal will act fairly normal for us */ |
|
|
set_term(); |
|
|
/* Setup stdout, stderr on the supplied terminal */ |
|
|
dup(0); |
|
|
dup(0); |
|
|
|
|
|
messageD(CONSOLE | LOG, "Trying to re-exec %s", a->command); |
|
|
execl(a->command, a->command, NULL); |
|
|
|
|
|
message(CONSOLE | LOG, "exec of '%s' failed: %m", |
|
|
a->command); |
|
|
sync(); |
|
|
sleep(2); |
|
|
init_reboot(RB_HALT_SYSTEM); |
|
|
loop_forever(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
static void shutdown_signal(int sig) |
|
565 |
{ |
{ |
566 |
char *m; |
const char *m = "halt"; |
567 |
int rb; |
int rb; |
568 |
|
|
569 |
shutdown_system(); |
kill_all_processes(); |
570 |
|
|
571 |
|
rb = RB_HALT_SYSTEM; |
572 |
if (sig == SIGTERM) { |
if (sig == SIGTERM) { |
573 |
m = "reboot"; |
m = "reboot"; |
574 |
rb = RB_AUTOBOOT; |
rb = RB_AUTOBOOT; |
575 |
} else if (sig == SIGUSR2) { |
} else if (sig == SIGUSR2) { |
576 |
m = "poweroff"; |
m = "poweroff"; |
577 |
rb = RB_POWER_OFF; |
rb = RB_POWER_OFF; |
|
} else { |
|
|
m = "halt"; |
|
|
rb = RB_HALT_SYSTEM; |
|
578 |
} |
} |
579 |
message(CONSOLE | LOG, "Requesting system %s.", m); |
message(L_CONSOLE | L_LOG, "Requesting system %s", m); |
|
sync(); |
|
|
|
|
580 |
/* allow time for last message to reach serial console */ |
/* allow time for last message to reach serial console */ |
581 |
sleep(2); |
sleep(2); |
|
|
|
582 |
init_reboot(rb); |
init_reboot(rb); |
583 |
loop_forever(); |
loop_forever(); |
584 |
} |
} |
585 |
|
|
586 |
static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED) |
/* Handler for QUIT - exec "restart" action, |
587 |
|
* else (no such action defined) do nothing */ |
588 |
|
static void exec_restart_action(int sig UNUSED_PARAM) |
589 |
|
{ |
590 |
|
struct init_action *a; |
591 |
|
|
592 |
|
for (a = init_action_list; a; a = a->next) { |
593 |
|
if (a->action_type & RESTART) { |
594 |
|
kill_all_processes(); |
595 |
|
|
596 |
|
/* unblock all signals (blocked in kill_all_processes()) */ |
597 |
|
sigprocmask_allsigs(SIG_UNBLOCK); |
598 |
|
|
599 |
|
/* Open the new terminal device */ |
600 |
|
open_stdio_to_tty(a->terminal, 0 /* - halt if open fails */); |
601 |
|
|
602 |
|
messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command); |
603 |
|
init_exec(a->command); |
604 |
|
sleep(2); |
605 |
|
init_reboot(RB_HALT_SYSTEM); |
606 |
|
loop_forever(); |
607 |
|
} |
608 |
|
} |
609 |
|
} |
610 |
|
|
611 |
|
static void ctrlaltdel_signal(int sig UNUSED_PARAM) |
612 |
{ |
{ |
613 |
run_actions(CTRLALTDEL); |
run_actions(CTRLALTDEL); |
614 |
} |
} |
615 |
|
|
616 |
|
/* The SIGCONT handler is set to record_signo(). |
617 |
|
* It just sets bb_got_signal = SIGCONT. */ |
618 |
|
|
619 |
/* The SIGSTOP & SIGTSTP handler */ |
/* The SIGSTOP & SIGTSTP handler */ |
620 |
static void stop_handler(int sig ATTRIBUTE_UNUSED) |
static void stop_handler(int sig UNUSED_PARAM) |
621 |
{ |
{ |
622 |
int saved_errno = errno; |
int saved_errno = errno; |
623 |
|
|
624 |
got_cont = 0; |
bb_got_signal = 0; |
625 |
while (!got_cont) |
while (bb_got_signal == 0) |
626 |
pause(); |
pause(); |
|
got_cont = 0; |
|
|
errno = saved_errno; |
|
|
} |
|
627 |
|
|
628 |
/* The SIGCONT handler */ |
errno = saved_errno; |
|
static void cont_handler(int sig ATTRIBUTE_UNUSED) |
|
|
{ |
|
|
got_cont = 1; |
|
629 |
} |
} |
630 |
|
|
631 |
#endif /* ! ENABLE_DEBUG_INIT */ |
static void new_init_action(uint8_t action_type, const char *command, const char *cons) |
|
|
|
|
static void new_init_action(int action, const char *command, const char *cons) |
|
632 |
{ |
{ |
633 |
struct init_action *new_action, *a, *last; |
struct init_action *a, *last; |
|
|
|
|
if (*cons == '\0') |
|
|
cons = console; |
|
|
|
|
|
if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST)) |
|
|
return; |
|
634 |
|
|
635 |
new_action = xzalloc(sizeof(struct init_action)); |
// Why? |
636 |
|
// if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST)) |
637 |
|
// return; |
638 |
|
|
639 |
/* Append to the end of the list */ |
/* Append to the end of the list */ |
640 |
for (a = last = init_action_list; a; a = a->next) { |
for (a = last = init_action_list; a; a = a->next) { |
641 |
/* don't enter action if it's already in the list, |
/* don't enter action if it's already in the list, |
642 |
* but do overwrite existing actions */ |
* but do overwrite existing actions */ |
643 |
if ((strcmp(a->command, command) == 0) && |
if ((strcmp(a->command, command) == 0) |
644 |
(strcmp(a->terminal, cons) ==0)) { |
&& (strcmp(a->terminal, cons) == 0) |
645 |
a->action = action; |
) { |
646 |
free(new_action); |
a->action_type = action_type; |
647 |
return; |
return; |
648 |
} |
} |
649 |
last = a; |
last = a; |
650 |
} |
} |
651 |
|
|
652 |
|
a = xzalloc(sizeof(*a)); |
653 |
if (last) { |
if (last) { |
654 |
last->next = new_action; |
last->next = a; |
655 |
} else { |
} else { |
656 |
init_action_list = new_action; |
init_action_list = a; |
|
} |
|
|
strcpy(new_action->command, command); |
|
|
new_action->action = action; |
|
|
strcpy(new_action->terminal, cons); |
|
|
messageD(LOG|CONSOLE, "command='%s' action='%d' terminal='%s'\n", |
|
|
new_action->command, new_action->action, new_action->terminal); |
|
|
} |
|
|
|
|
|
static void delete_init_action(struct init_action *action) |
|
|
{ |
|
|
struct init_action *a, *b = NULL; |
|
|
|
|
|
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; |
|
|
} |
|
657 |
} |
} |
658 |
|
a->action_type = action_type; |
659 |
|
safe_strncpy(a->command, command, sizeof(a->command)); |
660 |
|
safe_strncpy(a->terminal, cons, sizeof(a->terminal)); |
661 |
|
messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n", |
662 |
|
a->command, a->action_type, a->terminal); |
663 |
} |
} |
664 |
|
|
665 |
/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, |
/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined, |
672 |
static void parse_inittab(void) |
static void parse_inittab(void) |
673 |
{ |
{ |
674 |
#if ENABLE_FEATURE_USE_INITTAB |
#if ENABLE_FEATURE_USE_INITTAB |
675 |
FILE *file; |
char *token[4]; |
676 |
char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE]; |
parser_t *parser = config_open2("/etc/inittab", fopen_for_read); |
|
char tmpConsole[CONSOLE_BUFF_SIZE]; |
|
|
char *id, *runlev, *action, *command, *eol; |
|
|
const struct init_action_type *a = actions; |
|
677 |
|
|
678 |
|
if (parser == NULL) |
|
file = fopen(INITTAB, "r"); |
|
|
if (file == NULL) { |
|
|
/* No inittab file -- set up some default behavior */ |
|
679 |
#endif |
#endif |
680 |
|
{ |
681 |
|
/* No inittab file -- set up some default behavior */ |
682 |
/* Reboot on Ctrl-Alt-Del */ |
/* Reboot on Ctrl-Alt-Del */ |
683 |
new_init_action(CTRLALTDEL, "/sbin/reboot", ""); |
new_init_action(CTRLALTDEL, "reboot", ""); |
684 |
/* Umount all filesystems on halt/reboot */ |
/* Umount all filesystems on halt/reboot */ |
685 |
new_init_action(SHUTDOWN, "/bin/umount -a -r", ""); |
new_init_action(SHUTDOWN, "umount -a -r", ""); |
686 |
/* Swapoff on halt/reboot */ |
/* Swapoff on halt/reboot */ |
687 |
if(ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "/sbin/swapoff -a", ""); |
if (ENABLE_SWAPONOFF) |
688 |
/* Prepare to restart init when a HUP is received */ |
new_init_action(SHUTDOWN, "swapoff -a", ""); |
689 |
new_init_action(RESTART, "/sbin/init", ""); |
/* Prepare to restart init when a QUIT is received */ |
690 |
|
new_init_action(RESTART, "init", ""); |
691 |
/* Askfirst shell on tty1-4 */ |
/* Askfirst shell on tty1-4 */ |
692 |
new_init_action(ASKFIRST, bb_default_login_shell, ""); |
new_init_action(ASKFIRST, bb_default_login_shell, ""); |
693 |
|
//TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users |
694 |
new_init_action(ASKFIRST, bb_default_login_shell, VC_2); |
new_init_action(ASKFIRST, bb_default_login_shell, VC_2); |
695 |
new_init_action(ASKFIRST, bb_default_login_shell, VC_3); |
new_init_action(ASKFIRST, bb_default_login_shell, VC_3); |
696 |
new_init_action(ASKFIRST, bb_default_login_shell, VC_4); |
new_init_action(ASKFIRST, bb_default_login_shell, VC_4); |
697 |
/* sysinit */ |
/* sysinit */ |
698 |
new_init_action(SYSINIT, INIT_SCRIPT, ""); |
new_init_action(SYSINIT, INIT_SCRIPT, ""); |
|
|
|
699 |
return; |
return; |
|
#if ENABLE_FEATURE_USE_INITTAB |
|
700 |
} |
} |
701 |
|
|
702 |
while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) { |
#if ENABLE_FEATURE_USE_INITTAB |
703 |
/* Skip leading spaces */ |
/* optional_tty:ignored_runlevel:action:command |
704 |
for (id = buf; *id == ' ' || *id == '\t'; id++); |
* Delims are not to be collapsed and need exactly 4 tokens |
705 |
|
*/ |
706 |
/* Skip the line if it's a comment */ |
while (config_read(parser, token, 4, 0, "#:", |
707 |
if (*id == '#' || *id == '\n') |
PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) { |
708 |
continue; |
/* order must correspond to SYSINIT..RESTART constants */ |
709 |
|
static const char actions[] ALIGN1 = |
710 |
/* Trim the trailing \n */ |
"sysinit\0""respawn\0""askfirst\0""wait\0""once\0" |
711 |
eol = strrchr(id, '\n'); |
"ctrlaltdel\0""shutdown\0""restart\0"; |
712 |
if (eol != NULL) |
int action; |
713 |
*eol = '\0'; |
char *tty = token[0]; |
714 |
|
|
715 |
/* Keep a copy around for posterity's sake (and error msgs) */ |
if (!token[3]) /* less than 4 tokens */ |
716 |
strcpy(lineAsRead, buf); |
goto bad_entry; |
717 |
|
action = index_in_strings(actions, token[2]); |
718 |
/* Separate the ID field from the runlevels */ |
if (action < 0 || !token[3][0]) /* token[3]: command */ |
719 |
runlev = strchr(id, ':'); |
goto bad_entry; |
720 |
if (runlev == NULL || *(runlev + 1) == '\0') { |
/* turn .*TTY -> /dev/TTY */ |
721 |
message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); |
if (tty[0]) { |
722 |
continue; |
if (strncmp(tty, "/dev/", 5) == 0) |
723 |
} else { |
tty += 5; |
724 |
*runlev = '\0'; |
tty = concat_path_file("/dev/", tty); |
725 |
++runlev; |
} |
726 |
} |
new_init_action(1 << action, token[3], tty); |
727 |
|
if (tty[0]) |
728 |
/* Separate the runlevels from the action */ |
free(tty); |
729 |
action = strchr(runlev, ':'); |
continue; |
730 |
if (action == NULL || *(action + 1) == '\0') { |
bad_entry: |
731 |
message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); |
message(L_LOG | L_CONSOLE, "Bad inittab entry at line %d", |
732 |
continue; |
parser->lineno); |
|
} else { |
|
|
*action = '\0'; |
|
|
++action; |
|
|
} |
|
|
|
|
|
/* Separate the action from the command */ |
|
|
command = strchr(action, ':'); |
|
|
if (command == NULL || *(command + 1) == '\0') { |
|
|
message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); |
|
|
continue; |
|
|
} else { |
|
|
*command = '\0'; |
|
|
++command; |
|
|
} |
|
|
|
|
|
/* Ok, now process it */ |
|
|
for (a = actions; a->name != 0; a++) { |
|
|
if (strcmp(a->name, action) == 0) { |
|
|
if (*id != '\0') { |
|
|
if(strncmp(id, "/dev/", 5) == 0) |
|
|
id += 5; |
|
|
strcpy(tmpConsole, "/dev/"); |
|
|
safe_strncpy(tmpConsole + 5, id, |
|
|
CONSOLE_BUFF_SIZE - 5); |
|
|
id = tmpConsole; |
|
|
} |
|
|
new_init_action(a->action, command, id); |
|
|
break; |
|
|
} |
|
|
} |
|
|
if (a->name == 0) { |
|
|
/* Choke on an unknown action */ |
|
|
message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead); |
|
|
} |
|
733 |
} |
} |
734 |
fclose(file); |
config_close(parser); |
735 |
return; |
#endif |
|
#endif /* FEATURE_USE_INITTAB */ |
|
736 |
} |
} |
737 |
|
|
738 |
#if ENABLE_FEATURE_USE_INITTAB |
#if ENABLE_FEATURE_USE_INITTAB |
739 |
static void reload_signal(int sig ATTRIBUTE_UNUSED) |
static void reload_signal(int sig UNUSED_PARAM) |
740 |
{ |
{ |
741 |
struct init_action *a, *tmp; |
struct init_action *a, *tmp; |
742 |
|
|
743 |
message(LOG, "Reloading /etc/inittab"); |
message(L_LOG, "reloading /etc/inittab"); |
744 |
|
|
745 |
/* disable old entrys */ |
/* disable old entrys */ |
746 |
for (a = init_action_list; a; a = a->next ) { |
for (a = init_action_list; a; a = a->next) { |
747 |
a->action = ONCE; |
a->action_type = ONCE; |
748 |
} |
} |
749 |
|
|
750 |
parse_inittab(); |
parse_inittab(); |
751 |
|
|
752 |
|
if (ENABLE_FEATURE_KILL_REMOVED) { |
753 |
|
/* Be nice and send SIGTERM first */ |
754 |
|
for (a = init_action_list; a; a = a->next) { |
755 |
|
pid_t pid = a->pid; |
756 |
|
if ((a->action_type & ONCE) && pid != 0) { |
757 |
|
kill(pid, SIGTERM); |
758 |
|
} |
759 |
|
} |
760 |
|
#if CONFIG_FEATURE_KILL_DELAY |
761 |
|
/* NB: parent will wait in NOMMU case */ |
762 |
|
if ((BB_MMU ? fork() : vfork()) == 0) { /* child */ |
763 |
|
sleep(CONFIG_FEATURE_KILL_DELAY); |
764 |
|
for (a = init_action_list; a; a = a->next) { |
765 |
|
pid_t pid = a->pid; |
766 |
|
if ((a->action_type & ONCE) && pid != 0) { |
767 |
|
kill(pid, SIGKILL); |
768 |
|
} |
769 |
|
} |
770 |
|
_exit(EXIT_SUCCESS); |
771 |
|
} |
772 |
|
#endif |
773 |
|
} |
774 |
|
|
775 |
/* remove unused entrys */ |
/* remove unused entrys */ |
776 |
for (a = init_action_list; a; a = tmp) { |
for (a = init_action_list; a; a = tmp) { |
777 |
tmp = a->next; |
tmp = a->next; |
778 |
if (a->action & (ONCE | SYSINIT | WAIT ) && |
if ((a->action_type & (ONCE | SYSINIT | WAIT)) && a->pid == 0) { |
|
a->pid == 0 ) { |
|
779 |
delete_init_action(a); |
delete_init_action(a); |
780 |
} |
} |
781 |
} |
} |
782 |
run_actions(RESPAWN); |
run_actions(RESPAWN | ASKFIRST); |
|
return; |
|
783 |
} |
} |
784 |
#endif /* FEATURE_USE_INITTAB */ |
#endif |
785 |
|
|
786 |
int init_main(int argc, char **argv) |
int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
787 |
|
int init_main(int argc UNUSED_PARAM, char **argv) |
788 |
{ |
{ |
789 |
struct init_action *a; |
struct init_action *a; |
790 |
pid_t wpid; |
pid_t wpid; |
791 |
|
|
792 |
die_sleep = 30 * 24*60*60; /* if xmalloc will ever die... */ |
die_sleep = 30 * 24*60*60; /* if xmalloc will ever die... */ |
793 |
|
|
794 |
if (argc > 1 && !strcmp(argv[1], "-q")) { |
if (argv[1] && !strcmp(argv[1], "-q")) { |
795 |
return kill(1,SIGHUP); |
return kill(1, SIGHUP); |
796 |
} |
} |
797 |
#if !ENABLE_DEBUG_INIT |
|
798 |
/* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ |
if (!DEBUG_INIT) { |
799 |
if (getpid() != 1 && |
/* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ |
800 |
(!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc"))) |
if (getpid() != 1 |
801 |
{ |
&& (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc")) |
802 |
bb_show_usage(); |
) { |
803 |
|
bb_show_usage(); |
804 |
|
} |
805 |
|
/* Set up sig handlers -- be sure to |
806 |
|
* clear all of these in run() */ |
807 |
|
signal(SIGQUIT, exec_restart_action); |
808 |
|
bb_signals(0 |
809 |
|
+ (1 << SIGUSR1) /* halt */ |
810 |
|
+ (1 << SIGUSR2) /* poweroff */ |
811 |
|
+ (1 << SIGTERM) /* reboot */ |
812 |
|
, halt_reboot_pwoff); |
813 |
|
signal(SIGINT, ctrlaltdel_signal); |
814 |
|
signal(SIGCONT, record_signo); |
815 |
|
bb_signals(0 |
816 |
|
+ (1 << SIGSTOP) |
817 |
|
+ (1 << SIGTSTP) |
818 |
|
, stop_handler); |
819 |
|
|
820 |
|
/* Turn off rebooting via CTL-ALT-DEL -- we get a |
821 |
|
* SIGINT on CAD so we can shut things down gracefully... */ |
822 |
|
init_reboot(RB_DISABLE_CAD); |
823 |
} |
} |
|
/* Set up sig handlers -- be sure to |
|
|
* clear all of these in run() */ |
|
|
signal(SIGHUP, exec_signal); |
|
|
signal(SIGQUIT, exec_signal); |
|
|
signal(SIGUSR1, shutdown_signal); |
|
|
signal(SIGUSR2, shutdown_signal); |
|
|
signal(SIGINT, ctrlaltdel_signal); |
|
|
signal(SIGTERM, shutdown_signal); |
|
|
signal(SIGCONT, cont_handler); |
|
|
signal(SIGSTOP, stop_handler); |
|
|
signal(SIGTSTP, stop_handler); |
|
|
|
|
|
/* Turn off rebooting via CTL-ALT-DEL -- we get a |
|
|
* SIGINT on CAD so we can shut things down gracefully... */ |
|
|
init_reboot(RB_DISABLE_CAD); |
|
|
#endif |
|
824 |
|
|
825 |
/* Figure out where the default console should be */ |
/* Figure out where the default console should be */ |
826 |
console_init(); |
console_init(); |
827 |
|
set_sane_term(); |
828 |
/* Close whatever files are open, and reset the console. */ |
xchdir("/"); |
|
close(0); |
|
|
close(1); |
|
|
close(2); |
|
|
|
|
|
if (device_open(console, O_RDWR | O_NOCTTY) == 0) { |
|
|
set_term(); |
|
|
close(0); |
|
|
} |
|
|
|
|
|
chdir("/"); |
|
829 |
setsid(); |
setsid(); |
|
{ |
|
|
const char * const *e; |
|
|
/* Make sure environs is set to something sane */ |
|
|
for (e = environment; *e; e++) |
|
|
putenv((char *) *e); |
|
|
} |
|
830 |
|
|
831 |
if (argc > 1) setenv("RUNLEVEL", argv[1], 1); |
/* Make sure environs is set to something sane */ |
832 |
|
putenv((char *) "HOME=/"); |
833 |
|
putenv((char *) bb_PATH_root_path); |
834 |
|
putenv((char *) "SHELL=/bin/sh"); |
835 |
|
putenv((char *) "USER=root"); /* needed? why? */ |
836 |
|
|
837 |
|
if (argv[1]) |
838 |
|
xsetenv("RUNLEVEL", argv[1]); |
839 |
|
|
840 |
/* Hello world */ |
/* Hello world */ |
841 |
message(MAYBE_CONSOLE | LOG, "init started: %s", bb_msg_full_version); |
message(MAYBE_CONSOLE | L_LOG, "init started: %s", bb_banner); |
842 |
|
|
843 |
/* Make sure there is enough memory to do something useful. */ |
/* Make sure there is enough memory to do something useful. */ |
844 |
if (ENABLE_SWAPONOFF) { |
if (ENABLE_SWAPONOFF) { |
847 |
if (!sysinfo(&info) && |
if (!sysinfo(&info) && |
848 |
(info.mem_unit ? : 1) * (long long)info.totalram < 1024*1024) |
(info.mem_unit ? : 1) * (long long)info.totalram < 1024*1024) |
849 |
{ |
{ |
850 |
message(CONSOLE,"Low memory: forcing swapon."); |
message(L_CONSOLE, "Low memory, forcing swapon"); |
851 |
/* swapon -a requires /proc typically */ |
/* swapon -a requires /proc typically */ |
852 |
new_init_action(SYSINIT, "/bin/mount -t proc proc /proc", ""); |
new_init_action(SYSINIT, "mount -t proc proc /proc", ""); |
853 |
/* Try to turn on swap */ |
/* Try to turn on swap */ |
854 |
new_init_action(SYSINIT, "/sbin/swapon -a", ""); |
new_init_action(SYSINIT, "swapon -a", ""); |
855 |
run_actions(SYSINIT); /* wait and removing */ |
run_actions(SYSINIT); /* wait and removing */ |
856 |
} |
} |
857 |
} |
} |
858 |
|
|
859 |
/* Check if we are supposed to be in single user mode */ |
/* Check if we are supposed to be in single user mode */ |
860 |
if (argc > 1 |
if (argv[1] |
861 |
&& (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1')) |
&& (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1')) |
862 |
) { |
) { |
863 |
|
/* ??? shouldn't we set RUNLEVEL="b" here? */ |
864 |
/* Start a shell on console */ |
/* Start a shell on console */ |
865 |
new_init_action(RESPAWN, bb_default_login_shell, ""); |
new_init_action(RESPAWN, bb_default_login_shell, ""); |
866 |
} else { |
} else { |
877 |
if (getenv("SELINUX_INIT") == NULL) { |
if (getenv("SELINUX_INIT") == NULL) { |
878 |
int enforce = 0; |
int enforce = 0; |
879 |
|
|
880 |
putenv("SELINUX_INIT=YES"); |
putenv((char*)"SELINUX_INIT=YES"); |
881 |
if (selinux_init_load_policy(&enforce) == 0) { |
if (selinux_init_load_policy(&enforce) == 0) { |
882 |
execv(argv[0], argv); |
BB_EXECVP(argv[0], argv); |
883 |
} else if (enforce > 0) { |
} else if (enforce > 0) { |
884 |
/* SELinux in enforcing mode but load_policy failed */ |
/* SELinux in enforcing mode but load_policy failed */ |
885 |
/* At this point, we probably can't open /dev/console, so log() won't work */ |
message(L_CONSOLE, "cannot load SELinux Policy. " |
886 |
message(CONSOLE,"Unable to load SELinux Policy. Machine is in enforcing mode. Halting now."); |
"Machine is in enforcing mode. Halting now."); |
887 |
exit(1); |
exit(EXIT_FAILURE); |
888 |
} |
} |
889 |
} |
} |
890 |
#endif /* CONFIG_SELINUX */ |
#endif /* CONFIG_SELINUX */ |
891 |
|
|
892 |
/* Make the command line just say "init" -- thats all, nothing else */ |
/* Make the command line just say "init" - thats all, nothing else */ |
893 |
fixup_argv(argc, argv, "init"); |
strncpy(argv[0], "init", strlen(argv[0])); |
894 |
|
/* Wipe argv[1]-argv[N] so they don't clutter the ps listing */ |
895 |
|
while (*++argv) |
896 |
|
memset(*argv, 0, strlen(*argv)); |
897 |
|
|
898 |
/* Now run everything that needs to be run */ |
/* Now run everything that needs to be run */ |
899 |
|
|
906 |
/* Next run anything to be run only once */ |
/* Next run anything to be run only once */ |
907 |
run_actions(ONCE); |
run_actions(ONCE); |
908 |
|
|
|
#if ENABLE_FEATURE_USE_INITTAB |
|
909 |
/* Redefine SIGHUP to reread /etc/inittab */ |
/* Redefine SIGHUP to reread /etc/inittab */ |
910 |
|
#if ENABLE_FEATURE_USE_INITTAB |
911 |
signal(SIGHUP, reload_signal); |
signal(SIGHUP, reload_signal); |
912 |
#else |
#else |
913 |
signal(SIGHUP, SIG_IGN); |
signal(SIGHUP, SIG_IGN); |
914 |
#endif /* FEATURE_USE_INITTAB */ |
#endif |
|
|
|
915 |
|
|
916 |
/* Now run the looping stuff for the rest of forever */ |
/* Now run the looping stuff for the rest of forever */ |
917 |
while (1) { |
while (1) { |
918 |
/* run the respawn stuff */ |
/* run the respawn/askfirst stuff */ |
919 |
run_actions(RESPAWN); |
run_actions(RESPAWN | ASKFIRST); |
|
|
|
|
/* run the askfirst stuff */ |
|
|
run_actions(ASKFIRST); |
|
920 |
|
|
921 |
/* Don't consume all CPU time -- sleep a bit */ |
/* Don't consume all CPU time -- sleep a bit */ |
922 |
sleep(1); |
sleep(1); |
923 |
|
|
924 |
/* Wait for a child process to exit */ |
/* Wait for any child process to exit */ |
925 |
wpid = wait(NULL); |
wpid = wait(NULL); |
926 |
while (wpid > 0) { |
while (wpid > 0) { |
927 |
/* Find out who died and clean up their corpse */ |
/* Find out who died and clean up their corpse */ |
930 |
/* Set the pid to 0 so that the process gets |
/* Set the pid to 0 so that the process gets |
931 |
* restarted by run_actions() */ |
* restarted by run_actions() */ |
932 |
a->pid = 0; |
a->pid = 0; |
933 |
message(LOG, "Process '%s' (pid %d) exited. " |
message(L_LOG, "process '%s' (pid %d) exited. " |
934 |
"Scheduling it for restart.", |
"Scheduling for restart.", |
935 |
a->command, wpid); |
a->command, wpid); |
936 |
} |
} |
937 |
} |
} |
938 |
/* see if anyone else is waiting to be reaped */ |
/* see if anyone else is waiting to be reaped */ |
939 |
wpid = waitpid(-1, NULL, WNOHANG); |
wpid = wait_any_nohang(NULL); |
940 |
} |
} |
941 |
} |
} |
942 |
} |
} |