service_windows.go 9.8 KB

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