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 FILE *fp; 117 struct statfs sfs; 118 bool supported = false; 119 bool zfs = false; 120 121 if (statfs("/boot", &sfs) != 0) 122 err(1, "statfs /boot"); 123 if (strcmp(sfs.f_fstypename, "ufs") == 0) { 124 /* 125 * Only UFS supports the full nextboot protocol. 126 */ 127 supported = true; 128 } else if (strcmp(sfs.f_fstypename, "zfs") == 0) { 129 zfs = true; 130 } 131 132 if (zfs) { 133 char *slash; 134 135 if ((slash = strchr(sfs.f_mntfromname, '/')) == NULL) 136 E("Can't find ZFS pool name in %s", sfs.f_mntfromname); 137 *slash = '\0'; 138 zfsbootcfg(sfs.f_mntfromname, force); 139 } 140 141 fp = fopen(fn, "w"); 142 if (fp == NULL) 143 E("Can't create %s", fn); 144 145 if (fprintf(fp,"%s%s", 146 supported ? "nextboot_enable=\"YES\"\n" : "", 147 env != NULL ? env : "") < 0) { 148 int e; 149 150 e = errno; 151 fclose(fp); 152 if (unlink(fn)) 153 warn("unlink %s", fn); 154 errno = e; 155 E("Can't write %s", fn); 156 } 157 fclose(fp); 158 } 159 160 static char * 161 split_kv(char *raw) 162 { 163 char *eq; 164 int len; 165 166 eq = strchr(raw, '='); 167 if (eq == NULL) 168 errx(1, "No = in environment string %s", raw); 169 *eq++ = '\0'; 170 len = strlen(eq); 171 if (len == 0) 172 errx(1, "Invalid null value %s=", raw); 173 if (eq[0] == '"') { 174 if (len < 2 || eq[len - 1] != '"') 175 errx(1, "Invalid string '%s'", eq); 176 eq[len - 1] = '\0'; 177 return (eq + 1); 178 } 179 return (eq); 180 } 181 182 static void 183 add_env(char **env, const char *key, const char *value) 184 { 185 char *oldenv; 186 187 oldenv = *env; 188 asprintf(env, "%s%s=\"%s\"\n", oldenv != NULL ? oldenv : "", key, value); 189 if (env == NULL) 190 errx(1, "No memory to build env array"); 191 free(oldenv); 192 } 193 194 /* 195 * Different options are valid for different programs. 196 */ 197 #define GETOPT_REBOOT "cDde:k:lNno:pqr" 198 #define GETOPT_NEXTBOOT "De:k:o:" 199 200 int 201 main(int argc, char *argv[]) 202 { 203 struct utmpx utx; 204 const struct passwd *pw; 205 int ch, howto = 0, i, sverrno; 206 bool Dflag, fflag, lflag, Nflag, nflag, qflag; 207 uint64_t pageins; 208 const char *user, *kernel = NULL, *getopts = GETOPT_REBOOT; 209 char *env = NULL, *v; 210 211 if (strstr(getprogname(), "halt") != NULL) { 212 dohalt = true; 213 howto = RB_HALT; 214 } else if (strcmp(getprogname(), "nextboot") == 0) { 215 donextboot = true; 216 getopts = GETOPT_NEXTBOOT; /* Note: reboot's extra opts return '?' */ 217 } else { 218 /* reboot */ 219 howto = 0; 220 } 221 Dflag = fflag = lflag = Nflag = nflag = qflag = false; 222 while ((ch = getopt(argc, argv, getopts)) != -1) { 223 switch(ch) { 224 case 'c': 225 howto |= RB_POWERCYCLE; 226 break; 227 case 'D': 228 Dflag = true; 229 break; 230 case 'd': 231 howto |= RB_DUMP; 232 break; 233 case 'e': 234 v = split_kv(optarg); 235 add_env(&env, optarg, v); 236 break; 237 case 'f': 238 fflag = true; 239 break; 240 case 'k': 241 kernel = optarg; 242 break; 243 case 'l': 244 lflag = true; 245 break; 246 case 'n': 247 nflag = true; 248 howto |= RB_NOSYNC; 249 break; 250 case 'N': 251 nflag = true; 252 Nflag = true; 253 break; 254 case 'o': 255 add_env(&env, "kernel_options", optarg); 256 break; 257 case 'p': 258 howto |= RB_POWEROFF; 259 break; 260 case 'q': 261 qflag = true; 262 break; 263 case 'r': 264 howto |= RB_REROOT; 265 break; 266 case '?': 267 default: 268 usage(); 269 } 270 } 271 272 argc -= optind; 273 argv += optind; 274 if (argc != 0) 275 usage(); 276 277 if (Dflag && ((howto & ~RB_HALT) != 0 || kernel != NULL)) 278 errx(1, "cannot delete existing nextboot config and do anything else"); 279 if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT)) 280 errx(1, "cannot dump (-d) when halting; must reboot instead"); 281 if (Nflag && (howto & RB_NOSYNC) != 0) 282 errx(1, "-N cannot be used with -n"); 283 if ((howto & RB_POWEROFF) && (howto & RB_POWERCYCLE)) 284 errx(1, "-c and -p cannot be used together"); 285 if ((howto & RB_REROOT) != 0 && howto != RB_REROOT) 286 errx(1, "-r cannot be used with -c, -d, -n, or -p"); 287 if ((howto & RB_REROOT) != 0 && kernel != NULL) 288 errx(1, "-r and -k cannot be used together, there is no next kernel"); 289 290 if (Dflag) { 291 if (unlink(PATH_NEXTBOOT) != 0 && errno != ENOENT) 292 warn("unlink " PATH_NEXTBOOT); 293 exit(0); 294 } 295 296 if (!donextboot && geteuid() != 0) { 297 errno = EPERM; 298 err(1, NULL); 299 } 300 301 if (qflag) { 302 reboot(howto); 303 err(1, NULL); 304 } 305 306 if (kernel != NULL) { 307 if (!fflag) { 308 char *k; 309 struct stat sb; 310 311 asprintf(&k, "/boot/%s/kernel", kernel); 312 if (k == NULL) 313 errx(1, "No memory to check %s", kernel); 314 if (stat(k, &sb) != 0) 315 err(1, "stat %s", k); 316 if (!S_ISREG(sb.st_mode)) 317 errx(1, "%s is not a file", k); 318 free(k); 319 } 320 add_env(&env, "kernel", kernel); 321 } 322 323 if (env != NULL) 324 write_nextboot(PATH_NEXTBOOT, env, fflag); 325 if (donextboot) 326 exit (0); 327 328 /* Log the reboot. */ 329 if (!lflag) { 330 if ((user = getlogin()) == NULL) 331 user = (pw = getpwuid(getuid())) ? 332 pw->pw_name : "???"; 333 if (dohalt) { 334 openlog("halt", 0, LOG_AUTH | LOG_CONS); 335 syslog(LOG_CRIT, "halted by %s", user); 336 } else if (howto & RB_REROOT) { 337 openlog("reroot", 0, LOG_AUTH | LOG_CONS); 338 syslog(LOG_CRIT, "rerooted by %s", user); 339 } else if (howto & RB_POWEROFF) { 340 openlog("reboot", 0, LOG_AUTH | LOG_CONS); 341 syslog(LOG_CRIT, "powered off by %s", user); 342 } else if (howto & RB_POWERCYCLE) { 343 openlog("reboot", 0, LOG_AUTH | LOG_CONS); 344 syslog(LOG_CRIT, "power cycled by %s", user); 345 } else { 346 openlog("reboot", 0, LOG_AUTH | LOG_CONS); 347 syslog(LOG_CRIT, "rebooted by %s", user); 348 } 349 } 350 utx.ut_type = SHUTDOWN_TIME; 351 gettimeofday(&utx.ut_tv, NULL); 352 pututxline(&utx); 353 354 /* 355 * Do a sync early on, so disks start transfers while we're off 356 * killing processes. Don't worry about writes done before the 357 * processes die, the reboot system call syncs the disks. 358 */ 359 if (!nflag) 360 sync(); 361 362 /* 363 * Ignore signals that we can get as a result of killing 364 * parents, group leaders, etc. 365 */ 366 (void)signal(SIGHUP, SIG_IGN); 367 (void)signal(SIGINT, SIG_IGN); 368 (void)signal(SIGQUIT, SIG_IGN); 369 (void)signal(SIGTERM, SIG_IGN); 370 (void)signal(SIGTSTP, SIG_IGN); 371 372 /* 373 * If we're running in a pipeline, we don't want to die 374 * after killing whatever we're writing to. 375 */ 376 (void)signal(SIGPIPE, SIG_IGN); 377 378 /* 379 * Only init(8) can perform rerooting. 380 */ 381 if (howto & RB_REROOT) { 382 if (kill(1, SIGEMT) == -1) 383 err(1, "SIGEMT init"); 384 385 return (0); 386 } 387 388 /* Just stop init -- if we fail, we'll restart it. */ 389 BOOTTRACE("SIGTSTP to init(8)..."); 390 if (kill(1, SIGTSTP) == -1) 391 err(1, "SIGTSTP init"); 392 393 /* Send a SIGTERM first, a chance to save the buffers. */ 394 BOOTTRACE("SIGTERM to all other processes..."); 395 if (kill(-1, SIGTERM) == -1 && errno != ESRCH) 396 err(1, "SIGTERM processes"); 397 398 /* 399 * After the processes receive the signal, start the rest of the 400 * buffers on their way. Wait 5 seconds between the SIGTERM and 401 * the SIGKILL to give everybody a chance. If there is a lot of 402 * paging activity then wait longer, up to a maximum of approx 403 * 60 seconds. 404 */ 405 sleep(2); 406 for (i = 0; i < 20; i++) { 407 pageins = get_pageins(); 408 if (!nflag) 409 sync(); 410 sleep(3); 411 if (get_pageins() == pageins) 412 break; 413 } 414 415 for (i = 1;; ++i) { 416 BOOTTRACE("SIGKILL to all other processes(%d)...", i); 417 if (kill(-1, SIGKILL) == -1) { 418 if (errno == ESRCH) 419 break; 420 goto restart; 421 } 422 if (i > 5) { 423 (void)fprintf(stderr, 424 "WARNING: some process(es) wouldn't die\n"); 425 break; 426 } 427 (void)sleep(2 * i); 428 } 429 430 reboot(howto); 431 /* FALLTHROUGH */ 432 433 restart: 434 BOOTTRACE("SIGHUP to init(8)..."); 435 sverrno = errno; 436 errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "", 437 strerror(sverrno)); 438 /* NOTREACHED */ 439 } 440 441 static void 442 usage(void) 443 { 444 445 (void)fprintf(stderr, dohalt ? 446 "usage: halt [-clNnpq] [-k kernel]\n" : 447 "usage: reboot [-cdlNnpqr] [-k kernel]\n"); 448 exit(1); 449 } 450 451 static uint64_t 452 get_pageins(void) 453 { 454 uint64_t pageins; 455 size_t len; 456 457 len = sizeof(pageins); 458 if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0) 459 != 0) { 460 warn("v_swappgsin"); 461 return (0); 462 } 463 return (pageins); 464 } 465