xref: /openbsd/sbin/reboot/reboot.c (revision 3dbceb11)
1*3dbceb11Ssf /*	$OpenBSD: reboot.c,v 1.38 2017/08/22 00:30:16 sf Exp $	*/
2df930be7Sderaadt /*	$NetBSD: reboot.c,v 1.8 1995/10/05 05:36:22 mycroft Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1980, 1986, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt  * modification, are permitted provided that the following conditions
10df930be7Sderaadt  * are met:
11df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
161ef0d710Smillert  * 3. Neither the name of the University nor the names of its contributors
17df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
18df930be7Sderaadt  *    without specific prior written permission.
19df930be7Sderaadt  *
20df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30df930be7Sderaadt  * SUCH DAMAGE.
31df930be7Sderaadt  */
32df930be7Sderaadt 
3301de6d07Smickey #include <sys/types.h>
34df930be7Sderaadt #include <sys/reboot.h>
3538bb3b35Shalex #include <sys/sysctl.h>
369efb13d0Sguenther #include <sys/time.h>
373729fd61Sart #include <sys/wait.h>
3838bb3b35Shalex #include <machine/cpu.h>
39df930be7Sderaadt #include <signal.h>
40df930be7Sderaadt #include <pwd.h>
41df930be7Sderaadt #include <errno.h>
4201de6d07Smickey #include <err.h>
43a1521482Smillert #include <fcntl.h>
44ff86fde3Smillert #include <termios.h>
45df930be7Sderaadt #include <syslog.h>
46df930be7Sderaadt #include <unistd.h>
47df930be7Sderaadt #include <stdio.h>
48df930be7Sderaadt #include <stdlib.h>
49df930be7Sderaadt #include <string.h>
50833e69e8Sderaadt #include <paths.h>
5101de6d07Smickey #include <util.h>
52df930be7Sderaadt 
53c72b5b24Smillert void	usage(void);
5401de6d07Smickey extern char *__progname;
55df930be7Sderaadt 
5671025d0dSderaadt int	dohalt;
5771025d0dSderaadt 
5875a54d2eSderaadt #define _PATH_RC	"/etc/rc"
59833e69e8Sderaadt 
60*3dbceb11Ssf static void
sleep_while_procs(int seconds)61*3dbceb11Ssf sleep_while_procs(int seconds)
62*3dbceb11Ssf {
63*3dbceb11Ssf 	while (seconds > 0) {
64*3dbceb11Ssf 		if (kill(-1, 0) == -1 && errno == ESRCH)
65*3dbceb11Ssf 			return;
66*3dbceb11Ssf 		sleep(1);
67*3dbceb11Ssf 		seconds--;
68*3dbceb11Ssf 	}
69*3dbceb11Ssf }
70*3dbceb11Ssf 
71df930be7Sderaadt int
main(int argc,char * argv[])72bc52e260Sderaadt main(int argc, char *argv[])
73df930be7Sderaadt {
740fd6fe2bSdhill 	unsigned int i;
75df930be7Sderaadt 	struct passwd *pw;
7671025d0dSderaadt 	int ch, howto, lflag, nflag, pflag, qflag;
77df930be7Sderaadt 	char *p, *user;
785b0d3c01Smillert 	sigset_t mask;
79df930be7Sderaadt 
8001de6d07Smickey 	p = __progname;
81ddf9f12cSweingart 
82ddf9f12cSweingart 	/* Nuke login shell */
8371025d0dSderaadt 	if (*p == '-')
8471025d0dSderaadt 		p++;
85ddf9f12cSweingart 
8601de6d07Smickey 	howto = dohalt = lflag = nflag = pflag = qflag = 0;
87ddf9f12cSweingart 	if (!strcmp(p, "halt")) {
88df930be7Sderaadt 		dohalt = 1;
89df930be7Sderaadt 		howto = RB_HALT;
9001de6d07Smickey 	}
9101de6d07Smickey 
92f403a9a2Sdownsj 	while ((ch = getopt(argc, argv, "dlnpq")) != -1)
93df930be7Sderaadt 		switch (ch) {
94f403a9a2Sdownsj 		case 'd':
95f403a9a2Sdownsj 			howto |= RB_DUMP;
96f403a9a2Sdownsj 			break;
97df930be7Sderaadt 		case 'l':	/* Undocumented; used by shutdown. */
98df930be7Sderaadt 			lflag = 1;
99df930be7Sderaadt 			break;
100df930be7Sderaadt 		case 'n':
101df930be7Sderaadt 			nflag = 1;
102df930be7Sderaadt 			howto |= RB_NOSYNC;
103df930be7Sderaadt 			break;
104f403a9a2Sdownsj 		case 'p':
105f403a9a2Sdownsj 			/* Only works if we're called as halt. */
106f403a9a2Sdownsj 			if (dohalt) {
107f403a9a2Sdownsj 				pflag = 1;
108f403a9a2Sdownsj 				howto |= RB_POWERDOWN;
109f403a9a2Sdownsj 			}
110f403a9a2Sdownsj 			break;
111df930be7Sderaadt 		case 'q':
112df930be7Sderaadt 			qflag = 1;
113df930be7Sderaadt 			break;
114df930be7Sderaadt 		default:
115df930be7Sderaadt 			usage();
116df930be7Sderaadt 		}
117df930be7Sderaadt 	argc -= optind;
118df930be7Sderaadt 	argv += optind;
119df930be7Sderaadt 
12071025d0dSderaadt 	if (argc)
12171025d0dSderaadt 		usage();
12271025d0dSderaadt 
123df930be7Sderaadt 	if (geteuid())
12401de6d07Smickey 		errx(1, "%s", strerror(EPERM));
125df930be7Sderaadt 
12602638669Snatano #ifdef CPU_LIDACTION
12738bb3b35Shalex 	if (howto & RB_POWERDOWN) {
12838bb3b35Shalex 		/* Disable suspending on laptop lid close */
1292d357aedSnatano 		int mib[] = {CTL_MACHDEP, CPU_LIDACTION};
1302d357aedSnatano 		int lidaction = 0;
13138bb3b35Shalex 
1322d357aedSnatano 		if (sysctl(mib, 2, NULL, NULL, &lidaction,
1332d357aedSnatano 		    sizeof(lidaction)) == -1 && errno != EOPNOTSUPP)
13438bb3b35Shalex 			warn("sysctl");
13538bb3b35Shalex 	}
13602638669Snatano #endif /* CPU_LIDACTION */
13738bb3b35Shalex 
138df930be7Sderaadt 	if (qflag) {
139df930be7Sderaadt 		reboot(howto);
14001de6d07Smickey 		err(1, "reboot");
141df930be7Sderaadt 	}
142df930be7Sderaadt 
143df930be7Sderaadt 	/* Log the reboot. */
144df930be7Sderaadt 	if (!lflag)  {
145df930be7Sderaadt 		if ((user = getlogin()) == NULL)
146df930be7Sderaadt 			user = (pw = getpwuid(getuid())) ?
147df930be7Sderaadt 			    pw->pw_name : "???";
148df930be7Sderaadt 		if (dohalt) {
149df930be7Sderaadt 			openlog("halt", 0, LOG_AUTH | LOG_CONS);
150f403a9a2Sdownsj 			if (pflag) {
151f403a9a2Sdownsj 				syslog(LOG_CRIT,
152f403a9a2Sdownsj 					"halted (with powerdown) by %s", user);
153f403a9a2Sdownsj 			} else {
154df930be7Sderaadt 				syslog(LOG_CRIT, "halted by %s", user);
155f403a9a2Sdownsj 			}
156df930be7Sderaadt 		} else {
157df930be7Sderaadt 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
158df930be7Sderaadt 			syslog(LOG_CRIT, "rebooted by %s", user);
159df930be7Sderaadt 		}
160df930be7Sderaadt 	}
161df930be7Sderaadt 	logwtmp("~", "shutdown", "");
162df930be7Sderaadt 
163df930be7Sderaadt 	/*
164df930be7Sderaadt 	 * Do a sync early on, so disks start transfers while we're off
165df930be7Sderaadt 	 * killing processes.  Don't worry about writes done before the
166df930be7Sderaadt 	 * processes die, the reboot system call syncs the disks.
167df930be7Sderaadt 	 */
168df930be7Sderaadt 	if (!nflag)
169df930be7Sderaadt 		sync();
170df930be7Sderaadt 
171df930be7Sderaadt 	/* Just stop init -- if we fail, we'll restart it. */
172df930be7Sderaadt 	if (kill(1, SIGTSTP) == -1)
17301de6d07Smickey 		err(1, "SIGTSTP init");
174df930be7Sderaadt 
175df930be7Sderaadt 	/* Ignore the SIGHUP we get when our parent shell dies. */
176df930be7Sderaadt 	(void)signal(SIGHUP, SIG_IGN);
177df930be7Sderaadt 
17839c69f54Sderaadt 	/*
17939c69f54Sderaadt 	 * If we're running in a pipeline, we don't want to die
18039c69f54Sderaadt 	 * after killing whatever we're writing to.
18139c69f54Sderaadt 	 */
18239c69f54Sderaadt 	(void)signal(SIGPIPE, SIG_IGN);
18339c69f54Sderaadt 
18475a54d2eSderaadt 	if (access(_PATH_RC, R_OK) != -1) {
185833e69e8Sderaadt 		pid_t pid;
186c4849fe1Sderaadt 		struct termios t;
187433c54bfShenning 		int fd, status;
188833e69e8Sderaadt 
189833e69e8Sderaadt 		switch ((pid = fork())) {
190833e69e8Sderaadt 		case -1:
191833e69e8Sderaadt 			break;
192833e69e8Sderaadt 		case 0:
193c4849fe1Sderaadt 			if (revoke(_PATH_CONSOLE) == -1)
19466420897Smickey 				warn("revoke");
195c4849fe1Sderaadt 			if (setsid() == -1)
19666420897Smickey 				warn("setsid");
197c4849fe1Sderaadt 			fd = open(_PATH_CONSOLE, O_RDWR);
198c4849fe1Sderaadt 			if (fd == -1)
19966420897Smickey 				warn("open");
200c4849fe1Sderaadt 			dup2(fd, 0);
201c4849fe1Sderaadt 			dup2(fd, 1);
202c4849fe1Sderaadt 			dup2(fd, 2);
203c4849fe1Sderaadt 			if (fd > 2)
204c4849fe1Sderaadt 				close(fd);
205c4849fe1Sderaadt 
206c4849fe1Sderaadt 			/* At a minimum... */
207c4849fe1Sderaadt 			tcgetattr(0, &t);
208c4849fe1Sderaadt 			t.c_oflag |= (ONLCR | OPOST);
209c4849fe1Sderaadt 			tcsetattr(0, TCSANOW, &t);
210c4849fe1Sderaadt 
211c96f6a27Sderaadt 			execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL);
212c4849fe1Sderaadt 			_exit(1);
213833e69e8Sderaadt 		default:
214433c54bfShenning 			/* rc exits 2 if powerdown=YES in rc.shutdown */
215433c54bfShenning 			waitpid(pid, &status, 0);
2169179587aStedu 			if (dohalt && WIFEXITED(status) && WEXITSTATUS(status) == 2)
217433c54bfShenning 				howto |= RB_POWERDOWN;
218833e69e8Sderaadt 		}
219833e69e8Sderaadt 	}
220833e69e8Sderaadt 
2215b0d3c01Smillert 	/*
2225b0d3c01Smillert 	 * Point of no return, block all signals so we are sure to
2235b0d3c01Smillert 	 * reach the call to reboot(2) unmolested.
2245b0d3c01Smillert 	 */
2255b0d3c01Smillert 	sigfillset(&mask);
2265b0d3c01Smillert 	sigprocmask(SIG_BLOCK, &mask, NULL);
2275b0d3c01Smillert 
228df930be7Sderaadt 	/* Send a SIGTERM first, a chance to save the buffers. */
22913422552Sderaadt 	if (kill(-1, SIGTERM) == -1) {
23013422552Sderaadt 		/*
23113422552Sderaadt 		 * If ESRCH, everything's OK: we're the only non-system
23213422552Sderaadt 		 * process!  That can happen e.g. via 'exec reboot' in
23313422552Sderaadt 		 * single-user mode.
23413422552Sderaadt 		 */
23513422552Sderaadt 		if (errno != ESRCH) {
23601de6d07Smickey 			warn("SIGTERM processes");
23713422552Sderaadt 			goto restart;
23813422552Sderaadt 		}
23913422552Sderaadt 	}
240df930be7Sderaadt 
241df930be7Sderaadt 	/*
242df930be7Sderaadt 	 * After the processes receive the signal, start the rest of the
243f3e19106Sderaadt 	 * buffers on their way.  Wait 5 seconds between the SIGTERM and
244f3e19106Sderaadt 	 * the SIGKILL to give everybody a chance.
245df930be7Sderaadt 	 */
246*3dbceb11Ssf 	sleep_while_procs(2);
247df930be7Sderaadt 	if (!nflag)
248df930be7Sderaadt 		sync();
249*3dbceb11Ssf 	sleep_while_procs(3);
250df930be7Sderaadt 
251df930be7Sderaadt 	for (i = 1;; ++i) {
252df930be7Sderaadt 		if (kill(-1, SIGKILL) == -1) {
253df930be7Sderaadt 			if (errno == ESRCH)
254df930be7Sderaadt 				break;
255df930be7Sderaadt 			goto restart;
256df930be7Sderaadt 		}
257df930be7Sderaadt 		if (i > 5) {
25801de6d07Smickey 			warnx("WARNING: some process(es) wouldn't die");
259df930be7Sderaadt 			break;
260df930be7Sderaadt 		}
261*3dbceb11Ssf 		sleep_while_procs(2 * i);
262df930be7Sderaadt 	}
263df930be7Sderaadt 
264df930be7Sderaadt 	reboot(howto);
265df930be7Sderaadt 	/* FALLTHROUGH */
266df930be7Sderaadt 
267df930be7Sderaadt restart:
26801de6d07Smickey 	errx(1, kill(1, SIGHUP) == -1 ? "(can't restart init): " : "");
269df930be7Sderaadt 	/* NOTREACHED */
270df930be7Sderaadt }
271df930be7Sderaadt 
272df930be7Sderaadt void
usage(void)273bc52e260Sderaadt usage(void)
274df930be7Sderaadt {
27571025d0dSderaadt 	fprintf(stderr, "usage: %s [-dn%sq]\n", __progname,
27671025d0dSderaadt 	    dohalt ? "p" : "");
277df930be7Sderaadt 	exit(1);
278df930be7Sderaadt }
279