service.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // Package service allows to start and stop the SFTPGo service
  2. package service
  3. import (
  4. "errors"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "github.com/rs/zerolog"
  9. "github.com/drakkan/sftpgo/v2/common"
  10. "github.com/drakkan/sftpgo/v2/config"
  11. "github.com/drakkan/sftpgo/v2/dataprovider"
  12. "github.com/drakkan/sftpgo/v2/httpd"
  13. "github.com/drakkan/sftpgo/v2/logger"
  14. "github.com/drakkan/sftpgo/v2/sdk/plugin"
  15. "github.com/drakkan/sftpgo/v2/util"
  16. "github.com/drakkan/sftpgo/v2/version"
  17. )
  18. const (
  19. logSender = "service"
  20. )
  21. var (
  22. chars = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
  23. )
  24. // Service defines the SFTPGo service
  25. type Service struct {
  26. ConfigDir string
  27. ConfigFile string
  28. LogFilePath string
  29. LogMaxSize int
  30. LogMaxBackups int
  31. LogMaxAge int
  32. PortableMode int
  33. PortableUser dataprovider.User
  34. LogCompress bool
  35. LogVerbose bool
  36. LoadDataClean bool
  37. LoadDataFrom string
  38. LoadDataMode int
  39. LoadDataQuotaScan int
  40. Shutdown chan bool
  41. Error error
  42. }
  43. func (s *Service) initLogger() {
  44. logLevel := zerolog.DebugLevel
  45. if !s.LogVerbose {
  46. logLevel = zerolog.InfoLevel
  47. }
  48. if !filepath.IsAbs(s.LogFilePath) && util.IsFileInputValid(s.LogFilePath) {
  49. s.LogFilePath = filepath.Join(s.ConfigDir, s.LogFilePath)
  50. }
  51. logger.InitLogger(s.LogFilePath, s.LogMaxSize, s.LogMaxBackups, s.LogMaxAge, s.LogCompress, logLevel)
  52. if s.PortableMode == 1 {
  53. logger.EnableConsoleLogger(logLevel)
  54. if s.LogFilePath == "" {
  55. logger.DisableLogger()
  56. }
  57. }
  58. }
  59. // Start initializes the service
  60. func (s *Service) Start() error {
  61. s.initLogger()
  62. logger.Info(logSender, "", "starting SFTPGo %v, config dir: %v, config file: %v, log max size: %v log max backups: %v "+
  63. "log max age: %v log verbose: %v, log compress: %v, load data from: %#v", version.GetAsString(), s.ConfigDir, s.ConfigFile,
  64. s.LogMaxSize, s.LogMaxBackups, s.LogMaxAge, s.LogVerbose, s.LogCompress, s.LoadDataFrom)
  65. // in portable mode we don't read configuration from file
  66. if s.PortableMode != 1 {
  67. err := config.LoadConfig(s.ConfigDir, s.ConfigFile)
  68. if err != nil {
  69. logger.Error(logSender, "", "error loading configuration: %v", err)
  70. return err
  71. }
  72. }
  73. if !config.HasServicesToStart() {
  74. infoString := "no service configured, nothing to do"
  75. logger.Info(logSender, "", infoString)
  76. logger.InfoToConsole(infoString)
  77. return errors.New(infoString)
  78. }
  79. err := common.Initialize(config.GetCommonConfig())
  80. if err != nil {
  81. logger.Error(logSender, "", "%v", err)
  82. logger.ErrorToConsole("%v", err)
  83. os.Exit(1)
  84. }
  85. kmsConfig := config.GetKMSConfig()
  86. err = kmsConfig.Initialize()
  87. if err != nil {
  88. logger.Error(logSender, "", "unable to initialize KMS: %v", err)
  89. logger.ErrorToConsole("unable to initialize KMS: %v", err)
  90. os.Exit(1)
  91. }
  92. if err := plugin.Initialize(config.GetPluginsConfig(), s.LogVerbose); err != nil {
  93. logger.Error(logSender, "", "unable to initialize plugin: %v", err)
  94. logger.ErrorToConsole("unable to initialize plugin: %v", err)
  95. os.Exit(1)
  96. }
  97. providerConf := config.GetProviderConf()
  98. err = dataprovider.Initialize(providerConf, s.ConfigDir, s.PortableMode == 0)
  99. if err != nil {
  100. logger.Error(logSender, "", "error initializing data provider: %v", err)
  101. logger.ErrorToConsole("error initializing data provider: %v", err)
  102. return err
  103. }
  104. if s.PortableMode == 1 {
  105. // create the user for portable mode
  106. err = dataprovider.AddUser(&s.PortableUser)
  107. if err != nil {
  108. logger.ErrorToConsole("error adding portable user: %v", err)
  109. return err
  110. }
  111. }
  112. err = s.loadInitialData()
  113. if err != nil {
  114. logger.Error(logSender, "", "unable to load initial data: %v", err)
  115. logger.ErrorToConsole("unable to load initial data: %v", err)
  116. }
  117. httpConfig := config.GetHTTPConfig()
  118. err = httpConfig.Initialize(s.ConfigDir)
  119. if err != nil {
  120. logger.Error(logSender, "", "error initializing http client: %v", err)
  121. logger.ErrorToConsole("error initializing http client: %v", err)
  122. return err
  123. }
  124. s.startServices()
  125. go common.Config.ExecuteStartupHook() //nolint:errcheck
  126. return nil
  127. }
  128. func (s *Service) startServices() {
  129. sftpdConf := config.GetSFTPDConfig()
  130. ftpdConf := config.GetFTPDConfig()
  131. httpdConf := config.GetHTTPDConfig()
  132. webDavDConf := config.GetWebDAVDConfig()
  133. telemetryConf := config.GetTelemetryConfig()
  134. if sftpdConf.ShouldBind() {
  135. go func() {
  136. redactedConf := sftpdConf
  137. redactedConf.KeyboardInteractiveHook = util.GetRedactedURL(sftpdConf.KeyboardInteractiveHook)
  138. logger.Debug(logSender, "", "initializing SFTP server with config %+v", redactedConf)
  139. if err := sftpdConf.Initialize(s.ConfigDir); err != nil {
  140. logger.Error(logSender, "", "could not start SFTP server: %v", err)
  141. logger.ErrorToConsole("could not start SFTP server: %v", err)
  142. s.Error = err
  143. }
  144. s.Shutdown <- true
  145. }()
  146. } else {
  147. logger.Debug(logSender, "", "SFTP server not started, disabled in config file")
  148. }
  149. if httpdConf.ShouldBind() {
  150. go func() {
  151. if err := httpdConf.Initialize(s.ConfigDir); err != nil {
  152. logger.Error(logSender, "", "could not start HTTP server: %v", err)
  153. logger.ErrorToConsole("could not start HTTP server: %v", err)
  154. s.Error = err
  155. }
  156. s.Shutdown <- true
  157. }()
  158. } else {
  159. logger.Debug(logSender, "", "HTTP server not started, disabled in config file")
  160. if s.PortableMode != 1 {
  161. logger.DebugToConsole("HTTP server not started, disabled in config file")
  162. }
  163. }
  164. if ftpdConf.ShouldBind() {
  165. go func() {
  166. if err := ftpdConf.Initialize(s.ConfigDir); err != nil {
  167. logger.Error(logSender, "", "could not start FTP server: %v", err)
  168. logger.ErrorToConsole("could not start FTP server: %v", err)
  169. s.Error = err
  170. }
  171. s.Shutdown <- true
  172. }()
  173. } else {
  174. logger.Debug(logSender, "", "FTP server not started, disabled in config file")
  175. }
  176. if webDavDConf.ShouldBind() {
  177. go func() {
  178. if err := webDavDConf.Initialize(s.ConfigDir); err != nil {
  179. logger.Error(logSender, "", "could not start WebDAV server: %v", err)
  180. logger.ErrorToConsole("could not start WebDAV server: %v", err)
  181. s.Error = err
  182. }
  183. s.Shutdown <- true
  184. }()
  185. } else {
  186. logger.Debug(logSender, "", "WebDAV server not started, disabled in config file")
  187. }
  188. if telemetryConf.ShouldBind() {
  189. go func() {
  190. if err := telemetryConf.Initialize(s.ConfigDir); err != nil {
  191. logger.Error(logSender, "", "could not start telemetry server: %v", err)
  192. logger.ErrorToConsole("could not start telemetry server: %v", err)
  193. s.Error = err
  194. }
  195. s.Shutdown <- true
  196. }()
  197. } else {
  198. logger.Debug(logSender, "", "telemetry server not started, disabled in config file")
  199. if s.PortableMode != 1 {
  200. logger.DebugToConsole("telemetry server not started, disabled in config file")
  201. }
  202. }
  203. }
  204. // Wait blocks until the service exits
  205. func (s *Service) Wait() {
  206. if s.PortableMode != 1 {
  207. registerSignals()
  208. }
  209. <-s.Shutdown
  210. }
  211. // Stop terminates the service unblocking the Wait method
  212. func (s *Service) Stop() {
  213. close(s.Shutdown)
  214. logger.Debug(logSender, "", "Service stopped")
  215. }
  216. func (s *Service) loadInitialData() error {
  217. if s.LoadDataFrom == "" {
  218. return nil
  219. }
  220. if !filepath.IsAbs(s.LoadDataFrom) {
  221. return fmt.Errorf("invalid input_file %#v, it must be an absolute path", s.LoadDataFrom)
  222. }
  223. if s.LoadDataMode < 0 || s.LoadDataMode > 1 {
  224. return fmt.Errorf("invalid loaddata-mode %v", s.LoadDataMode)
  225. }
  226. if s.LoadDataQuotaScan < 0 || s.LoadDataQuotaScan > 2 {
  227. return fmt.Errorf("invalid loaddata-scan %v", s.LoadDataQuotaScan)
  228. }
  229. info, err := os.Stat(s.LoadDataFrom)
  230. if err != nil {
  231. return err
  232. }
  233. if info.Size() > httpd.MaxRestoreSize {
  234. return fmt.Errorf("unable to restore input file %#v size too big: %v/%v bytes",
  235. s.LoadDataFrom, info.Size(), httpd.MaxRestoreSize)
  236. }
  237. content, err := os.ReadFile(s.LoadDataFrom)
  238. if err != nil {
  239. return fmt.Errorf("unable to read input file %#v: %v", s.LoadDataFrom, err)
  240. }
  241. dump, err := dataprovider.ParseDumpData(content)
  242. if err != nil {
  243. return fmt.Errorf("unable to parse file to restore %#v: %v", s.LoadDataFrom, err)
  244. }
  245. err = s.restoreDump(&dump)
  246. if err != nil {
  247. return err
  248. }
  249. logger.Info(logSender, "", "data loaded from file %#v mode: %v, quota scan %v", s.LoadDataFrom,
  250. s.LoadDataMode, s.LoadDataQuotaScan)
  251. logger.InfoToConsole("data loaded from file %#v mode: %v, quota scan %v", s.LoadDataFrom,
  252. s.LoadDataMode, s.LoadDataQuotaScan)
  253. if s.LoadDataClean {
  254. err = os.Remove(s.LoadDataFrom)
  255. if err == nil {
  256. logger.Info(logSender, "", "file %#v deleted after successful load", s.LoadDataFrom)
  257. logger.InfoToConsole("file %#v deleted after successful load", s.LoadDataFrom)
  258. } else {
  259. logger.Warn(logSender, "", "unable to delete file %#v after successful load: %v", s.LoadDataFrom, err)
  260. logger.WarnToConsole("unable to delete file %#v after successful load: %v", s.LoadDataFrom, err)
  261. }
  262. }
  263. return nil
  264. }
  265. func (s *Service) restoreDump(dump *dataprovider.BackupData) error {
  266. err := httpd.RestoreAdmins(dump.Admins, s.LoadDataFrom, s.LoadDataMode)
  267. if err != nil {
  268. return fmt.Errorf("unable to restore admins from file %#v: %v", s.LoadDataFrom, err)
  269. }
  270. err = httpd.RestoreFolders(dump.Folders, s.LoadDataFrom, s.LoadDataMode, s.LoadDataQuotaScan)
  271. if err != nil {
  272. return fmt.Errorf("unable to restore folders from file %#v: %v", s.LoadDataFrom, err)
  273. }
  274. err = httpd.RestoreUsers(dump.Users, s.LoadDataFrom, s.LoadDataMode, s.LoadDataQuotaScan)
  275. if err != nil {
  276. return fmt.Errorf("unable to restore users from file %#v: %v", s.LoadDataFrom, err)
  277. }
  278. return nil
  279. }