source: MondoRescue/branches/stable/mindi-busybox/init/init.c@ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File size: 25.5 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini init implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 * Adjusted by so many folks, it's impossible to keep track.
8 *
9 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10 */
11
12#include "libbb.h"
13#include <paths.h>
14//#include <signal.h>
15//#include <sys/ioctl.h>
16//#include <sys/wait.h>
17#include <sys/reboot.h>
18
19#if ENABLE_FEATURE_INIT_SYSLOG
20# include <sys/syslog.h>
21#endif
22
23#define INIT_BUFFS_SIZE 256
24#define CONSOLE_NAME_SIZE 32
25#define MAXENV 16 /* Number of env. vars */
26
27#if ENABLE_FEATURE_INIT_COREDUMPS
28/*
29 * When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called
30 * before processes are spawned to set core file size as unlimited.
31 * This is for debugging only. Don't use this is production, unless
32 * you want core dumps lying about....
33 */
34#define CORE_ENABLE_FLAG_FILE "/.init_enable_core"
35#include <sys/resource.h>
36#endif
37
38#define INITTAB "/etc/inittab" /* inittab file location */
39#ifndef INIT_SCRIPT
40#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */
41#endif
42
43/* Allowed init action types */
44#define SYSINIT 0x001
45#define RESPAWN 0x002
46#define ASKFIRST 0x004
47#define WAIT 0x008
48#define ONCE 0x010
49#define CTRLALTDEL 0x020
50#define SHUTDOWN 0x040
51#define RESTART 0x080
52
53/* A mapping between "inittab" action name strings and action type codes. */
54struct init_action_type {
55 const char *name;
56 int action;
57};
58
59static const struct init_action_type actions[] = {
60 {"sysinit", SYSINIT},
61 {"respawn", RESPAWN},
62 {"askfirst", ASKFIRST},
63 {"wait", WAIT},
64 {"once", ONCE},
65 {"ctrlaltdel", CTRLALTDEL},
66 {"shutdown", SHUTDOWN},
67 {"restart", RESTART},
68 {0, 0}
69};
70
71/* Set up a linked list of init_actions, to be read from inittab */
72struct init_action {
73 struct init_action *next;
74 int action;
75 pid_t pid;
76 char command[INIT_BUFFS_SIZE];
77 char terminal[CONSOLE_NAME_SIZE];
78};
79
80/* Static variables */
81static struct init_action *init_action_list = NULL;
82
83#if !ENABLE_FEATURE_INIT_SYSLOG
84static const char *log_console = VC_5;
85#endif
86#if !ENABLE_DEBUG_INIT
87static sig_atomic_t got_cont = 0;
88#endif
89
90enum {
91 L_LOG = 0x1,
92 L_CONSOLE = 0x2,
93
94#if ENABLE_FEATURE_EXTRA_QUIET
95 MAYBE_CONSOLE = 0x0,
96#else
97 MAYBE_CONSOLE = L_CONSOLE,
98#endif
99
100#ifndef RB_HALT_SYSTEM
101 RB_HALT_SYSTEM = 0xcdef0123, /* FIXME: this overflows enum */
102 RB_ENABLE_CAD = 0x89abcdef,
103 RB_DISABLE_CAD = 0,
104 RB_POWER_OFF = 0x4321fedc,
105 RB_AUTOBOOT = 0x01234567,
106#endif
107};
108
109static const char *const environment[] = {
110 "HOME=/",
111 bb_PATH_root_path,
112 "SHELL=/bin/sh",
113 "USER=root",
114 NULL
115};
116
117/* Function prototypes */
118static void delete_init_action(struct init_action *a);
119static int waitfor(const struct init_action *a, pid_t pid);
120#if !ENABLE_DEBUG_INIT
121static void shutdown_signal(int sig);
122#endif
123
124#if !ENABLE_DEBUG_INIT
125static void loop_forever(void)
126{
127 while (1)
128 sleep(1);
129}
130#endif
131
132/* Print a message to the specified device.
133 * Device may be bitwise-or'd from L_LOG | L_CONSOLE */
134#if ENABLE_DEBUG_INIT
135#define messageD message
136#else
137#define messageD(...) do {} while (0)
138#endif
139static void message(int device, const char *fmt, ...)
140 __attribute__ ((format(printf, 2, 3)));
141static void message(int device, const char *fmt, ...)
142{
143#if !ENABLE_FEATURE_INIT_SYSLOG
144 static int log_fd = -1;
145#endif
146
147 va_list arguments;
148 int l;
149 char msg[128];
150
151 msg[0] = '\r';
152 va_start(arguments, fmt);
153 vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments);
154 va_end(arguments);
155 msg[sizeof(msg) - 2] = '\0';
156 l = strlen(msg);
157
158#if ENABLE_FEATURE_INIT_SYSLOG
159 /* Log the message to syslogd */
160 if (device & L_LOG) {
161 /* don't out "\r" */
162 openlog(applet_name, 0, LOG_DAEMON);
163 syslog(LOG_INFO, "init: %s", msg + 1);
164 closelog();
165 }
166 msg[l++] = '\n';
167 msg[l] = '\0';
168#else
169 msg[l++] = '\n';
170 msg[l] = '\0';
171 /* Take full control of the log tty, and never close it.
172 * It's mine, all mine! Muhahahaha! */
173 if (log_fd < 0) {
174 if (!log_console) {
175 log_fd = 2;
176 } else {
177 log_fd = device_open(log_console, O_WRONLY | O_NONBLOCK | O_NOCTTY);
178 if (log_fd < 0) {
179 bb_error_msg("can't log to %s", log_console);
180 device = L_CONSOLE;
181 } else {
182 fcntl(log_fd, F_SETFD, FD_CLOEXEC);
183 }
184 }
185 }
186 if (device & L_LOG) {
187 full_write(log_fd, msg, l);
188 if (log_fd == 2)
189 return; /* don't print dup messages */
190 }
191#endif
192
193 if (device & L_CONSOLE) {
194 /* Send console messages to console so people will see them. */
195 full_write(2, msg, l);
196 }
197}
198
199/* Set terminal settings to reasonable defaults */
200static void set_sane_term(void)
201{
202 struct termios tty;
203
204 tcgetattr(STDIN_FILENO, &tty);
205
206 /* set control chars */
207 tty.c_cc[VINTR] = 3; /* C-c */
208 tty.c_cc[VQUIT] = 28; /* C-\ */
209 tty.c_cc[VERASE] = 127; /* C-? */
210 tty.c_cc[VKILL] = 21; /* C-u */
211 tty.c_cc[VEOF] = 4; /* C-d */
212 tty.c_cc[VSTART] = 17; /* C-q */
213 tty.c_cc[VSTOP] = 19; /* C-s */
214 tty.c_cc[VSUSP] = 26; /* C-z */
215
216 /* use line dicipline 0 */
217 tty.c_line = 0;
218
219 /* Make it be sane */
220 tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
221 tty.c_cflag |= CREAD | HUPCL | CLOCAL;
222
223 /* input modes */
224 tty.c_iflag = ICRNL | IXON | IXOFF;
225
226 /* output modes */
227 tty.c_oflag = OPOST | ONLCR;
228
229 /* local modes */
230 tty.c_lflag =
231 ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
232
233 tcsetattr(STDIN_FILENO, TCSANOW, &tty);
234}
235
236/* From <linux/serial.h> */
237struct serial_struct {
238 int type;
239 int line;
240 unsigned int port;
241 int irq;
242 int flags;
243 int xmit_fifo_size;
244 int custom_divisor;
245 int baud_base;
246 unsigned short close_delay;
247 char io_type;
248 char reserved_char[1];
249 int hub6;
250 unsigned short closing_wait; /* time to wait before closing */
251 unsigned short closing_wait2; /* no longer used... */
252 unsigned char *iomem_base;
253 unsigned short iomem_reg_shift;
254 unsigned int port_high;
255 unsigned long iomap_base; /* cookie passed into ioremap */
256 int reserved[1];
257 /* Paranoia (imagine 64bit kernel overwriting 32bit userspace stack) */
258 uint32_t bbox_reserved[16];
259};
260static void console_init(void)
261{
262 struct serial_struct sr;
263 char *s;
264
265 s = getenv("CONSOLE");
266 if (!s) s = getenv("console");
267 if (s) {
268 int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY);
269 if (fd >= 0) {
270 dup2(fd, 0);
271 dup2(fd, 1);
272 dup2(fd, 2);
273 while (fd > 2) close(fd--);
274 }
275 messageD(L_LOG, "console='%s'", s);
276 } else {
277 /* Make sure fd 0,1,2 are not closed */
278 bb_sanitize_stdio();
279 }
280
281 s = getenv("TERM");
282 if (ioctl(0, TIOCGSERIAL, &sr) == 0) {
283 /* Force the TERM setting to vt102 for serial console --
284 * if TERM is set to linux (the default) */
285 if (!s || strcmp(s, "linux") == 0)
286 putenv((char*)"TERM=vt102");
287#if !ENABLE_FEATURE_INIT_SYSLOG
288 log_console = NULL;
289#endif
290 } else if (!s)
291 putenv((char*)"TERM=linux");
292}
293
294static void fixup_argv(char **argv)
295{
296 /* Fix up argv[0] to be certain we claim to be init */
297 strncpy(argv[0], "init", strlen(argv[0]));
298
299 /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
300 while (*++argv)
301 memset(*argv, 0, strlen(*argv));
302}
303
304/* Open the new terminal device */
305static void open_stdio_to_tty(const char* tty_name, int fail)
306{
307 /* empty tty_name means "use init's tty", else... */
308 if (tty_name[0]) {
309 int fd = device_open(tty_name, O_RDWR);
310 if (fd < 0) {
311 message(L_LOG | L_CONSOLE, "Can't open %s: %s",
312 tty_name, strerror(errno));
313 if (fail)
314 _exit(1);
315#if !ENABLE_DEBUG_INIT
316 shutdown_signal(SIGUSR1);
317#else
318 _exit(2);
319#endif
320 } else {
321 dup2(fd, 0);
322 dup2(fd, 1);
323 dup2(fd, 2);
324 if (fd > 2) close(fd);
325 }
326 }
327 set_sane_term();
328}
329
330static pid_t run(const struct init_action *a)
331{
332 int i;
333 pid_t pid;
334 char *s, *tmpCmd, *cmdpath;
335 char *cmd[INIT_BUFFS_SIZE];
336 char buf[INIT_BUFFS_SIZE + 6]; /* INIT_BUFFS_SIZE+strlen("exec ")+1 */
337 sigset_t nmask, omask;
338
339 /* Block sigchild while forking. */
340 sigemptyset(&nmask);
341 sigaddset(&nmask, SIGCHLD);
342 sigprocmask(SIG_BLOCK, &nmask, &omask);
343 pid = fork();
344 sigprocmask(SIG_SETMASK, &omask, NULL);
345
346 if (pid)
347 return pid;
348
349 /* Reset signal handlers that were set by the parent process */
350 signal(SIGUSR1, SIG_DFL);
351 signal(SIGUSR2, SIG_DFL);
352 signal(SIGINT, SIG_DFL);
353 signal(SIGTERM, SIG_DFL);
354 signal(SIGHUP, SIG_DFL);
355 signal(SIGQUIT, SIG_DFL);
356 signal(SIGCONT, SIG_DFL);
357 signal(SIGSTOP, SIG_DFL);
358 signal(SIGTSTP, SIG_DFL);
359
360 /* Create a new session and make ourself the process
361 * group leader */
362 setsid();
363
364 /* Open the new terminal device */
365 open_stdio_to_tty(a->terminal, 1);
366
367 /* If the init Action requires us to wait, then force the
368 * supplied terminal to be the controlling tty. */
369 if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
370
371 /* Now fork off another process to just hang around */
372 if ((pid = fork()) < 0) {
373 message(L_LOG | L_CONSOLE, "Can't fork");
374 _exit(1);
375 }
376
377 if (pid > 0) {
378
379 /* We are the parent -- wait till the child is done */
380 signal(SIGINT, SIG_IGN);
381 signal(SIGTSTP, SIG_IGN);
382 signal(SIGQUIT, SIG_IGN);
383 signal(SIGCHLD, SIG_DFL);
384
385 waitfor(NULL, pid);
386 /* See if stealing the controlling tty back is necessary */
387 if (tcgetpgrp(0) != getpid())
388 _exit(0);
389
390 /* Use a temporary process to steal the controlling tty. */
391 if ((pid = fork()) < 0) {
392 message(L_LOG | L_CONSOLE, "Can't fork");
393 _exit(1);
394 }
395 if (pid == 0) {
396 setsid();
397 ioctl(0, TIOCSCTTY, 1);
398 _exit(0);
399 }
400 waitfor(NULL, pid);
401 _exit(0);
402 }
403
404 /* Now fall though to actually execute things */
405 }
406
407 /* See if any special /bin/sh requiring characters are present */
408 if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
409 cmd[0] = (char*)DEFAULT_SHELL;
410 cmd[1] = (char*)"-c";
411 cmd[2] = strcat(strcpy(buf, "exec "), a->command);
412 cmd[3] = NULL;
413 } else {
414 /* Convert command (char*) into cmd (char**, one word per string) */
415 strcpy(buf, a->command);
416 s = buf;
417 for (tmpCmd = buf, i = 0; (tmpCmd = strsep(&s, " \t")) != NULL;) {
418 if (*tmpCmd != '\0') {
419 cmd[i] = tmpCmd;
420 i++;
421 }
422 }
423 cmd[i] = NULL;
424 }
425
426 cmdpath = cmd[0];
427
428 /*
429 * Interactive shells want to see a dash in argv[0]. This
430 * typically is handled by login, argv will be setup this
431 * way if a dash appears at the front of the command path
432 * (like "-/bin/sh").
433 */
434 if (*cmdpath == '-') {
435 /* skip over the dash */
436 ++cmdpath;
437
438 /* find the last component in the command pathname */
439 s = bb_get_last_path_component(cmdpath);
440
441 /* make a new argv[0] */
442 if ((cmd[0] = malloc(strlen(s) + 2)) == NULL) {
443 message(L_LOG | L_CONSOLE, bb_msg_memory_exhausted);
444 cmd[0] = cmdpath;
445 } else {
446 cmd[0][0] = '-';
447 strcpy(cmd[0] + 1, s);
448 }
449#if ENABLE_FEATURE_INIT_SCTTY
450 /* Establish this process as session leader and
451 * (attempt) to make the tty (if any) a controlling tty.
452 */
453 setsid();
454 ioctl(0, TIOCSCTTY, 0 /*don't steal it*/);
455#endif
456 }
457
458#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
459 if (a->action & ASKFIRST) {
460 static const char press_enter[] ALIGN1 =
461#ifdef CUSTOMIZED_BANNER
462#include CUSTOMIZED_BANNER
463#endif
464 "\nPlease press Enter to activate this console. ";
465 char c;
466 /*
467 * Save memory by not exec-ing anything large (like a shell)
468 * before the user wants it. This is critical if swap is not
469 * enabled and the system has low memory. Generally this will
470 * be run on the second virtual console, and the first will
471 * be allowed to start a shell or whatever an init script
472 * specifies.
473 */
474 messageD(L_LOG, "waiting for enter to start '%s'"
475 "(pid %d, tty '%s')\n",
476 cmdpath, getpid(), a->terminal);
477 full_write(1, press_enter, sizeof(press_enter) - 1);
478 while (read(0, &c, 1) == 1 && c != '\n')
479 ;
480 }
481#endif
482 /* Log the process name and args */
483 message(L_LOG, "starting pid %d, tty '%s': '%s'",
484 getpid(), a->terminal, cmdpath);
485
486#if ENABLE_FEATURE_INIT_COREDUMPS
487 {
488 struct stat sb;
489 if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) {
490 struct rlimit limit;
491
492 limit.rlim_cur = RLIM_INFINITY;
493 limit.rlim_max = RLIM_INFINITY;
494 setrlimit(RLIMIT_CORE, &limit);
495 }
496 }
497#endif
498 /* Now run it. The new program will take over this PID,
499 * so nothing further in init.c should be run. */
500 BB_EXECVP(cmdpath, cmd);
501
502 /* We're still here? Some error happened. */
503 message(L_LOG | L_CONSOLE, "Cannot run '%s': %s",
504 cmdpath, strerror(errno));
505 _exit(-1);
506}
507
508static int waitfor(const struct init_action *a, pid_t pid)
509{
510 int runpid;
511 int status, wpid;
512
513 runpid = (NULL == a)? pid : run(a);
514 while (1) {
515 wpid = waitpid(runpid, &status, 0);
516 if (wpid == runpid)
517 break;
518 if (wpid == -1 && errno == ECHILD) {
519 /* we missed its termination */
520 break;
521 }
522 /* FIXME other errors should maybe trigger an error, but allow
523 * the program to continue */
524 }
525 return wpid;
526}
527
528/* Run all commands of a particular type */
529static void run_actions(int action)
530{
531 struct init_action *a, *tmp;
532
533 for (a = init_action_list; a; a = tmp) {
534 tmp = a->next;
535 if (a->action == action) {
536 /* a->terminal of "" means "init's console" */
537 if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {
538 delete_init_action(a);
539 } else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
540 waitfor(a, 0);
541 delete_init_action(a);
542 } else if (a->action & ONCE) {
543 run(a);
544 delete_init_action(a);
545 } else if (a->action & (RESPAWN | ASKFIRST)) {
546 /* Only run stuff with pid==0. If they have
547 * a pid, that means it is still running */
548 if (a->pid == 0) {
549 a->pid = run(a);
550 }
551 }
552 }
553 }
554}
555
556#if !ENABLE_DEBUG_INIT
557static void init_reboot(unsigned long magic)
558{
559 pid_t pid;
560 /* We have to fork here, since the kernel calls do_exit(0) in
561 * linux/kernel/sys.c, which can cause the machine to panic when
562 * the init process is killed.... */
563 pid = vfork();
564 if (pid == 0) { /* child */
565 reboot(magic);
566 _exit(0);
567 }
568 waitpid(pid, NULL, 0);
569}
570
571static void shutdown_system(void)
572{
573 sigset_t block_signals;
574
575 /* run everything to be run at "shutdown". This is done _prior_
576 * to killing everything, in case people wish to use scripts to
577 * shut things down gracefully... */
578 run_actions(SHUTDOWN);
579
580 /* first disable all our signals */
581 sigemptyset(&block_signals);
582 sigaddset(&block_signals, SIGHUP);
583 sigaddset(&block_signals, SIGQUIT);
584 sigaddset(&block_signals, SIGCHLD);
585 sigaddset(&block_signals, SIGUSR1);
586 sigaddset(&block_signals, SIGUSR2);
587 sigaddset(&block_signals, SIGINT);
588 sigaddset(&block_signals, SIGTERM);
589 sigaddset(&block_signals, SIGCONT);
590 sigaddset(&block_signals, SIGSTOP);
591 sigaddset(&block_signals, SIGTSTP);
592 sigprocmask(SIG_BLOCK, &block_signals, NULL);
593
594 message(L_CONSOLE | L_LOG, "The system is going down NOW!");
595
596 /* Allow Ctrl-Alt-Del to reboot system. */
597 init_reboot(RB_ENABLE_CAD);
598
599 /* Send signals to every process _except_ pid 1 */
600 message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "TERM");
601 kill(-1, SIGTERM);
602 sync();
603 sleep(1);
604
605 message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "KILL");
606 kill(-1, SIGKILL);
607 sync();
608 sleep(1);
609}
610
611static void exec_signal(int sig ATTRIBUTE_UNUSED)
612{
613 struct init_action *a, *tmp;
614 sigset_t unblock_signals;
615
616 for (a = init_action_list; a; a = tmp) {
617 tmp = a->next;
618 if (a->action & RESTART) {
619 shutdown_system();
620
621 /* unblock all signals, blocked in shutdown_system() */
622 sigemptyset(&unblock_signals);
623 sigaddset(&unblock_signals, SIGHUP);
624 sigaddset(&unblock_signals, SIGQUIT);
625 sigaddset(&unblock_signals, SIGCHLD);
626 sigaddset(&unblock_signals, SIGUSR1);
627 sigaddset(&unblock_signals, SIGUSR2);
628 sigaddset(&unblock_signals, SIGINT);
629 sigaddset(&unblock_signals, SIGTERM);
630 sigaddset(&unblock_signals, SIGCONT);
631 sigaddset(&unblock_signals, SIGSTOP);
632 sigaddset(&unblock_signals, SIGTSTP);
633 sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL);
634
635 /* Open the new terminal device */
636 open_stdio_to_tty(a->terminal, 0);
637
638 messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command);
639 BB_EXECLP(a->command, a->command, NULL);
640
641 message(L_CONSOLE | L_LOG, "Cannot run '%s': %s",
642 a->command, strerror(errno));
643 sleep(2);
644 init_reboot(RB_HALT_SYSTEM);
645 loop_forever();
646 }
647 }
648}
649
650static void shutdown_signal(int sig)
651{
652 const char *m;
653 int rb;
654
655 shutdown_system();
656
657 m = "halt";
658 rb = RB_HALT_SYSTEM;
659 if (sig == SIGTERM) {
660 m = "reboot";
661 rb = RB_AUTOBOOT;
662 } else if (sig == SIGUSR2) {
663 m = "poweroff";
664 rb = RB_POWER_OFF;
665 }
666 message(L_CONSOLE | L_LOG, "Requesting system %s", m);
667 /* allow time for last message to reach serial console */
668 sleep(2);
669 init_reboot(rb);
670 loop_forever();
671}
672
673static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)
674{
675 run_actions(CTRLALTDEL);
676}
677
678/* The SIGSTOP & SIGTSTP handler */
679static void stop_handler(int sig ATTRIBUTE_UNUSED)
680{
681 int saved_errno = errno;
682
683 got_cont = 0;
684 while (!got_cont)
685 pause();
686 got_cont = 0;
687 errno = saved_errno;
688}
689
690/* The SIGCONT handler */
691static void cont_handler(int sig ATTRIBUTE_UNUSED)
692{
693 got_cont = 1;
694}
695
696#endif /* !ENABLE_DEBUG_INIT */
697
698static void new_init_action(int action, const char *command, const char *cons)
699{
700 struct init_action *new_action, *a, *last;
701
702 if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST))
703 return;
704
705 /* Append to the end of the list */
706 for (a = last = init_action_list; a; a = a->next) {
707 /* don't enter action if it's already in the list,
708 * but do overwrite existing actions */
709 if ((strcmp(a->command, command) == 0)
710 && (strcmp(a->terminal, cons) == 0)
711 ) {
712 a->action = action;
713 return;
714 }
715 last = a;
716 }
717
718 new_action = xzalloc(sizeof(struct init_action));
719 if (last) {
720 last->next = new_action;
721 } else {
722 init_action_list = new_action;
723 }
724 strcpy(new_action->command, command);
725 new_action->action = action;
726 strcpy(new_action->terminal, cons);
727 messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
728 new_action->command, new_action->action, new_action->terminal);
729}
730
731static void delete_init_action(struct init_action *action)
732{
733 struct init_action *a, *b = NULL;
734
735 for (a = init_action_list; a; b = a, a = a->next) {
736 if (a == action) {
737 if (b == NULL) {
738 init_action_list = a->next;
739 } else {
740 b->next = a->next;
741 }
742 free(a);
743 break;
744 }
745 }
746}
747
748/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
749 * then parse_inittab() simply adds in some default
750 * actions(i.e., runs INIT_SCRIPT and then starts a pair
751 * of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB
752 * _is_ defined, but /etc/inittab is missing, this
753 * results in the same set of default behaviors.
754 */
755static void parse_inittab(void)
756{
757#if ENABLE_FEATURE_USE_INITTAB
758 FILE *file;
759 char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE];
760 char tmpConsole[CONSOLE_NAME_SIZE];
761 char *id, *runlev, *action, *command, *eol;
762 const struct init_action_type *a = actions;
763
764 file = fopen(INITTAB, "r");
765 if (file == NULL) {
766 /* No inittab file -- set up some default behavior */
767#endif
768 /* Reboot on Ctrl-Alt-Del */
769 new_init_action(CTRLALTDEL, "reboot", "");
770 /* Umount all filesystems on halt/reboot */
771 new_init_action(SHUTDOWN, "umount -a -r", "");
772 /* Swapoff on halt/reboot */
773 if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
774 /* Prepare to restart init when a HUP is received */
775 new_init_action(RESTART, "init", "");
776 /* Askfirst shell on tty1-4 */
777 new_init_action(ASKFIRST, bb_default_login_shell, "");
778 new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
779 new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
780 new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
781 /* sysinit */
782 new_init_action(SYSINIT, INIT_SCRIPT, "");
783
784 return;
785#if ENABLE_FEATURE_USE_INITTAB
786 }
787
788 while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) {
789 /* Skip leading spaces */
790 for (id = buf; *id == ' ' || *id == '\t'; id++);
791
792 /* Skip the line if it's a comment */
793 if (*id == '#' || *id == '\n')
794 continue;
795
796 /* Trim the trailing \n */
797 //XXX: chomp() ?
798 eol = strrchr(id, '\n');
799 if (eol != NULL)
800 *eol = '\0';
801
802 /* Keep a copy around for posterity's sake (and error msgs) */
803 strcpy(lineAsRead, buf);
804
805 /* Separate the ID field from the runlevels */
806 runlev = strchr(id, ':');
807 if (runlev == NULL || *(runlev + 1) == '\0') {
808 message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
809 continue;
810 } else {
811 *runlev = '\0';
812 ++runlev;
813 }
814
815 /* Separate the runlevels from the action */
816 action = strchr(runlev, ':');
817 if (action == NULL || *(action + 1) == '\0') {
818 message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
819 continue;
820 } else {
821 *action = '\0';
822 ++action;
823 }
824
825 /* Separate the action from the command */
826 command = strchr(action, ':');
827 if (command == NULL || *(command + 1) == '\0') {
828 message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
829 continue;
830 } else {
831 *command = '\0';
832 ++command;
833 }
834
835 /* Ok, now process it */
836 for (a = actions; a->name != 0; a++) {
837 if (strcmp(a->name, action) == 0) {
838 if (*id != '\0') {
839 if (strncmp(id, "/dev/", 5) == 0)
840 id += 5;
841 strcpy(tmpConsole, "/dev/");
842 safe_strncpy(tmpConsole + 5, id,
843 sizeof(tmpConsole) - 5);
844 id = tmpConsole;
845 }
846 new_init_action(a->action, command, id);
847 break;
848 }
849 }
850 if (a->name == 0) {
851 /* Choke on an unknown action */
852 message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
853 }
854 }
855 fclose(file);
856#endif /* FEATURE_USE_INITTAB */
857}
858
859#if ENABLE_FEATURE_USE_INITTAB
860static void reload_signal(int sig ATTRIBUTE_UNUSED)
861{
862 struct init_action *a, *tmp;
863
864 message(L_LOG, "reloading /etc/inittab");
865
866 /* disable old entrys */
867 for (a = init_action_list; a; a = a->next ) {
868 a->action = ONCE;
869 }
870
871 parse_inittab();
872
873 /* remove unused entrys */
874 for (a = init_action_list; a; a = tmp) {
875 tmp = a->next;
876 if ((a->action & (ONCE | SYSINIT | WAIT)) && a->pid == 0) {
877 delete_init_action(a);
878 }
879 }
880 run_actions(RESPAWN);
881}
882#endif /* FEATURE_USE_INITTAB */
883
884int init_main(int argc, char **argv);
885int init_main(int argc, char **argv)
886{
887 struct init_action *a;
888 pid_t wpid;
889
890 die_sleep = 30 * 24*60*60; /* if xmalloc will ever die... */
891
892 if (argc > 1 && !strcmp(argv[1], "-q")) {
893 return kill(1, SIGHUP);
894 }
895#if !ENABLE_DEBUG_INIT
896 /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
897 if (getpid() != 1
898 && (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc"))
899 ) {
900 bb_show_usage();
901 }
902 /* Set up sig handlers -- be sure to
903 * clear all of these in run() */
904 signal(SIGHUP, exec_signal);
905 signal(SIGQUIT, exec_signal);
906 signal(SIGUSR1, shutdown_signal);
907 signal(SIGUSR2, shutdown_signal);
908 signal(SIGINT, ctrlaltdel_signal);
909 signal(SIGTERM, shutdown_signal);
910 signal(SIGCONT, cont_handler);
911 signal(SIGSTOP, stop_handler);
912 signal(SIGTSTP, stop_handler);
913
914 /* Turn off rebooting via CTL-ALT-DEL -- we get a
915 * SIGINT on CAD so we can shut things down gracefully... */
916 init_reboot(RB_DISABLE_CAD);
917#endif
918
919
920 /* Figure out where the default console should be */
921 console_init();
922 set_sane_term();
923 chdir("/");
924 setsid();
925 {
926 const char *const *e;
927 /* Make sure environs is set to something sane */
928 for (e = environment; *e; e++)
929 putenv((char *) *e);
930 }
931
932 if (argc > 1) setenv("RUNLEVEL", argv[1], 1);
933
934 /* Hello world */
935 message(MAYBE_CONSOLE | L_LOG, "init started: %s", bb_banner);
936
937 /* Make sure there is enough memory to do something useful. */
938 if (ENABLE_SWAPONOFF) {
939 struct sysinfo info;
940
941 if (!sysinfo(&info) &&
942 (info.mem_unit ? : 1) * (long long)info.totalram < 1024*1024)
943 {
944 message(L_CONSOLE, "Low memory, forcing swapon");
945 /* swapon -a requires /proc typically */
946 new_init_action(SYSINIT, "mount -t proc proc /proc", "");
947 /* Try to turn on swap */
948 new_init_action(SYSINIT, "swapon -a", "");
949 run_actions(SYSINIT); /* wait and removing */
950 }
951 }
952
953 /* Check if we are supposed to be in single user mode */
954 if (argc > 1
955 && (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1'))
956 ) {
957 /* Start a shell on console */
958 new_init_action(RESPAWN, bb_default_login_shell, "");
959 } else {
960 /* Not in single user mode -- see what inittab says */
961
962 /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
963 * then parse_inittab() simply adds in some default
964 * actions(i.e., runs INIT_SCRIPT and then starts a pair
965 * of "askfirst" shells */
966 parse_inittab();
967 }
968
969#if ENABLE_SELINUX
970 if (getenv("SELINUX_INIT") == NULL) {
971 int enforce = 0;
972
973 putenv((char*)"SELINUX_INIT=YES");
974 if (selinux_init_load_policy(&enforce) == 0) {
975 BB_EXECVP(argv[0], argv);
976 } else if (enforce > 0) {
977 /* SELinux in enforcing mode but load_policy failed */
978 /* At this point, we probably can't open /dev/console, so log() won't work */
979 message(L_CONSOLE, "Cannot load SELinux Policy. "
980 "Machine is in enforcing mode. Halting now.");
981 exit(1);
982 }
983 }
984#endif /* CONFIG_SELINUX */
985
986 /* Make the command line just say "init" -- thats all, nothing else */
987 fixup_argv(argv);
988
989 /* Now run everything that needs to be run */
990
991 /* First run the sysinit command */
992 run_actions(SYSINIT);
993
994 /* Next run anything that wants to block */
995 run_actions(WAIT);
996
997 /* Next run anything to be run only once */
998 run_actions(ONCE);
999
1000#if ENABLE_FEATURE_USE_INITTAB
1001 /* Redefine SIGHUP to reread /etc/inittab */
1002 signal(SIGHUP, reload_signal);
1003#else
1004 signal(SIGHUP, SIG_IGN);
1005#endif /* FEATURE_USE_INITTAB */
1006
1007 /* Now run the looping stuff for the rest of forever */
1008 while (1) {
1009 /* run the respawn stuff */
1010 run_actions(RESPAWN);
1011
1012 /* run the askfirst stuff */
1013 run_actions(ASKFIRST);
1014
1015 /* Don't consume all CPU time -- sleep a bit */
1016 sleep(1);
1017
1018 /* Wait for a child process to exit */
1019 wpid = wait(NULL);
1020 while (wpid > 0) {
1021 /* Find out who died and clean up their corpse */
1022 for (a = init_action_list; a; a = a->next) {
1023 if (a->pid == wpid) {
1024 /* Set the pid to 0 so that the process gets
1025 * restarted by run_actions() */
1026 a->pid = 0;
1027 message(L_LOG, "process '%s' (pid %d) exited. "
1028 "Scheduling it for restart.",
1029 a->command, wpid);
1030 }
1031 }
1032 /* see if anyone else is waiting to be reaped */
1033 wpid = waitpid(-1, NULL, WNOHANG);
1034 }
1035 }
1036}
Note: See TracBrowser for help on using the repository browser.