service_windows.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. // Copyright (C) 2019-2023 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. common.WaitForTransfers(graceTime)
  118. break loop
  119. case svc.ParamChange:
  120. logger.Debug(logSender, "", "Received reload request")
  121. err := dataprovider.ReloadConfig()
  122. if err != nil {
  123. logger.Warn(logSender, "", "error reloading dataprovider configuration: %v", err)
  124. }
  125. err = httpd.ReloadCertificateMgr()
  126. if err != nil {
  127. logger.Warn(logSender, "", "error reloading cert manager: %v", err)
  128. }
  129. err = ftpd.ReloadCertificateMgr()
  130. if err != nil {
  131. logger.Warn(logSender, "", "error reloading FTPD cert manager: %v", err)
  132. }
  133. err = webdavd.ReloadCertificateMgr()
  134. if err != nil {
  135. logger.Warn(logSender, "", "error reloading WebDAV cert manager: %v", err)
  136. }
  137. err = telemetry.ReloadCertificateMgr()
  138. if err != nil {
  139. logger.Warn(logSender, "", "error reloading telemetry cert manager: %v", err)
  140. }
  141. err = common.Reload()
  142. if err != nil {
  143. logger.Warn(logSender, "", "error reloading common configs: %v", err)
  144. }
  145. err = sftpd.Reload()
  146. if err != nil {
  147. logger.Warn(logSender, "", "error reloading sftpd revoked certificates: %v", err)
  148. }
  149. case rotateLogCmd:
  150. logger.Debug(logSender, "", "Received log file rotation request")
  151. err := logger.RotateLogFile()
  152. if err != nil {
  153. logger.Warn(logSender, "", "error rotating log file: %v", err)
  154. }
  155. default:
  156. continue loop
  157. }
  158. }
  159. return false, 0
  160. }
  161. func (s *WindowsService) RunService() error {
  162. exePath, err := s.getExePath()
  163. if err != nil {
  164. return err
  165. }
  166. isService, err := svc.IsWindowsService()
  167. if err != nil {
  168. return err
  169. }
  170. s.isInteractive = !isService
  171. dir := filepath.Dir(exePath)
  172. if err = os.Chdir(dir); err != nil {
  173. return err
  174. }
  175. if s.isInteractive {
  176. return s.Start()
  177. }
  178. return svc.Run(serviceName, s)
  179. }
  180. func (s *WindowsService) Start() error {
  181. m, err := mgr.Connect()
  182. if err != nil {
  183. return err
  184. }
  185. defer m.Disconnect()
  186. service, err := m.OpenService(serviceName)
  187. if err != nil {
  188. return fmt.Errorf("could not access service: %v", err)
  189. }
  190. defer service.Close()
  191. err = service.Start()
  192. if err != nil {
  193. return fmt.Errorf("could not start service: %v", err)
  194. }
  195. return nil
  196. }
  197. func (s *WindowsService) Reload() error {
  198. m, err := mgr.Connect()
  199. if err != nil {
  200. return err
  201. }
  202. defer m.Disconnect()
  203. service, err := m.OpenService(serviceName)
  204. if err != nil {
  205. return fmt.Errorf("could not access service: %v", err)
  206. }
  207. defer service.Close()
  208. _, err = service.Control(svc.ParamChange)
  209. if err != nil {
  210. return fmt.Errorf("could not send control=%d: %v", svc.ParamChange, err)
  211. }
  212. return nil
  213. }
  214. func (s *WindowsService) RotateLogFile() error {
  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. return fmt.Errorf("could not access service: %v", err)
  223. }
  224. defer service.Close()
  225. _, err = service.Control(rotateLogCmd)
  226. if err != nil {
  227. return fmt.Errorf("could not send control=%d: %v", rotateLogCmd, err)
  228. }
  229. return nil
  230. }
  231. func (s *WindowsService) Install(args ...string) error {
  232. exePath, err := s.getExePath()
  233. if err != nil {
  234. return err
  235. }
  236. m, err := mgr.Connect()
  237. if err != nil {
  238. return err
  239. }
  240. defer m.Disconnect()
  241. service, err := m.OpenService(serviceName)
  242. if err == nil {
  243. service.Close()
  244. return fmt.Errorf("service %s already exists", serviceName)
  245. }
  246. config := mgr.Config{
  247. DisplayName: serviceName,
  248. Description: serviceDesc,
  249. StartType: mgr.StartAutomatic}
  250. service, err = m.CreateService(serviceName, exePath, config, args...)
  251. if err != nil {
  252. return err
  253. }
  254. defer service.Close()
  255. err = eventlog.InstallAsEventCreate(serviceName, eventlog.Error|eventlog.Warning|eventlog.Info)
  256. if err != nil {
  257. if !strings.Contains(err.Error(), "exists") {
  258. service.Delete()
  259. return fmt.Errorf("SetupEventLogSource() failed: %s", err)
  260. }
  261. }
  262. recoveryActions := []mgr.RecoveryAction{
  263. {
  264. Type: mgr.ServiceRestart,
  265. Delay: 5 * time.Second,
  266. },
  267. {
  268. Type: mgr.ServiceRestart,
  269. Delay: 60 * time.Second,
  270. },
  271. {
  272. Type: mgr.ServiceRestart,
  273. Delay: 90 * time.Second,
  274. },
  275. }
  276. err = service.SetRecoveryActions(recoveryActions, uint32(300))
  277. if err != nil {
  278. service.Delete()
  279. return fmt.Errorf("unable to set recovery actions: %v", err)
  280. }
  281. return nil
  282. }
  283. func (s *WindowsService) Uninstall() 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("service %s is not installed", serviceName)
  292. }
  293. defer service.Close()
  294. err = service.Delete()
  295. if err != nil {
  296. return err
  297. }
  298. err = eventlog.Remove(serviceName)
  299. if err != nil {
  300. return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
  301. }
  302. return nil
  303. }
  304. func (s *WindowsService) Stop() error {
  305. m, err := mgr.Connect()
  306. if err != nil {
  307. return err
  308. }
  309. defer m.Disconnect()
  310. service, err := m.OpenService(serviceName)
  311. if err != nil {
  312. return fmt.Errorf("could not access service: %v", err)
  313. }
  314. defer service.Close()
  315. status, err := service.Control(svc.Stop)
  316. if err != nil {
  317. return fmt.Errorf("could not send control=%d: %v", svc.Stop, err)
  318. }
  319. timeout := time.Now().Add(10 * time.Second)
  320. for status.State != svc.Stopped {
  321. if timeout.Before(time.Now()) {
  322. return fmt.Errorf("timeout waiting for service to go to state=%d", svc.Stopped)
  323. }
  324. time.Sleep(300 * time.Millisecond)
  325. status, err = service.Query()
  326. if err != nil {
  327. return fmt.Errorf("could not retrieve service status: %v", err)
  328. }
  329. }
  330. return nil
  331. }
  332. func (s *WindowsService) Status() (Status, error) {
  333. m, err := mgr.Connect()
  334. if err != nil {
  335. return StatusUnknown, err
  336. }
  337. defer m.Disconnect()
  338. service, err := m.OpenService(serviceName)
  339. if err != nil {
  340. return StatusUnknown, fmt.Errorf("could not access service: %v", err)
  341. }
  342. defer service.Close()
  343. status, err := service.Query()
  344. if err != nil {
  345. return StatusUnknown, fmt.Errorf("could not query service status: %v", err)
  346. }
  347. switch status.State {
  348. case svc.StartPending:
  349. return StatusStartPending, nil
  350. case svc.Running:
  351. return StatusRunning, nil
  352. case svc.PausePending:
  353. return StatusPausePending, nil
  354. case svc.Paused:
  355. return StatusPaused, nil
  356. case svc.ContinuePending:
  357. return StatusContinuePending, nil
  358. case svc.StopPending:
  359. return StatusStopPending, nil
  360. case svc.Stopped:
  361. return StatusStopped, nil
  362. default:
  363. return StatusUnknown, fmt.Errorf("unknown status %v", status)
  364. }
  365. }
  366. func (s *WindowsService) getExePath() (string, error) {
  367. return os.Executable()
  368. }