service_sysv_linux.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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. "strings"
  11. "syscall"
  12. "text/template"
  13. "time"
  14. )
  15. type sysv struct {
  16. i Interface
  17. platform string
  18. *Config
  19. }
  20. func newSystemVService(i Interface, platform string, c *Config) (Service, error) {
  21. s := &sysv{
  22. i: i,
  23. platform: platform,
  24. Config: c,
  25. }
  26. return s, nil
  27. }
  28. func (s *sysv) String() string {
  29. if len(s.DisplayName) > 0 {
  30. return s.DisplayName
  31. }
  32. return s.Name
  33. }
  34. func (s *sysv) Platform() string {
  35. return s.platform
  36. }
  37. var errNoUserServiceSystemV = errors.New("User services are not supported on SystemV.")
  38. func (s *sysv) configPath() (cp string, err error) {
  39. if s.Option.bool(optionUserService, optionUserServiceDefault) {
  40. err = errNoUserServiceSystemV
  41. return
  42. }
  43. cp = "/etc/init.d/" + s.Config.Name
  44. return
  45. }
  46. func (s *sysv) template() *template.Template {
  47. customScript := s.Option.string(optionSysvScript, "")
  48. if customScript != "" {
  49. return template.Must(template.New("").Funcs(tf).Parse(customScript))
  50. } else {
  51. return template.Must(template.New("").Funcs(tf).Parse(sysvScript))
  52. }
  53. }
  54. func (s *sysv) Install() error {
  55. confPath, err := s.configPath()
  56. if err != nil {
  57. return err
  58. }
  59. _, err = os.Stat(confPath)
  60. if err == nil {
  61. return fmt.Errorf("Init already exists: %s", confPath)
  62. }
  63. f, err := os.Create(confPath)
  64. if err != nil {
  65. return err
  66. }
  67. defer f.Close()
  68. path, err := s.execPath()
  69. if err != nil {
  70. return err
  71. }
  72. var to = &struct {
  73. *Config
  74. Path string
  75. }{
  76. s.Config,
  77. path,
  78. }
  79. err = s.template().Execute(f, to)
  80. if err != nil {
  81. return err
  82. }
  83. if err = os.Chmod(confPath, 0755); err != nil {
  84. return err
  85. }
  86. for _, i := range [...]string{"2", "3", "4", "5"} {
  87. if err = os.Symlink(confPath, "/etc/rc"+i+".d/S50"+s.Name); err != nil {
  88. continue
  89. }
  90. }
  91. for _, i := range [...]string{"0", "1", "6"} {
  92. if err = os.Symlink(confPath, "/etc/rc"+i+".d/K02"+s.Name); err != nil {
  93. continue
  94. }
  95. }
  96. return nil
  97. }
  98. func (s *sysv) Uninstall() error {
  99. cp, err := s.configPath()
  100. if err != nil {
  101. return err
  102. }
  103. if err := os.Remove(cp); err != nil {
  104. return err
  105. }
  106. return nil
  107. }
  108. func (s *sysv) Logger(errs chan<- error) (Logger, error) {
  109. if system.Interactive() {
  110. return ConsoleLogger, nil
  111. }
  112. return s.SystemLogger(errs)
  113. }
  114. func (s *sysv) SystemLogger(errs chan<- error) (Logger, error) {
  115. return newSysLogger(s.Name, errs)
  116. }
  117. func (s *sysv) Run() (err error) {
  118. err = s.i.Start(s)
  119. if err != nil {
  120. return err
  121. }
  122. s.Option.funcSingle(optionRunWait, func() {
  123. var sigChan = make(chan os.Signal, 3)
  124. signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt)
  125. <-sigChan
  126. })()
  127. return s.i.Stop(s)
  128. }
  129. func (s *sysv) Status() (Status, error) {
  130. _, out, err := runWithOutput("service", s.Name, "status")
  131. if err != nil {
  132. return StatusUnknown, err
  133. }
  134. switch {
  135. case strings.HasPrefix(out, "Running"):
  136. return StatusRunning, nil
  137. case strings.HasPrefix(out, "Stopped"):
  138. return StatusStopped, nil
  139. default:
  140. return StatusUnknown, ErrNotInstalled
  141. }
  142. }
  143. func (s *sysv) Start() error {
  144. return run("service", s.Name, "start")
  145. }
  146. func (s *sysv) Stop() error {
  147. return run("service", s.Name, "stop")
  148. }
  149. func (s *sysv) Restart() error {
  150. err := s.Stop()
  151. if err != nil {
  152. return err
  153. }
  154. time.Sleep(50 * time.Millisecond)
  155. return s.Start()
  156. }
  157. const sysvScript = `#!/bin/sh
  158. # For RedHat and cousins:
  159. # chkconfig: - 99 01
  160. # description: {{.Description}}
  161. # processname: {{.Path}}
  162. ### BEGIN INIT INFO
  163. # Provides: {{.Path}}
  164. # Required-Start:
  165. # Required-Stop:
  166. # Default-Start: 2 3 4 5
  167. # Default-Stop: 0 1 6
  168. # Short-Description: {{.DisplayName}}
  169. # Description: {{.Description}}
  170. ### END INIT INFO
  171. cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}"
  172. name=$(basename $(readlink -f $0))
  173. pid_file="/var/run/$name.pid"
  174. stdout_log="/var/log/$name.log"
  175. stderr_log="/var/log/$name.err"
  176. [ -e /etc/sysconfig/$name ] && . /etc/sysconfig/$name
  177. get_pid() {
  178. cat "$pid_file"
  179. }
  180. is_running() {
  181. [ -f "$pid_file" ] && ps $(get_pid) > /dev/null 2>&1
  182. }
  183. case "$1" in
  184. start)
  185. if is_running; then
  186. echo "Already started"
  187. else
  188. echo "Starting $name"
  189. {{if .WorkingDirectory}}cd '{{.WorkingDirectory}}'{{end}}
  190. $cmd >> "$stdout_log" 2>> "$stderr_log" &
  191. echo $! > "$pid_file"
  192. if ! is_running; then
  193. echo "Unable to start, see $stdout_log and $stderr_log"
  194. exit 1
  195. fi
  196. fi
  197. ;;
  198. stop)
  199. if is_running; then
  200. echo -n "Stopping $name.."
  201. kill $(get_pid)
  202. for i in $(seq 1 10)
  203. do
  204. if ! is_running; then
  205. break
  206. fi
  207. echo -n "."
  208. sleep 1
  209. done
  210. echo
  211. if is_running; then
  212. echo "Not stopped; may still be shutting down or shutdown may have failed"
  213. exit 1
  214. else
  215. echo "Stopped"
  216. if [ -f "$pid_file" ]; then
  217. rm "$pid_file"
  218. fi
  219. fi
  220. else
  221. echo "Not running"
  222. fi
  223. ;;
  224. restart)
  225. $0 stop
  226. if is_running; then
  227. echo "Unable to stop, will not attempt to start"
  228. exit 1
  229. fi
  230. $0 start
  231. ;;
  232. status)
  233. if is_running; then
  234. echo "Running"
  235. else
  236. echo "Stopped"
  237. exit 1
  238. fi
  239. ;;
  240. *)
  241. echo "Usage: $0 {start|stop|restart|status}"
  242. exit 1
  243. ;;
  244. esac
  245. exit 0
  246. `