Include -Wunreachable-code when building.
[sysvinit.git] / src / halt.c
CommitLineData
a74aeac6
PR
1/*
2 * Halt Stop the system running.
3 * It re-enables CTRL-ALT-DEL, so that a hard reboot can
4 * be done. If called as reboot, it will reboot the system.
5 *
6 * If the system is not in runlevel 0 or 6, halt will just
7 * execute a "shutdown -h" to halt the system, and reboot will
8 * execute an "shutdown -r". This is for compatibility with
9 * sysvinit 2.4.
10 *
11 * Usage: halt [-n] [-w] [-d] [-f] [-h] [-i] [-p]
12 * -n: don't sync before halting the system
13 * -w: only write a wtmp reboot record and exit.
14 * -d: don't write a wtmp record.
15 * -f: force halt/reboot, don't call shutdown.
16 * -h: put harddisks in standby mode
17 * -i: shut down all network interfaces.
18 * -p: power down the system (if possible, otherwise halt).
19 *
20 * Reboot and halt are both this program. Reboot
21 * is just a link to halt. Invoking the program
22 * as poweroff implies the -p option.
23 *
24 * Author: Miquel van Smoorenburg, miquels@cistron.nl
25 *
26 * Version: 2.86, 30-Jul-2004
27 *
28 * This file is part of the sysvinit suite,
29 * Copyright (C) 1991-2004 Miquel van Smoorenburg.
30 *
31 * This program is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published by
33 * the Free Software Foundation; either version 2 of the License, or
34 * (at your option) any later version.
35 *
36 * This program is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
40 *
41 * You should have received a copy of the GNU General Public License
42 * along with this program; if not, write to the Free Software
43 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
44 */
45
46#include <sys/types.h>
47#include <sys/stat.h>
48#include <sys/param.h>
49#include <stdlib.h>
50#include <utmp.h>
51#include <fcntl.h>
52#include <string.h>
53#include <unistd.h>
54#include <sys/times.h>
55#include <time.h>
56#include <signal.h>
57#include <stdio.h>
58#include <getopt.h>
59#include "reboot.h"
60
61char *Version = "@(#)halt 2.86 31-Jul-2004 miquels@cistron.nl";
62char *progname;
63
64#define KERNEL_MONITOR 1 /* If halt() puts you into the kernel monitor. */
65#define RUNLVL_PICKY 0 /* Be picky about the runlevel */
66
67extern int ifdown(void);
68extern int hddown(void);
5f959b29 69extern int hdflush(void);
a74aeac6
PR
70extern void write_wtmp(char *user, char *id, int pid, int type, char *line);
71
72/*
73 * Send usage message.
74 */
75void usage(void)
76{
77 fprintf(stderr, "usage: %s [-n] [-w] [-d] [-f] [-h] [-i]%s\n",
78 progname, strcmp(progname, "halt") ? "" : " [-p]");
79 fprintf(stderr, "\t-n: don't sync before halting the system\n");
80 fprintf(stderr, "\t-w: only write a wtmp reboot record and exit.\n");
81 fprintf(stderr, "\t-d: don't write a wtmp record.\n");
82 fprintf(stderr, "\t-f: force halt/reboot, don't call shutdown.\n");
83 fprintf(stderr, "\t-h: put harddisks in standby mode.\n");
84 fprintf(stderr, "\t-i: shut down all network interfaces.\n");
85 if (!strcmp(progname, "halt"))
86 fprintf(stderr, "\t-p: power down the system (if possible, otherwise halt).\n");
87 exit(1);
88}
89
90/*
91 * See if we were started directly from init.
92 * Get the runlevel from /var/run/utmp or the environment.
93 */
94int get_runlevel(void)
95{
96 struct utmp *ut;
97 char *r;
98#if RUNLVL_PICKY
99 time_t boottime;
100#endif
101
102 /*
103 * First see if we were started directly from init.
104 */
105 if (getenv("INIT_VERSION") && (r = getenv("RUNLEVEL")) != NULL)
106 return *r;
107
108 /*
109 * Hmm, failed - read runlevel from /var/run/utmp..
110 */
111#if RUNLVL_PICKY
112 /*
113 * Get boottime from the kernel.
114 */
115 time(&boottime);
116 boottime -= (times(NULL) / HZ);
117#endif
118
119 /*
120 * Find runlevel in utmp.
121 */
122 setutent();
123 while ((ut = getutent()) != NULL) {
124#if RUNLVL_PICKY
125 /*
126 * Only accept value if it's from after boottime.
127 */
128 if (ut->ut_type == RUN_LVL && ut->ut_time > boottime)
129 return (ut->ut_pid & 255);
130#else
131 if (ut->ut_type == RUN_LVL)
132 return (ut->ut_pid & 255);
133#endif
134 }
135 endutent();
136
137 /* This should not happen but warn the user! */
138 fprintf(stderr, "WARNING: could not determine runlevel"
139 " - doing soft %s\n", progname);
140 fprintf(stderr, " (it's better to use shutdown instead of %s"
141 " from the command line)\n", progname);
142
143 return -1;
144}
145
146/*
147 * Switch to another runlevel.
148 */
149void do_shutdown(char *fl, char *tm)
150{
151 char *args[8];
152 int i = 0;
153
154 args[i++] = "shutdown";
155 args[i++] = fl;
156 if (tm) {
157 args[i++] = "-t";
158 args[i++] = tm;
159 }
160 args[i++] = "now";
161 args[i++] = NULL;
162
163 execv("/sbin/shutdown", args);
164 execv("/etc/shutdown", args);
165 execv("/bin/shutdown", args);
166
167 perror("shutdown");
168 exit(1);
169}
170
171/*
172 * Main program.
173 * Write a wtmp entry and reboot cq. halt.
174 */
175int main(int argc, char **argv)
176{
177 int do_reboot = 0;
178 int do_sync = 1;
179 int do_wtmp = 1;
180 int do_nothing = 0;
181 int do_hard = 0;
182 int do_ifdown = 0;
183 int do_hddown = 0;
184 int do_poweroff = 0;
185 int c;
186 char *tm = NULL;
187
188 /*
189 * Find out who we are
190 */
191 /* Remove dash passed on in argv[0] when used as login shell. */
192 if (argv[0][0] == '-') argv[0]++;
193 if ((progname = strrchr(argv[0], '/')) != NULL)
194 progname++;
195 else
196 progname = argv[0];
197
198 if (!strcmp(progname, "reboot")) do_reboot = 1;
199 if (!strcmp(progname, "poweroff")) do_poweroff = 1;
200
201 /*
202 * Get flags
203 */
204 while((c = getopt(argc, argv, ":ihdfnpwt:")) != EOF) {
205 switch(c) {
206 case 'n':
207 do_sync = 0;
208 do_wtmp = 0;
209 break;
210 case 'w':
211 do_nothing = 1;
212 break;
213 case 'd':
214 do_wtmp = 0;
215 break;
216 case 'f':
217 do_hard = 1;
218 break;
219 case 'i':
220 do_ifdown = 1;
221 break;
222 case 'h':
223 do_hddown = 1;
224 break;
225 case 'p':
226 do_poweroff = 1;
227 break;
228 case 't':
229 tm = optarg;
230 break;
231 default:
232 usage();
233 }
234 }
235 if (argc != optind) usage();
236
237 if (geteuid() != 0) {
238 fprintf(stderr, "%s: must be superuser.\n", progname);
239 exit(1);
240 }
241
c5ae561e
DWF
242 if (chdir("/")) {
243 fprintf(stderr, "%s: chdir(/): %m\n", progname);
244 exit(1);
245 }
a74aeac6
PR
246
247 if (!do_hard && !do_nothing) {
248 /*
249 * See if we are in runlevel 0 or 6.
250 */
251 c = get_runlevel();
252 if (c != '0' && c != '6')
253 do_shutdown(do_reboot ? "-r" : "-h", tm);
254 }
255
256 /*
257 * Record the fact that we're going down
258 */
259 if (do_wtmp)
260 write_wtmp("shutdown", "~~", 0, RUN_LVL, "~~");
261
262 /*
263 * Exit if all we wanted to do was write a wtmp record.
264 */
265 if (do_nothing && !do_hddown && !do_ifdown) exit(0);
266
267 if (do_sync) {
268 sync();
269 sleep(2);
270 }
271
272 if (do_ifdown)
273 (void)ifdown();
274
275 if (do_hddown)
276 (void)hddown();
46fd2e25
DWF
277 else
278 (void)hdflush();
a74aeac6
PR
279
280 if (do_nothing) exit(0);
281
282 if (do_reboot) {
283 init_reboot(BMAGIC_REBOOT);
284 } else {
285 /*
286 * Turn on hard reboot, CTRL-ALT-DEL will reboot now
287 */
288#ifdef BMAGIC_HARD
289 init_reboot(BMAGIC_HARD);
290#endif
291
292 /*
293 * Stop init; it is insensitive to the signals sent
294 * by the kernel.
295 */
296 kill(1, SIGTSTP);
297
298 /*
299 * Halt or poweroff.
300 */
301 if (do_poweroff)
302 init_reboot(BMAGIC_POWEROFF);
303 /*
304 * Fallthrough if failed.
305 */
306 init_reboot(BMAGIC_HALT);
307 }
308
309 /*
310 * If we return, we (c)ontinued from the kernel monitor.
311 */
312#ifdef BMAGIC_SOFT
313 init_reboot(BMAGIC_SOFT);
314#endif
315 kill(1, SIGCONT);
316
317 exit(0);
318}