power.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. package power
  2. import (
  3. "errors"
  4. "os"
  5. "path/filepath"
  6. "strconv"
  7. "strings"
  8. "syscall"
  9. "golang.org/x/net/context"
  10. "github.com/docker/engine-api/types"
  11. "github.com/docker/engine-api/types/container"
  12. "github.com/docker/engine-api/types/filters"
  13. "github.com/rancher/os/log"
  14. "github.com/rancher/os/docker"
  15. "github.com/rancher/os/util"
  16. )
  17. func runDocker(name string) error {
  18. if os.ExpandEnv("${IN_DOCKER}") == "true" {
  19. return nil
  20. }
  21. client, err := docker.NewSystemClient()
  22. if err != nil {
  23. return err
  24. }
  25. cmd := []string{name}
  26. if name == "" {
  27. name = filepath.Base(os.Args[0])
  28. cmd = os.Args
  29. }
  30. existing, err := client.ContainerInspect(context.Background(), name)
  31. if err == nil && existing.ID != "" {
  32. err := client.ContainerRemove(context.Background(), types.ContainerRemoveOptions{
  33. ContainerID: existing.ID,
  34. })
  35. if err != nil {
  36. return err
  37. }
  38. }
  39. currentContainerID, err := util.GetCurrentContainerID()
  40. if err != nil {
  41. return err
  42. }
  43. currentContainer, err := client.ContainerInspect(context.Background(), currentContainerID)
  44. if err != nil {
  45. return err
  46. }
  47. powerContainer, err := client.ContainerCreate(context.Background(),
  48. &container.Config{
  49. Image: currentContainer.Config.Image,
  50. Cmd: cmd,
  51. Env: []string{
  52. "IN_DOCKER=true",
  53. },
  54. },
  55. &container.HostConfig{
  56. PidMode: "host",
  57. VolumesFrom: []string{
  58. currentContainer.ID,
  59. },
  60. Privileged: true,
  61. }, nil, name)
  62. if err != nil {
  63. return err
  64. }
  65. go func() {
  66. client.ContainerAttach(context.Background(), types.ContainerAttachOptions{
  67. ContainerID: powerContainer.ID,
  68. Stream: true,
  69. Stderr: true,
  70. Stdout: true,
  71. })
  72. }()
  73. err = client.ContainerStart(context.Background(), powerContainer.ID)
  74. if err != nil {
  75. return err
  76. }
  77. _, err = client.ContainerWait(context.Background(), powerContainer.ID)
  78. if err != nil {
  79. log.Fatal(err)
  80. }
  81. os.Exit(0)
  82. return nil
  83. }
  84. func common(name string) {
  85. if os.Geteuid() != 0 {
  86. log.Fatalf("%s: Need to be root", os.Args[0])
  87. }
  88. if err := runDocker(name); err != nil {
  89. log.Fatal(err)
  90. }
  91. }
  92. func Off() {
  93. common("poweroff")
  94. reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
  95. }
  96. func Reboot() {
  97. common("reboot")
  98. reboot(syscall.LINUX_REBOOT_CMD_RESTART)
  99. }
  100. func Halt() {
  101. common("halt")
  102. reboot(syscall.LINUX_REBOOT_CMD_HALT)
  103. }
  104. func reboot(code uint) {
  105. err := shutDownContainers()
  106. if err != nil {
  107. log.Error(err)
  108. }
  109. syscall.Sync()
  110. err = syscall.Reboot(int(code))
  111. if err != nil {
  112. log.Fatal(err)
  113. }
  114. }
  115. func shutDownContainers() error {
  116. var err error
  117. shutDown := true
  118. timeout := 2
  119. for i, arg := range os.Args {
  120. if arg == "-f" || arg == "--f" || arg == "--force" {
  121. shutDown = false
  122. }
  123. if arg == "-t" || arg == "--t" || arg == "--timeout" {
  124. if len(os.Args) > i+1 {
  125. t, err := strconv.Atoi(os.Args[i+1])
  126. if err != nil {
  127. return err
  128. }
  129. timeout = t
  130. } else {
  131. log.Error("please specify a timeout")
  132. }
  133. }
  134. }
  135. if !shutDown {
  136. return nil
  137. }
  138. client, err := docker.NewSystemClient()
  139. if err != nil {
  140. return err
  141. }
  142. filter := filters.NewArgs()
  143. filter.Add("status", "running")
  144. opts := types.ContainerListOptions{
  145. All: true,
  146. Filter: filter,
  147. }
  148. containers, err := client.ContainerList(context.Background(), opts)
  149. if err != nil {
  150. return err
  151. }
  152. currentContainerID, err := util.GetCurrentContainerID()
  153. if err != nil {
  154. return err
  155. }
  156. var stopErrorStrings []string
  157. for _, container := range containers {
  158. if container.ID == currentContainerID {
  159. continue
  160. }
  161. log.Infof("Stopping %s : %v", container.ID[:12], container.Names)
  162. stopErr := client.ContainerStop(context.Background(), container.ID, timeout)
  163. if stopErr != nil {
  164. stopErrorStrings = append(stopErrorStrings, " ["+container.ID+"] "+stopErr.Error())
  165. }
  166. }
  167. var waitErrorStrings []string
  168. for _, container := range containers {
  169. if container.ID == currentContainerID {
  170. continue
  171. }
  172. _, waitErr := client.ContainerWait(context.Background(), container.ID)
  173. if waitErr != nil {
  174. waitErrorStrings = append(waitErrorStrings, " ["+container.ID+"] "+waitErr.Error())
  175. }
  176. }
  177. if len(waitErrorStrings) != 0 || len(stopErrorStrings) != 0 {
  178. return errors.New("error while stopping \n1. STOP Errors [" + strings.Join(stopErrorStrings, ",") + "] \n2. WAIT Errors [" + strings.Join(waitErrorStrings, ",") + "]")
  179. }
  180. return nil
  181. }