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] [-nf"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] [-nf]" 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] [-nf]" 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 sleep1(); 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 sleep1(); 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