service_solaris.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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. "bytes"
  7. "encoding/xml"
  8. "fmt"
  9. "os"
  10. "os/signal"
  11. "regexp"
  12. "syscall"
  13. "text/template"
  14. "time"
  15. )
  16. const maxPathSize = 32 * 1024
  17. const version = "solaris-smf"
  18. type solarisSystem struct{}
  19. func (solarisSystem) String() string {
  20. return version
  21. }
  22. func (solarisSystem) Detect() bool {
  23. return true
  24. }
  25. func (solarisSystem) Interactive() bool {
  26. return interactive
  27. }
  28. func (solarisSystem) New(i Interface, c *Config) (Service, error) {
  29. s := &solarisService{
  30. i: i,
  31. Config: c,
  32. Prefix: c.Option.string(optionPrefix, optionPrefixDefault),
  33. }
  34. return s, nil
  35. }
  36. func init() {
  37. ChooseSystem(solarisSystem{})
  38. }
  39. var interactive = false
  40. func init() {
  41. var err error
  42. interactive, err = isInteractive()
  43. if err != nil {
  44. panic(err)
  45. }
  46. }
  47. func isInteractive() (bool, error) {
  48. // The PPid of a service process be 1 / init.
  49. return os.Getppid() != 1, nil
  50. }
  51. type solarisService struct {
  52. i Interface
  53. *Config
  54. Prefix string
  55. }
  56. func (s *solarisService) String() string {
  57. if len(s.DisplayName) > 0 {
  58. return s.DisplayName
  59. }
  60. return s.Name
  61. }
  62. func (s *solarisService) Platform() string {
  63. return version
  64. }
  65. func (s *solarisService) template() *template.Template {
  66. functions := template.FuncMap{
  67. "bool": func(v bool) string {
  68. if v {
  69. return "true"
  70. }
  71. return "false"
  72. },
  73. }
  74. customConfig := s.Option.string(optionSysvScript, "")
  75. if customConfig != "" {
  76. return template.Must(template.New("").Funcs(functions).Parse(customConfig))
  77. } else {
  78. return template.Must(template.New("").Funcs(functions).Parse(manifest))
  79. }
  80. }
  81. func (s *solarisService) configPath() (string, error) {
  82. return "/lib/svc/manifest/" + s.Prefix + "/" + s.Config.Name + ".xml", nil
  83. }
  84. func (s *solarisService) getFMRI() string {
  85. return "svc:/" + s.Prefix + "/" + s.Config.Name + ":default"
  86. }
  87. func (s *solarisService) Install() error {
  88. // write start script
  89. confPath, err := s.configPath()
  90. if err != nil {
  91. return err
  92. }
  93. _, err = os.Stat(confPath)
  94. if err == nil {
  95. return fmt.Errorf("Manifest already exists: %s", confPath)
  96. }
  97. f, err := os.Create(confPath)
  98. if err != nil {
  99. return err
  100. }
  101. defer f.Close()
  102. path, err := s.execPath()
  103. if err != nil {
  104. return err
  105. }
  106. Display := ""
  107. escaped := &bytes.Buffer{}
  108. if err := xml.EscapeText(escaped, []byte(s.DisplayName)); err == nil {
  109. Display = escaped.String()
  110. }
  111. var to = &struct {
  112. *Config
  113. Prefix string
  114. Display string
  115. Path string
  116. }{
  117. s.Config,
  118. s.Prefix,
  119. Display,
  120. path,
  121. }
  122. err = s.template().Execute(f, to)
  123. if err != nil {
  124. return err
  125. }
  126. // import service
  127. err = run("svcadm", "restart", "manifest-import")
  128. if err != nil {
  129. return err
  130. }
  131. return nil
  132. }
  133. func (s *solarisService) Uninstall() error {
  134. s.Stop()
  135. confPath, err := s.configPath()
  136. if err != nil {
  137. return err
  138. }
  139. err = os.Remove(confPath)
  140. if err != nil {
  141. return err
  142. }
  143. // unregister service
  144. err = run("svcadm", "restart", "manifest-import")
  145. if err != nil {
  146. return err
  147. }
  148. return nil
  149. }
  150. func (s *solarisService) Status() (Status, error) {
  151. fmri := s.getFMRI()
  152. exitCode, out, err := runWithOutput("svcs", fmri)
  153. if exitCode != 0 {
  154. return StatusUnknown, ErrNotInstalled
  155. }
  156. re := regexp.MustCompile(`(degraded|disabled|legacy_run|maintenance|offline|online)\s+\w+` + fmri)
  157. matches := re.FindStringSubmatch(out)
  158. if len(matches) == 2 {
  159. status := string(matches[1])
  160. if status == "online" {
  161. return StatusRunning, nil
  162. } else {
  163. return StatusStopped, nil
  164. }
  165. }
  166. return StatusUnknown, err
  167. }
  168. func (s *solarisService) Start() error {
  169. return run("/usr/sbin/svcadm", "enable", s.getFMRI())
  170. }
  171. func (s *solarisService) Stop() error {
  172. return run("/usr/sbin/svcadm", "disable", s.getFMRI())
  173. }
  174. func (s *solarisService) Restart() error {
  175. err := s.Stop()
  176. if err != nil {
  177. return err
  178. }
  179. time.Sleep(50 * time.Millisecond)
  180. return s.Start()
  181. }
  182. func (s *solarisService) Run() error {
  183. var err error
  184. err = s.i.Start(s)
  185. if err != nil {
  186. return err
  187. }
  188. s.Option.funcSingle(optionRunWait, func() {
  189. var sigChan = make(chan os.Signal, 3)
  190. signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt)
  191. <-sigChan
  192. })()
  193. return s.i.Stop(s)
  194. }
  195. func (s *solarisService) Logger(errs chan<- error) (Logger, error) {
  196. if interactive {
  197. return ConsoleLogger, nil
  198. }
  199. return s.SystemLogger(errs)
  200. }
  201. func (s *solarisService) SystemLogger(errs chan<- error) (Logger, error) {
  202. return newSysLogger(s.Name, errs)
  203. }
  204. var manifest = `<?xml version="1.0"?>
  205. <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
  206. <service_bundle type='manifest' name='golang-{{.Name}}'>
  207. <service
  208. name='{{.Prefix}}/{{.Name}}'
  209. type='service'
  210. version='1'>
  211. <create_default_instance enabled='false' />
  212. <single_instance />
  213. <!--
  214. Wait for network interfaces to be initialized.
  215. -->
  216. <dependency name='network'
  217. grouping='require_all'
  218. restart_on='restart'
  219. type='service'>
  220. <service_fmri value='svc:/milestone/network:default'/>
  221. </dependency>
  222. <!--
  223. Wait for all local filesystems to be mounted.
  224. -->
  225. <dependency name='filesystem-local'
  226. grouping='require_all'
  227. restart_on='none'
  228. type='service'>
  229. <service_fmri
  230. value='svc:/system/filesystem/local:default'/>
  231. </dependency>
  232. <exec_method
  233. type='method'
  234. name='start'
  235. exec='bash -c {{.Path}} &amp;'
  236. timeout_seconds='10' />
  237. <exec_method
  238. type='method'
  239. name='stop'
  240. exec='pkill -TERM -f {{.Path}}'
  241. timeout_seconds='60' />
  242. <!--
  243. <property_group name='startd' type='framework'>
  244. <propval name='duration' type='astring' value='transient' />
  245. </property_group>
  246. -->
  247. <stability value='Unstable' />
  248. <template>
  249. <common_name>
  250. <loctext xml:lang='C'>
  251. {{.Display}}
  252. </loctext>
  253. </common_name>
  254. </template>
  255. </service>
  256. </service_bundle>
  257. `