diff options
Diffstat (limited to 'init.c')
-rw-r--r-- | init.c | 1045 |
1 files changed, 0 insertions, 1045 deletions
diff --git a/init.c b/init.c deleted file mode 100644 index 068e1df16..000000000 --- a/init.c +++ /dev/null | |||
@@ -1,1045 +0,0 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini init implementation for busybox | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. | ||
7 | * Adjusted by so many folks, it's impossible to keep track. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | /* Turn this on to disable all the dangerous | ||
26 | rebooting stuff when debugging. | ||
27 | #define DEBUG_INIT | ||
28 | */ | ||
29 | |||
30 | #include <stdio.h> | ||
31 | #include <stdlib.h> | ||
32 | #include <errno.h> | ||
33 | #include <paths.h> | ||
34 | #include <signal.h> | ||
35 | #include <stdarg.h> | ||
36 | #include <string.h> | ||
37 | #include <termios.h> | ||
38 | #include <unistd.h> | ||
39 | #include <limits.h> | ||
40 | #include <sys/fcntl.h> | ||
41 | #include <sys/ioctl.h> | ||
42 | #include <sys/mount.h> | ||
43 | #include <sys/types.h> | ||
44 | #include <sys/wait.h> | ||
45 | #include "busybox.h" | ||
46 | #ifdef BB_SYSLOGD | ||
47 | # include <sys/syslog.h> | ||
48 | #endif | ||
49 | |||
50 | |||
51 | /* From <linux/vt.h> */ | ||
52 | struct vt_stat { | ||
53 | unsigned short v_active; /* active vt */ | ||
54 | unsigned short v_signal; /* signal to send */ | ||
55 | unsigned short v_state; /* vt bitmask */ | ||
56 | }; | ||
57 | static const int VT_GETSTATE = 0x5603; /* get global vt state info */ | ||
58 | |||
59 | /* From <linux/serial.h> */ | ||
60 | struct serial_struct { | ||
61 | int type; | ||
62 | int line; | ||
63 | int port; | ||
64 | int irq; | ||
65 | int flags; | ||
66 | int xmit_fifo_size; | ||
67 | int custom_divisor; | ||
68 | int baud_base; | ||
69 | unsigned short close_delay; | ||
70 | char reserved_char[2]; | ||
71 | int hub6; | ||
72 | unsigned short closing_wait; /* time to wait before closing */ | ||
73 | unsigned short closing_wait2; /* no longer used... */ | ||
74 | int reserved[4]; | ||
75 | }; | ||
76 | |||
77 | |||
78 | |||
79 | #ifndef RB_HALT_SYSTEM | ||
80 | static const int RB_HALT_SYSTEM = 0xcdef0123; | ||
81 | static const int RB_ENABLE_CAD = 0x89abcdef; | ||
82 | static const int RB_DISABLE_CAD = 0; | ||
83 | #define RB_POWER_OFF 0x4321fedc | ||
84 | static const int RB_AUTOBOOT = 0x01234567; | ||
85 | #endif | ||
86 | |||
87 | #if (__GNU_LIBRARY__ > 5) || defined(__dietlibc__) | ||
88 | #include <sys/reboot.h> | ||
89 | #define init_reboot(magic) reboot(magic) | ||
90 | #else | ||
91 | #define init_reboot(magic) reboot(0xfee1dead, 672274793, magic) | ||
92 | #endif | ||
93 | |||
94 | #ifndef _PATH_STDPATH | ||
95 | #define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" | ||
96 | #endif | ||
97 | |||
98 | |||
99 | #if defined BB_FEATURE_INIT_COREDUMPS | ||
100 | /* | ||
101 | * When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called | ||
102 | * before processes are spawned to set core file size as unlimited. | ||
103 | * This is for debugging only. Don't use this is production, unless | ||
104 | * you want core dumps lying about.... | ||
105 | */ | ||
106 | #define CORE_ENABLE_FLAG_FILE "/.init_enable_core" | ||
107 | #include <sys/resource.h> | ||
108 | #include <sys/time.h> | ||
109 | #endif | ||
110 | |||
111 | #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) | ||
112 | |||
113 | #if __GNU_LIBRARY__ > 5 | ||
114 | #include <sys/kdaemon.h> | ||
115 | #else | ||
116 | extern int bdflush (int func, long int data); | ||
117 | #endif | ||
118 | |||
119 | |||
120 | #define SHELL "/bin/sh" /* Default shell */ | ||
121 | #define LOGIN_SHELL "-" SHELL /* Default login shell */ | ||
122 | #define INITTAB "/etc/inittab" /* inittab file location */ | ||
123 | #ifndef INIT_SCRIPT | ||
124 | #define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */ | ||
125 | #endif | ||
126 | |||
127 | #define MAXENV 16 /* Number of env. vars */ | ||
128 | //static const int MAXENV = 16; /* Number of env. vars */ | ||
129 | static const int LOG = 0x1; | ||
130 | static const int CONSOLE = 0x2; | ||
131 | |||
132 | /* Allowed init action types */ | ||
133 | typedef enum { | ||
134 | SYSINIT = 1, | ||
135 | RESPAWN, | ||
136 | ASKFIRST, | ||
137 | WAIT, | ||
138 | ONCE, | ||
139 | CTRLALTDEL, | ||
140 | SHUTDOWN | ||
141 | } initActionEnum; | ||
142 | |||
143 | /* A mapping between "inittab" action name strings and action type codes. */ | ||
144 | typedef struct initActionType { | ||
145 | const char *name; | ||
146 | initActionEnum action; | ||
147 | } initActionType; | ||
148 | |||
149 | static const struct initActionType actions[] = { | ||
150 | {"sysinit", SYSINIT}, | ||
151 | {"respawn", RESPAWN}, | ||
152 | {"askfirst", ASKFIRST}, | ||
153 | {"wait", WAIT}, | ||
154 | {"once", ONCE}, | ||
155 | {"ctrlaltdel", CTRLALTDEL}, | ||
156 | {"shutdown", SHUTDOWN}, | ||
157 | {0, 0} | ||
158 | }; | ||
159 | |||
160 | /* Set up a linked list of initActions, to be read from inittab */ | ||
161 | typedef struct initActionTag initAction; | ||
162 | struct initActionTag { | ||
163 | pid_t pid; | ||
164 | char process[256]; | ||
165 | char console[256]; | ||
166 | initAction *nextPtr; | ||
167 | initActionEnum action; | ||
168 | }; | ||
169 | static initAction *initActionList = NULL; | ||
170 | |||
171 | |||
172 | static char *secondConsole = VC_2; | ||
173 | static char *thirdConsole = VC_3; | ||
174 | static char *fourthConsole = VC_4; | ||
175 | static char *log = VC_5; | ||
176 | static int kernelVersion = 0; | ||
177 | static char termType[32] = "TERM=linux"; | ||
178 | static char console[32] = _PATH_CONSOLE; | ||
179 | |||
180 | static void delete_initAction(initAction * action); | ||
181 | |||
182 | static void loop_forever(void) | ||
183 | { | ||
184 | while (1) | ||
185 | sleep (1); | ||
186 | } | ||
187 | |||
188 | /* Print a message to the specified device. | ||
189 | * Device may be bitwise-or'd from LOG | CONSOLE */ | ||
190 | static void message(int device, char *fmt, ...) | ||
191 | __attribute__ ((format (printf, 2, 3))); | ||
192 | static void message(int device, char *fmt, ...) | ||
193 | { | ||
194 | va_list arguments; | ||
195 | int fd; | ||
196 | |||
197 | #ifdef BB_SYSLOGD | ||
198 | |||
199 | /* Log the message to syslogd */ | ||
200 | if (device & LOG) { | ||
201 | char msg[1024]; | ||
202 | |||
203 | va_start(arguments, fmt); | ||
204 | vsnprintf(msg, sizeof(msg), fmt, arguments); | ||
205 | va_end(arguments); | ||
206 | syslog_msg(LOG_USER, LOG_USER|LOG_INFO, msg); | ||
207 | } | ||
208 | #else | ||
209 | static int log_fd = -1; | ||
210 | |||
211 | /* Take full control of the log tty, and never close it. | ||
212 | * It's mine, all mine! Muhahahaha! */ | ||
213 | if (log_fd < 0) { | ||
214 | if (log == NULL) { | ||
215 | /* don't even try to log, because there is no such console */ | ||
216 | log_fd = -2; | ||
217 | /* log to main console instead */ | ||
218 | device = CONSOLE; | ||
219 | } else if ((log_fd = device_open(log, O_RDWR|O_NDELAY)) < 0) { | ||
220 | log_fd = -2; | ||
221 | fprintf(stderr, "Bummer, can't write to log on %s!\r\n", log); | ||
222 | log = NULL; | ||
223 | device = CONSOLE; | ||
224 | } | ||
225 | } | ||
226 | if ((device & LOG) && (log_fd >= 0)) { | ||
227 | va_start(arguments, fmt); | ||
228 | vdprintf(log_fd, fmt, arguments); | ||
229 | va_end(arguments); | ||
230 | } | ||
231 | #endif | ||
232 | |||
233 | if (device & CONSOLE) { | ||
234 | /* Always send console messages to /dev/console so people will see them. */ | ||
235 | if ( | ||
236 | (fd = | ||
237 | device_open(_PATH_CONSOLE, | ||
238 | O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0) { | ||
239 | va_start(arguments, fmt); | ||
240 | vdprintf(fd, fmt, arguments); | ||
241 | va_end(arguments); | ||
242 | close(fd); | ||
243 | } else { | ||
244 | fprintf(stderr, "Bummer, can't print: "); | ||
245 | va_start(arguments, fmt); | ||
246 | vfprintf(stderr, fmt, arguments); | ||
247 | va_end(arguments); | ||
248 | } | ||
249 | } | ||
250 | } | ||
251 | |||
252 | /* Set terminal settings to reasonable defaults */ | ||
253 | static void set_term(int fd) | ||
254 | { | ||
255 | struct termios tty; | ||
256 | |||
257 | tcgetattr(fd, &tty); | ||
258 | |||
259 | /* set control chars */ | ||
260 | tty.c_cc[VINTR] = 3; /* C-c */ | ||
261 | tty.c_cc[VQUIT] = 28; /* C-\ */ | ||
262 | tty.c_cc[VERASE] = 127; /* C-? */ | ||
263 | tty.c_cc[VKILL] = 21; /* C-u */ | ||
264 | tty.c_cc[VEOF] = 4; /* C-d */ | ||
265 | tty.c_cc[VSTART] = 17; /* C-q */ | ||
266 | tty.c_cc[VSTOP] = 19; /* C-s */ | ||
267 | tty.c_cc[VSUSP] = 26; /* C-z */ | ||
268 | |||
269 | /* use line dicipline 0 */ | ||
270 | tty.c_line = 0; | ||
271 | |||
272 | /* Make it be sane */ | ||
273 | tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD; | ||
274 | tty.c_cflag |= CREAD|HUPCL|CLOCAL; | ||
275 | |||
276 | |||
277 | /* input modes */ | ||
278 | tty.c_iflag = ICRNL | IXON | IXOFF; | ||
279 | |||
280 | /* output modes */ | ||
281 | tty.c_oflag = OPOST | ONLCR; | ||
282 | |||
283 | /* local modes */ | ||
284 | tty.c_lflag = | ||
285 | ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; | ||
286 | |||
287 | tcsetattr(fd, TCSANOW, &tty); | ||
288 | } | ||
289 | |||
290 | /* How much memory does this machine have? | ||
291 | Units are kBytes to avoid overflow on 4GB machines */ | ||
292 | static int check_free_memory(void) | ||
293 | { | ||
294 | struct sysinfo info; | ||
295 | unsigned int result, u, s=10; | ||
296 | |||
297 | if (sysinfo(&info) != 0) { | ||
298 | perror_msg("Error checking free memory"); | ||
299 | return -1; | ||
300 | } | ||
301 | |||
302 | /* Kernels 2.0.x and 2.2.x return info.mem_unit==0 with values in bytes. | ||
303 | * Kernels 2.4.0 return info.mem_unit in bytes. */ | ||
304 | u = info.mem_unit; | ||
305 | if (u==0) u=1; | ||
306 | while ( (u&1) == 0 && s > 0 ) { u>>=1; s--; } | ||
307 | result = (info.totalram>>s) + (info.totalswap>>s); | ||
308 | result = result*u; | ||
309 | if (result < 0) result = INT_MAX; | ||
310 | return result; | ||
311 | } | ||
312 | |||
313 | static void console_init(void) | ||
314 | { | ||
315 | int fd; | ||
316 | int tried_devcons = 0; | ||
317 | int tried_vtprimary = 0; | ||
318 | struct vt_stat vt; | ||
319 | struct serial_struct sr; | ||
320 | char *s; | ||
321 | |||
322 | if ((s = getenv("TERM")) != NULL) { | ||
323 | snprintf(termType, sizeof(termType) - 1, "TERM=%s", s); | ||
324 | } | ||
325 | |||
326 | if ((s = getenv("CONSOLE")) != NULL) { | ||
327 | safe_strncpy(console, s, sizeof(console)); | ||
328 | } | ||
329 | #if #cpu(sparc) | ||
330 | /* sparc kernel supports console=tty[ab] parameter which is also | ||
331 | * passed to init, so catch it here */ | ||
332 | else if ((s = getenv("console")) != NULL) { | ||
333 | /* remap tty[ab] to /dev/ttyS[01] */ | ||
334 | if (strcmp(s, "ttya") == 0) | ||
335 | safe_strncpy(console, SC_0, sizeof(console)); | ||
336 | else if (strcmp(s, "ttyb") == 0) | ||
337 | safe_strncpy(console, SC_1, sizeof(console)); | ||
338 | } | ||
339 | #endif | ||
340 | else { | ||
341 | /* 2.2 kernels: identify the real console backend and try to use it */ | ||
342 | if (ioctl(0, TIOCGSERIAL, &sr) == 0) { | ||
343 | /* this is a serial console */ | ||
344 | snprintf(console, sizeof(console) - 1, SC_FORMAT, sr.line); | ||
345 | } else if (ioctl(0, VT_GETSTATE, &vt) == 0) { | ||
346 | /* this is linux virtual tty */ | ||
347 | snprintf(console, sizeof(console) - 1, VC_FORMAT, vt.v_active); | ||
348 | } else { | ||
349 | safe_strncpy(console, _PATH_CONSOLE, sizeof(console)); | ||
350 | tried_devcons++; | ||
351 | } | ||
352 | } | ||
353 | |||
354 | while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0) { | ||
355 | /* Can't open selected console -- try /dev/console */ | ||
356 | if (!tried_devcons) { | ||
357 | tried_devcons++; | ||
358 | safe_strncpy(console, _PATH_CONSOLE, sizeof(console)); | ||
359 | continue; | ||
360 | } | ||
361 | /* Can't open selected console -- try vt1 */ | ||
362 | if (!tried_vtprimary) { | ||
363 | tried_vtprimary++; | ||
364 | safe_strncpy(console, VC_1, sizeof(console)); | ||
365 | continue; | ||
366 | } | ||
367 | break; | ||
368 | } | ||
369 | if (fd < 0) { | ||
370 | /* Perhaps we should panic here? */ | ||
371 | safe_strncpy(console, "/dev/null", sizeof(console)); | ||
372 | } else { | ||
373 | /* check for serial console and disable logging to tty5 & running a | ||
374 | * shell to tty2-4 */ | ||
375 | if (ioctl(0, TIOCGSERIAL, &sr) == 0) { | ||
376 | log = NULL; | ||
377 | secondConsole = NULL; | ||
378 | thirdConsole = NULL; | ||
379 | fourthConsole = NULL; | ||
380 | /* Force the TERM setting to vt102 for serial console -- | ||
381 | * iff TERM is set to linux (the default) */ | ||
382 | if (strcmp( termType, "TERM=linux" ) == 0) | ||
383 | safe_strncpy(termType, "TERM=vt102", sizeof(termType)); | ||
384 | message(LOG | CONSOLE, | ||
385 | "serial console detected. Disabling virtual terminals.\r\n"); | ||
386 | } | ||
387 | close(fd); | ||
388 | } | ||
389 | message(LOG, "console=%s\n", console); | ||
390 | } | ||
391 | |||
392 | static void fixup_argv(int argc, char **argv, char *new_argv0) | ||
393 | { | ||
394 | int len; | ||
395 | /* Fix up argv[0] to be certain we claim to be init */ | ||
396 | len = strlen(argv[0]); | ||
397 | memset(argv[0], 0, len); | ||
398 | strncpy(argv[0], new_argv0, len); | ||
399 | |||
400 | /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */ | ||
401 | len = 1; | ||
402 | while (argc > len) { | ||
403 | memset(argv[len], 0, strlen(argv[len])); | ||
404 | len++; | ||
405 | } | ||
406 | } | ||
407 | |||
408 | |||
409 | static pid_t run(char *command, char *terminal, int get_enter) | ||
410 | { | ||
411 | int i, j; | ||
412 | int fd; | ||
413 | pid_t pid; | ||
414 | char *tmpCmd, *s; | ||
415 | char *cmd[255], *cmdpath; | ||
416 | char buf[255]; | ||
417 | struct stat sb; | ||
418 | static const char press_enter[] = | ||
419 | |||
420 | #ifdef CUSTOMIZED_BANNER | ||
421 | #include CUSTOMIZED_BANNER | ||
422 | #endif | ||
423 | |||
424 | "\nPlease press Enter to activate this console. "; | ||
425 | char *environment[MAXENV+1] = { | ||
426 | termType, | ||
427 | "HOME=/", | ||
428 | "PATH=/usr/bin:/bin:/usr/sbin:/sbin", | ||
429 | "SHELL=" SHELL, | ||
430 | "USER=root", | ||
431 | NULL | ||
432 | }; | ||
433 | |||
434 | /* inherit environment to the child, merging our values -andy */ | ||
435 | for (i=0; environ[i]; i++) { | ||
436 | for (j=0; environment[j]; j++) { | ||
437 | s = strchr(environment[j], '='); | ||
438 | if (!strncmp(environ[i], environment[j], s - environment[j])) | ||
439 | break; | ||
440 | } | ||
441 | if (!environment[j]) { | ||
442 | environment[j++] = environ[i]; | ||
443 | environment[j] = NULL; | ||
444 | } | ||
445 | } | ||
446 | |||
447 | if ((pid = fork()) == 0) { | ||
448 | /* Clean up */ | ||
449 | ioctl(0, TIOCNOTTY, 0); | ||
450 | close(0); | ||
451 | close(1); | ||
452 | close(2); | ||
453 | setsid(); | ||
454 | |||
455 | /* Reset signal handlers set for parent process */ | ||
456 | signal(SIGUSR1, SIG_DFL); | ||
457 | signal(SIGUSR2, SIG_DFL); | ||
458 | signal(SIGINT, SIG_DFL); | ||
459 | signal(SIGTERM, SIG_DFL); | ||
460 | signal(SIGHUP, SIG_DFL); | ||
461 | |||
462 | if ((fd = device_open(terminal, O_RDWR)) < 0) { | ||
463 | if (stat(terminal, &sb) != 0) { | ||
464 | message(LOG | CONSOLE, "device '%s' does not exist.\n", | ||
465 | terminal); | ||
466 | exit(1); | ||
467 | } | ||
468 | message(LOG | CONSOLE, "Bummer, can't open %s\r\n", terminal); | ||
469 | exit(1); | ||
470 | } | ||
471 | dup2(fd, 0); | ||
472 | dup2(fd, 1); | ||
473 | dup2(fd, 2); | ||
474 | ioctl(0, TIOCSCTTY, 1); | ||
475 | tcsetpgrp(0, getpgrp()); | ||
476 | set_term(0); | ||
477 | |||
478 | /* See if any special /bin/sh requiring characters are present */ | ||
479 | if (strpbrk(command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) { | ||
480 | cmd[0] = SHELL; | ||
481 | cmd[1] = "-c"; | ||
482 | strcpy(buf, "exec "); | ||
483 | strncat(buf, command, sizeof(buf) - strlen(buf) - 1); | ||
484 | cmd[2] = buf; | ||
485 | cmd[3] = NULL; | ||
486 | } else { | ||
487 | /* Convert command (char*) into cmd (char**, one word per string) */ | ||
488 | for (tmpCmd = command, i = 0; | ||
489 | (tmpCmd = strsep(&command, " \t")) != NULL;) { | ||
490 | if (*tmpCmd != '\0') { | ||
491 | cmd[i] = tmpCmd; | ||
492 | tmpCmd++; | ||
493 | i++; | ||
494 | } | ||
495 | } | ||
496 | cmd[i] = NULL; | ||
497 | } | ||
498 | |||
499 | cmdpath = cmd[0]; | ||
500 | |||
501 | /* | ||
502 | Interactive shells want to see a dash in argv[0]. This | ||
503 | typically is handled by login, argv will be setup this | ||
504 | way if a dash appears at the front of the command path | ||
505 | (like "-/bin/sh"). | ||
506 | */ | ||
507 | |||
508 | if (*cmdpath == '-') { | ||
509 | |||
510 | /* skip over the dash */ | ||
511 | ++cmdpath; | ||
512 | |||
513 | /* find the last component in the command pathname */ | ||
514 | s = get_last_path_component(cmdpath); | ||
515 | |||
516 | /* make a new argv[0] */ | ||
517 | if ((cmd[0] = malloc(strlen(s)+2)) == NULL) { | ||
518 | message(LOG | CONSOLE, "malloc failed"); | ||
519 | cmd[0] = cmdpath; | ||
520 | } else { | ||
521 | cmd[0][0] = '-'; | ||
522 | strcpy(cmd[0]+1, s); | ||
523 | } | ||
524 | } | ||
525 | |||
526 | if (get_enter == TRUE) { | ||
527 | /* | ||
528 | * Save memory by not exec-ing anything large (like a shell) | ||
529 | * before the user wants it. This is critical if swap is not | ||
530 | * enabled and the system has low memory. Generally this will | ||
531 | * be run on the second virtual console, and the first will | ||
532 | * be allowed to start a shell or whatever an init script | ||
533 | * specifies. | ||
534 | */ | ||
535 | #ifdef DEBUG_INIT | ||
536 | message(LOG, "Waiting for enter to start '%s' (pid %d, console %s)\r\n", | ||
537 | cmd[0], getpid(), terminal); | ||
538 | #endif | ||
539 | write(fileno(stdout), press_enter, sizeof(press_enter) - 1); | ||
540 | getc(stdin); | ||
541 | } | ||
542 | |||
543 | #ifdef DEBUG_INIT | ||
544 | /* Log the process name and args */ | ||
545 | message(LOG, "Starting pid %d, console %s: '%s'\r\n", | ||
546 | getpid(), terminal, command); | ||
547 | #endif | ||
548 | |||
549 | #if defined BB_FEATURE_INIT_COREDUMPS | ||
550 | if (stat (CORE_ENABLE_FLAG_FILE, &sb) == 0) { | ||
551 | struct rlimit limit; | ||
552 | limit.rlim_cur = RLIM_INFINITY; | ||
553 | limit.rlim_max = RLIM_INFINITY; | ||
554 | setrlimit(RLIMIT_CORE, &limit); | ||
555 | } | ||
556 | #endif | ||
557 | |||
558 | /* Now run it. The new program will take over this PID, | ||
559 | * so nothing further in init.c should be run. */ | ||
560 | execve(cmdpath, cmd, environment); | ||
561 | |||
562 | /* We're still here? Some error happened. */ | ||
563 | message(LOG | CONSOLE, "Bummer, could not run '%s': %s\n", cmdpath, | ||
564 | strerror(errno)); | ||
565 | exit(-1); | ||
566 | } | ||
567 | return pid; | ||
568 | } | ||
569 | |||
570 | static int waitfor(char *command, char *terminal, int get_enter) | ||
571 | { | ||
572 | int status, wpid; | ||
573 | int pid = run(command, terminal, get_enter); | ||
574 | |||
575 | while (1) { | ||
576 | wpid = wait(&status); | ||
577 | if (wpid > 0 && wpid != pid) { | ||
578 | continue; | ||
579 | } | ||
580 | if (wpid == pid) | ||
581 | break; | ||
582 | } | ||
583 | return wpid; | ||
584 | } | ||
585 | |||
586 | /* Make sure there is enough memory to do something useful. * | ||
587 | * Calls "swapon -a" if needed so be sure /etc/fstab is present... */ | ||
588 | static void check_memory(void) | ||
589 | { | ||
590 | struct stat statBuf; | ||
591 | |||
592 | if (check_free_memory() > 1000) | ||
593 | return; | ||
594 | |||
595 | if (stat("/etc/fstab", &statBuf) == 0) { | ||
596 | /* swapon -a requires /proc typically */ | ||
597 | waitfor("mount proc /proc -t proc", console, FALSE); | ||
598 | /* Try to turn on swap */ | ||
599 | waitfor("swapon -a", console, FALSE); | ||
600 | if (check_free_memory() < 1000) | ||
601 | goto goodnight; | ||
602 | } else | ||
603 | goto goodnight; | ||
604 | return; | ||
605 | |||
606 | goodnight: | ||
607 | message(CONSOLE, | ||
608 | "Sorry, your computer does not have enough memory.\r\n"); | ||
609 | loop_forever(); | ||
610 | } | ||
611 | |||
612 | /* Run all commands to be run right before halt/reboot */ | ||
613 | static void run_actions(initActionEnum action) | ||
614 | { | ||
615 | initAction *a, *tmp; | ||
616 | for (a = initActionList; a; a = tmp) { | ||
617 | tmp = a->nextPtr; | ||
618 | if (a->action == action) { | ||
619 | waitfor(a->process, a->console, FALSE); | ||
620 | delete_initAction(a); | ||
621 | } | ||
622 | } | ||
623 | } | ||
624 | |||
625 | |||
626 | #ifndef DEBUG_INIT | ||
627 | static void shutdown_system(void) | ||
628 | { | ||
629 | |||
630 | /* first disable our SIGHUP signal */ | ||
631 | signal(SIGHUP, SIG_DFL); | ||
632 | |||
633 | /* Allow Ctrl-Alt-Del to reboot system. */ | ||
634 | init_reboot(RB_ENABLE_CAD); | ||
635 | |||
636 | message(CONSOLE|LOG, "\r\nThe system is going down NOW !!\r\n"); | ||
637 | sync(); | ||
638 | |||
639 | /* Send signals to every process _except_ pid 1 */ | ||
640 | message(CONSOLE|LOG, "Sending SIGTERM to all processes.\r\n"); | ||
641 | kill(-1, SIGTERM); | ||
642 | sleep(1); | ||
643 | sync(); | ||
644 | |||
645 | message(CONSOLE|LOG, "Sending SIGKILL to all processes.\r\n"); | ||
646 | kill(-1, SIGKILL); | ||
647 | sleep(1); | ||
648 | |||
649 | /* run everything to be run at "shutdown" */ | ||
650 | run_actions(SHUTDOWN); | ||
651 | |||
652 | sync(); | ||
653 | if (kernelVersion > 0 && kernelVersion <= KERNEL_VERSION(2,2,11)) { | ||
654 | /* bdflush, kupdate not needed for kernels >2.2.11 */ | ||
655 | bdflush(1, 0); | ||
656 | sync(); | ||
657 | } | ||
658 | } | ||
659 | |||
660 | static void halt_signal(int sig) | ||
661 | { | ||
662 | shutdown_system(); | ||
663 | message(CONSOLE|LOG, | ||
664 | "The system is halted. Press %s or turn off power\r\n", | ||
665 | (secondConsole == NULL) /* serial console */ | ||
666 | ? "Reset" : "CTRL-ALT-DEL"); | ||
667 | sync(); | ||
668 | |||
669 | /* allow time for last message to reach serial console */ | ||
670 | sleep(2); | ||
671 | |||
672 | if (sig == SIGUSR2 && kernelVersion >= KERNEL_VERSION(2,2,0)) | ||
673 | init_reboot(RB_POWER_OFF); | ||
674 | else | ||
675 | init_reboot(RB_HALT_SYSTEM); | ||
676 | |||
677 | loop_forever(); | ||
678 | } | ||
679 | |||
680 | static void reboot_signal(int sig) | ||
681 | { | ||
682 | shutdown_system(); | ||
683 | message(CONSOLE|LOG, "Please stand by while rebooting the system.\r\n"); | ||
684 | sync(); | ||
685 | |||
686 | /* allow time for last message to reach serial console */ | ||
687 | sleep(2); | ||
688 | |||
689 | init_reboot(RB_AUTOBOOT); | ||
690 | |||
691 | loop_forever(); | ||
692 | } | ||
693 | |||
694 | static void ctrlaltdel_signal(int sig) | ||
695 | { | ||
696 | run_actions(CTRLALTDEL); | ||
697 | } | ||
698 | |||
699 | #endif /* ! DEBUG_INIT */ | ||
700 | |||
701 | static void new_initAction(initActionEnum action, char *process, char *cons) | ||
702 | { | ||
703 | initAction *newAction; | ||
704 | #ifdef BB_FEATURE_INIT_NORMAL_ORDER | ||
705 | initAction *a; | ||
706 | #endif | ||
707 | |||
708 | if (*cons == '\0') | ||
709 | cons = console; | ||
710 | |||
711 | /* If BusyBox detects that a serial console is in use, | ||
712 | * then entries not refering to the console or null devices will _not_ be run. | ||
713 | * The exception to this rule is the null device. | ||
714 | */ | ||
715 | if (secondConsole == NULL && strcmp(cons, console) | ||
716 | && strcmp(cons, "/dev/null")) | ||
717 | return; | ||
718 | if (strcmp(cons, "/dev/null") == 0 && action == ASKFIRST) | ||
719 | return; | ||
720 | |||
721 | newAction = calloc((size_t) (1), sizeof(initAction)); | ||
722 | if (!newAction) { | ||
723 | message(LOG | CONSOLE, "Memory allocation failure\n"); | ||
724 | loop_forever(); | ||
725 | } | ||
726 | #ifdef BB_FEATURE_INIT_NORMAL_ORDER | ||
727 | for (a = initActionList; a && a->nextPtr; a = a->nextPtr) ; | ||
728 | if (a) { | ||
729 | a->nextPtr = newAction; | ||
730 | } else { | ||
731 | initActionList = newAction; | ||
732 | } | ||
733 | #else | ||
734 | newAction->nextPtr = initActionList; | ||
735 | initActionList = newAction; | ||
736 | #endif | ||
737 | strncpy(newAction->process, process, 255); | ||
738 | newAction->action = action; | ||
739 | strncpy(newAction->console, cons, 255); | ||
740 | newAction->pid = 0; | ||
741 | // message(LOG|CONSOLE, "process='%s' action='%d' console='%s'\n", | ||
742 | // newAction->process, newAction->action, newAction->console); | ||
743 | } | ||
744 | |||
745 | static void delete_initAction(initAction * action) | ||
746 | { | ||
747 | initAction *a, *b = NULL; | ||
748 | |||
749 | for (a = initActionList; a; b = a, a = a->nextPtr) { | ||
750 | if (a == action) { | ||
751 | if (b == NULL) { | ||
752 | initActionList = a->nextPtr; | ||
753 | } else { | ||
754 | b->nextPtr = a->nextPtr; | ||
755 | } | ||
756 | free(a); | ||
757 | break; | ||
758 | } | ||
759 | } | ||
760 | } | ||
761 | |||
762 | /* NOTE that if BB_FEATURE_USE_INITTAB is NOT defined, | ||
763 | * then parse_inittab() simply adds in some default | ||
764 | * actions(i.e., runs INIT_SCRIPT and then starts a pair | ||
765 | * of "askfirst" shells). If BB_FEATURE_USE_INITTAB | ||
766 | * _is_ defined, but /etc/inittab is missing, this | ||
767 | * results in the same set of default behaviors. | ||
768 | * */ | ||
769 | static void parse_inittab(void) | ||
770 | { | ||
771 | #ifdef BB_FEATURE_USE_INITTAB | ||
772 | FILE *file; | ||
773 | char buf[256], lineAsRead[256], tmpConsole[256]; | ||
774 | char *id, *runlev, *action, *process, *eol; | ||
775 | const struct initActionType *a = actions; | ||
776 | int foundIt; | ||
777 | |||
778 | |||
779 | file = fopen(INITTAB, "r"); | ||
780 | if (file == NULL) { | ||
781 | /* No inittab file -- set up some default behavior */ | ||
782 | #endif | ||
783 | /* Reboot on Ctrl-Alt-Del */ | ||
784 | new_initAction(CTRLALTDEL, "/sbin/reboot", console); | ||
785 | #ifdef BB_FEATURE_INIT_NORMAL_ORDER | ||
786 | /* Umount all filesystems on halt/reboot */ | ||
787 | new_initAction(SHUTDOWN, "/bin/umount -a -r", console); | ||
788 | /* Swapoff on halt/reboot */ | ||
789 | new_initAction(SHUTDOWN, "/sbin/swapoff -a", console); | ||
790 | #else | ||
791 | /* Swapoff on halt/reboot */ | ||
792 | new_initAction(SHUTDOWN, "/sbin/swapoff -a", console); | ||
793 | /* Umount all filesystems on halt/reboot */ | ||
794 | new_initAction(SHUTDOWN, "/bin/umount -a -r", console); | ||
795 | #endif | ||
796 | /* Askfirst shell on tty1 */ | ||
797 | new_initAction(ASKFIRST, LOGIN_SHELL, console); | ||
798 | /* Askfirst shell on tty2 */ | ||
799 | if (secondConsole != NULL) | ||
800 | new_initAction(ASKFIRST, LOGIN_SHELL, secondConsole); | ||
801 | /* Askfirst shell on tty3 */ | ||
802 | if (thirdConsole != NULL) | ||
803 | new_initAction(ASKFIRST, LOGIN_SHELL, thirdConsole); | ||
804 | /* Askfirst shell on tty4 */ | ||
805 | if (fourthConsole != NULL) | ||
806 | new_initAction(ASKFIRST, LOGIN_SHELL, fourthConsole); | ||
807 | /* sysinit */ | ||
808 | new_initAction(SYSINIT, INIT_SCRIPT, console); | ||
809 | |||
810 | return; | ||
811 | #ifdef BB_FEATURE_USE_INITTAB | ||
812 | } | ||
813 | |||
814 | while (fgets(buf, 255, file) != NULL) { | ||
815 | foundIt = FALSE; | ||
816 | /* Skip leading spaces */ | ||
817 | for (id = buf; *id == ' ' || *id == '\t'; id++); | ||
818 | |||
819 | /* Skip the line if it's a comment */ | ||
820 | if (*id == '#' || *id == '\n') | ||
821 | continue; | ||
822 | |||
823 | /* Trim the trailing \n */ | ||
824 | eol = strrchr(id, '\n'); | ||
825 | if (eol != NULL) | ||
826 | *eol = '\0'; | ||
827 | |||
828 | /* Keep a copy around for posterity's sake (and error msgs) */ | ||
829 | strcpy(lineAsRead, buf); | ||
830 | |||
831 | /* Separate the ID field from the runlevels */ | ||
832 | runlev = strchr(id, ':'); | ||
833 | if (runlev == NULL || *(runlev + 1) == '\0') { | ||
834 | message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); | ||
835 | continue; | ||
836 | } else { | ||
837 | *runlev = '\0'; | ||
838 | ++runlev; | ||
839 | } | ||
840 | |||
841 | /* Separate the runlevels from the action */ | ||
842 | action = strchr(runlev, ':'); | ||
843 | if (action == NULL || *(action + 1) == '\0') { | ||
844 | message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); | ||
845 | continue; | ||
846 | } else { | ||
847 | *action = '\0'; | ||
848 | ++action; | ||
849 | } | ||
850 | |||
851 | /* Separate the action from the process */ | ||
852 | process = strchr(action, ':'); | ||
853 | if (process == NULL || *(process + 1) == '\0') { | ||
854 | message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); | ||
855 | continue; | ||
856 | } else { | ||
857 | *process = '\0'; | ||
858 | ++process; | ||
859 | } | ||
860 | |||
861 | /* Ok, now process it */ | ||
862 | a = actions; | ||
863 | while (a->name != 0) { | ||
864 | if (strcmp(a->name, action) == 0) { | ||
865 | if (*id != '\0') { | ||
866 | strcpy(tmpConsole, "/dev/"); | ||
867 | strncat(tmpConsole, id, 200); | ||
868 | id = tmpConsole; | ||
869 | } | ||
870 | new_initAction(a->action, process, id); | ||
871 | foundIt = TRUE; | ||
872 | } | ||
873 | a++; | ||
874 | } | ||
875 | if (foundIt == TRUE) | ||
876 | continue; | ||
877 | else { | ||
878 | /* Choke on an unknown action */ | ||
879 | message(LOG | CONSOLE, "Bad inittab entry: %s\n", lineAsRead); | ||
880 | } | ||
881 | } | ||
882 | return; | ||
883 | #endif /* BB_FEATURE_USE_INITTAB */ | ||
884 | } | ||
885 | |||
886 | |||
887 | |||
888 | extern int init_main(int argc, char **argv) | ||
889 | { | ||
890 | initAction *a, *tmp; | ||
891 | pid_t wpid; | ||
892 | int status; | ||
893 | |||
894 | #ifndef DEBUG_INIT | ||
895 | /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ | ||
896 | if (getpid() != 1 | ||
897 | #ifdef BB_FEATURE_LINUXRC | ||
898 | && strstr(applet_name, "linuxrc") == NULL | ||
899 | #endif | ||
900 | ) | ||
901 | { | ||
902 | show_usage(); | ||
903 | } | ||
904 | /* Set up sig handlers -- be sure to | ||
905 | * clear all of these in run() */ | ||
906 | signal(SIGUSR1, halt_signal); | ||
907 | signal(SIGUSR2, halt_signal); | ||
908 | signal(SIGINT, ctrlaltdel_signal); | ||
909 | signal(SIGTERM, reboot_signal); | ||
910 | |||
911 | /* Turn off rebooting via CTL-ALT-DEL -- we get a | ||
912 | * SIGINT on CAD so we can shut things down gracefully... */ | ||
913 | init_reboot(RB_DISABLE_CAD); | ||
914 | #endif | ||
915 | |||
916 | /* Figure out what kernel this is running */ | ||
917 | kernelVersion = get_kernel_revision(); | ||
918 | |||
919 | /* Figure out where the default console should be */ | ||
920 | console_init(); | ||
921 | |||
922 | /* Close whatever files are open, and reset the console. */ | ||
923 | close(0); | ||
924 | close(1); | ||
925 | close(2); | ||
926 | set_term(0); | ||
927 | chdir("/"); | ||
928 | setsid(); | ||
929 | |||
930 | /* Make sure PATH is set to something sane */ | ||
931 | putenv("PATH="_PATH_STDPATH); | ||
932 | |||
933 | /* Hello world */ | ||
934 | #ifndef DEBUG_INIT | ||
935 | message( | ||
936 | #if ! defined BB_FEATURE_EXTRA_QUIET | ||
937 | CONSOLE| | ||
938 | #endif | ||
939 | LOG, | ||
940 | "init started: %s\r\n", full_version); | ||
941 | #else | ||
942 | message( | ||
943 | #if ! defined BB_FEATURE_EXTRA_QUIET | ||
944 | CONSOLE| | ||
945 | #endif | ||
946 | LOG, | ||
947 | "init(%d) started: %s\r\n", getpid(), full_version); | ||
948 | #endif | ||
949 | |||
950 | |||
951 | /* Make sure there is enough memory to do something useful. */ | ||
952 | check_memory(); | ||
953 | |||
954 | /* Check if we are supposed to be in single user mode */ | ||
955 | if (argc > 1 && (!strcmp(argv[1], "single") || | ||
956 | !strcmp(argv[1], "-s") || !strcmp(argv[1], "1"))) { | ||
957 | /* Ask first then start a shell on tty2-4 */ | ||
958 | if (secondConsole != NULL) | ||
959 | new_initAction(ASKFIRST, LOGIN_SHELL, secondConsole); | ||
960 | if (thirdConsole != NULL) | ||
961 | new_initAction(ASKFIRST, LOGIN_SHELL, thirdConsole); | ||
962 | if (fourthConsole != NULL) | ||
963 | new_initAction(ASKFIRST, LOGIN_SHELL, fourthConsole); | ||
964 | /* Start a shell on tty1 */ | ||
965 | new_initAction(RESPAWN, LOGIN_SHELL, console); | ||
966 | } else { | ||
967 | /* Not in single user mode -- see what inittab says */ | ||
968 | |||
969 | /* NOTE that if BB_FEATURE_USE_INITTAB is NOT defined, | ||
970 | * then parse_inittab() simply adds in some default | ||
971 | * actions(i.e., runs INIT_SCRIPT and then starts a pair | ||
972 | * of "askfirst" shells */ | ||
973 | parse_inittab(); | ||
974 | } | ||
975 | |||
976 | /* Make the command line just say "init" -- thats all, nothing else */ | ||
977 | fixup_argv(argc, argv, "init"); | ||
978 | |||
979 | /* Now run everything that needs to be run */ | ||
980 | |||
981 | /* First run the sysinit command */ | ||
982 | run_actions(SYSINIT); | ||
983 | /* Next run anything that wants to block */ | ||
984 | run_actions(WAIT); | ||
985 | /* Next run anything to be run only once */ | ||
986 | for (a = initActionList; a; a = tmp) { | ||
987 | tmp = a->nextPtr; | ||
988 | if (a->action == ONCE) { | ||
989 | run(a->process, a->console, FALSE); | ||
990 | /* Now remove the "once" entry from the list */ | ||
991 | delete_initAction(a); | ||
992 | } | ||
993 | } | ||
994 | /* If there is nothing else to do, stop */ | ||
995 | if (initActionList == NULL) { | ||
996 | message(LOG | CONSOLE, | ||
997 | "No more tasks for init -- sleeping forever.\n"); | ||
998 | loop_forever(); | ||
999 | } | ||
1000 | |||
1001 | /* Now run the looping stuff for the rest of forever */ | ||
1002 | while (1) { | ||
1003 | for (a = initActionList; a; a = a->nextPtr) { | ||
1004 | /* Only run stuff with pid==0. If they have | ||
1005 | * a pid, that means they are still running */ | ||
1006 | if (a->pid == 0) { | ||
1007 | switch (a->action) { | ||
1008 | case RESPAWN: | ||
1009 | /* run the respawn stuff */ | ||
1010 | a->pid = run(a->process, a->console, FALSE); | ||
1011 | break; | ||
1012 | case ASKFIRST: | ||
1013 | /* run the askfirst stuff */ | ||
1014 | a->pid = run(a->process, a->console, TRUE); | ||
1015 | break; | ||
1016 | /* silence the compiler's incessant whining */ | ||
1017 | default: | ||
1018 | break; | ||
1019 | } | ||
1020 | } | ||
1021 | } | ||
1022 | /* Wait for a child process to exit */ | ||
1023 | wpid = wait(&status); | ||
1024 | if (wpid > 0) { | ||
1025 | /* Find out who died and clean up their corpse */ | ||
1026 | for (a = initActionList; a; a = a->nextPtr) { | ||
1027 | if (a->pid == wpid) { | ||
1028 | a->pid = 0; | ||
1029 | message(LOG, | ||
1030 | "Process '%s' (pid %d) exited. Scheduling it for restart.\n", | ||
1031 | a->process, wpid); | ||
1032 | } | ||
1033 | } | ||
1034 | } | ||
1035 | sleep(1); | ||
1036 | } | ||
1037 | } | ||
1038 | |||
1039 | /* | ||
1040 | Local Variables: | ||
1041 | c-file-style: "linux" | ||
1042 | c-basic-offset: 4 | ||
1043 | tab-width: 4 | ||
1044 | End: | ||
1045 | */ | ||