gitea源码

path.go 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package setting
  4. import (
  5. "errors"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "strings"
  10. "code.gitea.io/gitea/modules/log"
  11. "code.gitea.io/gitea/modules/tempdir"
  12. )
  13. var (
  14. // AppPath represents the path to the gitea binary
  15. AppPath string
  16. // AppWorkPath is the "working directory" of Gitea. It maps to the: WORK_PATH in app.ini, "--work-path" flag, environment variable GITEA_WORK_DIR.
  17. // If that is not set it is the default set here by the linker or failing that the directory of AppPath.
  18. // It is used as the base path for several other paths.
  19. AppWorkPath string
  20. CustomPath string // Custom directory path. Env: GITEA_CUSTOM
  21. CustomConf string
  22. appWorkPathBuiltin string
  23. customPathBuiltin string
  24. customConfBuiltin string
  25. AppWorkPathMismatch bool
  26. )
  27. func getAppPath() (string, error) {
  28. var appPath string
  29. var err error
  30. if IsWindows && filepath.IsAbs(os.Args[0]) {
  31. appPath = filepath.Clean(os.Args[0])
  32. } else {
  33. appPath, err = exec.LookPath(os.Args[0])
  34. }
  35. if err != nil {
  36. if !errors.Is(err, exec.ErrDot) {
  37. return "", err
  38. }
  39. appPath, err = filepath.Abs(os.Args[0])
  40. }
  41. if err != nil {
  42. return "", err
  43. }
  44. appPath, err = filepath.Abs(appPath)
  45. if err != nil {
  46. return "", err
  47. }
  48. // Note: (legacy code) we don't use path.Dir here because it does not handle case which path starts with two "/" in Windows: "//psf/Home/..."
  49. return strings.ReplaceAll(appPath, "\\", "/"), err
  50. }
  51. func init() {
  52. var err error
  53. if AppPath, err = getAppPath(); err != nil {
  54. log.Fatal("Failed to get app path: %v", err)
  55. }
  56. if AppWorkPath == "" {
  57. AppWorkPath = filepath.Dir(AppPath)
  58. }
  59. appWorkPathBuiltin = AppWorkPath
  60. customPathBuiltin = CustomPath
  61. customConfBuiltin = CustomConf
  62. }
  63. type ArgWorkPathAndCustomConf struct {
  64. WorkPath string
  65. CustomPath string
  66. CustomConf string
  67. }
  68. type stringWithDefault struct {
  69. Value string
  70. IsSet bool
  71. }
  72. func (s *stringWithDefault) Set(v string) {
  73. s.Value = v
  74. s.IsSet = true
  75. }
  76. // InitWorkPathAndCommonConfig will set AppWorkPath, CustomPath and CustomConf, init default config provider by CustomConf and load common settings,
  77. func InitWorkPathAndCommonConfig(getEnvFn func(name string) string, args ArgWorkPathAndCustomConf) {
  78. InitWorkPathAndCfgProvider(getEnvFn, args)
  79. LoadCommonSettings()
  80. }
  81. // InitWorkPathAndCfgProvider will set AppWorkPath, CustomPath and CustomConf, init default config provider by CustomConf
  82. func InitWorkPathAndCfgProvider(getEnvFn func(name string) string, args ArgWorkPathAndCustomConf) {
  83. tryAbsPath := func(paths ...string) string {
  84. s := paths[len(paths)-1]
  85. for i := len(paths) - 2; i >= 0; i-- {
  86. if filepath.IsAbs(s) {
  87. break
  88. }
  89. s = filepath.Join(paths[i], s)
  90. }
  91. return s
  92. }
  93. var err error
  94. tmpWorkPath := stringWithDefault{Value: appWorkPathBuiltin}
  95. if tmpWorkPath.Value == "" {
  96. tmpWorkPath.Value = filepath.Dir(AppPath)
  97. }
  98. tmpCustomPath := stringWithDefault{Value: customPathBuiltin}
  99. if tmpCustomPath.Value == "" {
  100. tmpCustomPath.Value = "custom"
  101. }
  102. tmpCustomConf := stringWithDefault{Value: customConfBuiltin}
  103. if tmpCustomConf.Value == "" {
  104. tmpCustomConf.Value = "conf/app.ini"
  105. }
  106. readFromEnv := func() {
  107. envWorkPath := getEnvFn("GITEA_WORK_DIR")
  108. if envWorkPath != "" {
  109. tmpWorkPath.Set(envWorkPath)
  110. if !filepath.IsAbs(tmpWorkPath.Value) {
  111. log.Fatal("GITEA_WORK_DIR (work path) must be absolute path")
  112. }
  113. }
  114. envCustomPath := getEnvFn("GITEA_CUSTOM")
  115. if envCustomPath != "" {
  116. tmpCustomPath.Set(envCustomPath)
  117. if !filepath.IsAbs(tmpCustomPath.Value) {
  118. log.Fatal("GITEA_CUSTOM (custom path) must be absolute path")
  119. }
  120. }
  121. }
  122. readFromArgs := func() {
  123. if args.WorkPath != "" {
  124. tmpWorkPath.Set(args.WorkPath)
  125. if !filepath.IsAbs(tmpWorkPath.Value) {
  126. log.Fatal("--work-path must be absolute path")
  127. }
  128. }
  129. if args.CustomPath != "" {
  130. tmpCustomPath.Set(args.CustomPath) // if it is not abs, it will be based on work-path, it shouldn't happen
  131. if !filepath.IsAbs(tmpCustomPath.Value) {
  132. log.Error("--custom-path must be absolute path")
  133. }
  134. }
  135. if args.CustomConf != "" {
  136. tmpCustomConf.Set(args.CustomConf)
  137. if !filepath.IsAbs(tmpCustomConf.Value) {
  138. // the config path can be relative to the real current working path
  139. if tmpCustomConf.Value, err = filepath.Abs(tmpCustomConf.Value); err != nil {
  140. log.Fatal("Failed to get absolute path of config %q: %v", tmpCustomConf.Value, err)
  141. }
  142. }
  143. }
  144. }
  145. readFromEnv()
  146. readFromArgs()
  147. if !tmpCustomConf.IsSet {
  148. tmpCustomConf.Set(tryAbsPath(tmpWorkPath.Value, tmpCustomPath.Value, tmpCustomConf.Value))
  149. }
  150. // only read the config but do not load/init anything more, because the AppWorkPath and CustomPath are not ready
  151. InitCfgProvider(tmpCustomConf.Value)
  152. if HasInstallLock(CfgProvider) {
  153. ClearEnvConfigKeys() // if the instance has been installed, do not pass the environment variables to sub-processes
  154. }
  155. configWorkPath := ConfigSectionKeyString(CfgProvider.Section(""), "WORK_PATH")
  156. if configWorkPath != "" {
  157. if !filepath.IsAbs(configWorkPath) {
  158. log.Fatal("WORK_PATH in %q must be absolute path", configWorkPath)
  159. }
  160. configWorkPath = filepath.Clean(configWorkPath)
  161. if tmpWorkPath.Value != "" && (getEnvFn("GITEA_WORK_DIR") != "" || args.WorkPath != "") {
  162. fi1, err1 := os.Stat(tmpWorkPath.Value)
  163. fi2, err2 := os.Stat(configWorkPath)
  164. if err1 != nil || err2 != nil || !os.SameFile(fi1, fi2) {
  165. AppWorkPathMismatch = true
  166. }
  167. }
  168. tmpWorkPath.Set(configWorkPath)
  169. }
  170. tmpCustomPath.Set(tryAbsPath(tmpWorkPath.Value, tmpCustomPath.Value))
  171. AppWorkPath = tmpWorkPath.Value
  172. CustomPath = tmpCustomPath.Value
  173. CustomConf = tmpCustomConf.Value
  174. }
  175. // AppDataTempDir returns a managed temporary directory for the application data.
  176. // Using empty sub will get the managed base temp directory, and it's safe to delete it.
  177. // Gitea only creates subdirectories under it, but not the APP_TEMP_PATH directory itself.
  178. // * When APP_TEMP_PATH="/tmp": the managed temp directory is "/tmp/gitea-tmp"
  179. // * When APP_TEMP_PATH is not set: the managed temp directory is "/{APP_DATA_PATH}/tmp"
  180. func AppDataTempDir(sub string) *tempdir.TempDir {
  181. if appTempPathInternal != "" {
  182. return tempdir.New(appTempPathInternal, "gitea-tmp/"+sub)
  183. }
  184. if AppDataPath == "" {
  185. panic("setting.AppDataPath is not set")
  186. }
  187. return tempdir.New(AppDataPath, "tmp/"+sub)
  188. }