123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- package power
- import (
- "fmt"
- "os"
- "os/exec"
- "path/filepath"
- "syscall"
- "github.com/rancher/os/cmd/control/install"
- "github.com/rancher/os/config"
- "github.com/rancher/os/pkg/log"
- "github.com/codegangsta/cli"
- )
- 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
- }
|