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>