Date: Mon, 14 May 2012 00:06:46 +0200 From: Jilles Tjoelker <jilles@stack.nl> To: freebsd-arch@freebsd.org Subject: [patch] halt/reboot/shutdown cleanup Message-ID: <20120513220646.GA12826@stack.nl>
next in thread | raw e-mail | index | archive | help
There is duplication of code between sbin/reboot and init: both can
cleanly shut down the system, for some value of "cleanly". Because the
code in init is activated by the kernel (sending a signal to init), it
makes sense to keep that and remove the duplicate from sbin/reboot. This
also ensures that /etc/rc.shutdown is executed in all "clean" shutdowns.
Before 9.0, init could not shut down the system 100% correctly during
single user mode (for example, it inappropriately ran /etc/rc.shutdown),
but this has been fixed.
Also, the normal forms of halt and reboot will now call shutdown so
users get a clear message of the event.
Halt and reboot still support the -q option to invoke reboot(2) without
anything else. The -d and -n options now require -q (because init is
signaled if -q is not used, and init will not do dump or nosync).
The -l option of halt and reboot now not only suppresses logging, but
also user notification. It does this by signaling init directly and not
going through shutdown.
The -o option of shutdown goes away because there does not seem any
point in executing halt or reboot if they are going to send the same
signal to init anyway.
diff --git a/include/paths.h b/include/paths.h
index 6503934..f93cf39 100644
--- a/include/paths.h
+++ b/include/paths.h
@@ -84,6 +84,7 @@
#define _PATH_RSH "/usr/bin/rsh"
#define _PATH_SENDMAIL "/usr/sbin/sendmail"
#define _PATH_SHELLS "/etc/shells"
+#define _PATH_SHUTDOWN "/sbin/shutdown"
#define _PATH_TTY "/dev/tty"
#define _PATH_UNIX "don't use _PATH_UNIX"
#define _PATH_VI "/usr/bin/vi"
diff --git a/sbin/reboot/reboot.8 b/sbin/reboot/reboot.8
index 13d7098..50c27ee 100644
--- a/sbin/reboot/reboot.8
+++ b/sbin/reboot/reboot.8
@@ -55,12 +55,11 @@ The
.Nm halt
and
.Nm
-utilities flush the file system cache to disk, send all running processes
-a
-.Dv SIGTERM
-(and subsequently a
-.Dv SIGKILL )
-and, respectively, halt or restart the system.
+utilities invoke
+.Xr shutdown 8
+or signal
+.Xr init 8
+to, respectively, halt or restart the system.
The action is logged, including entering a shutdown record into the user
accounting database.
.Pp
@@ -69,7 +68,9 @@ The options are as follows:
.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
+supported only when rebooting together with the
+.Fl q
+option, and it has no effect unless a dump
device has previously been specified with
.Xr dumpon 8 .
.It Fl k Ar kernel
@@ -86,25 +87,14 @@ This may change in the future.
.It Fl l
The halt or reboot is
.Em not
-logged to the system log.
-This option is intended for applications such as
-.Xr shutdown 8 ,
-that call
-.Nm
-or
-.Nm halt
-and log this themselves.
+logged to the system log and not announced to users.
.It Fl n
The file system cache is not flushed.
-This option should probably not be used.
+This option should probably not be used and only works together with the
+.Fl q
+option.
.It Fl p
The system will turn off the power if it can.
-If the power down action fails, the system
-will halt or reboot normally, depending on whether
-.Nm halt
-or
-.Nm
-was called.
.It Fl q
The system is halted or restarted quickly and ungracefully, and only
the flushing of the file system cache is performed (if the
@@ -122,12 +112,6 @@ utilities are nothing more than aliases for the
and
.Nm
utilities.
-.Pp
-Normally, the
-.Xr shutdown 8
-utility is used when the system needs to be halted or restarted, giving
-users advance warning of their impending doom and cleanly terminating
-specific programs.
.Sh SEE ALSO
.Xr getutxent 3 ,
.Xr boot 8 ,
diff --git a/sbin/reboot/reboot.c b/sbin/reboot/reboot.c
index d927db0..285437a 100644
--- a/sbin/reboot/reboot.c
+++ b/sbin/reboot/reboot.c
@@ -44,31 +44,27 @@ __FBSDID("$FreeBSD$");
#include <sys/reboot.h>
#include <sys/time.h>
#include <sys/types.h>
-#include <sys/sysctl.h>
#include <signal.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
+#include <paths.h>
#include <pwd.h>
#include <syslog.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <utmpx.h>
static void usage(void);
-static u_int get_pageins(void);
static int dohalt;
int
main(int argc, char *argv[])
{
- struct utmpx utx;
const struct passwd *pw;
- int ch, howto, i, fd, lflag, nflag, qflag, sverrno;
- u_int pageins;
+ int ch, howto, fd, lflag, qflag;
const char *user, *kernel = NULL;
if (strcmp(getprogname(), "halt") == 0) {
@@ -76,7 +72,7 @@ main(int argc, char *argv[])
howto = RB_HALT;
} else
howto = 0;
- lflag = nflag = qflag = 0;
+ lflag = qflag = 0;
while ((ch = getopt(argc, argv, "dk:lnpq")) != -1)
switch(ch) {
case 'd':
@@ -89,7 +85,6 @@ main(int argc, char *argv[])
lflag = 1;
break;
case 'n':
- nflag = 1;
howto |= RB_NOSYNC;
break;
case 'p':
@@ -107,6 +102,10 @@ main(int argc, char *argv[])
if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
errx(1, "cannot dump (-d) when halting; must reboot instead");
+ if (!qflag && (howto & RB_DUMP) != 0)
+ errx(1, "cannot dump (-d) without immediate reboot(2) (-q)");
+ if (!qflag && (howto & RB_NOSYNC) != 0)
+ errx(1, "cannot disable sync (-n) without immediate reboot(2) (-q)");
if (geteuid()) {
errno = EPERM;
err(1, NULL);
@@ -129,6 +128,16 @@ main(int argc, char *argv[])
}
}
+#ifndef RESCUE
+ if (!lflag) {
+ execl(_PATH_SHUTDOWN, _PATH_SHUTDOWN,
+ (howto & RB_POWEROFF) != 0 ? "-p" :
+ (howto & RB_HALT) != 0 ? "-h" :
+ "-r", "now", (char *)NULL);
+ warn("cannot execute %s; signaling init(8)", _PATH_SHUTDOWN);
+ }
+#endif
+
/* Log the reboot. */
if (!lflag) {
if ((user = getlogin()) == NULL)
@@ -142,80 +151,12 @@ main(int argc, char *argv[])
syslog(LOG_CRIT, "rebooted by %s", user);
}
}
- utx.ut_type = SHUTDOWN_TIME;
- gettimeofday(&utx.ut_tv, NULL);
- pututxline(&utx);
-
- /*
- * Do a sync early on, so disks start transfers while we're off
- * killing processes. Don't worry about writes done before the
- * processes die, the reboot system call syncs the disks.
- */
- if (!nflag)
- sync();
-
- /*
- * Ignore signals that we can get as a result of killing
- * parents, group leaders, etc.
- */
- (void)signal(SIGHUP, SIG_IGN);
- (void)signal(SIGINT, SIG_IGN);
- (void)signal(SIGQUIT, SIG_IGN);
- (void)signal(SIGTERM, SIG_IGN);
- (void)signal(SIGTSTP, SIG_IGN);
-
- /*
- * If we're running in a pipeline, we don't want to die
- * after killing whatever we're writing to.
- */
- (void)signal(SIGPIPE, SIG_IGN);
-
- /* Just stop init -- if we fail, we'll restart it. */
- if (kill(1, SIGTSTP) == -1)
- err(1, "SIGTSTP init");
-
- /* Send a SIGTERM first, a chance to save the buffers. */
- if (kill(-1, SIGTERM) == -1 && errno != ESRCH)
- err(1, "SIGTERM processes");
-
- /*
- * After the processes receive the signal, start the rest of the
- * buffers on their way. Wait 5 seconds between the SIGTERM and
- * the SIGKILL to give everybody a chance. If there is a lot of
- * paging activity then wait longer, up to a maximum of approx
- * 60 seconds.
- */
- sleep(2);
- for (i = 0; i < 20; i++) {
- pageins = get_pageins();
- if (!nflag)
- sync();
- sleep(3);
- if (get_pageins() == pageins)
- break;
- }
-
- for (i = 1;; ++i) {
- if (kill(-1, SIGKILL) == -1) {
- if (errno == ESRCH)
- break;
- goto restart;
- }
- if (i > 5) {
- (void)fprintf(stderr,
- "WARNING: some process(es) wouldn't die\n");
- break;
- }
- (void)sleep(2 * i);
- }
-
- reboot(howto);
- /* FALLTHROUGH */
-restart:
- sverrno = errno;
- errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "",
- strerror(sverrno));
+ if (kill(1, (howto & RB_POWEROFF) != 0 ? SIGUSR2 :
+ (howto & RB_HALT) != 0 ? SIGUSR1 :
+ SIGINT) == -1)
+ err(1, "signal init");
+ pause();
/* NOTREACHED */
}
@@ -228,18 +169,3 @@ usage(void)
"usage: reboot [-dlnpq] [-k kernel]\n");
exit(1);
}
-
-static u_int
-get_pageins(void)
-{
- u_int pageins;
- size_t len;
-
- len = sizeof(pageins);
- if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0)
- != 0) {
- warnx("v_swappgsin");
- return (0);
- }
- return pageins;
-}
diff --git a/sbin/shutdown/shutdown.8 b/sbin/shutdown/shutdown.8
index b7067e1..37152ac 100644
--- a/sbin/shutdown/shutdown.8
+++ b/sbin/shutdown/shutdown.8
@@ -42,10 +42,6 @@
.Fl h | Fl p |
.Fl r | Fl k
.Oc
-.Oo
-.Fl o
-.Op Fl n
-.Oc
.Ar time
.Op Ar warning-message ...
.Nm poweroff
@@ -77,30 +73,6 @@ The
option
does not actually halt the system, but leaves the
system multi-user with logins disabled (for all but super-user).
-.It Fl o
-If one of the
-.Fl h ,
-.Fl p
-or
-.Fl r
-options are specified,
-.Nm
-will execute
-.Xr halt 8
-or
-.Xr reboot 8
-instead of sending a signal to
-.Xr init 8 .
-.It Fl n
-If the
-.Fl o
-option is specified, prevent the file system cache from being flushed by passing
-.Fl n
-to
-.Xr halt 8
-or
-.Xr reboot 8 .
-This option should probably not be used.
.It Ar time
.Ar Time
is the time at which
diff --git a/sbin/shutdown/shutdown.c b/sbin/shutdown/shutdown.c
index 6e662a8..945bef2 100644
--- a/sbin/shutdown/shutdown.c
+++ b/sbin/shutdown/shutdown.c
@@ -88,9 +88,9 @@ static struct interval {
#undef S
static time_t offset, shuttime;
-static int dohalt, dopower, doreboot, killflg, mbuflen, oflag;
+static int dohalt, dopower, doreboot, killflg, mbuflen;
static char mbuf[BUFSIZ];
-static const char *nosync, *whom;
+static const char *whom;
static void badtime(void);
static void perform_shutdown(void);
@@ -116,7 +116,6 @@ main(int argc, char **argv)
errx(1, "NOT super-user");
#endif
- nosync = NULL;
readstdin = 0;
/*
@@ -152,10 +151,8 @@ main(int argc, char **argv)
killflg = 1;
break;
case 'n':
- nosync = "-n";
- break;
case 'o':
- oflag = 1;
+ /* Ignore -n and -o for compatibility. */
break;
case 'p':
dopower = 1;
@@ -176,12 +173,6 @@ main(int argc, char **argv)
if (killflg + doreboot + dohalt + dopower > 1)
usage("incompatible switches -h, -k, -p and -r");
- if (oflag && !(dohalt || dopower || doreboot))
- usage("-o requires -h, -p or -r");
-
- if (nosync != NULL && !oflag)
- usage("-n requires -o");
-
getoffset(*argv++);
poweroff:
@@ -351,8 +342,6 @@ timeout(int signo __unused)
static void
perform_shutdown(void)
{
- char *empty_environ[] = { NULL };
-
syslog(LOG_NOTICE, "%s by %s: %s",
doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" :
"shutdown", whom, mbuf);
@@ -370,39 +359,12 @@ perform_shutdown(void)
(void)printf("halt");
else if (dopower)
(void)printf("power-down");
- if (nosync != NULL)
- (void)printf(" no sync");
(void)printf("\nkill -HUP 1\n");
#else
- if (!oflag) {
- (void)kill(1, doreboot ? SIGINT : /* reboot */
- dohalt ? SIGUSR1 : /* halt */
- dopower ? SIGUSR2 : /* power-down */
- SIGTERM); /* single-user */
- } else {
- if (doreboot) {
- execle(_PATH_REBOOT, "reboot", "-l", nosync,
- (char *)NULL, empty_environ);
- syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
- _PATH_REBOOT);
- warn(_PATH_REBOOT);
- }
- else if (dohalt) {
- execle(_PATH_HALT, "halt", "-l", nosync,
- (char *)NULL, empty_environ);
- syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
- _PATH_HALT);
- warn(_PATH_HALT);
- }
- else if (dopower) {
- execle(_PATH_HALT, "halt", "-l", "-p", nosync,
- (char *)NULL, empty_environ);
- syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
- _PATH_HALT);
- warn(_PATH_HALT);
- }
- (void)kill(1, SIGTERM); /* to single-user */
- }
+ (void)kill(1, doreboot ? SIGINT : /* reboot */
+ dohalt ? SIGUSR1 : /* halt */
+ dopower ? SIGUSR2 : /* power-down */
+ SIGTERM); /* single-user */
#endif
finish(0);
}
@@ -534,7 +496,7 @@ usage(const char *cp)
if (cp != NULL)
warnx("%s", cp);
(void)fprintf(stderr,
- "usage: shutdown [-] [-h | -p | -r | -k] [-o [-n]] time [warning-message ...]\n"
+ "usage: shutdown [-] [-h | -p | -r | -k] time [warning-message ...]\n"
" poweroff\n");
exit(1);
}
--
Jilles Tjoelker
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120513220646.GA12826>
