Index: lib/libc/sys/reboot.2 =================================================================== --- lib/libc/sys/reboot.2 +++ lib/libc/sys/reboot.2 @@ -28,7 +28,7 @@ .\" @(#)reboot.2 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd September 18, 2015 +.Dd October 24, 2017 .Dt REBOOT 2 .Os .Sh NAME @@ -84,6 +84,14 @@ .It Dv RB_HALT The processor is simply halted; no reboot takes place. This option should be used with caution. +.It Dv RB_POWERCYCLE +After halting, the shutdown code will do what it can to turn +off the power and then turn the power back on. +This requires hardware support, usually an auxiliary microprocessor +that can sequence the power supply. +At present only the +.Xr ipmi 4 +driver implements this feature. .It Dv RB_POWEROFF After halting, the shutdown code will do what it can to turn off the power. Index: lib/libsysdecode/flags.c =================================================================== --- lib/libsysdecode/flags.c +++ lib/libsysdecode/flags.c @@ -647,9 +647,9 @@ /* * RB_AUTOBOOT is special in that its value is zero, but it is * also an implied argument if a different operation is not - * requested via RB_HALT, RB_POWEROFF, or RB_REROOT. + * requested via RB_HALT, RB_POWERCYCLE, RB_POWEROFF, or RB_REROOT. */ - if (howto != 0 && (howto & (RB_HALT | RB_POWEROFF | RB_REROOT)) == 0) { + if (howto != 0 && (howto & (RB_HALT | RB_POWEROFF | RB_REROOT | RB_POWERCYCLE)) == 0) { fputs("RB_AUTOBOOT|", fp); printed = true; } else Index: sbin/init/init.8 =================================================================== --- sbin/init/init.8 +++ sbin/init/init.8 @@ -286,6 +286,7 @@ .It Sy "Run-level Signal Action" .It Cm 0 Ta Dv SIGUSR1 Ta "Halt" .It Cm 0 Ta Dv SIGUSR2 Ta "Halt and turn the power off" +.It Cm 0 Ta Dv SIGWINCH Ta "Halt and turn the power off and then back on" .It Cm 1 Ta Dv SIGTERM Ta "Go to single-user mode" .It Cm 6 Ta Dv SIGINT Ta "Reboot the machine" .It Cm c Ta Dv SIGTSTP Ta "Block further logins" Index: sbin/init/init.c =================================================================== --- sbin/init/init.c +++ sbin/init/init.c @@ -305,12 +305,12 @@ handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGXCPU, SIGXFSZ, 0); handle(transition_handler, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP, - SIGUSR1, SIGUSR2, 0); + SIGUSR1, SIGUSR2, SIGWINCH, 0); handle(alrm_handler, SIGALRM, 0); sigfillset(&mask); delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGEMT, SIGTERM, SIGTSTP, - SIGALRM, SIGUSR1, SIGUSR2, 0); + SIGALRM, SIGUSR1, SIGUSR2, SIGWINCH, 0); sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; @@ -1557,8 +1557,9 @@ current_state == clean_ttys || current_state == catatonia) requested_transition = clean_ttys; break; + case SIGWINCH: case SIGUSR2: - howto = RB_POWEROFF; + howto = sig == SIGUSR2 ? RB_POWEROFF : RB_POWERCYCLE; case SIGUSR1: howto |= RB_HALT; case SIGINT: Index: sbin/reboot/reboot.8 =================================================================== --- sbin/reboot/reboot.8 +++ sbin/reboot/reboot.8 @@ -28,7 +28,7 @@ .\" @(#)reboot.8 8.1 (Berkeley) 6/9/93 .\" $FreeBSD$ .\" -.Dd March 19, 2017 +.Dd October 23, 2017 .Dt REBOOT 8 .Os .Sh NAME @@ -42,7 +42,7 @@ .Op Fl lNnpq .Op Fl k Ar kernel .Nm -.Op Fl dlNnpqr +.Op Fl cdlNnpqr .Op Fl k Ar kernel .Nm fasthalt .Op Fl lNnpq @@ -66,6 +66,20 @@ .Pp The options are as follows: .Bl -tag -width indent +.It Fl c +The system will turn off the power and then turn it back on 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. +At the present time, only the +.Xr ipmi 4 +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 The system is requested to create a crash dump. This option is @@ -162,6 +176,7 @@ .Sh SEE ALSO .Xr kenv 1 , .Xr getutxent 3 , +.Xr ipmi 4 , .Xr boot 8 , .Xr dumpon 8 , .Xr nextboot 8 , Index: sbin/reboot/reboot.c =================================================================== --- sbin/reboot/reboot.c +++ sbin/reboot/reboot.c @@ -77,8 +77,11 @@ } else howto = 0; lflag = nflag = qflag = Nflag = 0; - while ((ch = getopt(argc, argv, "dk:lNnpqr")) != -1) + while ((ch = getopt(argc, argv, "cdk:lNnpqr")) != -1) switch(ch) { + case 'c': + howto |= RB_POWERCYCLE; + break; case 'd': howto |= RB_DUMP; break; @@ -116,8 +119,10 @@ errx(1, "cannot dump (-d) when halting; must reboot instead"); if (Nflag && (howto & RB_NOSYNC) != 0) errx(1, "-N cannot be used with -n"); + if ((howto & RB_POWEROFF) && (howto & RB_POWERCYCLE)) + errx(1, "-c and -p cannot be used together"); if ((howto & RB_REROOT) != 0 && howto != RB_REROOT) - errx(1, "-r cannot be used with -d, -n, or -p"); + errx(1, "-r cannot be used with -c, -d, -n, or -p"); if (geteuid()) { errno = EPERM; err(1, NULL); @@ -151,6 +156,12 @@ } else if (howto & RB_REROOT) { openlog("reroot", 0, LOG_AUTH | LOG_CONS); syslog(LOG_CRIT, "rerooted by %s", user); + } else if (howto & RB_POWEROFF) { + openlog("reboot", 0, LOG_AUTH | LOG_CONS); + syslog(LOG_CRIT, "powered off by %s", user); + } else if (howto & RB_POWERCYCLE) { + openlog("reboot", 0, LOG_AUTH | LOG_CONS); + syslog(LOG_CRIT, "power cycled by %s", user); } else { openlog("reboot", 0, LOG_AUTH | LOG_CONS); syslog(LOG_CRIT, "rebooted by %s", user); Index: sbin/shutdown/shutdown.8 =================================================================== --- sbin/shutdown/shutdown.8 +++ sbin/shutdown/shutdown.8 @@ -28,7 +28,7 @@ .\" @(#)shutdown.8 8.2 (Berkeley) 4/27/95 .\" $FreeBSD$ .\" -.Dd September 21, 2016 +.Dd October 23, 2017 .Dt SHUTDOWN 8 .Os .Sh NAME @@ -39,7 +39,7 @@ .Nm .Op Fl .Oo -.Fl h | Fl p | +.Fl c | Fl h | Fl p | .Fl r | Fl k .Oc .Oo @@ -59,12 +59,22 @@ .Pp The following options are available: .Bl -tag -width indent +.It Fl c +The system is power cycled (power turned off and then back on) +at the specified time. +If the hardware doesn't support power cycle, the system will be +halted. +At the present time, only systems with BMC supported by the +.Xr ipmi 4 +driver that implement this functionality support this flag. +The amount of time the system is off is dependent on the device +that implements this feature. .It Fl h The system is halted at the specified .Ar time . .It Fl p The system is halted and the power is turned off -(hardware support required) +(hardware support required, otherwise the system is halted) at the specified .Ar time . .It Fl r @@ -79,6 +89,7 @@ system multi-user with logins disabled (for all but super-user). .It Fl o If one of the +.Fl c , .Fl h , .Fl p or Index: sbin/shutdown/shutdown.c =================================================================== --- sbin/shutdown/shutdown.c +++ sbin/shutdown/shutdown.c @@ -89,7 +89,7 @@ #undef S static time_t offset, shuttime; -static int dohalt, dopower, doreboot, killflg, mbuflen, oflag; +static int docycle, dohalt, dopower, doreboot, killflg, mbuflen, oflag; static char mbuf[BUFSIZ]; static const char *nosync, *whom; @@ -141,11 +141,14 @@ goto poweroff; } - while ((ch = getopt(argc, argv, "-hknopr")) != -1) + while ((ch = getopt(argc, argv, "-chknopr")) != -1) switch (ch) { case '-': readstdin = 1; break; + case 'c': + docycle = 1; + break; case 'h': dohalt = 1; break; @@ -174,11 +177,11 @@ if (argc < 1) usage((char *)NULL); - if (killflg + doreboot + dohalt + dopower > 1) - usage("incompatible switches -h, -k, -p and -r"); + if (killflg + doreboot + dohalt + dopower + docycle > 1) + usage("incompatible switches -c, -h, -k, -p and -r"); - if (oflag && !(dohalt || dopower || doreboot)) - usage("-o requires -h, -p or -r"); + if (oflag && !(dohalt || dopower || doreboot || docycle)) + usage("-o requires -c, -h, -p or -r"); if (nosync != NULL && !oflag) usage("-n requires -o"); @@ -356,8 +359,8 @@ char *empty_environ[] = { NULL }; syslog(LOG_NOTICE, "%s by %s: %s", - doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : - "shutdown", whom, mbuf); + doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : + docycle ? "power-cycle" : "shutdown", whom, mbuf); (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); if (killflg) { @@ -367,6 +370,8 @@ #ifdef DEBUG if (doreboot) (void)printf("reboot"); + else if (docycle) + (void)printf("power-cycle"); else if (dohalt) (void)printf("halt"); else if (dopower) @@ -379,6 +384,7 @@ (void)kill(1, doreboot ? SIGINT : /* reboot */ dohalt ? SIGUSR1 : /* halt */ dopower ? SIGUSR2 : /* power-down */ + docycle ? SIGWINCH : /* power-cycle */ SIGTERM); /* single-user */ } else { if (doreboot) { @@ -402,6 +408,13 @@ _PATH_HALT); warn(_PATH_HALT); } + else if (docycle) { + execle(_PATH_HALT, "halt", "-l", "-c", 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 */ } #endif Index: share/man/man4/ipmi.4 =================================================================== --- share/man/man4/ipmi.4 +++ share/man/man4/ipmi.4 @@ -90,6 +90,17 @@ .Tn Linux driver; however, not all features described in the standard are supported. +.Pp +The +.Nm +driver implements the power cycling option to +.Xr shutdown 8 +to implement power cycling of the system. +The motherboard's BMC must support the chassis device and the optional +power cycle subcomand of the chassis control command as described in section 28.3 +if the IPMI standard. +The length of time the system is off will be at least one second, but +may be longer if the power cycle interval has been set (see section 28.9). .Sh IOCTLS Sending and receiving messages through the .Nm @@ -179,6 +190,8 @@ .Sh SEE ALSO .Xr ioctl 2 , .Xr watchdog 4 , +.Xr reboot 8 , +.Xr shutdown 8 , .Xr watchdog 8 , .Xr watchdogd 8 , .Xr watchdog 9 Index: sys/cam/ata/ata_da.c =================================================================== --- sys/cam/ata/ata_da.c +++ sys/cam/ata/ata_da.c @@ -3513,7 +3513,7 @@ adaflush(); if (ada_spindown_shutdown != 0 && - (howto & (RB_HALT | RB_POWEROFF)) != 0) + (howto & (RB_HALT | RB_POWEROFF | RB_POWERCYCLE)) != 0) adaspindown(ATA_STANDBY_IMMEDIATE, 0); } Index: sys/dev/ipmi/ipmi.c =================================================================== --- sys/dev/ipmi/ipmi.c +++ sys/dev/ipmi/ipmi.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -689,6 +690,45 @@ } } +static void +ipmi_power_cycle(void *arg, int howto) +{ + struct ipmi_softc *sc = arg; + struct ipmi_request *req; + + /* + * Ignore everything except power cycling requests + */ + if ((howto & RB_POWERCYCLE) == 0) + return; + + device_printf(sc->ipmi_dev, "Power cycling using IPMI\n"); + + /* + * Send a CHASSIS_CONTROL command to the CHASSIS device, subcommand 2 + * as described in IPMI v2.0 spec section 28.3. + */ + IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_CHASSIS_REQUEST, 0), + IPMI_CHASSIS_CONTROL, 1, 0); + req->ir_request[0] = IPMI_CC_POWER_CYCLE; + + ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); + + if (req->ir_error != 0 || req->ir_compcode != 0) { + device_printf(sc->ipmi_dev, "Power cycling via IPMI failed code %#x %#x\n", + req->ir_error, req->ir_compcode); + return; + } + + /* + * BMCs are notoriously slow, give it up to 10s to effect the power + * down leg of the power cycle. If that fails, fallback to the next + * hanlder in the shutdown_final chain and/or the platform failsafe. + */ + DELAY(10 * 1000 * 1000); + device_printf(sc->ipmi_dev, "Power cycling via IPMI timed out\n"); +} + static void ipmi_startup(void *arg) { @@ -737,10 +777,12 @@ } device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " - "version %d.%d\n", - req->ir_reply[1] & 0x0f, - req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, - req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4); + "version %d.%d, device support mask %#x\n", + req->ir_reply[1] & 0x0f, + req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, + req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4, req->ir_reply[5]); + + sc->ipmi_dev_support = req->ir_reply[5]; IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_CLEAR_FLAGS, 1, 0); @@ -792,6 +834,17 @@ return; } sc->ipmi_cdev->si_drv1 = sc; + + /* + * Power cycle the system off using IPMI. We use last - 1 since we don't + * handle all the other kinds of reboots. We'll let others handle them. + * We only try to do this if the BMC supports the Chassis device. + */ + if (sc->ipmi_dev_support & IPMI_ADS_CHASSIS) { + device_printf(dev, "Establishing power cycle handler\n"); + sc->ipmi_power_cycle_tag = EVENTHANDLER_REGISTER(shutdown_final, + ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 1); + } } int @@ -844,6 +897,10 @@ ipmi_set_watchdog(sc, 0); } + /* Detach from shutdown handling for power cycle reboot */ + if (sc->ipmi_power_cycle_tag) + EVENTHANDLER_DEREGISTER(shutdown_final, sc->ipmi_power_cycle_tag); + /* XXX: should use shutdown callout I think. */ /* If the backend uses a kthread, shut it down. */ IPMI_LOCK(sc); Index: sys/dev/ipmi/ipmivars.h =================================================================== --- sys/dev/ipmi/ipmivars.h +++ sys/dev/ipmi/ipmivars.h @@ -103,9 +103,11 @@ void *ipmi_irq; int ipmi_detaching; int ipmi_opened; + uint8_t ipmi_dev_support; /* IPMI_ADS_* */ struct cdev *ipmi_cdev; TAILQ_HEAD(,ipmi_request) ipmi_pending_requests; int ipmi_driver_requests_polled; + eventhandler_tag ipmi_power_cycle_tag; eventhandler_tag ipmi_watchdog_tag; int ipmi_watchdog_active; struct intr_config_hook ipmi_ich; Index: sys/kern/kern_shutdown.c =================================================================== --- sys/kern/kern_shutdown.c +++ sys/kern/kern_shutdown.c @@ -263,6 +263,8 @@ PROC_LOCK(initproc); if (howto & RB_POWEROFF) kern_psignal(initproc, SIGUSR2); + else if (howto & RB_POWERCYCLE) + kern_psignal(initproc, SIGWINCH); else if (howto & RB_HALT) kern_psignal(initproc, SIGUSR1); else @@ -797,7 +799,7 @@ poweroff_wait(void *junk, int howto) { - if (!(howto & RB_POWEROFF) || poweroff_delay <= 0) + if ((howto & (RB_POWEROFF | RB_POWERCYCLE)) == 0 || poweroff_delay <= 0) return; DELAY(poweroff_delay * 1000); } Index: sys/sys/ipmi.h =================================================================== --- sys/sys/ipmi.h +++ sys/sys/ipmi.h @@ -56,8 +56,25 @@ #define IPMI_ASYNC_EVENT_RECV_TYPE 2 #define IPMI_CMD_RECV_TYPE 3 +#define IPMI_CHASSIS_REQUEST 0x00 +# define IPMI_CHASSIS_CONTROL 0x02 +# define IPMI_CC_POWER_DOWN 0x0 +# define IPMI_CC_POWER_UP 0x1 +# define IPMI_CC_POWER_CYCLE 0x2 +# define IPMI_CC_HARD_RESET 0x3 +# define IPMI_CC_PULSE_DI 0x4 +# define IPMI_CC_SOFT_OVERTEMP 0x5 + #define IPMI_APP_REQUEST 0x06 #define IPMI_GET_DEVICE_ID 0x01 +# define IPMI_ADS_CHASSIS 0x80 +# define IPMI_ADS_BRIDGE 0x40 +# define IPMI_ADS_EVENT_GEN 0x20 +# define IPMI_ADS_EVENT_RCV 0x10 +# define IPMI_ADS_FRU_INV 0x08 +# define IPMI_ADS_SEL 0x04 +# define IPMI_ADS_SDR 0x02 +# define IPMI_ADS_SENSOR 0x01 #define IPMI_CLEAR_FLAGS 0x30 #define IPMI_GET_MSG_FLAGS 0x31 # define IPMI_MSG_AVAILABLE 0x01 Index: sys/sys/reboot.h =================================================================== --- sys/sys/reboot.h +++ sys/sys/reboot.h @@ -60,6 +60,7 @@ #define RB_RESERVED2 0x80000 /* reserved for internal use of boot blocks */ #define RB_PAUSE 0x100000 /* pause after each output line during probe */ #define RB_REROOT 0x200000 /* unmount the rootfs and mount it again */ +#define RB_POWERCYCLE 0x400000 /* Power cycle if possible */ #define RB_MULTIPLE 0x20000000 /* use multiple consoles */ #define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */