power.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. package power
  2. import (
  3. "bufio"
  4. "errors"
  5. "os"
  6. "path/filepath"
  7. "strconv"
  8. "strings"
  9. "syscall"
  10. log "github.com/Sirupsen/logrus"
  11. dockerClient "github.com/fsouza/go-dockerclient"
  12. "github.com/rancherio/os/docker"
  13. )
  14. const (
  15. DOCKER_CGROUPS_FILE = "/proc/self/cgroup"
  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. exiting, err := client.InspectContainer(name)
  31. if exiting != nil {
  32. err := client.RemoveContainer(dockerClient.RemoveContainerOptions{
  33. ID: exiting.ID,
  34. Force: true,
  35. })
  36. if err != nil {
  37. return err
  38. }
  39. }
  40. currentContainerId, err := getCurrentContainerId()
  41. if err != nil {
  42. return err
  43. }
  44. currentContainer, err := client.InspectContainer(currentContainerId)
  45. if err != nil {
  46. return err
  47. }
  48. powerContainer, err := client.CreateContainer(dockerClient.CreateContainerOptions{
  49. Name: name,
  50. Config: &dockerClient.Config{
  51. Image: currentContainer.Config.Image,
  52. Cmd: cmd,
  53. Env: []string{
  54. "IN_DOCKER=true",
  55. },
  56. },
  57. HostConfig: &dockerClient.HostConfig{
  58. PidMode: "host",
  59. VolumesFrom: []string{
  60. currentContainer.ID,
  61. },
  62. Privileged: true,
  63. },
  64. })
  65. if err != nil {
  66. return err
  67. }
  68. go func() {
  69. client.AttachToContainer(dockerClient.AttachToContainerOptions{
  70. Container: powerContainer.ID,
  71. OutputStream: os.Stdout,
  72. ErrorStream: os.Stderr,
  73. Stderr: true,
  74. Stdout: true,
  75. })
  76. }()
  77. err = client.StartContainer(powerContainer.ID, powerContainer.HostConfig)
  78. if err != nil {
  79. return err
  80. }
  81. _, err = client.WaitContainer(powerContainer.ID)
  82. if err != nil {
  83. log.Fatal(err)
  84. }
  85. os.Exit(0)
  86. return nil
  87. }
  88. func common(name string) {
  89. if os.Geteuid() != 0 {
  90. log.Fatalf("%s: Need to be root", os.Args[0])
  91. }
  92. if err := runDocker(name); err != nil {
  93. log.Fatal(err)
  94. }
  95. }
  96. func PowerOff() {
  97. common("poweroff")
  98. reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
  99. }
  100. func Reboot() {
  101. common("reboot")
  102. reboot(syscall.LINUX_REBOOT_CMD_RESTART)
  103. }
  104. func Halt() {
  105. common("halt")
  106. reboot(syscall.LINUX_REBOOT_CMD_HALT)
  107. }
  108. func reboot(code int) {
  109. err := shutDownContainers()
  110. if err != nil {
  111. log.Error(err)
  112. }
  113. syscall.Sync()
  114. err = syscall.Reboot(code)
  115. if err != nil {
  116. log.Fatal(err)
  117. }
  118. }
  119. func shutDownContainers() error {
  120. var err error
  121. shutDown := true
  122. timeout := 2
  123. for i, arg := range os.Args {
  124. if arg == "-f" || arg == "--f" || arg == "--force" {
  125. shutDown = false
  126. }
  127. if arg == "-t" || arg == "--t" || arg == "--timeout" {
  128. if len(os.Args) > i+1 {
  129. t, err := strconv.Atoi(os.Args[i+1])
  130. if err != nil {
  131. return err
  132. }
  133. timeout = t
  134. } else {
  135. log.Error("please specify a timeout")
  136. }
  137. }
  138. }
  139. if !shutDown {
  140. return nil
  141. }
  142. client, err := docker.NewSystemClient()
  143. if err != nil {
  144. return err
  145. }
  146. opts := dockerClient.ListContainersOptions{
  147. All: true,
  148. Filters: map[string][]string{
  149. "status": {"running"},
  150. },
  151. }
  152. containers, err := client.ListContainers(opts)
  153. if err != nil {
  154. return err
  155. }
  156. currentContainerId, err := getCurrentContainerId()
  157. if err != nil {
  158. return err
  159. }
  160. var stopErrorStrings []string
  161. for _, container := range containers {
  162. if container.ID == currentContainerId {
  163. continue
  164. }
  165. log.Infof("Stopping %s : %v", container.ID[:12], container.Names)
  166. stopErr := client.StopContainer(container.ID, uint(timeout))
  167. if stopErr != nil {
  168. stopErrorStrings = append(stopErrorStrings, " ["+container.ID+"] "+stopErr.Error())
  169. }
  170. }
  171. var waitErrorStrings []string
  172. for _, container := range containers {
  173. if container.ID == currentContainerId {
  174. continue
  175. }
  176. _, waitErr := client.WaitContainer(container.ID)
  177. if waitErr != nil {
  178. waitErrorStrings = append(waitErrorStrings, " ["+container.ID+"] "+waitErr.Error())
  179. }
  180. }
  181. if len(waitErrorStrings) != 0 || len(stopErrorStrings) != 0 {
  182. return errors.New("error while stopping \n1. STOP Errors [" + strings.Join(stopErrorStrings, ",") + "] \n2. WAIT Errors [" + strings.Join(waitErrorStrings, ",") + "]")
  183. }
  184. return nil
  185. }
  186. func getCurrentContainerId() (string, error) {
  187. file, err := os.Open(DOCKER_CGROUPS_FILE)
  188. if err != nil {
  189. return "", err
  190. }
  191. fileReader := bufio.NewScanner(file)
  192. if !fileReader.Scan() {
  193. return "", errors.New("Empty file /proc/self/cgroup")
  194. }
  195. line := fileReader.Text()
  196. parts := strings.Split(line, "/")
  197. for len(parts) != 3 {
  198. if !fileReader.Scan() {
  199. return "", errors.New("Found no docker cgroups")
  200. }
  201. line = fileReader.Text()
  202. parts = strings.Split(line, "/")
  203. if len(parts) == 3 {
  204. if strings.HasSuffix(parts[1], "docker") {
  205. break
  206. } else {
  207. parts = nil
  208. }
  209. }
  210. }
  211. return parts[len(parts)-1:][0], nil
  212. }