service_windows.go 8.9 KB

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