service_upstart_linux.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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. "errors"
  7. "fmt"
  8. "os"
  9. "os/exec"
  10. "os/signal"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "text/template"
  15. "time"
  16. )
  17. func isUpstart() bool {
  18. if _, err := os.Stat("/sbin/upstart-udev-bridge"); err == nil {
  19. return true
  20. }
  21. if _, err := os.Stat("/sbin/init"); err == nil {
  22. if out, err := exec.Command("/sbin/init", "--version").Output(); err == nil {
  23. if strings.Contains(string(out), "init (upstart") {
  24. return true
  25. }
  26. }
  27. }
  28. return false
  29. }
  30. type upstart struct {
  31. i Interface
  32. *Config
  33. }
  34. func newUpstartService(i Interface, c *Config) (Service, error) {
  35. s := &upstart{
  36. i: i,
  37. Config: c,
  38. }
  39. return s, nil
  40. }
  41. func (s *upstart) String() string {
  42. if len(s.DisplayName) > 0 {
  43. return s.DisplayName
  44. }
  45. return s.Name
  46. }
  47. // Upstart has some support for user services in graphical sessions.
  48. // Due to the mix of actual support for user services over versions, just don't bother.
  49. // Upstart will be replaced by systemd in most cases anyway.
  50. var errNoUserServiceUpstart = errors.New("User services are not supported on Upstart.")
  51. func (s *upstart) configPath() (cp string, err error) {
  52. if s.Option.bool(optionUserService, optionUserServiceDefault) {
  53. err = errNoUserServiceUpstart
  54. return
  55. }
  56. cp = "/etc/init/" + s.Config.Name + ".conf"
  57. return
  58. }
  59. func (s *upstart) hasKillStanza() bool {
  60. defaultValue := true
  61. out, err := exec.Command("/sbin/init", "--version").Output()
  62. if err != nil {
  63. return defaultValue
  64. }
  65. re := regexp.MustCompile(`init \(upstart (\d+.\d+.\d+)\)`)
  66. matches := re.FindStringSubmatch(string(out))
  67. if len(matches) != 2 {
  68. return defaultValue
  69. }
  70. version := make([]int, 3)
  71. for idx, vStr := range strings.Split(matches[1], ".") {
  72. version[idx], err = strconv.Atoi(vStr)
  73. if err != nil {
  74. return defaultValue
  75. }
  76. }
  77. maxVersion := []int{0, 6, 5}
  78. if versionAtMost(version, maxVersion) {
  79. return false
  80. }
  81. return defaultValue
  82. }
  83. func versionAtMost(version, max []int) bool {
  84. for idx, m := range max {
  85. v := version[idx]
  86. if v > m {
  87. return false
  88. }
  89. }
  90. return true
  91. }
  92. func (s *upstart) template() *template.Template {
  93. return template.Must(template.New("").Funcs(tf).Parse(upstartScript))
  94. }
  95. func (s *upstart) Install() error {
  96. confPath, err := s.configPath()
  97. if err != nil {
  98. return err
  99. }
  100. _, err = os.Stat(confPath)
  101. if err == nil {
  102. return fmt.Errorf("Init already exists: %s", confPath)
  103. }
  104. f, err := os.Create(confPath)
  105. if err != nil {
  106. return err
  107. }
  108. defer f.Close()
  109. path, err := s.execPath()
  110. if err != nil {
  111. return err
  112. }
  113. var to = &struct {
  114. *Config
  115. Path string
  116. HasKillStanza bool
  117. }{
  118. s.Config,
  119. path,
  120. s.hasKillStanza(),
  121. }
  122. return s.template().Execute(f, to)
  123. }
  124. func (s *upstart) Uninstall() error {
  125. cp, err := s.configPath()
  126. if err != nil {
  127. return err
  128. }
  129. if err := os.Remove(cp); err != nil {
  130. return err
  131. }
  132. return nil
  133. }
  134. func (s *upstart) Logger(errs chan<- error) (Logger, error) {
  135. if system.Interactive() {
  136. return ConsoleLogger, nil
  137. }
  138. return s.SystemLogger(errs)
  139. }
  140. func (s *upstart) SystemLogger(errs chan<- error) (Logger, error) {
  141. return newSysLogger(s.Name, errs)
  142. }
  143. func (s *upstart) Run() (err error) {
  144. err = s.i.Start(s)
  145. if err != nil {
  146. return err
  147. }
  148. s.Option.funcSingle(optionRunWait, func() {
  149. var sigChan = make(chan os.Signal, 3)
  150. signal.Notify(sigChan, os.Interrupt, os.Kill)
  151. <-sigChan
  152. })()
  153. return s.i.Stop(s)
  154. }
  155. func (s *upstart) Start() error {
  156. return run("initctl", "start", s.Name)
  157. }
  158. func (s *upstart) Stop() error {
  159. return run("initctl", "stop", s.Name)
  160. }
  161. func (s *upstart) Restart() error {
  162. err := s.Stop()
  163. if err != nil {
  164. return err
  165. }
  166. time.Sleep(50 * time.Millisecond)
  167. return s.Start()
  168. }
  169. // The upstart script should stop with an INT or the Go runtime will terminate
  170. // the program before the Stop handler can run.
  171. const upstartScript = `# {{.Description}}
  172. {{if .DisplayName}}description "{{.DisplayName}}"{{end}}
  173. {{if .HasKillStanza}}kill signal INT{{end}}
  174. {{if .ChRoot}}chroot {{.ChRoot}}{{end}}
  175. {{if .WorkingDirectory}}chdir {{.WorkingDirectory}}{{end}}
  176. start on filesystem or runlevel [2345]
  177. stop on runlevel [!2345]
  178. {{if .UserName}}setuid {{.UserName}}{{end}}
  179. respawn
  180. respawn limit 10 5
  181. umask 022
  182. console none
  183. pre-start script
  184. test -x {{.Path}} || { stop; exit 0; }
  185. end script
  186. # Start
  187. exec {{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}
  188. `