shutdown.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. package power
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. "path/filepath"
  7. "syscall"
  8. "github.com/codegangsta/cli"
  9. "github.com/rancher/os/cmd/control/install"
  10. "github.com/rancher/os/config"
  11. "github.com/rancher/os/log"
  12. )
  13. var (
  14. haltFlag bool
  15. poweroffFlag bool
  16. rebootFlag bool
  17. forceFlag bool
  18. kexecFlag bool
  19. previouskexecFlag bool
  20. kexecAppendFlag string
  21. )
  22. func Shutdown() {
  23. log.InitLogger()
  24. app := cli.NewApp()
  25. app.Name = os.Args[0]
  26. app.Usage = fmt.Sprintf("%s RancherOS\nbuilt: %s", app.Name, config.BuildDate)
  27. app.Version = config.Version
  28. app.Author = "Rancher Labs, Inc."
  29. app.EnableBashCompletion = true
  30. app.Action = shutdown
  31. app.Flags = []cli.Flag{
  32. // --no-wall
  33. // Do not send wall message before halt, power-off,
  34. // reboot.
  35. // halt, poweroff, reboot ONLY
  36. // -f, --force
  37. // Force immediate halt, power-off, reboot. Do not
  38. // contact the init system.
  39. cli.BoolFlag{
  40. Name: "f, force",
  41. Usage: "Force immediate halt, power-off, reboot. Do not contact the init system.",
  42. Destination: &forceFlag,
  43. },
  44. // -w, --wtmp-only
  45. // Only write wtmp shutdown entry, do not actually
  46. // halt, power-off, reboot.
  47. // -d, --no-wtmp
  48. // Do not write wtmp shutdown entry.
  49. // -n, --no-sync
  50. // Don't sync hard disks/storage media before halt,
  51. // power-off, reboot.
  52. // shutdown ONLY
  53. // -h
  54. // Equivalent to --poweroff, unless --halt is
  55. // specified.
  56. // -k
  57. // Do not halt, power-off, reboot, just write wall
  58. // message.
  59. // -c
  60. // Cancel a pending shutdown. This may be used
  61. // cancel the effect of an invocation of shutdown
  62. // with a time argument that is not "+0" or "now".
  63. }
  64. // -H, --halt
  65. // Halt the machine.
  66. if app.Name == "halt" {
  67. app.Flags = append(app.Flags, cli.BoolTFlag{
  68. Name: "H, halt",
  69. Usage: "halt the machine",
  70. Destination: &haltFlag,
  71. })
  72. } else {
  73. app.Flags = append(app.Flags, cli.BoolFlag{
  74. Name: "H, halt",
  75. Usage: "halt the machine",
  76. Destination: &haltFlag,
  77. })
  78. }
  79. // -P, --poweroff
  80. // Power-off the machine (the default for shutdown cmd).
  81. if app.Name == "poweroff" {
  82. app.Flags = append(app.Flags, cli.BoolTFlag{
  83. Name: "P, poweroff",
  84. Usage: "halt the machine",
  85. Destination: &poweroffFlag,
  86. })
  87. } else {
  88. app.Flags = append(app.Flags, cli.BoolFlag{
  89. Name: "P, poweroff",
  90. Usage: "halt the machine",
  91. Destination: &poweroffFlag,
  92. })
  93. }
  94. // -r, --reboot
  95. // Reboot the machine.
  96. if app.Name == "reboot" {
  97. app.Flags = append(app.Flags, cli.BoolTFlag{
  98. Name: "r, reboot",
  99. Usage: "reboot after shutdown",
  100. Destination: &rebootFlag,
  101. })
  102. // OR? maybe implement it as a `kexec` cli tool?
  103. app.Flags = append(app.Flags, cli.BoolFlag{
  104. Name: "kexec",
  105. Usage: "kexec the default RancherOS cfg",
  106. Destination: &kexecFlag,
  107. })
  108. app.Flags = append(app.Flags, cli.BoolFlag{
  109. Name: "kexec-previous",
  110. Usage: "kexec the previous RancherOS cfg",
  111. Destination: &previouskexecFlag,
  112. })
  113. app.Flags = append(app.Flags, cli.StringFlag{
  114. Name: "kexec-append",
  115. Usage: "kexec using the specified kernel boot params (ignores global.cfg)",
  116. Destination: &kexecAppendFlag,
  117. })
  118. } else {
  119. app.Flags = append(app.Flags, cli.BoolFlag{
  120. Name: "r, reboot",
  121. Usage: "reboot after shutdown",
  122. Destination: &rebootFlag,
  123. })
  124. }
  125. //TODO: add the time and msg flags...
  126. app.HideHelp = true
  127. app.Run(os.Args)
  128. }
  129. func Kexec(previous bool, bootDir, append string) error {
  130. cfg := "linux-current.cfg"
  131. if previous {
  132. cfg = "linux-previous.cfg"
  133. }
  134. cfgFile := filepath.Join(bootDir, cfg)
  135. vmlinuzFile, initrdFile, err := install.ReadSyslinuxCfg(cfgFile)
  136. if err != nil {
  137. log.Errorf("%s", err)
  138. return err
  139. }
  140. globalCfgFile := filepath.Join(bootDir, "global.cfg")
  141. if append == "" {
  142. append, err = install.ReadGlobalCfg(globalCfgFile)
  143. if err != nil {
  144. log.Errorf("%s", err)
  145. return err
  146. }
  147. }
  148. // TODO: read global.cfg if append == ""
  149. // kexec -l ${DIST}/vmlinuz --initrd=${DIST}/initrd --append="${kernelArgs} ${APPEND}" -f
  150. cmd := exec.Command(
  151. "kexec",
  152. "-l", vmlinuzFile,
  153. "--initrd", initrdFile,
  154. "--append", append,
  155. "-f")
  156. log.Debugf("Run(%#v)", cmd)
  157. cmd.Stderr = os.Stderr
  158. if _, err := cmd.Output(); err != nil {
  159. log.Errorf("Failed to kexec: %s", err)
  160. return err
  161. }
  162. log.Infof("kexec'd to new install")
  163. return nil
  164. }
  165. // Reboot is used by installation / upgrade
  166. // TODO: add kexec option
  167. func Reboot() {
  168. reboot("reboot", false, syscall.LINUX_REBOOT_CMD_RESTART)
  169. }
  170. func shutdown(c *cli.Context) error {
  171. // the shutdown command's default is poweroff
  172. var powerCmd uint
  173. powerCmd = syscall.LINUX_REBOOT_CMD_POWER_OFF
  174. if rebootFlag {
  175. powerCmd = syscall.LINUX_REBOOT_CMD_RESTART
  176. } else if poweroffFlag {
  177. powerCmd = syscall.LINUX_REBOOT_CMD_POWER_OFF
  178. } else if haltFlag {
  179. powerCmd = syscall.LINUX_REBOOT_CMD_HALT
  180. }
  181. timeArg := c.Args().Get(0)
  182. if c.App.Name == "shutdown" && timeArg != "" {
  183. if timeArg != "now" {
  184. err := fmt.Errorf("Sorry, can't parse '%s' as time value (only 'now' supported)", timeArg)
  185. log.Error(err)
  186. return err
  187. }
  188. // TODO: if there are more params, LOG them
  189. }
  190. reboot(c.App.Name, forceFlag, powerCmd)
  191. return nil
  192. }