service_windows.go 8.8 KB

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