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