service_systemd_linux.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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/signal"
  10. "syscall"
  11. "text/template"
  12. )
  13. func isSystemd() bool {
  14. if _, err := os.Stat("/run/systemd/system"); err == nil {
  15. return true
  16. }
  17. return false
  18. }
  19. type systemd struct {
  20. i Interface
  21. *Config
  22. }
  23. func newSystemdService(i Interface, c *Config) (Service, error) {
  24. s := &systemd{
  25. i: i,
  26. Config: c,
  27. }
  28. return s, nil
  29. }
  30. func (s *systemd) String() string {
  31. if len(s.DisplayName) > 0 {
  32. return s.DisplayName
  33. }
  34. return s.Name
  35. }
  36. // Systemd services should be supported, but are not currently.
  37. var errNoUserServiceSystemd = errors.New("User services are not supported on systemd.")
  38. func (s *systemd) configPath() (cp string, err error) {
  39. if s.Option.bool(optionUserService, optionUserServiceDefault) {
  40. err = errNoUserServiceSystemd
  41. return
  42. }
  43. cp = "/etc/systemd/system/" + s.Config.Name + ".service"
  44. return
  45. }
  46. func (s *systemd) template() *template.Template {
  47. return template.Must(template.New("").Funcs(tf).Parse(systemdScript))
  48. }
  49. func (s *systemd) Install() error {
  50. confPath, err := s.configPath()
  51. if err != nil {
  52. return err
  53. }
  54. _, err = os.Stat(confPath)
  55. if err == nil {
  56. return fmt.Errorf("Init already exists: %s", confPath)
  57. }
  58. f, err := os.Create(confPath)
  59. if err != nil {
  60. return err
  61. }
  62. defer f.Close()
  63. path, err := s.execPath()
  64. if err != nil {
  65. return err
  66. }
  67. var to = &struct {
  68. *Config
  69. Path string
  70. ReloadSignal string
  71. PIDFile string
  72. }{
  73. s.Config,
  74. path,
  75. s.Option.string(optionReloadSignal, ""),
  76. s.Option.string(optionPIDFile, ""),
  77. }
  78. err = s.template().Execute(f, to)
  79. if err != nil {
  80. return err
  81. }
  82. err = run("systemctl", "enable", s.Name+".service")
  83. if err != nil {
  84. return err
  85. }
  86. return run("systemctl", "daemon-reload")
  87. }
  88. func (s *systemd) Uninstall() error {
  89. err := run("systemctl", "disable", s.Name+".service")
  90. if err != nil {
  91. return err
  92. }
  93. cp, err := s.configPath()
  94. if err != nil {
  95. return err
  96. }
  97. if err := os.Remove(cp); err != nil {
  98. return err
  99. }
  100. return nil
  101. }
  102. func (s *systemd) Logger(errs chan<- error) (Logger, error) {
  103. if system.Interactive() {
  104. return ConsoleLogger, nil
  105. }
  106. return s.SystemLogger(errs)
  107. }
  108. func (s *systemd) SystemLogger(errs chan<- error) (Logger, error) {
  109. return newSysLogger(s.Name, errs)
  110. }
  111. func (s *systemd) Run() (err error) {
  112. err = s.i.Start(s)
  113. if err != nil {
  114. return err
  115. }
  116. s.Option.funcSingle(optionRunWait, func() {
  117. var sigChan = make(chan os.Signal, 3)
  118. signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt)
  119. <-sigChan
  120. })()
  121. return s.i.Stop(s)
  122. }
  123. func (s *systemd) Start() error {
  124. return run("systemctl", "start", s.Name+".service")
  125. }
  126. func (s *systemd) Stop() error {
  127. return run("systemctl", "stop", s.Name+".service")
  128. }
  129. func (s *systemd) Restart() error {
  130. return run("systemctl", "restart", s.Name+".service")
  131. }
  132. const systemdScript = `[Unit]
  133. Description={{.Description}}
  134. ConditionFileIsExecutable={{.Path|cmdEscape}}
  135. [Service]
  136. StartLimitInterval=5
  137. StartLimitBurst=10
  138. ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}}
  139. {{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}}
  140. {{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}}
  141. {{if .UserName}}User={{.UserName}}{{end}}
  142. {{if .ReloadSignal}}ExecReload=/bin/kill -{{.ReloadSignal}} "$MAINPID"{{end}}
  143. {{if .PIDFile}}PIDFile={{.PIDFile|cmd}}{{end}}
  144. Restart=always
  145. RestartSec=120
  146. EnvironmentFile=-/etc/sysconfig/{{.Name}}
  147. [Install]
  148. WantedBy=multi-user.target
  149. `