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
sleep_while_procs(int seconds)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
main(int argc,char * argv[])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
usage(void)273 usage(void)
274 {
275 fprintf(stderr, "usage: %s [-dn%sq]\n", __progname,
276 dohalt ? "p" : "");
277 exit(1);
278 }
279