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 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 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 if ((slash = strchr(sfs.f_mntfromname, '/')) == NULL) 138 E("Can't find ZFS pool name in %s", sfs.f_mntfromname); 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 * 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 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 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 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 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