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/reboot.h> 35 #include <sys/stat.h> 36 #include <sys/sysctl.h> 37 #include <sys/time.h> 38 39 #include <err.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <pwd.h> 43 #include <signal.h> 44 #include <stdbool.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <syslog.h> 49 #include <unistd.h> 50 #include <utmpx.h> 51 52 #define PATH_NEXTBOOT "/boot/nextboot.conf" 53 54 static void usage(void) __dead2; 55 static uint64_t get_pageins(void); 56 57 static bool dohalt; 58 59 static void 60 write_nextboot(const char *fn, const char *kernel, bool force) 61 { 62 FILE *fp; 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 fp = fopen(fn, "w"); 73 if (fp == NULL) 74 E("Can't create %s to boot %s", fn, kernel); 75 76 if (fprintf(fp, 77 "nextboot_enable=\"YES\"\n" 78 "kernel=\"%s\"\n", kernel) < 0) { 79 int e; 80 81 e = errno; 82 fclose(fp); 83 if (unlink(fn)) 84 warn("unlink %s", fn); 85 errno = e; 86 E("Can't write %s", fn); 87 } 88 fclose(fp); 89 #undef E 90 } 91 92 int 93 main(int argc, char *argv[]) 94 { 95 struct utmpx utx; 96 const struct passwd *pw; 97 int ch, howto, i, sverrno; 98 bool Dflag, fflag, lflag, Nflag, nflag, qflag; 99 uint64_t pageins; 100 const char *user, *kernel = NULL; 101 102 if (strstr(getprogname(), "halt") != NULL) { 103 dohalt = true; 104 howto = RB_HALT; 105 } else 106 howto = 0; 107 Dflag = fflag = lflag = Nflag = nflag = qflag = false; 108 while ((ch = getopt(argc, argv, "cDdk:lNnpqr")) != -1) 109 switch(ch) { 110 case 'c': 111 howto |= RB_POWERCYCLE; 112 break; 113 case 'D': 114 Dflag = true; 115 break; 116 case 'd': 117 howto |= RB_DUMP; 118 break; 119 case 'f': 120 fflag = true; 121 break; 122 case 'k': 123 kernel = optarg; 124 break; 125 case 'l': 126 lflag = true; 127 break; 128 case 'n': 129 nflag = true; 130 howto |= RB_NOSYNC; 131 break; 132 case 'N': 133 nflag = true; 134 Nflag = true; 135 break; 136 case 'p': 137 howto |= RB_POWEROFF; 138 break; 139 case 'q': 140 qflag = true; 141 break; 142 case 'r': 143 howto |= RB_REROOT; 144 break; 145 case '?': 146 default: 147 usage(); 148 } 149 argc -= optind; 150 argv += optind; 151 if (argc != 0) 152 usage(); 153 154 if (Dflag && ((howto & ~RB_HALT) != 0 || kernel != NULL)) 155 errx(1, "cannot delete existing nextboot config and do anything else"); 156 if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT)) 157 errx(1, "cannot dump (-d) when halting; must reboot instead"); 158 if (Nflag && (howto & RB_NOSYNC) != 0) 159 errx(1, "-N cannot be used with -n"); 160 if ((howto & RB_POWEROFF) && (howto & RB_POWERCYCLE)) 161 errx(1, "-c and -p cannot be used together"); 162 if ((howto & RB_REROOT) != 0 && howto != RB_REROOT) 163 errx(1, "-r cannot be used with -c, -d, -n, or -p"); 164 if ((howto & RB_REROOT) != 0 && kernel != NULL) 165 errx(1, "-r and -k cannot be used together, there is no next kernel"); 166 if (geteuid()) { 167 errno = EPERM; 168 err(1, NULL); 169 } 170 171 if (Dflag) { 172 if (unlink(PATH_NEXTBOOT) != 0) 173 err(1, "unlink %s", PATH_NEXTBOOT); 174 exit(0); 175 } 176 177 if (qflag) { 178 reboot(howto); 179 err(1, NULL); 180 } 181 182 if (kernel != NULL) { 183 if (!fflag) { 184 char *k; 185 struct stat sb; 186 187 asprintf(&k, "/boot/%s/kernel", kernel); 188 if (k == NULL) 189 errx(1, "No memory to check %s", kernel); 190 if (stat(k, &sb) != 0) 191 err(1, "stat %s", k); 192 if (!S_ISREG(sb.st_mode)) 193 errx(1, "%s is not a file", k); 194 free(k); 195 } 196 write_nextboot(PATH_NEXTBOOT, kernel, fflag); 197 } 198 199 /* Log the reboot. */ 200 if (!lflag) { 201 if ((user = getlogin()) == NULL) 202 user = (pw = getpwuid(getuid())) ? 203 pw->pw_name : "???"; 204 if (dohalt) { 205 openlog("halt", 0, LOG_AUTH | LOG_CONS); 206 syslog(LOG_CRIT, "halted by %s", user); 207 } else if (howto & RB_REROOT) { 208 openlog("reroot", 0, LOG_AUTH | LOG_CONS); 209 syslog(LOG_CRIT, "rerooted by %s", user); 210 } else if (howto & RB_POWEROFF) { 211 openlog("reboot", 0, LOG_AUTH | LOG_CONS); 212 syslog(LOG_CRIT, "powered off by %s", user); 213 } else if (howto & RB_POWERCYCLE) { 214 openlog("reboot", 0, LOG_AUTH | LOG_CONS); 215 syslog(LOG_CRIT, "power cycled by %s", user); 216 } else { 217 openlog("reboot", 0, LOG_AUTH | LOG_CONS); 218 syslog(LOG_CRIT, "rebooted by %s", user); 219 } 220 } 221 utx.ut_type = SHUTDOWN_TIME; 222 gettimeofday(&utx.ut_tv, NULL); 223 pututxline(&utx); 224 225 /* 226 * Do a sync early on, so disks start transfers while we're off 227 * killing processes. Don't worry about writes done before the 228 * processes die, the reboot system call syncs the disks. 229 */ 230 if (!nflag) 231 sync(); 232 233 /* 234 * Ignore signals that we can get as a result of killing 235 * parents, group leaders, etc. 236 */ 237 (void)signal(SIGHUP, SIG_IGN); 238 (void)signal(SIGINT, SIG_IGN); 239 (void)signal(SIGQUIT, SIG_IGN); 240 (void)signal(SIGTERM, SIG_IGN); 241 (void)signal(SIGTSTP, SIG_IGN); 242 243 /* 244 * If we're running in a pipeline, we don't want to die 245 * after killing whatever we're writing to. 246 */ 247 (void)signal(SIGPIPE, SIG_IGN); 248 249 /* 250 * Only init(8) can perform rerooting. 251 */ 252 if (howto & RB_REROOT) { 253 if (kill(1, SIGEMT) == -1) 254 err(1, "SIGEMT init"); 255 256 return (0); 257 } 258 259 /* Just stop init -- if we fail, we'll restart it. */ 260 BOOTTRACE("SIGTSTP to init(8)..."); 261 if (kill(1, SIGTSTP) == -1) 262 err(1, "SIGTSTP init"); 263 264 /* Send a SIGTERM first, a chance to save the buffers. */ 265 BOOTTRACE("SIGTERM to all other processes..."); 266 if (kill(-1, SIGTERM) == -1 && errno != ESRCH) 267 err(1, "SIGTERM processes"); 268 269 /* 270 * After the processes receive the signal, start the rest of the 271 * buffers on their way. Wait 5 seconds between the SIGTERM and 272 * the SIGKILL to give everybody a chance. If there is a lot of 273 * paging activity then wait longer, up to a maximum of approx 274 * 60 seconds. 275 */ 276 sleep(2); 277 for (i = 0; i < 20; i++) { 278 pageins = get_pageins(); 279 if (!nflag) 280 sync(); 281 sleep(3); 282 if (get_pageins() == pageins) 283 break; 284 } 285 286 for (i = 1;; ++i) { 287 BOOTTRACE("SIGKILL to all other processes(%d)...", i); 288 if (kill(-1, SIGKILL) == -1) { 289 if (errno == ESRCH) 290 break; 291 goto restart; 292 } 293 if (i > 5) { 294 (void)fprintf(stderr, 295 "WARNING: some process(es) wouldn't die\n"); 296 break; 297 } 298 (void)sleep(2 * i); 299 } 300 301 reboot(howto); 302 /* FALLTHROUGH */ 303 304 restart: 305 BOOTTRACE("SIGHUP to init(8)..."); 306 sverrno = errno; 307 errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "", 308 strerror(sverrno)); 309 /* NOTREACHED */ 310 } 311 312 static void 313 usage(void) 314 { 315 316 (void)fprintf(stderr, dohalt ? 317 "usage: halt [-clNnpq] [-k kernel]\n" : 318 "usage: reboot [-cdlNnpqr] [-k kernel]\n"); 319 exit(1); 320 } 321 322 static uint64_t 323 get_pageins(void) 324 { 325 uint64_t pageins; 326 size_t len; 327 328 len = sizeof(pageins); 329 if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0) 330 != 0) { 331 warn("v_swappgsin"); 332 return (0); 333 } 334 return (pageins); 335 } 336