You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

paths.go 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package doctor
  5. import (
  6. "fmt"
  7. "os"
  8. "code.gitea.io/gitea/modules/log"
  9. "code.gitea.io/gitea/modules/options"
  10. "code.gitea.io/gitea/modules/setting"
  11. )
  12. type configurationFile struct {
  13. Name string
  14. Path string
  15. IsDirectory bool
  16. Required bool
  17. Writable bool
  18. }
  19. func checkConfigurationFile(logger log.Logger, autofix bool, fileOpts configurationFile) error {
  20. logger.Info(`%-26s %q`, log.NewColoredValue(fileOpts.Name+":", log.Reset), fileOpts.Path)
  21. fi, err := os.Stat(fileOpts.Path)
  22. if err != nil {
  23. if os.IsNotExist(err) && autofix && fileOpts.IsDirectory {
  24. if err := os.MkdirAll(fileOpts.Path, 0777); err != nil {
  25. logger.Error(" Directory does not exist and could not be created. ERROR: %v", err)
  26. return fmt.Errorf("Configuration directory: \"%q\" does not exist and could not be created. ERROR: %v", fileOpts.Path, err)
  27. }
  28. fi, err = os.Stat(fileOpts.Path)
  29. }
  30. }
  31. if err != nil {
  32. if fileOpts.Required {
  33. logger.Error(" Is REQUIRED but is not accessible. ERROR: %v", err)
  34. return fmt.Errorf("Configuration file \"%q\" is not accessible but is required. Error: %v", fileOpts.Path, err)
  35. }
  36. logger.Warn(" NOTICE: is not accessible (Error: %v)", err)
  37. // this is a non-critical error
  38. return nil
  39. }
  40. if fileOpts.IsDirectory && !fi.IsDir() {
  41. logger.Error(" ERROR: not a directory")
  42. return fmt.Errorf("Configuration directory \"%q\" is not a directory. Error: %v", fileOpts.Path, err)
  43. } else if !fileOpts.IsDirectory && !fi.Mode().IsRegular() {
  44. logger.Error(" ERROR: not a regular file")
  45. return fmt.Errorf("Configuration file \"%q\" is not a regular file. Error: %v", fileOpts.Path, err)
  46. } else if fileOpts.Writable {
  47. if err := isWritableDir(fileOpts.Path); err != nil {
  48. logger.Error(" ERROR: is required to be writable but is not writable: %v", err)
  49. return fmt.Errorf("Configuration file \"%q\" is required to be writable but is not. Error: %v", fileOpts.Path, err)
  50. }
  51. }
  52. return nil
  53. }
  54. func checkConfigurationFiles(logger log.Logger, autofix bool) error {
  55. if fi, err := os.Stat(setting.CustomConf); err != nil || !fi.Mode().IsRegular() {
  56. logger.Error("Failed to find configuration file at '%s'.", setting.CustomConf)
  57. logger.Error("If you've never ran Gitea yet, this is normal and '%s' will be created for you on first run.", setting.CustomConf)
  58. logger.Error("Otherwise check that you are running this command from the correct path and/or provide a `--config` parameter.")
  59. logger.Critical("Cannot proceed without a configuration file")
  60. return err
  61. }
  62. setting.LoadFromExisting()
  63. configurationFiles := []configurationFile{
  64. {"Configuration File Path", setting.CustomConf, false, true, false},
  65. {"Repository Root Path", setting.RepoRootPath, true, true, true},
  66. {"Data Root Path", setting.AppDataPath, true, true, true},
  67. {"Custom File Root Path", setting.CustomPath, true, false, false},
  68. {"Work directory", setting.AppWorkPath, true, true, false},
  69. {"Log Root Path", setting.LogRootPath, true, true, true},
  70. }
  71. if options.IsDynamic() {
  72. configurationFiles = append(configurationFiles, configurationFile{"Static File Root Path", setting.StaticRootPath, true, true, false})
  73. }
  74. numberOfErrors := 0
  75. for _, configurationFile := range configurationFiles {
  76. if err := checkConfigurationFile(logger, autofix, configurationFile); err != nil {
  77. numberOfErrors++
  78. }
  79. }
  80. if numberOfErrors > 0 {
  81. logger.Critical("Please check your configuration files and try again.")
  82. return fmt.Errorf("%d configuration files with errors", numberOfErrors)
  83. }
  84. return nil
  85. }
  86. func isWritableDir(path string) error {
  87. // There's no platform-independent way of checking if a directory is writable
  88. // https://stackoverflow.com/questions/20026320/how-to-tell-if-folder-exists-and-is-writable
  89. tmpFile, err := os.CreateTemp(path, "doctors-order")
  90. if err != nil {
  91. return err
  92. }
  93. if err := os.Remove(tmpFile.Name()); err != nil {
  94. fmt.Printf("Warning: can't remove temporary file: '%s'\n", tmpFile.Name())
  95. }
  96. tmpFile.Close()
  97. return nil
  98. }
  99. func init() {
  100. Register(&Check{
  101. Title: "Check paths and basic configuration",
  102. Name: "paths",
  103. IsDefault: true,
  104. Run: checkConfigurationFiles,
  105. AbortIfFailed: true,
  106. SkipDatabaseInitialization: true,
  107. Priority: 1,
  108. })
  109. }