aboutsummaryrefslogtreecommitdiff
path: root/sbin/reboot
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/reboot')
-rw-r--r--sbin/reboot/Makefile10
-rw-r--r--sbin/reboot/boot_i386.88
-rw-r--r--sbin/reboot/nextboot.sh132
-rw-r--r--sbin/reboot/reboot.877
-rw-r--r--sbin/reboot/reboot.c302
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);
}