xref: /openbsd-src/sbin/reboot/reboot.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: reboot.c,v 1.29 2007/05/11 01:53:07 tedu 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 #ifndef lint
34 static char copyright[] =
35 "@(#) Copyright (c) 1980, 1986, 1993\n\
36 	The Regents of the University of California.  All rights reserved.\n";
37 #endif /* not lint */
38 
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)reboot.c	8.1 (Berkeley) 6/5/93";
42 #else
43 static char rcsid[] = "$OpenBSD: reboot.c,v 1.29 2007/05/11 01:53:07 tedu Exp $";
44 #endif
45 #endif /* not lint */
46 
47 #include <sys/types.h>
48 #include <sys/reboot.h>
49 #include <sys/fcntl.h>
50 #include <sys/wait.h>
51 #include <signal.h>
52 #include <pwd.h>
53 #include <errno.h>
54 #include <err.h>
55 #include <fcntl.h>
56 #include <termios.h>
57 #include <syslog.h>
58 #include <unistd.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <paths.h>
63 #include <util.h>
64 
65 void	usage(void);
66 extern char *__progname;
67 
68 int	dohalt;
69 
70 #define _PATH_RC	"/etc/rc"
71 
72 int
73 main(int argc, char *argv[])
74 {
75 	unsigned int i;
76 	struct passwd *pw;
77 	int ch, howto, lflag, nflag, pflag, qflag;
78 	char *p, *user;
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 	if (qflag) {
127 		reboot(howto);
128 		err(1, "reboot");
129 	}
130 
131 	/* Log the reboot. */
132 	if (!lflag)  {
133 		if ((user = getlogin()) == NULL)
134 			user = (pw = getpwuid(getuid())) ?
135 			    pw->pw_name : "???";
136 		if (dohalt) {
137 			openlog("halt", 0, LOG_AUTH | LOG_CONS);
138 			if (pflag) {
139 				syslog(LOG_CRIT,
140 					"halted (with powerdown) by %s", user);
141 			} else {
142 				syslog(LOG_CRIT, "halted by %s", user);
143 			}
144 		} else {
145 			openlog("reboot", 0, LOG_AUTH | LOG_CONS);
146 			syslog(LOG_CRIT, "rebooted by %s", user);
147 		}
148 	}
149 	logwtmp("~", "shutdown", "");
150 
151 	/*
152 	 * Do a sync early on, so disks start transfers while we're off
153 	 * killing processes.  Don't worry about writes done before the
154 	 * processes die, the reboot system call syncs the disks.
155 	 */
156 	if (!nflag)
157 		sync();
158 
159 	/* Just stop init -- if we fail, we'll restart it. */
160 	if (kill(1, SIGTSTP) == -1)
161 		err(1, "SIGTSTP init");
162 
163 	/* Ignore the SIGHUP we get when our parent shell dies. */
164 	(void)signal(SIGHUP, SIG_IGN);
165 
166 	/*
167 	 * If we're running in a pipeline, we don't want to die
168 	 * after killing whatever we're writing to.
169 	 */
170 	(void)signal(SIGPIPE, SIG_IGN);
171 
172 	if (access(_PATH_RC, R_OK) != -1) {
173 		pid_t pid;
174 		struct termios t;
175 		int fd, status;
176 
177 		switch ((pid = fork())) {
178 		case -1:
179 			break;
180 		case 0:
181 			if (revoke(_PATH_CONSOLE) == -1)
182 				warn("revoke");
183 			if (setsid() == -1)
184 				warn("setsid");
185 			fd = open(_PATH_CONSOLE, O_RDWR);
186 			if (fd == -1)
187 				warn("open");
188 			dup2(fd, 0);
189 			dup2(fd, 1);
190 			dup2(fd, 2);
191 			if (fd > 2)
192 				close(fd);
193 
194 			/* At a minimum... */
195 			tcgetattr(0, &t);
196 			t.c_oflag |= (ONLCR | OPOST);
197 			tcsetattr(0, TCSANOW, &t);
198 
199 			execl(_PATH_BSHELL, "sh", _PATH_RC, "shutdown", (char *)NULL);
200 			_exit(1);
201 		default:
202 			/* rc exits 2 if powerdown=YES in rc.shutdown */
203 			waitpid(pid, &status, 0);
204 			if (dohalt && WIFEXITED(status) && WEXITSTATUS(status) == 2)
205 				howto |= RB_POWERDOWN;
206 		}
207 	}
208 
209 	/* Send a SIGTERM first, a chance to save the buffers. */
210 	if (kill(-1, SIGTERM) == -1) {
211 		/*
212 		 * If ESRCH, everything's OK: we're the only non-system
213 		 * process!  That can happen e.g. via 'exec reboot' in
214 		 * single-user mode.
215 		 */
216 		if (errno != ESRCH) {
217 			warn("SIGTERM processes");
218 			goto restart;
219 		}
220 	}
221 
222 	/*
223 	 * After the processes receive the signal, start the rest of the
224 	 * buffers on their way.  Wait 5 seconds between the SIGTERM and
225 	 * the SIGKILL to give everybody a chance.
226 	 */
227 	sleep(2);
228 	if (!nflag)
229 		sync();
230 	sleep(3);
231 
232 	for (i = 1;; ++i) {
233 		if (kill(-1, SIGKILL) == -1) {
234 			if (errno == ESRCH)
235 				break;
236 			goto restart;
237 		}
238 		if (i > 5) {
239 			warnx("WARNING: some process(es) wouldn't die");
240 			break;
241 		}
242 		(void)sleep(2 * i);
243 	}
244 
245 	reboot(howto);
246 	/* FALLTHROUGH */
247 
248 restart:
249 	errx(1, kill(1, SIGHUP) == -1 ? "(can't restart init): " : "");
250 	/* NOTREACHED */
251 }
252 
253 void
254 usage(void)
255 {
256 	fprintf(stderr, "usage: %s [-dn%sq]\n", __progname,
257 	    dohalt ? "p" : "");
258 	exit(1);
259 }
260