xref: /openbsd-src/sbin/reboot/reboot.c (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1 /*	$OpenBSD: reboot.c,v 1.38 2017/08/22 00:30:16 sf 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/sysctl.h>
36 #include <sys/time.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 static void
61 sleep_while_procs(int seconds)
62 {
63 	while (seconds > 0) {
64 		if (kill(-1, 0) == -1 && errno == ESRCH)
65 			return;
66 		sleep(1);
67 		seconds--;
68 	}
69 }
70 
71 int
72 main(int argc, char *argv[])
73 {
74 	unsigned int i;
75 	struct passwd *pw;
76 	int ch, howto, lflag, nflag, pflag, qflag;
77 	char *p, *user;
78 	sigset_t mask;
79 
80 	p = __progname;
81 
82 	/* Nuke login shell */
83 	if (*p == '-')
84 		p++;
85 
86 	howto = dohalt = lflag = nflag = pflag = qflag = 0;
87 	if (!strcmp(p, "halt")) {
88 		dohalt = 1;
89 		howto = RB_HALT;
90 	}
91 
92 	while ((ch = getopt(argc, argv, "dlnpq")) != -1)
93 		switch (ch) {
94 		case 'd':
95 			howto |= RB_DUMP;
96 			break;
97 		case 'l':	/* Undocumented; used by shutdown. */
98 			lflag = 1;
99 			break;
100 		case 'n':
101 			nflag = 1;
102 			howto |= RB_NOSYNC;
103 			break;
104 		case 'p':
105 			/* Only works if we're called as halt. */
106 			if (dohalt) {
107 				pflag = 1;
108 				howto |= RB_POWERDOWN;
109 			}
110 			break;
111 		case 'q':
112 			qflag = 1;
113 			break;
114 		default:
115 			usage();
116 		}
117 	argc -= optind;
118 	argv += optind;
119 
120 	if (argc)
121 		usage();
122 
123 	if (geteuid())
124 		errx(1, "%s", strerror(EPERM));
125 
126 #ifdef CPU_LIDACTION
127 	if (howto & RB_POWERDOWN) {
128 		/* Disable suspending on laptop lid close */
129 		int mib[] = {CTL_MACHDEP, CPU_LIDACTION};
130 		int lidaction = 0;
131 
132 		if (sysctl(mib, 2, NULL, NULL, &lidaction,
133 		    sizeof(lidaction)) == -1 && errno != EOPNOTSUPP)
134 			warn("sysctl");
135 	}
136 #endif /* CPU_LIDACTION */
137 
138 	if (qflag) {
139 		reboot(howto);
140 		err(1, "reboot");
141 	}
142 
143 	/* Log the reboot. */
144 	if (!lflag)  {
145 		if ((user = getlogin()) == NULL)
146 			user = (pw = getpwuid(getuid())) ?
147 			    pw->pw_name : "???";
148 		if (dohalt) {
149 			openlog("halt", 0, LOG_AUTH | LOG_CONS);
150 			if (pflag) {
151 				syslog(LOG_CRIT,
152 					"halted (with powerdown) by %s", user);
153 			} else {
154 				syslog(LOG_CRIT, "halted by %s", user);
155 			}
156 		} else {
157 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
158 			syslog(LOG_CRIT, "rebooted by %s", user);
159 		}
160 	}
161 	logwtmp("~", "shutdown", "");
162 
163 	/*
164 	 * Do a sync early on, so disks start transfers while we're off
165 	 * killing processes.  Don't worry about writes done before the
166 	 * processes die, the reboot system call syncs the disks.
167 	 */
168 	if (!nflag)
169 		sync();
170 
171 	/* Just stop init -- if we fail, we'll restart it. */
172 	if (kill(1, SIGTSTP) == -1)
173 		err(1, "SIGTSTP init");
174 
175 	/* Ignore the SIGHUP we get when our parent shell dies. */
176 	(void)signal(SIGHUP, SIG_IGN);
177 
178 	/*
179 	 * If we're running in a pipeline, we don't want to die
180 	 * after killing whatever we're writing to.
181 	 */
182 	(void)signal(SIGPIPE, SIG_IGN);
183 
184 	if (access(_PATH_RC, R_OK) != -1) {
185 		pid_t pid;
186 		struct termios t;
187 		int fd, status;
188 
189 		switch ((pid = fork())) {
190 		case -1:
191 			break;
192 		case 0:
193 			if (revoke(_PATH_CONSOLE) == -1)
194 				warn("revoke");
195 			if (setsid() == -1)
196 				warn("setsid");
197 			fd = open(_PATH_CONSOLE, O_RDWR);
198 			if (fd == -1)
199 				warn("open");
200 			dup2(fd, 0);
201 			dup2(fd, 1);
202 			dup2(fd, 2);
203 			if (fd > 2)
204 				close(fd);
205 
206 			/* At a minimum... */
207 			tcgetattr(0, &t);
208 			t.c_oflag |= (ONLCR | OPOST);
209 			tcsetattr(0, TCSANOW, &t);
210 
211 			execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL);
212 			_exit(1);
213 		default:
214 			/* rc exits 2 if powerdown=YES in rc.shutdown */
215 			waitpid(pid, &status, 0);
216 			if (dohalt && WIFEXITED(status) && WEXITSTATUS(status) == 2)
217 				howto |= RB_POWERDOWN;
218 		}
219 	}
220 
221 	/*
222 	 * Point of no return, block all signals so we are sure to
223 	 * reach the call to reboot(2) unmolested.
224 	 */
225 	sigfillset(&mask);
226 	sigprocmask(SIG_BLOCK, &mask, NULL);
227 
228 	/* Send a SIGTERM first, a chance to save the buffers. */
229 	if (kill(-1, SIGTERM) == -1) {
230 		/*
231 		 * If ESRCH, everything's OK: we're the only non-system
232 		 * process!  That can happen e.g. via 'exec reboot' in
233 		 * single-user mode.
234 		 */
235 		if (errno != ESRCH) {
236 			warn("SIGTERM processes");
237 			goto restart;
238 		}
239 	}
240 
241 	/*
242 	 * After the processes receive the signal, start the rest of the
243 	 * buffers on their way.  Wait 5 seconds between the SIGTERM and
244 	 * the SIGKILL to give everybody a chance.
245 	 */
246 	sleep_while_procs(2);
247 	if (!nflag)
248 		sync();
249 	sleep_while_procs(3);
250 
251 	for (i = 1;; ++i) {
252 		if (kill(-1, SIGKILL) == -1) {
253 			if (errno == ESRCH)
254 				break;
255 			goto restart;
256 		}
257 		if (i > 5) {
258 			warnx("WARNING: some process(es) wouldn't die");
259 			break;
260 		}
261 		sleep_while_procs(2 * i);
262 	}
263 
264 	reboot(howto);
265 	/* FALLTHROUGH */
266 
267 restart:
268 	errx(1, kill(1, SIGHUP) == -1 ? "(can't restart init): " : "");
269 	/* NOTREACHED */
270 }
271 
272 void
273 usage(void)
274 {
275 	fprintf(stderr, "usage: %s [-dn%sq]\n", __progname,
276 	    dohalt ? "p" : "");
277 	exit(1);
278 }
279