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

paths.go 4.5 kB

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