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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 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
RevLine 
[821]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
[1770]12#include "libbb.h"
[821]13#include <paths.h>
[1770]14//#include <signal.h>
15//#include <sys/ioctl.h>
16//#include <sys/wait.h>
[821]17#include <sys/reboot.h>
18
[1770]19#if ENABLE_FEATURE_INIT_SYSLOG
[821]20# include <sys/syslog.h>
21#endif
22
23#define INIT_BUFFS_SIZE 256
[1770]24#define CONSOLE_NAME_SIZE 32
25#define MAXENV  16      /* Number of env. vars */
[821]26
[1770]27#if ENABLE_FEATURE_INIT_COREDUMPS
[821]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 {
[1770]73    struct init_action *next;
74    int action;
[821]75    pid_t pid;
76    char command[INIT_BUFFS_SIZE];
[1770]77    char terminal[CONSOLE_NAME_SIZE];
[821]78};
79
80/* Static variables */
81static struct init_action *init_action_list = NULL;
82
[1770]83#if !ENABLE_FEATURE_INIT_SYSLOG
84static const char *log_console = VC_5;
[821]85#endif
86#if !ENABLE_DEBUG_INIT
87static sig_atomic_t got_cont = 0;
88#endif
89
90enum {
[1770]91    L_LOG = 0x1,
92    L_CONSOLE = 0x2,
[821]93
[1770]94#if ENABLE_FEATURE_EXTRA_QUIET
[821]95    MAYBE_CONSOLE = 0x0,
96#else
[1770]97    MAYBE_CONSOLE = L_CONSOLE,
[821]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
[1770]109static const char *const environment[] = {
[821]110    "HOME=/",
[1770]111    bb_PATH_root_path,
[821]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
[1770]124#if !ENABLE_DEBUG_INIT
[821]125static void loop_forever(void)
126{
127    while (1)
128        sleep(1);
129}
[1770]130#endif
[821]131
132/* Print a message to the specified device.
[1770]133 * Device may be bitwise-or'd from L_LOG | L_CONSOLE */
[821]134#if ENABLE_DEBUG_INIT
135#define messageD message
136#else
[1770]137#define messageD(...)  do {} while (0)
[821]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{
[1770]143#if !ENABLE_FEATURE_INIT_SYSLOG
[821]144    static int log_fd = -1;
145#endif
146
[1770]147    va_list arguments;
148    int l;
149    char msg[128];
150
[821]151    msg[0] = '\r';
[1770]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);
[821]157
[1770]158#if ENABLE_FEATURE_INIT_SYSLOG
[821]159    /* Log the message to syslogd */
[1770]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);
[821]164        closelog();
165    }
166    msg[l++] = '\n';
[1770]167    msg[l] = '\0';
[821]168#else
169    msg[l++] = '\n';
[1770]170    msg[l] = '\0';
[821]171    /* Take full control of the log tty, and never close it.
172     * It's mine, all mine!  Muhahahaha! */
173    if (log_fd < 0) {
[1770]174        if (!log_console) {
175            log_fd = 2;
[821]176        } else {
[1770]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            }
[821]184        }
185    }
[1770]186    if (device & L_LOG) {
187        full_write(log_fd, msg, l);
188        if (log_fd == 2)
189            return; /* don't print dup messages */
[821]190    }
191#endif
192
[1770]193    if (device & L_CONSOLE) {
194        /* Send console messages to console so people will see them. */
195        full_write(2, msg, l);
[821]196    }
197}
198
199/* Set terminal settings to reasonable defaults */
[1770]200static void set_sane_term(void)
[821]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
[1770]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};
[821]260static void console_init(void)
261{
262    struct serial_struct sr;
263    char *s;
264
[1770]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);
[821]276    } else {
[1770]277        /* Make sure fd 0,1,2 are not closed */
278        bb_sanitize_stdio();
[821]279    }
280
[1770]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;
[821]289#endif
[1770]290    } else if (!s)
291        putenv((char*)"TERM=linux");
[821]292}
293
[1770]294static void fixup_argv(char **argv)
[821]295{
296    /* Fix up argv[0] to be certain we claim to be init */
[1770]297    strncpy(argv[0], "init", strlen(argv[0]));
[821]298
299    /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
[1770]300    while (*++argv)
301        memset(*argv, 0, strlen(*argv));
[821]302}
303
304/* Open the new terminal device */
[1770]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);
[821]315#if !ENABLE_DEBUG_INIT
[1770]316            shutdown_signal(SIGUSR1);
[821]317#else
[1770]318            _exit(2);
[821]319#endif
[1770]320        } else {
321            dup2(fd, 0);
322            dup2(fd, 1);
323            dup2(fd, 2);
324            if (fd > 2) close(fd);
325        }
[821]326    }
[1770]327    set_sane_term();
[821]328}
329
330static pid_t run(const struct init_action *a)
331{
332    int i;
333    pid_t pid;
[1770]334    char *s, *tmpCmd, *cmdpath;
335    char *cmd[INIT_BUFFS_SIZE];
[821]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);
[1770]343    pid = fork();
344    sigprocmask(SIG_SETMASK, &omask, NULL);
[821]345
[1770]346    if (pid)
347        return pid;
[821]348
[1770]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);
[821]359
[1770]360    /* Create a new session and make ourself the process
361     * group leader */
362    setsid();
[821]363
[1770]364    /* Open the new terminal device */
365    open_stdio_to_tty(a->terminal, 1);
[821]366
[1770]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)) {
[821]370
[1770]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        }
[821]376
[1770]377        if (pid > 0) {
[821]378
[1770]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. */
[821]391            if ((pid = fork()) < 0) {
[1770]392                message(L_LOG | L_CONSOLE, "Can't fork");
[821]393                _exit(1);
394            }
[1770]395            if (pid == 0) {
396                setsid();
397                ioctl(0, TIOCSCTTY, 1);
[821]398                _exit(0);
399            }
[1770]400            waitfor(NULL, pid);
401            _exit(0);
[821]402        }
403
[1770]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++;
[821]421            }
422        }
[1770]423        cmd[i] = NULL;
424    }
[821]425
[1770]426    cmdpath = cmd[0];
[821]427
[1770]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;
[821]437
[1770]438        /* find the last component in the command pathname */
439        s = bb_get_last_path_component(cmdpath);
[821]440
[1770]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*/);
[821]455#endif
[1770]456    }
[821]457
458#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
[1770]459    if (a->action & ASKFIRST) {
460        static const char press_enter[] ALIGN1 =
461#ifdef CUSTOMIZED_BANNER
462#include CUSTOMIZED_BANNER
[821]463#endif
[1770]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);
[821]485
[1770]486#if ENABLE_FEATURE_INIT_COREDUMPS
487    {
488        struct stat sb;
489        if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) {
490            struct rlimit limit;
[821]491
[1770]492            limit.rlim_cur = RLIM_INFINITY;
493            limit.rlim_max = RLIM_INFINITY;
494            setrlimit(RLIMIT_CORE, &limit);
[821]495        }
[1770]496    }
[821]497#endif
[1770]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);
[821]501
[1770]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);
[821]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) {
[1770]515        wpid = waitpid(runpid, &status, 0);
[821]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) {
[1770]536            /* a->terminal of "" means "init's console" */
537            if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {
[821]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.... */
[1770]563    pid = vfork();
564    if (pid == 0) { /* child */
[821]565        reboot(magic);
566        _exit(0);
567    }
[1770]568    waitpid(pid, NULL, 0);
[821]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
[1770]594    message(L_CONSOLE | L_LOG, "The system is going down NOW!");
595
[821]596    /* Allow Ctrl-Alt-Del to reboot system. */
597    init_reboot(RB_ENABLE_CAD);
598
599    /* Send signals to every process _except_ pid 1 */
[1770]600    message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "TERM");
[821]601    kill(-1, SIGTERM);
[1770]602    sync();
[821]603    sleep(1);
604
[1770]605    message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "KILL");
[821]606    kill(-1, SIGKILL);
[1770]607    sync();
[821]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 */
[1770]636            open_stdio_to_tty(a->terminal, 0);
[821]637
[1770]638            messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command);
639            BB_EXECLP(a->command, a->command, NULL);
[821]640
[1770]641            message(L_CONSOLE | L_LOG, "Cannot run '%s': %s",
642                    a->command, strerror(errno));
[821]643            sleep(2);
644            init_reboot(RB_HALT_SYSTEM);
645            loop_forever();
646        }
647    }
648}
649
650static void shutdown_signal(int sig)
651{
[1770]652    const char *m;
[821]653    int rb;
654
655    shutdown_system();
656
[1770]657    m = "halt";
658    rb = RB_HALT_SYSTEM;
[821]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    }
[1770]666    message(L_CONSOLE | L_LOG, "Requesting system %s", m);
[821]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
[1770]696#endif  /* !ENABLE_DEBUG_INIT */
[821]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 */
[1770]709        if ((strcmp(a->command, command) == 0)
710         && (strcmp(a->terminal, cons) == 0)
711        ) {
[821]712            a->action = action;
713            return;
714        }
715        last = a;
716    }
[1770]717
718    new_action = xzalloc(sizeof(struct init_action));
[821]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);
[1770]727    messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
[821]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{
[1770]757#if ENABLE_FEATURE_USE_INITTAB
[821]758    FILE *file;
759    char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE];
[1770]760    char tmpConsole[CONSOLE_NAME_SIZE];
[821]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 */
[1770]769        new_init_action(CTRLALTDEL, "reboot", "");
[821]770        /* Umount all filesystems on halt/reboot */
[1770]771        new_init_action(SHUTDOWN, "umount -a -r", "");
[821]772        /* Swapoff on halt/reboot */
[1770]773        if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
[821]774        /* Prepare to restart init when a HUP is received */
[1770]775        new_init_action(RESTART, "init", "");
[821]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;
[1770]785#if ENABLE_FEATURE_USE_INITTAB
[821]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 */
[1770]797        //XXX: chomp() ?
[821]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') {
[1770]808            message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
[821]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') {
[1770]818            message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
[821]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') {
[1770]828            message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
[821]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') {
[1770]839                    if (strncmp(id, "/dev/", 5) == 0)
[821]840                        id += 5;
841                    strcpy(tmpConsole, "/dev/");
842                    safe_strncpy(tmpConsole + 5, id,
[1770]843                        sizeof(tmpConsole) - 5);
[821]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 */
[1770]852            message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
[821]853        }
854    }
855    fclose(file);
[1770]856#endif /* FEATURE_USE_INITTAB */
[821]857}
858
[1770]859#if ENABLE_FEATURE_USE_INITTAB
[821]860static void reload_signal(int sig ATTRIBUTE_UNUSED)
861{
862    struct init_action *a, *tmp;
863
[1770]864    message(L_LOG, "reloading /etc/inittab");
[821]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;
[1770]876        if ((a->action & (ONCE | SYSINIT | WAIT)) && a->pid == 0) {
[821]877            delete_init_action(a);
878        }
879    }
880    run_actions(RESPAWN);
881}
[1770]882#endif  /* FEATURE_USE_INITTAB */
[821]883
[1770]884int init_main(int argc, char **argv);
[821]885int init_main(int argc, char **argv)
886{
887    struct init_action *a;
888    pid_t wpid;
889
[1770]890    die_sleep = 30 * 24*60*60; /* if xmalloc will ever die... */
891
[821]892    if (argc > 1 && !strcmp(argv[1], "-q")) {
[1770]893        return kill(1, SIGHUP);
[821]894    }
895#if !ENABLE_DEBUG_INIT
896    /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
[1770]897    if (getpid() != 1
898     && (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc"))
899    ) {
[821]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
[1770]919
[821]920    /* Figure out where the default console should be */
921    console_init();
[1770]922    set_sane_term();
[821]923    chdir("/");
924    setsid();
925    {
[1770]926        const char *const *e;
[821]927        /* Make sure environs is set to something sane */
[1770]928        for (e = environment; *e; e++)
[821]929            putenv((char *) *e);
930    }
[1770]931
932    if (argc > 1) setenv("RUNLEVEL", argv[1], 1);
933
[821]934    /* Hello world */
[1770]935    message(MAYBE_CONSOLE | L_LOG, "init started: %s", bb_banner);
[821]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) &&
[1770]942            (info.mem_unit ? : 1) * (long long)info.totalram < 1024*1024)
[821]943        {
[1770]944            message(L_CONSOLE, "Low memory, forcing swapon");
[821]945            /* swapon -a requires /proc typically */
[1770]946            new_init_action(SYSINIT, "mount -t proc proc /proc", "");
[821]947            /* Try to turn on swap */
[1770]948            new_init_action(SYSINIT, "swapon -a", "");
[821]949            run_actions(SYSINIT);   /* wait and removing */
950        }
951    }
952
953    /* Check if we are supposed to be in single user mode */
[1770]954    if (argc > 1
955     && (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1'))
956    ) {
[821]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
[1770]969#if ENABLE_SELINUX
[821]970    if (getenv("SELINUX_INIT") == NULL) {
971        int enforce = 0;
972
[1770]973        putenv((char*)"SELINUX_INIT=YES");
[821]974        if (selinux_init_load_policy(&enforce) == 0) {
[1770]975            BB_EXECVP(argv[0], argv);
[821]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 */
[1770]979            message(L_CONSOLE, "Cannot load SELinux Policy. "
980                "Machine is in enforcing mode. Halting now.");
[821]981            exit(1);
982        }
983    }
984#endif /* CONFIG_SELINUX */
985
986    /* Make the command line just say "init"  -- thats all, nothing else */
[1770]987    fixup_argv(argv);
[821]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
[1770]1000#if ENABLE_FEATURE_USE_INITTAB
[821]1001    /* Redefine SIGHUP to reread /etc/inittab */
1002    signal(SIGHUP, reload_signal);
1003#else
1004    signal(SIGHUP, SIG_IGN);
[1770]1005#endif /* FEATURE_USE_INITTAB */
[821]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;
[1770]1027                    message(L_LOG, "process '%s' (pid %d) exited. "
[821]1028                            "Scheduling it for restart.",
1029                            a->command, wpid);
1030                }
1031            }
1032            /* see if anyone else is waiting to be reaped */
[1770]1033            wpid = waitpid(-1, NULL, WNOHANG);
[821]1034        }
1035    }
1036}
Note: See TracBrowser for help on using the repository browser.