mirror of
https://github.com/rancher/os.git
synced 2025-05-08 08:06:18 +00:00
228 lines
5.9 KiB
Go
228 lines
5.9 KiB
Go
package power
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"github.com/codegangsta/cli"
|
|
"github.com/rancher/os/cmd/control/install"
|
|
"github.com/rancher/os/config"
|
|
"github.com/rancher/os/pkg/log"
|
|
)
|
|
|
|
var (
|
|
haltFlag bool
|
|
poweroffFlag bool
|
|
rebootFlag bool
|
|
forceFlag bool
|
|
kexecFlag bool
|
|
previouskexecFlag bool
|
|
kexecAppendFlag string
|
|
)
|
|
|
|
func Shutdown() {
|
|
log.InitLogger()
|
|
app := cli.NewApp()
|
|
|
|
app.Name = filepath.Base(os.Args[0])
|
|
app.Usage = fmt.Sprintf("%s RancherOS\nbuilt: %s", app.Name, config.BuildDate)
|
|
app.Version = config.Version
|
|
app.Author = "Rancher Labs, Inc."
|
|
app.EnableBashCompletion = true
|
|
app.Action = shutdown
|
|
app.Flags = []cli.Flag{
|
|
// --no-wall
|
|
// Do not send wall message before halt, power-off,
|
|
// reboot.
|
|
|
|
// halt, poweroff, reboot ONLY
|
|
// -f, --force
|
|
// Force immediate halt, power-off, reboot. Do not
|
|
// contact the init system.
|
|
cli.BoolFlag{
|
|
Name: "f, force",
|
|
Usage: "Force immediate halt, power-off, reboot. Do not contact the init system.",
|
|
Destination: &forceFlag,
|
|
},
|
|
|
|
// -w, --wtmp-only
|
|
// Only write wtmp shutdown entry, do not actually
|
|
// halt, power-off, reboot.
|
|
|
|
// -d, --no-wtmp
|
|
// Do not write wtmp shutdown entry.
|
|
|
|
// -n, --no-sync
|
|
// Don't sync hard disks/storage media before halt,
|
|
// power-off, reboot.
|
|
|
|
// shutdown ONLY
|
|
// -h
|
|
// Equivalent to --poweroff, unless --halt is
|
|
// specified.
|
|
|
|
// -k
|
|
// Do not halt, power-off, reboot, just write wall
|
|
// message.
|
|
|
|
// -c
|
|
// Cancel a pending shutdown. This may be used
|
|
// cancel the effect of an invocation of shutdown
|
|
// with a time argument that is not "+0" or "now".
|
|
|
|
}
|
|
// -H, --halt
|
|
// Halt the machine.
|
|
if app.Name == "halt" {
|
|
app.Flags = append(app.Flags, cli.BoolTFlag{
|
|
Name: "H, halt",
|
|
Usage: "halt the machine",
|
|
Destination: &haltFlag,
|
|
})
|
|
} else {
|
|
app.Flags = append(app.Flags, cli.BoolFlag{
|
|
Name: "H, halt",
|
|
Usage: "halt the machine",
|
|
Destination: &haltFlag,
|
|
})
|
|
}
|
|
// -P, --poweroff
|
|
// Power-off the machine (the default for shutdown cmd).
|
|
if app.Name == "poweroff" {
|
|
app.Flags = append(app.Flags, cli.BoolTFlag{
|
|
Name: "P, poweroff",
|
|
Usage: "poweroff the machine",
|
|
Destination: &poweroffFlag,
|
|
})
|
|
} else {
|
|
// shutdown -h
|
|
// Equivalent to --poweroff
|
|
if app.Name == "shutdown" {
|
|
app.Flags = append(app.Flags, cli.BoolFlag{
|
|
Name: "h",
|
|
Usage: "poweroff the machine",
|
|
Destination: &poweroffFlag,
|
|
})
|
|
}
|
|
app.Flags = append(app.Flags, cli.BoolFlag{
|
|
Name: "P, poweroff",
|
|
Usage: "poweroff the machine",
|
|
Destination: &poweroffFlag,
|
|
})
|
|
}
|
|
// -r, --reboot
|
|
// Reboot the machine.
|
|
if app.Name == "reboot" {
|
|
app.Flags = append(app.Flags, cli.BoolTFlag{
|
|
Name: "r, reboot",
|
|
Usage: "reboot after shutdown",
|
|
Destination: &rebootFlag,
|
|
})
|
|
// OR? maybe implement it as a `kexec` cli tool?
|
|
app.Flags = append(app.Flags, cli.BoolFlag{
|
|
Name: "kexec",
|
|
Usage: "kexec the default RancherOS cfg",
|
|
Destination: &kexecFlag,
|
|
})
|
|
app.Flags = append(app.Flags, cli.BoolFlag{
|
|
Name: "kexec-previous",
|
|
Usage: "kexec the previous RancherOS cfg",
|
|
Destination: &previouskexecFlag,
|
|
})
|
|
app.Flags = append(app.Flags, cli.StringFlag{
|
|
Name: "kexec-append",
|
|
Usage: "kexec using the specified kernel boot params (ignores global.cfg)",
|
|
Destination: &kexecAppendFlag,
|
|
})
|
|
} else {
|
|
app.Flags = append(app.Flags, cli.BoolFlag{
|
|
Name: "r, reboot",
|
|
Usage: "reboot after shutdown",
|
|
Destination: &rebootFlag,
|
|
})
|
|
}
|
|
//TODO: add the time and msg flags...
|
|
app.HideHelp = true
|
|
|
|
app.Run(os.Args)
|
|
}
|
|
|
|
func Kexec(previous bool, bootDir, append string) error {
|
|
cfg := "linux-current.cfg"
|
|
if previous {
|
|
cfg = "linux-previous.cfg"
|
|
}
|
|
cfgFile := filepath.Join(bootDir, cfg)
|
|
vmlinuzFile, initrdFile, err := install.ReadSyslinuxCfg(cfgFile)
|
|
if err != nil {
|
|
log.Errorf("%s", err)
|
|
return err
|
|
}
|
|
globalCfgFile := filepath.Join(bootDir, "global.cfg")
|
|
if append == "" {
|
|
append, err = install.ReadGlobalCfg(globalCfgFile)
|
|
if err != nil {
|
|
log.Errorf("%s", err)
|
|
return err
|
|
}
|
|
}
|
|
// TODO: read global.cfg if append == ""
|
|
// kexec -l ${DIST}/vmlinuz --initrd=${DIST}/initrd --append="${kernelArgs} ${APPEND}" -f
|
|
cmd := exec.Command(
|
|
"kexec",
|
|
"-l", vmlinuzFile,
|
|
"--initrd", initrdFile,
|
|
"--append", append,
|
|
"-f")
|
|
log.Debugf("Run(%#v)", cmd)
|
|
cmd.Stderr = os.Stderr
|
|
if _, err := cmd.Output(); err != nil {
|
|
log.Errorf("Failed to kexec: %s", err)
|
|
return err
|
|
}
|
|
log.Infof("kexec'd to new install")
|
|
return nil
|
|
}
|
|
|
|
// Reboot is used by installation / upgrade
|
|
// TODO: add kexec option
|
|
func Reboot() {
|
|
os.Args = []string{"reboot"}
|
|
reboot("reboot", false, syscall.LINUX_REBOOT_CMD_RESTART)
|
|
}
|
|
|
|
func shutdown(c *cli.Context) error {
|
|
// the shutdown command's default is poweroff
|
|
var powerCmd uint
|
|
powerCmd = syscall.LINUX_REBOOT_CMD_POWER_OFF
|
|
if rebootFlag {
|
|
powerCmd = syscall.LINUX_REBOOT_CMD_RESTART
|
|
} else if poweroffFlag {
|
|
powerCmd = syscall.LINUX_REBOOT_CMD_POWER_OFF
|
|
} else if haltFlag {
|
|
powerCmd = syscall.LINUX_REBOOT_CMD_HALT
|
|
}
|
|
|
|
timeArg := c.Args().Get(0)
|
|
// We may be called via an absolute path, so check that now and make sure we
|
|
// don't pass the wrong app name down. Aside from the logic in the immediate
|
|
// context here, the container name is derived from how we were called and
|
|
// cannot contain slashes.
|
|
appName := filepath.Base(c.App.Name)
|
|
if appName == "shutdown" && timeArg != "" {
|
|
if timeArg != "now" {
|
|
err := fmt.Errorf("Sorry, can't parse '%s' as time value (only 'now' supported)", timeArg)
|
|
log.Error(err)
|
|
return err
|
|
}
|
|
// TODO: if there are more params, LOG them
|
|
}
|
|
|
|
reboot(appName, forceFlag, powerCmd)
|
|
|
|
return nil
|
|
}
|