service.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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 provides a simple way to create a system service.
  5. // Currently supports Windows, Linux/(systemd | Upstart | SysV), and OSX/Launchd.
  6. //
  7. // Windows controls services by setting up callbacks that is non-trivial. This
  8. // is very different then other systems. This package provides the same API
  9. // despite the substantial differences.
  10. // It also can be used to detect how a program is called, from an interactive
  11. // terminal or from a service manager.
  12. //
  13. // Examples in the example/ folder.
  14. //
  15. // package main
  16. //
  17. // import (
  18. // "log"
  19. //
  20. // "github.com/penggy/service"
  21. // )
  22. //
  23. // var logger service.Logger
  24. //
  25. // type program struct{}
  26. //
  27. // func (p *program) Start(s service.Service) error {
  28. // // Start should not block. Do the actual work async.
  29. // go p.run()
  30. // return nil
  31. // }
  32. // func (p *program) run() {
  33. // // Do work here
  34. // }
  35. // func (p *program) Stop(s service.Service) error {
  36. // // Stop should not block. Return with a few seconds.
  37. // return nil
  38. // }
  39. //
  40. // func main() {
  41. // svcConfig := &service.Config{
  42. // Name: "GoServiceTest",
  43. // DisplayName: "Go Service Test",
  44. // Description: "This is a test Go service.",
  45. // }
  46. //
  47. // prg := &program{}
  48. // s, err := service.New(prg, svcConfig)
  49. // if err != nil {
  50. // log.Fatal(err)
  51. // }
  52. // logger, err = s.Logger(nil)
  53. // if err != nil {
  54. // log.Fatal(err)
  55. // }
  56. // err = s.Run()
  57. // if err != nil {
  58. // logger.Error(err)
  59. // }
  60. // }
  61. package service // import "github.com/penggy/service"
  62. import (
  63. "errors"
  64. "fmt"
  65. "os"
  66. "path/filepath"
  67. )
  68. const (
  69. optionKeepAlive = "KeepAlive"
  70. optionKeepAliveDefault = true
  71. optionRunAtLoad = "RunAtLoad"
  72. optionRunAtLoadDefault = false
  73. optionUserService = "UserService"
  74. optionUserServiceDefault = false
  75. optionSessionCreate = "SessionCreate"
  76. optionSessionCreateDefault = false
  77. optionRunWait = "RunWait"
  78. optionReloadSignal = "ReloadSignal"
  79. optionPIDFile = "PIDFile"
  80. )
  81. // Config provides the setup for a Service. The Name field is required.
  82. type Config struct {
  83. Name string // Required name of the service. No spaces suggested.
  84. DisplayName string // Display name, spaces allowed.
  85. Description string // Long description of service.
  86. UserName string // Run as username.
  87. Arguments []string // Run with arguments.
  88. // Optional field to specify the executable for service.
  89. // If empty the current executable is used.
  90. Executable string
  91. // Array of service dependencies.
  92. // Not yet implemented on Linux or OS X.
  93. Dependencies []string
  94. // The following fields are not supported on Windows.
  95. WorkingDirectory string // Initial working directory.
  96. ChRoot string
  97. // System specific options.
  98. // * OS X
  99. // - KeepAlive bool (true)
  100. // - RunAtLoad bool (false)
  101. // - UserService bool (false) - Install as a current user service.
  102. // - SessionCreate bool (false) - Create a full user session.
  103. // * POSIX
  104. // - RunWait func() (wait for SIGNAL) - Do not install signal but wait for this function to return.
  105. // - ReloadSignal string () [USR1, ...] - Signal to send on reaload.
  106. // - PIDFile string () [/run/prog.pid] - Location of the PID file.
  107. Option KeyValue
  108. }
  109. var (
  110. system System
  111. systemRegistry []System
  112. )
  113. var (
  114. // ErrNameFieldRequired is returned when Conifg.Name is empty.
  115. ErrNameFieldRequired = errors.New("Config.Name field is required.")
  116. // ErrNoServiceSystemDetected is returned when no system was detected.
  117. ErrNoServiceSystemDetected = errors.New("No service system detected.")
  118. )
  119. // New creates a new service based on a service interface and configuration.
  120. func New(i Interface, c *Config) (Service, error) {
  121. if len(c.Name) == 0 {
  122. return nil, ErrNameFieldRequired
  123. }
  124. if system == nil {
  125. return nil, ErrNoServiceSystemDetected
  126. }
  127. return system.New(i, c)
  128. }
  129. // KeyValue provides a list of platform specific options. See platform docs for
  130. // more details.
  131. type KeyValue map[string]interface{}
  132. // bool returns the value of the given name, assuming the value is a boolean.
  133. // If the value isn't found or is not of the type, the defaultValue is returned.
  134. func (kv KeyValue) bool(name string, defaultValue bool) bool {
  135. if v, found := kv[name]; found {
  136. if castValue, is := v.(bool); is {
  137. return castValue
  138. }
  139. }
  140. return defaultValue
  141. }
  142. // int returns the value of the given name, assuming the value is an int.
  143. // If the value isn't found or is not of the type, the defaultValue is returned.
  144. func (kv KeyValue) int(name string, defaultValue int) int {
  145. if v, found := kv[name]; found {
  146. if castValue, is := v.(int); is {
  147. return castValue
  148. }
  149. }
  150. return defaultValue
  151. }
  152. // string returns the value of the given name, assuming the value is a string.
  153. // If the value isn't found or is not of the type, the defaultValue is returned.
  154. func (kv KeyValue) string(name string, defaultValue string) string {
  155. if v, found := kv[name]; found {
  156. if castValue, is := v.(string); is {
  157. return castValue
  158. }
  159. }
  160. return defaultValue
  161. }
  162. // float64 returns the value of the given name, assuming the value is a float64.
  163. // If the value isn't found or is not of the type, the defaultValue is returned.
  164. func (kv KeyValue) float64(name string, defaultValue float64) float64 {
  165. if v, found := kv[name]; found {
  166. if castValue, is := v.(float64); is {
  167. return castValue
  168. }
  169. }
  170. return defaultValue
  171. }
  172. // funcSingle returns the value of the given name, assuming the value is a float64.
  173. // If the value isn't found or is not of the type, the defaultValue is returned.
  174. func (kv KeyValue) funcSingle(name string, defaultValue func()) func() {
  175. if v, found := kv[name]; found {
  176. if castValue, is := v.(func()); is {
  177. return castValue
  178. }
  179. }
  180. return defaultValue
  181. }
  182. // Platform returns a description of the system service.
  183. func Platform() string {
  184. if system == nil {
  185. return ""
  186. }
  187. return system.String()
  188. }
  189. // Interactive returns false if running under the OS service manager
  190. // and true otherwise.
  191. func Interactive() bool {
  192. if system == nil {
  193. return true
  194. }
  195. return system.Interactive()
  196. }
  197. func newSystem() System {
  198. for _, choice := range systemRegistry {
  199. if choice.Detect() == false {
  200. continue
  201. }
  202. return choice
  203. }
  204. return nil
  205. }
  206. // ChooseSystem chooses a system from the given system services.
  207. // SystemServices are considered in the order they are suggested.
  208. // Calling this may change what Interactive and Platform return.
  209. func ChooseSystem(a ...System) {
  210. systemRegistry = a
  211. system = newSystem()
  212. }
  213. // ChosenSystem returns the system that service will use.
  214. func ChosenSystem() System {
  215. return system
  216. }
  217. // AvailableSystems returns the list of system services considered
  218. // when choosing the system service.
  219. func AvailableSystems() []System {
  220. return systemRegistry
  221. }
  222. // System represents the service manager that is available.
  223. type System interface {
  224. // String returns a description of the system.
  225. String() string
  226. // Detect returns true if the system is available to use.
  227. Detect() bool
  228. // Interactive returns false if running under the system service manager
  229. // and true otherwise.
  230. Interactive() bool
  231. // New creates a new service for this system.
  232. New(i Interface, c *Config) (Service, error)
  233. }
  234. // Interface represents the service interface for a program. Start runs before
  235. // the hosting process is granted control and Stop runs when control is returned.
  236. //
  237. // 1. OS service manager executes user program.
  238. // 2. User program sees it is executed from a service manager (IsInteractive is false).
  239. // 3. User program calls Service.Run() which blocks.
  240. // 4. Interface.Start() is called and quickly returns.
  241. // 5. User program runs.
  242. // 6. OS service manager signals the user program to stop.
  243. // 7. Interface.Stop() is called and quickly returns.
  244. // - For a successful exit, os.Exit should not be called in Interface.Stop().
  245. // 8. Service.Run returns.
  246. // 9. User program should quickly exit.
  247. type Interface interface {
  248. // Start provides a place to initiate the service. The service doesn't not
  249. // signal a completed start until after this function returns, so the
  250. // Start function must not take more then a few seconds at most.
  251. Start(s Service) error
  252. // Stop provides a place to clean up program execution before it is terminated.
  253. // It should not take more then a few seconds to execute.
  254. // Stop should not call os.Exit directly in the function.
  255. Stop(s Service) error
  256. }
  257. // TODO: Add Configure to Service interface.
  258. // Service represents a service that can be run or controlled.
  259. type Service interface {
  260. // Run should be called shortly after the program entry point.
  261. // After Interface.Stop has finished running, Run will stop blocking.
  262. // After Run stops blocking, the program must exit shortly after.
  263. Run() error
  264. // Start signals to the OS service manager the given service should start.
  265. Start() error
  266. // Stop signals to the OS service manager the given service should stop.
  267. Stop() error
  268. // Restart signals to the OS service manager the given service should stop then start.
  269. Restart() error
  270. // Install setups up the given service in the OS service manager. This may require
  271. // greater rights. Will return an error if it is already installed.
  272. Install() error
  273. // Uninstall removes the given service from the OS service manager. This may require
  274. // greater rights. Will return an error if the service is not present.
  275. Uninstall() error
  276. // Opens and returns a system logger. If the user program is running
  277. // interactively rather then as a service, the returned logger will write to
  278. // os.Stderr. If errs is non-nil errors will be sent on errs as well as
  279. // returned from Logger's functions.
  280. Logger(errs chan<- error) (Logger, error)
  281. // SystemLogger opens and returns a system logger. If errs is non-nil errors
  282. // will be sent on errs as well as returned from Logger's functions.
  283. SystemLogger(errs chan<- error) (Logger, error)
  284. // String displays the name of the service. The display name if present,
  285. // otherwise the name.
  286. String() string
  287. }
  288. // ControlAction list valid string texts to use in Control.
  289. var ControlAction = [5]string{"start", "stop", "restart", "install", "uninstall"}
  290. // Control issues control functions to the service from a given action string.
  291. func Control(s Service, action string) error {
  292. var err error
  293. switch action {
  294. case ControlAction[0]:
  295. err = s.Start()
  296. case ControlAction[1]:
  297. err = s.Stop()
  298. case ControlAction[2]:
  299. err = s.Restart()
  300. case ControlAction[3]:
  301. err = s.Install()
  302. case ControlAction[4]:
  303. err = s.Uninstall()
  304. default:
  305. err = fmt.Errorf("Unknown action %s", action)
  306. }
  307. if err != nil {
  308. return fmt.Errorf("Failed to %s %v: %v", action, s, err)
  309. }
  310. return nil
  311. }
  312. // Logger writes to the system log.
  313. type Logger interface {
  314. Error(v ...interface{}) error
  315. Warning(v ...interface{}) error
  316. Info(v ...interface{}) error
  317. Errorf(format string, a ...interface{}) error
  318. Warningf(format string, a ...interface{}) error
  319. Infof(format string, a ...interface{}) error
  320. }
  321. func (c *Config) execPath() (string, error) {
  322. if len(c.Executable) != 0 {
  323. return filepath.Abs(c.Executable)
  324. }
  325. return os.Executable()
  326. }