service_windows.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. package service
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. "time"
  8. "golang.org/x/sys/windows/svc"
  9. "golang.org/x/sys/windows/svc/eventlog"
  10. "golang.org/x/sys/windows/svc/mgr"
  11. "github.com/drakkan/sftpgo/dataprovider"
  12. "github.com/drakkan/sftpgo/ftpd"
  13. "github.com/drakkan/sftpgo/httpd"
  14. "github.com/drakkan/sftpgo/logger"
  15. )
  16. const (
  17. serviceName = "SFTPGo"
  18. serviceDesc = "Full featured and highly configurable SFTP server"
  19. rotateLogCmd = svc.Cmd(128)
  20. acceptRotateLog = svc.Accepted(rotateLogCmd)
  21. )
  22. // Status defines service status
  23. type Status uint8
  24. // Supported values for service status
  25. const (
  26. StatusUnknown Status = iota
  27. StatusRunning
  28. StatusStopped
  29. StatusPaused
  30. StatusStartPending
  31. StatusPausePending
  32. StatusContinuePending
  33. StatusStopPending
  34. )
  35. type WindowsService struct {
  36. Service Service
  37. isInteractive bool
  38. }
  39. func (s Status) String() string {
  40. switch s {
  41. case StatusRunning:
  42. return "running"
  43. case StatusStopped:
  44. return "stopped"
  45. case StatusStartPending:
  46. return "start pending"
  47. case StatusPausePending:
  48. return "pause pending"
  49. case StatusPaused:
  50. return "paused"
  51. case StatusContinuePending:
  52. return "continue pending"
  53. case StatusStopPending:
  54. return "stop pending"
  55. default:
  56. return "unknown"
  57. }
  58. }
  59. func (s *WindowsService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
  60. const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptParamChange | acceptRotateLog
  61. changes <- svc.Status{State: svc.StartPending}
  62. if err := s.Service.Start(); err != nil {
  63. return true, 1
  64. }
  65. changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
  66. loop:
  67. for {
  68. c := <-r
  69. switch c.Cmd {
  70. case svc.Interrogate:
  71. logger.Debug(logSender, "", "Received service interrogate request, current status: %v", c.CurrentStatus)
  72. changes <- c.CurrentStatus
  73. case svc.Stop, svc.Shutdown:
  74. logger.Debug(logSender, "", "Received service stop request")
  75. changes <- svc.Status{State: svc.StopPending}
  76. s.Service.Stop()
  77. break loop
  78. case svc.ParamChange:
  79. logger.Debug(logSender, "", "Received reload request")
  80. err := dataprovider.ReloadConfig()
  81. if err != nil {
  82. logger.Warn(logSender, "", "error reloading dataprovider configuration: %v", err)
  83. }
  84. err = httpd.ReloadTLSCertificate()
  85. if err != nil {
  86. logger.Warn(logSender, "", "error reloading TLS certificate: %v", err)
  87. }
  88. err = ftpd.ReloadTLSCertificate()
  89. if err != nil {
  90. logger.Warn(logSender, "", "error reloading FTPD TLS certificate: %v", err)
  91. }
  92. case rotateLogCmd:
  93. logger.Debug(logSender, "", "Received log file rotation request")
  94. err := logger.RotateLogFile()
  95. if err != nil {
  96. logger.Warn(logSender, "", "error rotating log file: %v", err)
  97. }
  98. default:
  99. continue loop
  100. }
  101. }
  102. return false, 0
  103. }
  104. func (s *WindowsService) RunService() error {
  105. exePath, err := s.getExePath()
  106. if err != nil {
  107. return err
  108. }
  109. isIntSess, err := svc.IsAnInteractiveSession()
  110. if err != nil {
  111. return err
  112. }
  113. s.isInteractive = isIntSess
  114. dir := filepath.Dir(exePath)
  115. if err = os.Chdir(dir); err != nil {
  116. return err
  117. }
  118. if s.isInteractive {
  119. return s.Start()
  120. }
  121. return svc.Run(serviceName, s)
  122. }
  123. func (s *WindowsService) Start() error {
  124. m, err := mgr.Connect()
  125. if err != nil {
  126. return err
  127. }
  128. defer m.Disconnect()
  129. service, err := m.OpenService(serviceName)
  130. if err != nil {
  131. return fmt.Errorf("could not access service: %v", err)
  132. }
  133. defer service.Close()
  134. err = service.Start()
  135. if err != nil {
  136. return fmt.Errorf("could not start service: %v", err)
  137. }
  138. return nil
  139. }
  140. func (s *WindowsService) Reload() error {
  141. m, err := mgr.Connect()
  142. if err != nil {
  143. return err
  144. }
  145. defer m.Disconnect()
  146. service, err := m.OpenService(serviceName)
  147. if err != nil {
  148. return fmt.Errorf("could not access service: %v", err)
  149. }
  150. defer service.Close()
  151. _, err = service.Control(svc.ParamChange)
  152. if err != nil {
  153. return fmt.Errorf("could not send control=%d: %v", svc.ParamChange, err)
  154. }
  155. return nil
  156. }
  157. func (s *WindowsService) RotateLogFile() error {
  158. m, err := mgr.Connect()
  159. if err != nil {
  160. return err
  161. }
  162. defer m.Disconnect()
  163. service, err := m.OpenService(serviceName)
  164. if err != nil {
  165. return fmt.Errorf("could not access service: %v", err)
  166. }
  167. defer service.Close()
  168. _, err = service.Control(rotateLogCmd)
  169. if err != nil {
  170. return fmt.Errorf("could not send control=%d: %v", rotateLogCmd, err)
  171. }
  172. return nil
  173. }
  174. func (s *WindowsService) Install(args ...string) error {
  175. exePath, err := s.getExePath()
  176. if err != nil {
  177. return err
  178. }
  179. m, err := mgr.Connect()
  180. if err != nil {
  181. return err
  182. }
  183. defer m.Disconnect()
  184. service, err := m.OpenService(serviceName)
  185. if err == nil {
  186. service.Close()
  187. return fmt.Errorf("service %s already exists", serviceName)
  188. }
  189. config := mgr.Config{
  190. DisplayName: serviceName,
  191. Description: serviceDesc,
  192. StartType: mgr.StartAutomatic}
  193. service, err = m.CreateService(serviceName, exePath, config, args...)
  194. if err != nil {
  195. return err
  196. }
  197. defer service.Close()
  198. err = eventlog.InstallAsEventCreate(serviceName, eventlog.Error|eventlog.Warning|eventlog.Info)
  199. if err != nil {
  200. if !strings.Contains(err.Error(), "exists") {
  201. service.Delete()
  202. return fmt.Errorf("SetupEventLogSource() failed: %s", err)
  203. }
  204. }
  205. recoveryActions := []mgr.RecoveryAction{
  206. {
  207. Type: mgr.ServiceRestart,
  208. Delay: 0,
  209. },
  210. {
  211. Type: mgr.ServiceRestart,
  212. Delay: 60 * time.Second,
  213. },
  214. {
  215. Type: mgr.NoAction,
  216. },
  217. }
  218. err = service.SetRecoveryActions(recoveryActions, uint32(3600))
  219. if err != nil {
  220. service.Delete()
  221. return fmt.Errorf("unable to set recovery actions: %v", err)
  222. }
  223. return nil
  224. }
  225. func (s *WindowsService) Uninstall() error {
  226. m, err := mgr.Connect()
  227. if err != nil {
  228. return err
  229. }
  230. defer m.Disconnect()
  231. service, err := m.OpenService(serviceName)
  232. if err != nil {
  233. return fmt.Errorf("service %s is not installed", serviceName)
  234. }
  235. defer service.Close()
  236. err = service.Delete()
  237. if err != nil {
  238. return err
  239. }
  240. err = eventlog.Remove(serviceName)
  241. if err != nil {
  242. return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
  243. }
  244. return nil
  245. }
  246. func (s *WindowsService) Stop() error {
  247. m, err := mgr.Connect()
  248. if err != nil {
  249. return err
  250. }
  251. defer m.Disconnect()
  252. service, err := m.OpenService(serviceName)
  253. if err != nil {
  254. return fmt.Errorf("could not access service: %v", err)
  255. }
  256. defer service.Close()
  257. status, err := service.Control(svc.Stop)
  258. if err != nil {
  259. return fmt.Errorf("could not send control=%d: %v", svc.Stop, err)
  260. }
  261. timeout := time.Now().Add(10 * time.Second)
  262. for status.State != svc.Stopped {
  263. if timeout.Before(time.Now()) {
  264. return fmt.Errorf("timeout waiting for service to go to state=%d", svc.Stopped)
  265. }
  266. time.Sleep(300 * time.Millisecond)
  267. status, err = service.Query()
  268. if err != nil {
  269. return fmt.Errorf("could not retrieve service status: %v", err)
  270. }
  271. }
  272. return nil
  273. }
  274. func (s *WindowsService) Status() (Status, error) {
  275. m, err := mgr.Connect()
  276. if err != nil {
  277. return StatusUnknown, err
  278. }
  279. defer m.Disconnect()
  280. service, err := m.OpenService(serviceName)
  281. if err != nil {
  282. return StatusUnknown, fmt.Errorf("could not access service: %v", err)
  283. }
  284. defer service.Close()
  285. status, err := service.Query()
  286. if err != nil {
  287. return StatusUnknown, fmt.Errorf("could not query service status: %v", err)
  288. }
  289. switch status.State {
  290. case svc.StartPending:
  291. return StatusStartPending, nil
  292. case svc.Running:
  293. return StatusRunning, nil
  294. case svc.PausePending:
  295. return StatusPausePending, nil
  296. case svc.Paused:
  297. return StatusPaused, nil
  298. case svc.ContinuePending:
  299. return StatusContinuePending, nil
  300. case svc.StopPending:
  301. return StatusStopPending, nil
  302. case svc.Stopped:
  303. return StatusStopped, nil
  304. default:
  305. return StatusUnknown, fmt.Errorf("unknown status %v", status)
  306. }
  307. }
  308. func (s *WindowsService) getExePath() (string, error) {
  309. return os.Executable()
  310. }