service_linux.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Copyright 2015 Daniel Theophanes.
  2. // Use of this source code is governed by a zlib-style
  3. // license that can be found in the LICENSE file.
  4. package service
  5. import (
  6. "bufio"
  7. "fmt"
  8. "io/ioutil"
  9. "os"
  10. "strings"
  11. )
  12. var cgroupFile = "/proc/1/cgroup"
  13. type linuxSystemService struct {
  14. name string
  15. detect func() bool
  16. interactive func() bool
  17. new func(i Interface, platform string, c *Config) (Service, error)
  18. }
  19. func (sc linuxSystemService) String() string {
  20. return sc.name
  21. }
  22. func (sc linuxSystemService) Detect() bool {
  23. return sc.detect()
  24. }
  25. func (sc linuxSystemService) Interactive() bool {
  26. return sc.interactive()
  27. }
  28. func (sc linuxSystemService) New(i Interface, c *Config) (Service, error) {
  29. return sc.new(i, sc.String(), c)
  30. }
  31. func init() {
  32. ChooseSystem(linuxSystemService{
  33. name: "linux-systemd",
  34. detect: isSystemd,
  35. interactive: func() bool {
  36. is, _ := isInteractive()
  37. return is
  38. },
  39. new: newSystemdService,
  40. },
  41. linuxSystemService{
  42. name: "linux-upstart",
  43. detect: isUpstart,
  44. interactive: func() bool {
  45. is, _ := isInteractive()
  46. return is
  47. },
  48. new: newUpstartService,
  49. },
  50. linuxSystemService{
  51. name: "unix-systemv",
  52. detect: func() bool { return true },
  53. interactive: func() bool {
  54. is, _ := isInteractive()
  55. return is
  56. },
  57. new: newSystemVService,
  58. },
  59. )
  60. }
  61. func binaryName(pid int) (string, error) {
  62. statPath := fmt.Sprintf("/proc/%d/stat", pid)
  63. dataBytes, err := ioutil.ReadFile(statPath)
  64. if err != nil {
  65. return "", err
  66. }
  67. // First, parse out the image name
  68. data := string(dataBytes)
  69. binStart := strings.IndexRune(data, '(') + 1
  70. binEnd := strings.IndexRune(data[binStart:], ')')
  71. return data[binStart : binStart+binEnd], nil
  72. }
  73. func isInteractive() (bool, error) {
  74. inContainer, err := isInContainer(cgroupFile)
  75. if err != nil {
  76. return false, err
  77. }
  78. if inContainer {
  79. return true, nil
  80. }
  81. ppid := os.Getppid()
  82. if ppid == 1 {
  83. return false, nil
  84. }
  85. binary, _ := binaryName(ppid)
  86. return binary != "systemd", nil
  87. }
  88. // isInContainer checks if the service is being executed in docker or lxc
  89. // container.
  90. func isInContainer(cgroupPath string) (bool, error) {
  91. const maxlines = 5 // maximum lines to scan
  92. f, err := os.Open(cgroupPath)
  93. if err != nil {
  94. return false, err
  95. }
  96. defer f.Close()
  97. scan := bufio.NewScanner(f)
  98. lines := 0
  99. for scan.Scan() && !(lines > maxlines) {
  100. if strings.Contains(scan.Text(), "docker") || strings.Contains(scan.Text(), "lxc") {
  101. return true, nil
  102. }
  103. lines++
  104. }
  105. if err := scan.Err(); err != nil {
  106. return false, err
  107. }
  108. return false, nil
  109. }
  110. var tf = map[string]interface{}{
  111. "cmd": func(s string) string {
  112. return `"` + strings.Replace(s, `"`, `\"`, -1) + `"`
  113. },
  114. "cmdEscape": func(s string) string {
  115. return strings.Replace(s, " ", `\x20`, -1)
  116. },
  117. }