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