service_windows.go 10 KB

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