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,11 @@ .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. .It Dv RB_POWEROFF After halting, the shutdown code will do what it can to turn off the power. 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,14 @@ .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. .It Fl d The system is requested to create a crash dump. This option is 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; @@ -117,7 +120,7 @@ if (Nflag && (howto & RB_NOSYNC) != 0) errx(1, "-N cannot be used with -n"); 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 +154,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,18 @@ .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 is rebooted if +the hardware supports that. +Otherwise the system is halted. .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 +85,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: 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 @@ -689,6 +689,45 @@ } } +static void +ipmi_power_cycle(void *arg, int howto) +{ + struct ipmi_softc *sc = arg; + struct ipmi_request *req = &sc->ipmi_req; + + /* + * Ignore everything except power cycling requests + */ + if ((howto & RB_POWERCYCLE) == 0) + return; + + printf("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_INIT_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); + + if (req->ir_error != 0 || req->ir_compcode != 0) { + printf("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); + printf("Power cycling via IPMI timed out\n"); +} + static void ipmi_startup(void *arg) { @@ -742,6 +781,8 @@ 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); + 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 +833,16 @@ 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) + sc->ipmi_power_cycle_tag = EVENTHANDLER_REGISTER(shutdown_final, + ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 1); + } int @@ -844,6 +895,10 @@ ipmi_set_watchdog(sc, 0); } + /* Detach from watchdog handling and turn off watchdog. */ + 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 @@ -86,6 +86,13 @@ int smbus_address; }; +/* + * More than enough for any request / reply when we're using the + * static buffer in restricted situtions. Request + reply lengths must + * be less than this. + */ +#define IPMI_STATIC_REQ_LEN 32 + struct ipmi_softc { device_t ipmi_dev; union { @@ -103,21 +110,28 @@ 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; struct mtx ipmi_requests_lock; struct cv ipmi_request_added; struct proc *ipmi_kthread; + union { + struct ipmi_request _req; + char buff[sizeof(ipmi_request) + IPMI_STATIC_REQ_LEN]; + } _u; driver_intr_t *ipmi_intr; int (*ipmi_startup)(struct ipmi_softc *); int (*ipmi_enqueue_request)(struct ipmi_softc *, struct ipmi_request *); int (*ipmi_driver_request)(struct ipmi_softc *, struct ipmi_request *, int); }; +#define ipmi_static_req _u.req #define ipmi_ssif_smbus_address _iface.ssif.smbus_address #define ipmi_ssif_smbus _iface.ssif.smbus 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, SIGUWINCH); 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_CHASIS_REQUEST 0x00 +# define IPMI_CHASIS_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, otherwise POWEROFF */ #define RB_MULTIPLE 0x20000000 /* use multiple consoles */ #define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */