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 
write_wtmp(void)100 static 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  */
init_was_not_there(void)137 static 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 
164 int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
halt_main(int argc UNUSED_PARAM,char ** argv)165 int 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