aboutsummaryrefslogtreecommitdiff
path: root/init/halt.c
blob: e6eb28fc9e7a3958b98a5670b9e3bddafe4c3d10 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* vi: set sw=4 ts=4: */
/*
 * Poweroff reboot and halt, oh my.
 *
 * Copyright 2006 by Rob Landley <rob@landley.net>
 *
 * Licensed under GPLv2, see file LICENSE in this source tree.
 */

//config:config HALT
//config:	bool "halt (3.7 kb)"
//config:	default y
//config:	help
//config:	  Stop all processes and halt the system.
//config:
//config:config POWEROFF
//config:	bool "poweroff (3.7 kb)"
//config:	default y
//config:	help
//config:	  Stop all processes and power off the system.
//config:
//config:config REBOOT
//config:	bool "reboot (3.7 kb)"
//config:	default y
//config:	help
//config:	  Stop all processes and reboot the system.
//config:
//config:config FEATURE_CALL_TELINIT
//config:	bool "Call telinit on shutdown and reboot"
//config:	default y
//config:	depends on (HALT || POWEROFF || REBOOT) && !INIT
//config:	help
//config:	  Call an external program (normally telinit) to facilitate
//config:	  a switch to a proper runlevel.
//config:
//config:	  This option is only available if you selected halt and friends,
//config:	  but did not select init.
//config:
//config:config TELINIT_PATH
//config:	string "Path to telinit executable"
//config:	default "/sbin/telinit"
//config:	depends on FEATURE_CALL_TELINIT
//config:	help
//config:	  When busybox halt and friends have to call external telinit
//config:	  to facilitate proper shutdown, this path is to be used when
//config:	  locating telinit executable.

//applet:IF_HALT(APPLET(halt, BB_DIR_SBIN, BB_SUID_DROP))
//                   APPLET_ODDNAME:name      main  location     suid_type     help
//applet:IF_POWEROFF(APPLET_ODDNAME(poweroff, halt, BB_DIR_SBIN, BB_SUID_DROP, poweroff))
//applet:IF_REBOOT(  APPLET_ODDNAME(reboot,   halt, BB_DIR_SBIN, BB_SUID_DROP, reboot))

//kbuild:lib-$(CONFIG_HALT) += halt.o
//kbuild:lib-$(CONFIG_POWEROFF) += halt.o
//kbuild:lib-$(CONFIG_REBOOT) += halt.o

//usage:#define halt_trivial_usage
//usage:       "[-d DELAY] [-n] [-f]" IF_FEATURE_WTMP(" [-w]")
//usage:#define halt_full_usage "\n\n"
//usage:       "Halt the system\n"
//usage:     "\n	-d SEC	Delay interval"
//usage:     "\n	-n	Do not sync"
//usage:     "\n	-f	Force (don't go through init)"
//usage:	IF_FEATURE_WTMP(
//usage:     "\n	-w	Only write a wtmp record"
//usage:	)
//usage:
//usage:#define poweroff_trivial_usage
//usage:       "[-d DELAY] [-n] [-f]"
//usage:#define poweroff_full_usage "\n\n"
//usage:       "Halt and shut off power\n"
//usage:     "\n	-d SEC	Delay interval"
//usage:     "\n	-n	Do not sync"
//usage:     "\n	-f	Force (don't go through init)"
//usage:
//usage:#define reboot_trivial_usage
//usage:       "[-d DELAY] [-n] [-f]"
//usage:#define reboot_full_usage "\n\n"
//usage:       "Reboot the system\n"
//usage:     "\n	-d SEC	Delay interval"
//usage:     "\n	-n	Do not sync"
//usage:     "\n	-f	Force (don't go through init)"

#include "libbb.h"
#include "reboot.h"

#if ENABLE_FEATURE_WTMP
#include <sys/utsname.h>

static void write_wtmp(void)
{
	struct utmpx utmp;
	struct utsname uts;
	/* "man utmp" says wtmp file should *not* be created automagically */
	/*if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {
		close(creat(bb_path_wtmp_file, 0664));
	}*/
	memset(&utmp, 0, sizeof(utmp));
	utmp.ut_tv.tv_sec = time(NULL);
	strcpy(utmp.ut_user, "shutdown"); /* it is wide enough */
	utmp.ut_type = RUN_LVL;
	utmp.ut_id[0] = '~'; utmp.ut_id[1] = '~'; /* = strcpy(utmp.ut_id, "~~"); */
	utmp.ut_line[0] = '~'; utmp.ut_line[1] = '~'; /* = strcpy(utmp.ut_line, "~~"); */
	uname(&uts);
	safe_strncpy(utmp.ut_host, uts.release, sizeof(utmp.ut_host));
	updwtmpx(bb_path_wtmp_file, &utmp);
}
#else
#define write_wtmp() ((void)0)
#endif


int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int halt_main(int argc UNUSED_PARAM, char **argv)
{
	static const int magic[] = {
		RB_HALT_SYSTEM,
		RB_POWER_OFF,
		RB_AUTOBOOT
	};
	static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };

	int delay = 0;
	int which, flags, rc;

	/* Figure out which applet we're running */
	if (ENABLE_HALT && !ENABLE_POWEROFF && !ENABLE_REBOOT)
		which = 0;
	else
	if (!ENABLE_HALT && ENABLE_POWEROFF && !ENABLE_REBOOT)
		which = 1;
	else
	if (!ENABLE_HALT && !ENABLE_POWEROFF && ENABLE_REBOOT)
		which = 2;
	else
	for (which = 0; "hpr"[which] != applet_name[0]; which++)
		continue;

	/* Parse and handle arguments */
	/* We support -w even if !ENABLE_FEATURE_WTMP,
	 * in order to not break scripts.
	 * -i (shut down network interfaces) is ignored.
	 */
	flags = getopt32(argv, "d:+nfwi", &delay);

	sleep(delay);

	write_wtmp();

	if (flags & 8) /* -w */
		return EXIT_SUCCESS;

	if (!(flags & 2)) /* no -n */
		sync();

	/* Perform action. */
	rc = 1;
	if (!(flags & 4)) { /* no -f */
//TODO: I tend to think that signalling linuxrc is wrong
// pity original author didn't comment on it...
		if (ENABLE_LINUXRC) {
			/* talk to linuxrc */
			/* bbox init/linuxrc assumed */
			pid_t *pidlist = find_pid_by_name("linuxrc");
			if (pidlist[0] > 0)
				rc = kill(pidlist[0], signals[which]);
			if (ENABLE_FEATURE_CLEAN_UP)
				free(pidlist);
		}
		if (rc) {
			/* talk to init */
			if (!ENABLE_FEATURE_CALL_TELINIT) {
				/* bbox init assumed */
				rc = kill(1, signals[which]);
			} else {
				/* SysV style init assumed */
				/* runlevels:
				 * 0 == shutdown
				 * 6 == reboot */
				execlp(CONFIG_TELINIT_PATH,
						CONFIG_TELINIT_PATH,
						which == 2 ? "6" : "0",
						(char *)NULL
				);
				bb_perror_msg_and_die("can't execute '%s'",
						CONFIG_TELINIT_PATH);
			}
		}
	} else {
		rc = reboot(magic[which]);
	}

	if (rc)
		bb_perror_nomsg_and_die();
	return rc;
}