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