busybox/init/halt.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Poweroff reboot and halt, oh my.
   4 *
   5 * Copyright 2006 by Rob Landley <rob@landley.net>
   6 *
   7 * Licensed under GPLv2, see file LICENSE in this source tree.
   8 */
   9//config:config HALT
  10//config:       bool "halt (4 kb)"
  11//config:       default y
  12//config:       help
  13//config:       Stop all processes and halt the system.
  14//config:
  15//config:config POWEROFF
  16//config:       bool "poweroff (4 kb)"
  17//config:       default y
  18//config:       help
  19//config:       Stop all processes and power off the system.
  20//config:
  21//config:config REBOOT
  22//config:       bool "reboot (4 kb)"
  23//config:       default y
  24//config:       help
  25//config:       Stop all processes and reboot the system.
  26//config:
  27//config:config FEATURE_WAIT_FOR_INIT
  28//config:       bool "Before signaling init, make sure it is ready for it"
  29//config:       default y
  30//config:       depends on HALT || POWEROFF || REBOOT
  31//config:       help
  32//config:       In rare cases, poweroff may be commanded by firmware to OS
  33//config:       even before init process exists. On Linux, this spawns
  34//config:       "/sbin/poweroff" very early. This option adds code
  35//config:       which checks that init is ready to receive poweroff
  36//config:       commands. Code size increase of ~80 bytes.
  37//config:
  38//config:config FEATURE_CALL_TELINIT
  39//config:       bool "Call telinit on shutdown and reboot"
  40//config:       default y
  41//config:       depends on (HALT || POWEROFF || REBOOT) && !INIT
  42//config:       help
  43//config:       Call an external program (normally telinit) to facilitate
  44//config:       a switch to a proper runlevel.
  45//config:
  46//config:       This option is only available if you selected halt and friends,
  47//config:       but did not select init.
  48//config:
  49//config:config TELINIT_PATH
  50//config:       string "Path to telinit executable"
  51//config:       default "/sbin/telinit"
  52//config:       depends on FEATURE_CALL_TELINIT
  53//config:       help
  54//config:       When busybox halt and friends have to call external telinit
  55//config:       to facilitate proper shutdown, this path is to be used when
  56//config:       locating telinit executable.
  57
  58//applet:IF_HALT(APPLET(halt, BB_DIR_SBIN, BB_SUID_DROP))
  59//                   APPLET_ODDNAME:name      main  location     suid_type     help
  60//applet:IF_POWEROFF(APPLET_ODDNAME(poweroff, halt, BB_DIR_SBIN, BB_SUID_DROP, poweroff))
  61//applet:IF_REBOOT(  APPLET_ODDNAME(reboot,   halt, BB_DIR_SBIN, BB_SUID_DROP, reboot))
  62
  63//kbuild:lib-$(CONFIG_HALT) += halt.o
  64//kbuild:lib-$(CONFIG_POWEROFF) += halt.o
  65//kbuild:lib-$(CONFIG_REBOOT) += halt.o
  66
  67//usage:#define halt_trivial_usage
  68//usage:       "[-d DELAY] [-n] [-f]" IF_FEATURE_WTMP(" [-w]")
  69//usage:#define halt_full_usage "\n\n"
  70//usage:       "Halt the system\n"
  71//usage:     "\n        -d SEC  Delay interval"
  72//usage:     "\n        -n      Do not sync"
  73//usage:     "\n        -f      Force (don't go through init)"
  74//usage:        IF_FEATURE_WTMP(
  75//usage:     "\n        -w      Only write a wtmp record"
  76//usage:        )
  77//usage:
  78//usage:#define poweroff_trivial_usage
  79//usage:       "[-d DELAY] [-n] [-f]"
  80//usage:#define poweroff_full_usage "\n\n"
  81//usage:       "Halt and shut off power\n"
  82//usage:     "\n        -d SEC  Delay interval"
  83//usage:     "\n        -n      Do not sync"
  84//usage:     "\n        -f      Force (don't go through init)"
  85//usage:
  86//usage:#define reboot_trivial_usage
  87//usage:       "[-d DELAY] [-n] [-f]"
  88//usage:#define reboot_full_usage "\n\n"
  89//usage:       "Reboot the system\n"
  90//usage:     "\n        -d SEC  Delay interval"
  91//usage:     "\n        -n      Do not sync"
  92//usage:     "\n        -f      Force (don't go through init)"
  93
  94#include "libbb.h"
  95#include "reboot.h"
  96
  97#if ENABLE_FEATURE_WTMP
  98#include <sys/utsname.h>
  99
 100static void write_wtmp(void)
 101{
 102        struct utmpx utmp;
 103        struct utsname uts;
 104        /* "man utmp" says wtmp file should *not* be created automagically */
 105        /*if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {
 106                close(creat(bb_path_wtmp_file, 0664));
 107        }*/
 108        memset(&utmp, 0, sizeof(utmp));
 109        utmp.ut_tv.tv_sec = time(NULL);
 110        strcpy(utmp.ut_user, "shutdown"); /* it is wide enough */
 111        utmp.ut_type = RUN_LVL;
 112        utmp.ut_id[0] = '~'; utmp.ut_id[1] = '~'; /* = strcpy(utmp.ut_id, "~~"); */
 113        utmp.ut_line[0] = '~'; utmp.ut_line[1] = '~'; /* = strcpy(utmp.ut_line, "~~"); */
 114        uname(&uts);
 115        safe_strncpy(utmp.ut_host, uts.release, sizeof(utmp.ut_host));
 116        updwtmpx(bb_path_wtmp_file, &utmp);
 117}
 118#else
 119#define write_wtmp() ((void)0)
 120#endif
 121
 122#if ENABLE_FEATURE_WAIT_FOR_INIT
 123/* In Linux, "poweroff" may be spawned even before init.
 124 * For example, with ACPI:
 125 * linux/drivers/acpi/bus.c:
 126 *  static void sb_notify_work(struct work_struct *dummy)
 127 *      orderly_poweroff(true);
 128 * linux/kernel/reboot.c:
 129 *  static int run_cmd(const char *cmd)
 130 *      ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
 131 *  poweroff_cmd[] = "/sbin/poweroff";
 132 *  static int __orderly_poweroff(bool force)
 133 *      ret = run_cmd(poweroff_cmd);
 134 *
 135 * We want to make sure init exists and listens to signals.
 136 */
 137static int init_was_not_there(void)
 138{
 139        enum { initial = 5 }; /* 5 seconds should be plenty for timeout */
 140        int cnt = initial - 1;
 141
 142        /* Just existence of PID 1 does not mean it installed
 143         * the handlers already.
 144         */
 145#if 0
 146        while (kill(1, 0) != 0 && --cnt >= 0)
 147                sleep(1);
 148#endif
 149        /* ... so let's wait for some evidence a usual startup event,
 150         * mounting of /proc, happened. By that time init should be ready
 151         * for signals.
 152         */
 153        while (access("/proc/meminfo", F_OK) != 0 && --cnt >= 0)
 154                sleep(1);
 155
 156        /* Does it look like init wasn't there? */
 157        return (cnt != initial - 1);
 158}
 159#else
 160  /* Assume it's always there */
 161# define init_was_not_there() 0
 162#endif
 163
 164int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 165int halt_main(int argc UNUSED_PARAM, char **argv)
 166{
 167        static const int magic[] = {
 168                RB_HALT_SYSTEM,
 169                RB_POWER_OFF,
 170                RB_AUTOBOOT
 171        };
 172        static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };
 173
 174        int delay = 0;
 175        int which, flags, rc;
 176
 177        /* Figure out which applet we're running */
 178        if (ENABLE_HALT && !ENABLE_POWEROFF && !ENABLE_REBOOT)
 179                which = 0;
 180        else
 181        if (!ENABLE_HALT && ENABLE_POWEROFF && !ENABLE_REBOOT)
 182                which = 1;
 183        else
 184        if (!ENABLE_HALT && !ENABLE_POWEROFF && ENABLE_REBOOT)
 185                which = 2;
 186        else
 187        for (which = 0; "hpr"[which] != applet_name[0]; which++)
 188                continue;
 189
 190        /* Parse and handle arguments */
 191        /* We support -w even if !ENABLE_FEATURE_WTMP,
 192         * in order to not break scripts.
 193         * -i (shut down network interfaces) is ignored.
 194         */
 195        flags = getopt32(argv, "d:+nfwi", &delay);
 196
 197        sleep(delay);
 198
 199        write_wtmp();
 200
 201        if (flags & 8) /* -w */
 202                return EXIT_SUCCESS;
 203
 204        if (!(flags & 2)) /* no -n */
 205                sync();
 206
 207        /* Perform action. */
 208        rc = 1;
 209        if (!(flags & 4)) { /* no -f */
 210//TODO: I tend to think that signalling linuxrc is wrong
 211// pity original author didn't comment on it...
 212                if (ENABLE_LINUXRC) {
 213                        /* talk to linuxrc */
 214                        /* bbox init/linuxrc assumed */
 215                        pid_t *pidlist = find_pid_by_name("linuxrc");
 216                        if (pidlist[0] > 0)
 217                                rc = kill(pidlist[0], signals[which]);
 218                        if (ENABLE_FEATURE_CLEAN_UP)
 219                                free(pidlist);
 220                }
 221                if (rc) {
 222                        /* talk to init */
 223                        if (!ENABLE_FEATURE_CALL_TELINIT) {
 224                                /* bbox init assumed */
 225                                rc = kill(1, signals[which]);
 226                                if (init_was_not_there())
 227                                        rc = kill(1, signals[which]);
 228                        } else {
 229                                /* SysV style init assumed */
 230                                /* runlevels:
 231                                 * 0 == shutdown
 232                                 * 6 == reboot */
 233                                execlp(CONFIG_TELINIT_PATH,
 234                                                CONFIG_TELINIT_PATH,
 235                                                which == 2 ? "6" : "0",
 236                                                (char *)NULL
 237                                );
 238                                bb_perror_msg_and_die("can't execute '%s'",
 239                                                CONFIG_TELINIT_PATH);
 240                        }
 241                }
 242        } else {
 243                rc = reboot(magic[which]);
 244        }
 245
 246        if (rc)
 247                bb_perror_nomsg_and_die();
 248        return rc;
 249}
 250