xref: /openbsd/sbin/reboot/reboot.c (revision 264ca280)
1 /*	$OpenBSD: reboot.c,v 1.34 2015/01/16 06:40:00 deraadt Exp $	*/
2 /*	$NetBSD: reboot.c,v 1.8 1995/10/05 05:36:22 mycroft Exp $	*/
3 
4 /*
5  * Copyright (c) 1980, 1986, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/reboot.h>
35 #include <sys/fcntl.h>
36 #include <sys/sysctl.h>
37 #include <sys/wait.h>
38 #include <machine/cpu.h>
39 #include <signal.h>
40 #include <pwd.h>
41 #include <errno.h>
42 #include <err.h>
43 #include <fcntl.h>
44 #include <termios.h>
45 #include <syslog.h>
46 #include <unistd.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <paths.h>
51 #include <util.h>
52 
53 void	usage(void);
54 extern char *__progname;
55 
56 int	dohalt;
57 
58 #define _PATH_RC	"/etc/rc"
59 
60 int
61 main(int argc, char *argv[])
62 {
63 	unsigned int i;
64 	struct passwd *pw;
65 	int ch, howto, lflag, nflag, pflag, qflag;
66 	char *p, *user;
67 	sigset_t mask;
68 
69 	p = __progname;
70 
71 	/* Nuke login shell */
72 	if (*p == '-')
73 		p++;
74 
75 	howto = dohalt = lflag = nflag = pflag = qflag = 0;
76 	if (!strcmp(p, "halt")) {
77 		dohalt = 1;
78 		howto = RB_HALT;
79 	}
80 
81 	while ((ch = getopt(argc, argv, "dlnpq")) != -1)
82 		switch (ch) {
83 		case 'd':
84 			howto |= RB_DUMP;
85 			break;
86 		case 'l':	/* Undocumented; used by shutdown. */
87 			lflag = 1;
88 			break;
89 		case 'n':
90 			nflag = 1;
91 			howto |= RB_NOSYNC;
92 			break;
93 		case 'p':
94 			/* Only works if we're called as halt. */
95 			if (dohalt) {
96 				pflag = 1;
97 				howto |= RB_POWERDOWN;
98 			}
99 			break;
100 		case 'q':
101 			qflag = 1;
102 			break;
103 		default:
104 			usage();
105 		}
106 	argc -= optind;
107 	argv += optind;
108 
109 	if (argc)
110 		usage();
111 
112 	if (geteuid())
113 		errx(1, "%s", strerror(EPERM));
114 
115 #ifdef CPU_LIDSUSPEND
116 	if (howto & RB_POWERDOWN) {
117 		/* Disable suspending on laptop lid close */
118 		int mib[2];
119 		int lidsuspend = 0;
120 
121 		mib[0] = CTL_MACHDEP;
122 		mib[1] = CPU_LIDSUSPEND;
123 		if (sysctl(mib, 2, NULL, NULL, &lidsuspend,
124 		    sizeof(lidsuspend)) == -1 && errno != EOPNOTSUPP)
125 			warn("sysctl");
126 	}
127 #endif /* CPU_LIDSUSPEND */
128 
129 	if (qflag) {
130 		reboot(howto);
131 		err(1, "reboot");
132 	}
133 
134 	/* Log the reboot. */
135 	if (!lflag)  {
136 		if ((user = getlogin()) == NULL)
137 			user = (pw = getpwuid(getuid())) ?
138 			    pw->pw_name : "???";
139 		if (dohalt) {
140 			openlog("halt", 0, LOG_AUTH | LOG_CONS);
141 			if (pflag) {
142 				syslog(LOG_CRIT,
143 					"halted (with powerdown) by %s", user);
144 			} else {
145 				syslog(LOG_CRIT, "halted by %s", user);
146 			}
147 		} else {
148 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
149 			syslog(LOG_CRIT, "rebooted by %s", user);
150 		}
151 	}
152 	logwtmp("~", "shutdown", "");
153 
154 	/*
155 	 * Do a sync early on, so disks start transfers while we're off
156 	 * killing processes.  Don't worry about writes done before the
157 	 * processes die, the reboot system call syncs the disks.
158 	 */
159 	if (!nflag)
160 		sync();
161 
162 	/* Just stop init -- if we fail, we'll restart it. */
163 	if (kill(1, SIGTSTP) == -1)
164 		err(1, "SIGTSTP init");
165 
166 	/* Ignore the SIGHUP we get when our parent shell dies. */
167 	(void)signal(SIGHUP, SIG_IGN);
168 
169 	/*
170 	 * If we're running in a pipeline, we don't want to die
171 	 * after killing whatever we're writing to.
172 	 */
173 	(void)signal(SIGPIPE, SIG_IGN);
174 
175 	if (access(_PATH_RC, R_OK) != -1) {
176 		pid_t pid;
177 		struct termios t;
178 		int fd, status;
179 
180 		switch ((pid = fork())) {
181 		case -1:
182 			break;
183 		case 0:
184 			if (revoke(_PATH_CONSOLE) == -1)
185 				warn("revoke");
186 			if (setsid() == -1)
187 				warn("setsid");
188 			fd = open(_PATH_CONSOLE, O_RDWR);
189 			if (fd == -1)
190 				warn("open");
191 			dup2(fd, 0);
192 			dup2(fd, 1);
193 			dup2(fd, 2);
194 			if (fd > 2)
195 				close(fd);
196 
197 			/* At a minimum... */
198 			tcgetattr(0, &t);
199 			t.c_oflag |= (ONLCR | OPOST);
200 			tcsetattr(0, TCSANOW, &t);
201 
202 			execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL);
203 			_exit(1);
204 		default:
205 			/* rc exits 2 if powerdown=YES in rc.shutdown */
206 			waitpid(pid, &status, 0);
207 			if (dohalt && WIFEXITED(status) && WEXITSTATUS(status) == 2)
208 				howto |= RB_POWERDOWN;
209 		}
210 	}
211 
212 	/*
213 	 * Point of no return, block all signals so we are sure to
214 	 * reach the call to reboot(2) unmolested.
215 	 */
216 	sigfillset(&mask);
217 	sigprocmask(SIG_BLOCK, &mask, NULL);
218 
219 	/* Send a SIGTERM first, a chance to save the buffers. */
220 	if (kill(-1, SIGTERM) == -1) {
221 		/*
222 		 * If ESRCH, everything's OK: we're the only non-system
223 		 * process!  That can happen e.g. via 'exec reboot' in
224 		 * single-user mode.
225 		 */
226 		if (errno != ESRCH) {
227 			warn("SIGTERM processes");
228 			goto restart;
229 		}
230 	}
231 
232 	/*
233 	 * After the processes receive the signal, start the rest of the
234 	 * buffers on their way.  Wait 5 seconds between the SIGTERM and
235 	 * the SIGKILL to give everybody a chance.
236 	 */
237 	sleep(2);
238 	if (!nflag)
239 		sync();
240 	sleep(3);
241 
242 	for (i = 1;; ++i) {
243 		if (kill(-1, SIGKILL) == -1) {
244 			if (errno == ESRCH)
245 				break;
246 			goto restart;
247 		}
248 		if (i > 5) {
249 			warnx("WARNING: some process(es) wouldn't die");
250 			break;
251 		}
252 		(void)sleep(2 * i);
253 	}
254 
255 	reboot(howto);
256 	/* FALLTHROUGH */
257 
258 restart:
259 	errx(1, kill(1, SIGHUP) == -1 ? "(can't restart init): " : "");
260 	/* NOTREACHED */
261 }
262 
263 void
264 usage(void)
265 {
266 	fprintf(stderr, "usage: %s [-dn%sq]\n", __progname,
267 	    dohalt ? "p" : "");
268 	exit(1);
269 }
270