service_aix.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. //+build aix
  2. // Copyright 2015 Daniel Theophanes.
  3. // Use of this source code is governed by a zlib-style
  4. // license that can be found in the LICENSE file.
  5. package service
  6. import (
  7. "bytes"
  8. "fmt"
  9. "os"
  10. "os/exec"
  11. "os/signal"
  12. "regexp"
  13. "strconv"
  14. "strings"
  15. "syscall"
  16. "text/template"
  17. "time"
  18. )
  19. const maxPathSize = 32 * 1024
  20. const version = "aix-ssrc"
  21. type aixSystem struct{}
  22. func (aixSystem) String() string {
  23. return version
  24. }
  25. func (aixSystem) Detect() bool {
  26. return true
  27. }
  28. func (aixSystem) Interactive() bool {
  29. return interactive
  30. }
  31. func (aixSystem) New(i Interface, c *Config) (Service, error) {
  32. s := &aixService{
  33. i: i,
  34. Config: c,
  35. }
  36. return s, nil
  37. }
  38. func getPidOfSvcMaster() int {
  39. pat := regexp.MustCompile(`\s+root\s+(\d+)\s+\d+\s+\d+\s+\w+\s+\d+\s+\S+\s+[0-9:]+\s+/usr/sbin/srcmstr`)
  40. cmd := exec.Command("ps", "-ef")
  41. var out bytes.Buffer
  42. cmd.Stdout = &out
  43. pid := 0
  44. if err := cmd.Run(); err == nil {
  45. matches := pat.FindAllStringSubmatch(out.String(), -1)
  46. for _, match := range matches {
  47. pid, _ = strconv.Atoi(match[1])
  48. break
  49. }
  50. }
  51. return pid
  52. }
  53. func init() {
  54. ChooseSystem(aixSystem{})
  55. }
  56. var interactive = false
  57. func init() {
  58. var err error
  59. interactive, err = isInteractive()
  60. if err != nil {
  61. panic(err)
  62. }
  63. }
  64. func isInteractive() (bool, error) {
  65. // The PPid of a service process should match PID of srcmstr.
  66. return os.Getppid() != getPidOfSvcMaster(), nil
  67. }
  68. type aixService struct {
  69. i Interface
  70. *Config
  71. }
  72. func (s *aixService) String() string {
  73. if len(s.DisplayName) > 0 {
  74. return s.DisplayName
  75. }
  76. return s.Name
  77. }
  78. func (s *aixService) Platform() string {
  79. return version
  80. }
  81. func (s *aixService) template() *template.Template {
  82. functions := template.FuncMap{
  83. "bool": func(v bool) string {
  84. if v {
  85. return "true"
  86. }
  87. return "false"
  88. },
  89. }
  90. customConfig := s.Option.string(optionSysvScript, "")
  91. if customConfig != "" {
  92. return template.Must(template.New("").Funcs(functions).Parse(customConfig))
  93. } else {
  94. return template.Must(template.New("").Funcs(functions).Parse(svcConfig))
  95. }
  96. }
  97. func (s *aixService) configPath() (cp string, err error) {
  98. cp = "/etc/rc.d/init.d/" + s.Config.Name
  99. return
  100. }
  101. func (s *aixService) Install() error {
  102. // install service
  103. path, err := s.execPath()
  104. if err != nil {
  105. return err
  106. }
  107. err = run("mkssys", "-s", s.Name, "-p", path, "-u", "0", "-R", "-Q", "-S", "-n", "15", "-f", "9", "-d", "-w", "30")
  108. if err != nil {
  109. return err
  110. }
  111. // write start script
  112. confPath, err := s.configPath()
  113. if err != nil {
  114. return err
  115. }
  116. _, err = os.Stat(confPath)
  117. if err == nil {
  118. return fmt.Errorf("Init already exists: %s", confPath)
  119. }
  120. f, err := os.Create(confPath)
  121. if err != nil {
  122. return err
  123. }
  124. defer f.Close()
  125. var to = &struct {
  126. *Config
  127. Path string
  128. }{
  129. s.Config,
  130. path,
  131. }
  132. err = s.template().Execute(f, to)
  133. if err != nil {
  134. return err
  135. }
  136. if err = os.Chmod(confPath, 0755); err != nil {
  137. return err
  138. }
  139. for _, i := range [...]string{"2", "3"} {
  140. if err = os.Symlink(confPath, "/etc/rc"+i+".d/S50"+s.Name); err != nil {
  141. continue
  142. }
  143. if err = os.Symlink(confPath, "/etc/rc"+i+".d/K02"+s.Name); err != nil {
  144. continue
  145. }
  146. }
  147. return nil
  148. }
  149. func (s *aixService) Uninstall() error {
  150. s.Stop()
  151. err := run("rmssys", "-s", s.Name)
  152. if err != nil {
  153. return err
  154. }
  155. confPath, err := s.configPath()
  156. if err != nil {
  157. return err
  158. }
  159. return os.Remove(confPath)
  160. }
  161. func (s *aixService) Status() (Status, error) {
  162. exitCode, out, err := runWithOutput("lssrc", "-s", s.Name)
  163. if exitCode == 0 && err != nil {
  164. if !strings.Contains(err.Error(), "failed with stderr") {
  165. return StatusUnknown, err
  166. }
  167. }
  168. re := regexp.MustCompile(`\s+` + s.Name + `\s+(\w+\s+)?(\d+\s+)?(\w+)`)
  169. matches := re.FindStringSubmatch(out)
  170. if len(matches) == 4 {
  171. status := string(matches[3])
  172. if status == "inoperative" {
  173. return StatusStopped, nil
  174. } else if status == "active" {
  175. return StatusRunning, nil
  176. } else {
  177. fmt.Printf("Got unknown service status %s\n", status)
  178. return StatusUnknown, err
  179. }
  180. }
  181. confPath, err := s.configPath()
  182. if err != nil {
  183. return StatusUnknown, err
  184. }
  185. if _, err = os.Stat(confPath); err == nil {
  186. return StatusStopped, nil
  187. }
  188. return StatusUnknown, ErrNotInstalled
  189. }
  190. func (s *aixService) Start() error {
  191. return run("startsrc", "-s", s.Name)
  192. }
  193. func (s *aixService) Stop() error {
  194. return run("stopsrc", "-s", s.Name)
  195. }
  196. func (s *aixService) Restart() error {
  197. err := s.Stop()
  198. if err != nil {
  199. return err
  200. }
  201. time.Sleep(50 * time.Millisecond)
  202. return s.Start()
  203. }
  204. func (s *aixService) Run() error {
  205. var err error
  206. err = s.i.Start(s)
  207. if err != nil {
  208. return err
  209. }
  210. s.Option.funcSingle(optionRunWait, func() {
  211. var sigChan = make(chan os.Signal, 3)
  212. signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt)
  213. <-sigChan
  214. })()
  215. return s.i.Stop(s)
  216. }
  217. func (s *aixService) Logger(errs chan<- error) (Logger, error) {
  218. if interactive {
  219. return ConsoleLogger, nil
  220. }
  221. return s.SystemLogger(errs)
  222. }
  223. func (s *aixService) SystemLogger(errs chan<- error) (Logger, error) {
  224. return newSysLogger(s.Name, errs)
  225. }
  226. var svcConfig = `#!/bin/ksh
  227. case "$1" in
  228. start )
  229. startsrc -s {{.Name}}
  230. ;;
  231. stop )
  232. stopsrc -s {{.Name}}
  233. ;;
  234. * )
  235. echo "Usage: $0 (start | stop)"
  236. exit 1
  237. esac
  238. `