service_freebsd.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // Copyright 2019 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. "fmt"
  7. "os"
  8. "os/signal"
  9. "syscall"
  10. "text/template"
  11. )
  12. const version = "freebsd"
  13. type freebsdSystem struct{}
  14. func (freebsdSystem) String() string {
  15. return version
  16. }
  17. func (freebsdSystem) Detect() bool {
  18. return true
  19. }
  20. func (freebsdSystem) Interactive() bool {
  21. return interactive
  22. }
  23. func (freebsdSystem) New(i Interface, c *Config) (Service, error) {
  24. s := &freebsdService{
  25. i: i,
  26. Config: c,
  27. }
  28. return s, nil
  29. }
  30. func init() {
  31. ChooseSystem(freebsdSystem{})
  32. }
  33. var interactive = false
  34. func init() {
  35. var err error
  36. interactive, err = isInteractive()
  37. if err != nil {
  38. panic(err)
  39. }
  40. }
  41. func isInteractive() (bool, error) {
  42. return os.Getenv("IS_DAEMON") != "1", nil
  43. }
  44. type freebsdService struct {
  45. i Interface
  46. *Config
  47. }
  48. func (s *freebsdService) String() string {
  49. if len(s.DisplayName) > 0 {
  50. return s.DisplayName
  51. }
  52. return s.Name
  53. }
  54. func (s *freebsdService) Platform() string {
  55. return version
  56. }
  57. func (s *freebsdService) template() *template.Template {
  58. functions := template.FuncMap{
  59. "bool": func(v bool) string {
  60. if v {
  61. return "true"
  62. }
  63. return "false"
  64. },
  65. }
  66. customConfig := s.Option.string(optionSysvScript, "")
  67. if customConfig != "" {
  68. return template.Must(template.New("").Funcs(functions).Parse(customConfig))
  69. } else {
  70. return template.Must(template.New("").Funcs(functions).Parse(rcScript))
  71. }
  72. }
  73. func (s *freebsdService) configPath() (cp string, err error) {
  74. cp = "/usr/local/etc/rc.d/" + s.Config.Name
  75. return
  76. }
  77. func (s *freebsdService) Install() error {
  78. path, err := s.execPath()
  79. if err != nil {
  80. return err
  81. }
  82. // write start script
  83. confPath, err := s.configPath()
  84. if err != nil {
  85. return err
  86. }
  87. _, err = os.Stat(confPath)
  88. if err == nil {
  89. return fmt.Errorf("Init already exists: %s", confPath)
  90. }
  91. f, err := os.Create(confPath)
  92. if err != nil {
  93. return err
  94. }
  95. defer f.Close()
  96. var to = &struct {
  97. *Config
  98. Path string
  99. }{
  100. s.Config,
  101. path,
  102. }
  103. err = s.template().Execute(f, to)
  104. if err != nil {
  105. return err
  106. }
  107. if err = os.Chmod(confPath, 0755); err != nil {
  108. return err
  109. }
  110. return nil
  111. }
  112. func (s *freebsdService) Uninstall() error {
  113. cp, err := s.configPath()
  114. if err != nil {
  115. return err
  116. }
  117. return os.Remove(cp)
  118. }
  119. func (s *freebsdService) Status() (Status, error) {
  120. cp, err := s.configPath()
  121. if err != nil {
  122. return StatusUnknown, err
  123. }
  124. if _, err = os.Stat(cp); os.IsNotExist(err) {
  125. return StatusStopped, ErrNotInstalled
  126. }
  127. status, _, err := runCommand("service", false, s.Name, "status")
  128. if status == 1 {
  129. return StatusStopped, nil
  130. } else if err != nil {
  131. return StatusUnknown, err
  132. }
  133. return StatusRunning, nil
  134. }
  135. func (s *freebsdService) Start() error {
  136. return run("service", s.Name, "start")
  137. }
  138. func (s *freebsdService) Stop() error {
  139. return run("service", s.Name, "stop")
  140. }
  141. func (s *freebsdService) Restart() error {
  142. return run("service", s.Name, "restart")
  143. }
  144. func (s *freebsdService) Run() error {
  145. var err error
  146. err = s.i.Start(s)
  147. if err != nil {
  148. return err
  149. }
  150. s.Option.funcSingle(optionRunWait, func() {
  151. var sigChan = make(chan os.Signal, 3)
  152. signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt)
  153. <-sigChan
  154. })()
  155. return s.i.Stop(s)
  156. }
  157. func (s *freebsdService) Logger(errs chan<- error) (Logger, error) {
  158. if interactive {
  159. return ConsoleLogger, nil
  160. }
  161. return s.SystemLogger(errs)
  162. }
  163. func (s *freebsdService) SystemLogger(errs chan<- error) (Logger, error) {
  164. return newSysLogger(s.Name, errs)
  165. }
  166. var rcScript = `#!/bin/sh
  167. # PROVIDE: {{.Name}}
  168. # REQUIRE: SERVERS
  169. # KEYWORD: shutdown
  170. . /etc/rc.subr
  171. name="{{.Name}}"
  172. {{.Name}}_env="IS_DAEMON=1"
  173. pidfile="/var/run/${name}.pid"
  174. command="/usr/sbin/daemon"
  175. daemon_args="-P ${pidfile} -r -t \"${name}: daemon\"{{if .WorkingDirectory}} -c {{.WorkingDirectory}}{{end}}"
  176. command_args="${daemon_args} {{.Path}}{{range .Arguments}} {{.}}{{end}}"
  177. run_rc_command "$1"
  178. `