diff options
Diffstat (limited to 'sbin/reboot')
-rw-r--r-- | sbin/reboot/Makefile | 10 | ||||
-rw-r--r-- | sbin/reboot/boot_i386.8 | 8 | ||||
-rw-r--r-- | sbin/reboot/nextboot.sh | 132 | ||||
-rw-r--r-- | sbin/reboot/reboot.8 | 77 | ||||
-rw-r--r-- | sbin/reboot/reboot.c | 302 |
5 files changed, 327 insertions, 202 deletions
diff --git a/sbin/reboot/Makefile b/sbin/reboot/Makefile index 7f7fe4a77410..27805d307794 100644 --- a/sbin/reboot/Makefile +++ b/sbin/reboot/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - PACKAGE=runtime PROG= reboot MAN= reboot.8 nextboot.8 @@ -14,9 +12,9 @@ MAN+= boot_i386.8 MLINKS+= boot_i386.8 boot.8 .endif -LINKS= ${BINDIR}/reboot ${BINDIR}/halt ${BINDIR}/reboot ${BINDIR}/fastboot \ - ${BINDIR}/reboot ${BINDIR}/fasthalt - -SCRIPTS= nextboot.sh +LINKS= ${BINDIR}/reboot ${BINDIR}/halt \ + ${BINDIR}/reboot ${BINDIR}/fastboot \ + ${BINDIR}/reboot ${BINDIR}/fasthalt \ + ${BINDIR}/reboot ${BINDIR}/nextboot .include <bsd.prog.mk> diff --git a/sbin/reboot/boot_i386.8 b/sbin/reboot/boot_i386.8 index da8fc2e430ac..26dd588fb437 100644 --- a/sbin/reboot/boot_i386.8 +++ b/sbin/reboot/boot_i386.8 @@ -32,9 +32,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)boot_i386.8 8.2 (Berkeley) 4/19/94 -.\" -.Dd July 11, 2020 +.Dd April 7, 2025 .Dt BOOT 8 i386 .Os .Sh NAME @@ -47,14 +45,14 @@ An automatic consistency check of the file systems will be performed, and unless this fails, the system will resume multi-user operations. .Pp .Sy Cold starts . -Most i386 PCs attempt to boot first from floppy disk drive 0 (sometimes +Legacy i386 PCs attempt to boot first from floppy disk drive 0 (sometimes known as drive A:) and, failing that, from hard disk drive 0 (sometimes known as drive C:, or as drive 0x80 to the BIOS). Some BIOSes allow you to change this default sequence, and may also include a CD-ROM drive as a boot device. .Pp -Some newer PCs boot using UEFI firmware, not BIOS. +Contemporary PCs boot using UEFI firmware, not BIOS. That process is described in .Xr uefi 8 . diff --git a/sbin/reboot/nextboot.sh b/sbin/reboot/nextboot.sh deleted file mode 100644 index 0c9cca51eb8d..000000000000 --- a/sbin/reboot/nextboot.sh +++ /dev/null @@ -1,132 +0,0 @@ -#! /bin/sh -# -# SPDX-License-Identifier: BSD-2-Clause -# -# Copyright (c) 2002 Gordon Tetlow. All rights reserved. -# Copyright (c) 2012 Sandvine Incorporated. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# - -append="NO" -delete="NO" -kenv= -force="NO" -nextboot_file="/boot/nextboot.conf" -zfs= - -add_kenv() -{ - local var value - - var=$1 - # strip literal quotes if passed in - value=${2%\"*} - value=${value#*\"} - - if [ -n "${kenv}" ]; then - kenv="${kenv} -" - fi - kenv="${kenv}${var}=\"${value}\"" -} - -display_usage() { - cat <<-EOF - Usage: nextboot [-af] [-e variable=value] [-k kernel] [-o options] - nextboot -D - EOF -} - -while getopts "aDe:fk:o:" argument ; do - case "${argument}" in - a) - append="YES" - ;; - D) - delete="YES" - ;; - e) - var=${OPTARG%%=*} - value=${OPTARG#*=} - if [ -z "$var" -o -z "$value" ]; then - display_usage - exit 1 - fi - add_kenv "$var" "$value" - ;; - f) - force="YES" - ;; - k) - kernel="${OPTARG}" - add_kenv kernel "$kernel" - ;; - o) - add_kenv kernel_options "${OPTARG}" - ;; - *) - display_usage - exit 1 - ;; - esac -done - -if [ ${delete} = "YES" ]; then - rm -f ${nextboot_file} - exit 0 -fi - -if [ -z "${kenv}" ]; then - display_usage - exit 1 -fi - -if [ -n "${kernel}" -a ${force} = "NO" -a ! -d /boot/${kernel} ]; then - echo "Error: /boot/${kernel} doesn't exist. Use -f to override." - exit 1 -fi - -zfs=$(df -Tn "/boot/" 2>/dev/null | while read _fs _type _other ; do - [ "zfs" = "${_type}" ] || continue - echo "${_fs%%/*}" -done) - -set -e - -nextboot_tmp=$(mktemp $(dirname ${nextboot_file})/nextboot.XXXXXX) - -if [ -n "${zfs}" ]; then - zfsbootcfg -z ${zfs} -n freebsd:nvstore -k nextboot_enable -v YES - cat >> ${nextboot_tmp} << EOF -$kenv -EOF -else -cat >> ${nextboot_tmp} << EOF -nextboot_enable="YES" -$kenv -EOF -fi - -fsync ${nextboot_tmp} - -mv ${nextboot_tmp} ${nextboot_file} diff --git a/sbin/reboot/reboot.8 b/sbin/reboot/reboot.8 index 13119ecf64f7..1bbc39d52be4 100644 --- a/sbin/reboot/reboot.8 +++ b/sbin/reboot/reboot.8 @@ -25,9 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)reboot.8 8.1 (Berkeley) 6/9/93 -.\" -.Dd December 20, 2017 +.Dd April 12, 2025 .Dt REBOOT 8 .Os .Sh NAME @@ -38,17 +36,25 @@ .Nd stopping and restarting the system .Sh SYNOPSIS .Nm halt -.Op Fl lNnpq +.Op Fl DflNnpq +.Op Fl e Ar variable=value .Op Fl k Ar kernel +.Op Fl o Ar options .Nm -.Op Fl cdlNnpqr +.Op Fl cDdflNnpqr +.Op Fl e Ar variable=value .Op Fl k Ar kernel +.Op Fl o Ar options .Nm fasthalt -.Op Fl lNnpq +.Op Fl DflNnpq +.Op Fl e Ar variable=value .Op Fl k Ar kernel +.Op Fl o Ar options .Nm fastboot -.Op Fl dlNnpq +.Op Fl dDflNnpq +.Op Fl e Ar variable=value .Op Fl k Ar kernel +.Op Fl o Ar options .Sh DESCRIPTION The .Nm halt @@ -79,23 +85,56 @@ driver implements the power cycle functionality and only on hardware with a BMC that supports power cycling. Unlike power off, the amount of hardware that supports power cycling is small. +.It Fl D +Delete existing +.Nm nextboot +configuration and exit. .It Fl d The system is requested to create a crash dump. This option is supported only when rebooting, and it has no effect unless a dump device has previously been specified with .Xr dumpon 8 . -.It Fl k Ar kernel -Boot the specified -.Ar kernel +.It Fl e Ar variable=value +Sets +.Va variable +to +.Va value +in the loader's and kernel's environment. +If +.Va value +is not already enclosed in double quotes, they will be added before writing to the +.Nm nextboot +configuration. +Care should be taken if +.Va value +contains any characters that are special to the shell or loader's configuration +parsing code. +.It Fl f +Force reboot. +Normally, +.Nm +checks for the presence of the next kernel, +and absence of the +.Pa /var/run/noshutdown +file. +Without this flag, reboot is denied if one of the conditions failed. +.It Fl k Ar kname +Boot the specified kernel +.Ar kname on the next system boot. -If the kernel boots successfully, the +This is a one-shot option, the .Em default -kernel will be booted on successive boots, this is a one-shot option. -If the boot fails, the system will continue attempting to boot -.Ar kernel -until the boot process is interrupted and a valid kernel booted. -This may change in the future. +kernel will be booted on successive boots. +No +.Nm reboot +or +.Nm halt +will be performed if +.Em /boot/kname/kernel +does not exist unless the +.Fl f +flag is specified. .It Fl l The halt or reboot is .Em not @@ -120,6 +159,9 @@ This can happen when devices have been disconnected, such as with .It Fl n The file system cache is not flushed. This option should probably not be used. +.It Fl o Ar options +This option +allows the passing of kernel flags for the next boot. .It Fl p The system will turn off the power if it can. If the power down action fails, the system @@ -142,7 +184,7 @@ After changing vfs.root.mountfrom with .Nm Fl r can be used to change the root filesystem while preserving kernel state. This requires the -.Xr tmpfs 5 +.Xr tmpfs 4 kernel module to be loaded because .Xr init 8 needs a place to store itself after the old root is unmounted, but @@ -180,6 +222,7 @@ reboot -r .Ed .Sh SEE ALSO .Xr kenv 1 , +.Xr reboot 2 , .Xr getutxent 3 , .Xr ipmi 4 , .Xr boot 8 , diff --git a/sbin/reboot/reboot.c b/sbin/reboot/reboot.c index 6f656fd5f88c..f6065e80fb66 100644 --- a/sbin/reboot/reboot.c +++ b/sbin/reboot/reboot.c @@ -29,83 +29,258 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1986, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)reboot.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -#include <sys/types.h> #include <sys/boottrace.h> +#include <sys/mount.h> #include <sys/reboot.h> +#include <sys/stat.h> #include <sys/sysctl.h> #include <sys/time.h> +#include <sys/wait.h> -#include <signal.h> #include <err.h> #include <errno.h> #include <fcntl.h> +#include <paths.h> #include <pwd.h> -#include <syslog.h> +#include <signal.h> +#include <spawn.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <syslog.h> #include <unistd.h> #include <utmpx.h> +extern char **environ; + +#define PATH_NEXTBOOT "/boot/nextboot.conf" + static void usage(void) __dead2; -static u_int get_pageins(void); +static uint64_t get_pageins(void); + +static bool dohalt; +static bool donextboot; + +#define E(...) do { \ + if (force) { \ + warn( __VA_ARGS__ ); \ + return; \ + } \ + err(1, __VA_ARGS__); \ + } while (0) \ + +static void +zfsbootcfg(const char *pool, bool force) +{ + const char * const av[] = { + "zfsbootcfg", + "-z", + pool, + "-n", + "freebsd:nvstore", + "-k", + "nextboot_enable", + "-v", + "YES", + NULL + }; + int rv, status; + pid_t p; + + rv = posix_spawnp(&p, av[0], NULL, NULL, __DECONST(char **, av), + environ); + if (rv == -1) + E("system zfsbootcfg"); + if (waitpid(p, &status, WEXITED) < 0) { + if (errno == EINTR) + return; + E("waitpid zfsbootcfg"); + } + if (WIFEXITED(status)) { + int e = WEXITSTATUS(status); + + if (e == 0) + return; + if (e == 127) + E("zfsbootcfg not found in path"); + E("zfsbootcfg returned %d", e); + } + if (WIFSIGNALED(status)) + E("zfsbootcfg died with signal %d", WTERMSIG(status)); + E("zfsbootcfg unexpected status %d", status); +} + +static void +write_nextboot(const char *fn, const char *env, bool force) +{ + char tmp[PATH_MAX]; + FILE *fp; + struct statfs sfs; + int tmpfd; + bool supported = false; + bool zfs = false; + + if (statfs("/boot", &sfs) != 0) + err(1, "statfs /boot"); + if (strcmp(sfs.f_fstypename, "ufs") == 0) { + /* + * Only UFS supports the full nextboot protocol. + */ + supported = true; + } else if (strcmp(sfs.f_fstypename, "zfs") == 0) { + zfs = true; + } + + if (zfs) { + char *slash; + + slash = strchr(sfs.f_mntfromname, '/'); + if (slash != NULL) + *slash = '\0'; + zfsbootcfg(sfs.f_mntfromname, force); + } + + if (strlcpy(tmp, fn, sizeof(tmp)) >= sizeof(tmp)) + E("Path too long %s", fn); + if (strlcat(tmp, ".XXXXXX", sizeof(tmp)) >= sizeof(tmp)) + E("Path too long %s", fn); + tmpfd = mkstemp(tmp); + if (tmpfd == -1) + E("mkstemp %s", tmp); + + fp = fdopen(tmpfd, "w"); + if (fp == NULL) + E("fdopen %s", tmp); + + if (fprintf(fp, "%s%s", + supported ? "nextboot_enable=\"YES\"\n" : "", + env != NULL ? env : "") < 0) { + int e; + + e = errno; + if (unlink(tmp)) + warn("unlink %s", tmp); + errno = e; + E("Can't write %s", tmp); + } + if (fsync(fileno(fp)) != 0) + E("Can't fsync %s", fn); + if (rename(tmp, fn) != 0) { + int e; -static int dohalt; + e = errno; + if (unlink(tmp)) + warn("unlink %s", tmp); + errno = e; + E("Can't rename %s to %s", tmp, fn); + } + fclose(fp); +} + +static char * +split_kv(char *raw) +{ + char *eq; + int len; + + eq = strchr(raw, '='); + if (eq == NULL) + errx(1, "No = in environment string %s", raw); + *eq++ = '\0'; + len = strlen(eq); + if (len == 0) + errx(1, "Invalid null value %s=", raw); + if (eq[0] == '"') { + if (len < 2 || eq[len - 1] != '"') + errx(1, "Invalid string '%s'", eq); + eq[len - 1] = '\0'; + return (eq + 1); + } + return (eq); +} + +static void +add_env(char **env, const char *key, const char *value) +{ + char *oldenv; + + oldenv = *env; + asprintf(env, "%s%s=\"%s\"\n", oldenv != NULL ? oldenv : "", key, value); + if (env == NULL) + errx(1, "No memory to build env array"); + free(oldenv); +} + +/* + * Different options are valid for different programs. + */ +#define GETOPT_REBOOT "cDde:fk:lNno:pqr" +#define GETOPT_NEXTBOOT "De:fk:o:" int main(int argc, char *argv[]) { struct utmpx utx; const struct passwd *pw; - int ch, howto, i, fd, lflag, nflag, qflag, sverrno, Nflag; - u_int pageins; - const char *user, *kernel = NULL; + struct stat st; + int ch, howto = 0, i, sverrno; + bool Dflag, fflag, lflag, Nflag, nflag, qflag; + uint64_t pageins; + const char *user, *kernel = NULL, *getopts = GETOPT_REBOOT; + char *env = NULL, *v; if (strstr(getprogname(), "halt") != NULL) { - dohalt = 1; + dohalt = true; howto = RB_HALT; - } else + } else if (strcmp(getprogname(), "nextboot") == 0) { + donextboot = true; + getopts = GETOPT_NEXTBOOT; /* Note: reboot's extra opts return '?' */ + } else { + /* reboot */ howto = 0; - lflag = nflag = qflag = Nflag = 0; - while ((ch = getopt(argc, argv, "cdk:lNnpqr")) != -1) + } + Dflag = fflag = lflag = Nflag = nflag = qflag = false; + while ((ch = getopt(argc, argv, getopts)) != -1) { switch(ch) { case 'c': howto |= RB_POWERCYCLE; break; + case 'D': + Dflag = true; + break; case 'd': howto |= RB_DUMP; break; + case 'e': + v = split_kv(optarg); + add_env(&env, optarg, v); + break; + case 'f': + fflag = true; + break; case 'k': kernel = optarg; break; case 'l': - lflag = 1; + lflag = true; break; case 'n': - nflag = 1; + nflag = true; howto |= RB_NOSYNC; break; case 'N': - nflag = 1; - Nflag = 1; + nflag = true; + Nflag = true; + break; + case 'o': + add_env(&env, "kernel_options", optarg); break; case 'p': howto |= RB_POWEROFF; break; case 'q': - qflag = 1; + qflag = true; break; case 'r': howto |= RB_REROOT; @@ -114,11 +289,20 @@ main(int argc, char *argv[]) default: usage(); } + } + argc -= optind; argv += optind; if (argc != 0) usage(); + if (!donextboot && !fflag && stat(_PATH_NOSHUTDOWN, &st) == 0) { + errx(1, "Reboot cannot be done, " _PATH_NOSHUTDOWN + " is present"); + } + + if (Dflag && ((howto & ~RB_HALT) != 0 || kernel != NULL)) + errx(1, "cannot delete existing nextboot config and do anything else"); if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT)) errx(1, "cannot dump (-d) when halting; must reboot instead"); if (Nflag && (howto & RB_NOSYNC) != 0) @@ -127,7 +311,31 @@ main(int argc, char *argv[]) errx(1, "-c and -p cannot be used together"); if ((howto & RB_REROOT) != 0 && howto != RB_REROOT) errx(1, "-r cannot be used with -c, -d, -n, or -p"); - if (geteuid()) { + if ((howto & RB_REROOT) != 0 && kernel != NULL) + errx(1, "-r and -k cannot be used together, there is no next kernel"); + + if (Dflag) { + struct stat sb; + + /* + * Break the rule about stat then doing + * something. When we're booting, there's no + * race. When we're a read-only root, though, the + * read-only error takes priority over the file not + * there error in unlink. So stat it first and exit + * with success if it isn't there. Otherwise, let + * unlink sort error reporting. POSIX-1.2024 suggests + * ENOENT should be preferred to EROFS for unlink, + * but FreeBSD historically has preferred EROFS. + */ + if (stat(PATH_NEXTBOOT, &sb) != 0 && errno == ENOENT) + exit(0); + if (unlink(PATH_NEXTBOOT) != 0) + warn("unlink " PATH_NEXTBOOT); + exit(0); + } + + if (!donextboot && geteuid() != 0) { errno = EPERM; err(1, NULL); } @@ -138,17 +346,27 @@ main(int argc, char *argv[]) } if (kernel != NULL) { - fd = open("/boot/nextboot.conf", O_WRONLY | O_CREAT | O_TRUNC, - 0444); - if (fd > -1) { - (void)write(fd, "nextboot_enable=\"YES\"\n", 22); - (void)write(fd, "kernel=\"", 8L); - (void)write(fd, kernel, strlen(kernel)); - (void)write(fd, "\"\n", 2); - close(fd); + if (!fflag) { + char *k; + struct stat sb; + + asprintf(&k, "/boot/%s/kernel", kernel); + if (k == NULL) + errx(1, "No memory to check %s", kernel); + if (stat(k, &sb) != 0) + err(1, "stat %s", k); + if (!S_ISREG(sb.st_mode)) + errx(1, "%s is not a file", k); + free(k); } + add_env(&env, "kernel", kernel); } + if (env != NULL) + write_nextboot(PATH_NEXTBOOT, env, fflag); + if (donextboot) + exit (0); + /* Log the reboot. */ if (!lflag) { if ((user = getlogin()) == NULL) @@ -272,17 +490,17 @@ usage(void) exit(1); } -static u_int +static uint64_t get_pageins(void) { - u_int pageins; + uint64_t pageins; size_t len; len = sizeof(pageins); if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0) != 0) { - warnx("v_swappgsin"); + warn("v_swappgsin"); return (0); } - return pageins; + return (pageins); } |