1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/boottrace.h>
33 #include <sys/mount.h>
34 #include <sys/reboot.h>
35 #include <sys/stat.h>
36 #include <sys/sysctl.h>
37 #include <sys/time.h>
38 #include <sys/wait.h>
39
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <pwd.h>
44 #include <signal.h>
45 #include <spawn.h>
46 #include <stdbool.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <syslog.h>
51 #include <unistd.h>
52 #include <utmpx.h>
53
54 extern char **environ;
55
56 #define PATH_NEXTBOOT "/boot/nextboot.conf"
57
58 static void usage(void) __dead2;
59 static uint64_t get_pageins(void);
60
61 static bool dohalt;
62 static bool donextboot;
63
64 #define E(...) do { \
65 if (force) { \
66 warn( __VA_ARGS__ ); \
67 return; \
68 } \
69 err(1, __VA_ARGS__); \
70 } while (0) \
71
72 static void
zfsbootcfg(const char * pool,bool force)73 zfsbootcfg(const char *pool, bool force)
74 {
75 const char * const av[] = {
76 "zfsbootcfg",
77 "-z",
78 pool,
79 "-n",
80 "freebsd:nvstore",
81 "-k",
82 "nextboot_enable",
83 "-v",
84 "YES",
85 NULL
86 };
87 int rv, status;
88 pid_t p;
89
90 rv = posix_spawnp(&p, av[0], NULL, NULL, __DECONST(char **, av),
91 environ);
92 if (rv == -1)
93 E("system zfsbootcfg");
94 if (waitpid(p, &status, WEXITED) < 0) {
95 if (errno == EINTR)
96 return;
97 E("waitpid zfsbootcfg");
98 }
99 if (WIFEXITED(status)) {
100 int e = WEXITSTATUS(status);
101
102 if (e == 0)
103 return;
104 if (e == 127)
105 E("zfsbootcfg not found in path");
106 E("zfsbootcfg returned %d", e);
107 }
108 if (WIFSIGNALED(status))
109 E("zfsbootcfg died with signal %d", WTERMSIG(status));
110 E("zfsbootcfg unexpected status %d", status);
111 }
112
113 static void
write_nextboot(const char * fn,const char * env,bool force)114 write_nextboot(const char *fn, const char *env, bool force)
115 {
116 char tmp[PATH_MAX];
117 FILE *fp;
118 struct statfs sfs;
119 int tmpfd;
120 bool supported = false;
121 bool zfs = false;
122
123 if (statfs("/boot", &sfs) != 0)
124 err(1, "statfs /boot");
125 if (strcmp(sfs.f_fstypename, "ufs") == 0) {
126 /*
127 * Only UFS supports the full nextboot protocol.
128 */
129 supported = true;
130 } else if (strcmp(sfs.f_fstypename, "zfs") == 0) {
131 zfs = true;
132 }
133
134 if (zfs) {
135 char *slash;
136
137 slash = strchr(sfs.f_mntfromname, '/');
138 if (slash != NULL)
139 *slash = '\0';
140 zfsbootcfg(sfs.f_mntfromname, force);
141 }
142
143 if (strlcpy(tmp, fn, sizeof(tmp)) >= sizeof(tmp))
144 E("Path too long %s", fn);
145 if (strlcat(tmp, ".XXXXXX", sizeof(tmp)) >= sizeof(tmp))
146 E("Path too long %s", fn);
147 tmpfd = mkstemp(tmp);
148 if (tmpfd == -1)
149 E("mkstemp %s", tmp);
150
151 fp = fdopen(tmpfd, "w");
152 if (fp == NULL)
153 E("fdopen %s", tmp);
154
155 if (fprintf(fp, "%s%s",
156 supported ? "nextboot_enable=\"YES\"\n" : "",
157 env != NULL ? env : "") < 0) {
158 int e;
159
160 e = errno;
161 if (unlink(tmp))
162 warn("unlink %s", tmp);
163 errno = e;
164 E("Can't write %s", tmp);
165 }
166 if (fsync(fileno(fp)) != 0)
167 E("Can't fsync %s", fn);
168 if (rename(tmp, fn) != 0) {
169 int e;
170
171 e = errno;
172 if (unlink(tmp))
173 warn("unlink %s", tmp);
174 errno = e;
175 E("Can't rename %s to %s", tmp, fn);
176 }
177 fclose(fp);
178 }
179
180 static char *
split_kv(char * raw)181 split_kv(char *raw)
182 {
183 char *eq;
184 int len;
185
186 eq = strchr(raw, '=');
187 if (eq == NULL)
188 errx(1, "No = in environment string %s", raw);
189 *eq++ = '\0';
190 len = strlen(eq);
191 if (len == 0)
192 errx(1, "Invalid null value %s=", raw);
193 if (eq[0] == '"') {
194 if (len < 2 || eq[len - 1] != '"')
195 errx(1, "Invalid string '%s'", eq);
196 eq[len - 1] = '\0';
197 return (eq + 1);
198 }
199 return (eq);
200 }
201
202 static void
add_env(char ** env,const char * key,const char * value)203 add_env(char **env, const char *key, const char *value)
204 {
205 char *oldenv;
206
207 oldenv = *env;
208 asprintf(env, "%s%s=\"%s\"\n", oldenv != NULL ? oldenv : "", key, value);
209 if (env == NULL)
210 errx(1, "No memory to build env array");
211 free(oldenv);
212 }
213
214 /*
215 * Different options are valid for different programs.
216 */
217 #define GETOPT_REBOOT "cDde:fk:lNno:pqr"
218 #define GETOPT_NEXTBOOT "De:fk:o:"
219
220 int
main(int argc,char * argv[])221 main(int argc, char *argv[])
222 {
223 struct utmpx utx;
224 const struct passwd *pw;
225 int ch, howto = 0, i, sverrno;
226 bool Dflag, fflag, lflag, Nflag, nflag, qflag;
227 uint64_t pageins;
228 const char *user, *kernel = NULL, *getopts = GETOPT_REBOOT;
229 char *env = NULL, *v;
230
231 if (strstr(getprogname(), "halt") != NULL) {
232 dohalt = true;
233 howto = RB_HALT;
234 } else if (strcmp(getprogname(), "nextboot") == 0) {
235 donextboot = true;
236 getopts = GETOPT_NEXTBOOT; /* Note: reboot's extra opts return '?' */
237 } else {
238 /* reboot */
239 howto = 0;
240 }
241 Dflag = fflag = lflag = Nflag = nflag = qflag = false;
242 while ((ch = getopt(argc, argv, getopts)) != -1) {
243 switch(ch) {
244 case 'c':
245 howto |= RB_POWERCYCLE;
246 break;
247 case 'D':
248 Dflag = true;
249 break;
250 case 'd':
251 howto |= RB_DUMP;
252 break;
253 case 'e':
254 v = split_kv(optarg);
255 add_env(&env, optarg, v);
256 break;
257 case 'f':
258 fflag = true;
259 break;
260 case 'k':
261 kernel = optarg;
262 break;
263 case 'l':
264 lflag = true;
265 break;
266 case 'n':
267 nflag = true;
268 howto |= RB_NOSYNC;
269 break;
270 case 'N':
271 nflag = true;
272 Nflag = true;
273 break;
274 case 'o':
275 add_env(&env, "kernel_options", optarg);
276 break;
277 case 'p':
278 howto |= RB_POWEROFF;
279 break;
280 case 'q':
281 qflag = true;
282 break;
283 case 'r':
284 howto |= RB_REROOT;
285 break;
286 case '?':
287 default:
288 usage();
289 }
290 }
291
292 argc -= optind;
293 argv += optind;
294 if (argc != 0)
295 usage();
296
297 if (Dflag && ((howto & ~RB_HALT) != 0 || kernel != NULL))
298 errx(1, "cannot delete existing nextboot config and do anything else");
299 if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
300 errx(1, "cannot dump (-d) when halting; must reboot instead");
301 if (Nflag && (howto & RB_NOSYNC) != 0)
302 errx(1, "-N cannot be used with -n");
303 if ((howto & RB_POWEROFF) && (howto & RB_POWERCYCLE))
304 errx(1, "-c and -p cannot be used together");
305 if ((howto & RB_REROOT) != 0 && howto != RB_REROOT)
306 errx(1, "-r cannot be used with -c, -d, -n, or -p");
307 if ((howto & RB_REROOT) != 0 && kernel != NULL)
308 errx(1, "-r and -k cannot be used together, there is no next kernel");
309
310 if (Dflag) {
311 if (unlink(PATH_NEXTBOOT) != 0 && errno != ENOENT)
312 warn("unlink " PATH_NEXTBOOT);
313 exit(0);
314 }
315
316 if (!donextboot && geteuid() != 0) {
317 errno = EPERM;
318 err(1, NULL);
319 }
320
321 if (qflag) {
322 reboot(howto);
323 err(1, NULL);
324 }
325
326 if (kernel != NULL) {
327 if (!fflag) {
328 char *k;
329 struct stat sb;
330
331 asprintf(&k, "/boot/%s/kernel", kernel);
332 if (k == NULL)
333 errx(1, "No memory to check %s", kernel);
334 if (stat(k, &sb) != 0)
335 err(1, "stat %s", k);
336 if (!S_ISREG(sb.st_mode))
337 errx(1, "%s is not a file", k);
338 free(k);
339 }
340 add_env(&env, "kernel", kernel);
341 }
342
343 if (env != NULL)
344 write_nextboot(PATH_NEXTBOOT, env, fflag);
345 if (donextboot)
346 exit (0);
347
348 /* Log the reboot. */
349 if (!lflag) {
350 if ((user = getlogin()) == NULL)
351 user = (pw = getpwuid(getuid())) ?
352 pw->pw_name : "???";
353 if (dohalt) {
354 openlog("halt", 0, LOG_AUTH | LOG_CONS);
355 syslog(LOG_CRIT, "halted by %s", user);
356 } else if (howto & RB_REROOT) {
357 openlog("reroot", 0, LOG_AUTH | LOG_CONS);
358 syslog(LOG_CRIT, "rerooted by %s", user);
359 } else if (howto & RB_POWEROFF) {
360 openlog("reboot", 0, LOG_AUTH | LOG_CONS);
361 syslog(LOG_CRIT, "powered off by %s", user);
362 } else if (howto & RB_POWERCYCLE) {
363 openlog("reboot", 0, LOG_AUTH | LOG_CONS);
364 syslog(LOG_CRIT, "power cycled by %s", user);
365 } else {
366 openlog("reboot", 0, LOG_AUTH | LOG_CONS);
367 syslog(LOG_CRIT, "rebooted by %s", user);
368 }
369 }
370 utx.ut_type = SHUTDOWN_TIME;
371 gettimeofday(&utx.ut_tv, NULL);
372 pututxline(&utx);
373
374 /*
375 * Do a sync early on, so disks start transfers while we're off
376 * killing processes. Don't worry about writes done before the
377 * processes die, the reboot system call syncs the disks.
378 */
379 if (!nflag)
380 sync();
381
382 /*
383 * Ignore signals that we can get as a result of killing
384 * parents, group leaders, etc.
385 */
386 (void)signal(SIGHUP, SIG_IGN);
387 (void)signal(SIGINT, SIG_IGN);
388 (void)signal(SIGQUIT, SIG_IGN);
389 (void)signal(SIGTERM, SIG_IGN);
390 (void)signal(SIGTSTP, SIG_IGN);
391
392 /*
393 * If we're running in a pipeline, we don't want to die
394 * after killing whatever we're writing to.
395 */
396 (void)signal(SIGPIPE, SIG_IGN);
397
398 /*
399 * Only init(8) can perform rerooting.
400 */
401 if (howto & RB_REROOT) {
402 if (kill(1, SIGEMT) == -1)
403 err(1, "SIGEMT init");
404
405 return (0);
406 }
407
408 /* Just stop init -- if we fail, we'll restart it. */
409 BOOTTRACE("SIGTSTP to init(8)...");
410 if (kill(1, SIGTSTP) == -1)
411 err(1, "SIGTSTP init");
412
413 /* Send a SIGTERM first, a chance to save the buffers. */
414 BOOTTRACE("SIGTERM to all other processes...");
415 if (kill(-1, SIGTERM) == -1 && errno != ESRCH)
416 err(1, "SIGTERM processes");
417
418 /*
419 * After the processes receive the signal, start the rest of the
420 * buffers on their way. Wait 5 seconds between the SIGTERM and
421 * the SIGKILL to give everybody a chance. If there is a lot of
422 * paging activity then wait longer, up to a maximum of approx
423 * 60 seconds.
424 */
425 sleep(2);
426 for (i = 0; i < 20; i++) {
427 pageins = get_pageins();
428 if (!nflag)
429 sync();
430 sleep(3);
431 if (get_pageins() == pageins)
432 break;
433 }
434
435 for (i = 1;; ++i) {
436 BOOTTRACE("SIGKILL to all other processes(%d)...", i);
437 if (kill(-1, SIGKILL) == -1) {
438 if (errno == ESRCH)
439 break;
440 goto restart;
441 }
442 if (i > 5) {
443 (void)fprintf(stderr,
444 "WARNING: some process(es) wouldn't die\n");
445 break;
446 }
447 (void)sleep(2 * i);
448 }
449
450 reboot(howto);
451 /* FALLTHROUGH */
452
453 restart:
454 BOOTTRACE("SIGHUP to init(8)...");
455 sverrno = errno;
456 errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
457 strerror(sverrno));
458 /* NOTREACHED */
459 }
460
461 static void
usage(void)462 usage(void)
463 {
464
465 (void)fprintf(stderr, dohalt ?
466 "usage: halt [-clNnpq] [-k kernel]\n" :
467 "usage: reboot [-cdlNnpqr] [-k kernel]\n");
468 exit(1);
469 }
470
471 static uint64_t
get_pageins(void)472 get_pageins(void)
473 {
474 uint64_t pageins;
475 size_t len;
476
477 len = sizeof(pageins);
478 if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
479 != 0) {
480 warn("v_swappgsin");
481 return (0);
482 }
483 return (pageins);
484 }
485