locations.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Copyright (C) 2019 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package locations
  7. import (
  8. "fmt"
  9. "os"
  10. "path/filepath"
  11. "runtime"
  12. "strings"
  13. "time"
  14. "github.com/syncthing/syncthing/lib/fs"
  15. )
  16. type LocationEnum string
  17. // Use strings as keys to make printout and serialization of the locations map
  18. // more meaningful.
  19. const (
  20. ConfigFile LocationEnum = "config"
  21. CertFile LocationEnum = "certFile"
  22. KeyFile LocationEnum = "keyFile"
  23. HTTPSCertFile LocationEnum = "httpsCertFile"
  24. HTTPSKeyFile LocationEnum = "httpsKeyFile"
  25. Database LocationEnum = "database"
  26. LogFile LocationEnum = "logFile"
  27. CsrfTokens LocationEnum = "csrfTokens"
  28. PanicLog LocationEnum = "panicLog"
  29. AuditLog LocationEnum = "auditLog"
  30. GUIAssets LocationEnum = "GUIAssets"
  31. DefFolder LocationEnum = "defFolder"
  32. FailuresFile LocationEnum = "FailuresFile"
  33. )
  34. type BaseDirEnum string
  35. const (
  36. // Overridden by -home flag
  37. ConfigBaseDir BaseDirEnum = "config"
  38. DataBaseDir BaseDirEnum = "data"
  39. // User's home directory, *not* -home flag
  40. UserHomeBaseDir BaseDirEnum = "userHome"
  41. LevelDBDir = "index-v0.14.0.db"
  42. BadgerDir = "indexdb.badger"
  43. )
  44. // Platform dependent directories
  45. var baseDirs = make(map[BaseDirEnum]string, 3)
  46. func init() {
  47. userHome := userHomeDir()
  48. config := defaultConfigDir(userHome)
  49. baseDirs[UserHomeBaseDir] = userHome
  50. baseDirs[ConfigBaseDir] = config
  51. baseDirs[DataBaseDir] = defaultDataDir(userHome, config)
  52. err := expandLocations()
  53. if err != nil {
  54. fmt.Println(err)
  55. panic("Failed to expand locations at init time")
  56. }
  57. }
  58. func SetBaseDir(baseDirName BaseDirEnum, path string) error {
  59. _, ok := baseDirs[baseDirName]
  60. if !ok {
  61. return fmt.Errorf("unknown base dir: %s", baseDirName)
  62. }
  63. baseDirs[baseDirName] = filepath.Clean(path)
  64. return expandLocations()
  65. }
  66. func Get(location LocationEnum) string {
  67. return locations[location]
  68. }
  69. func GetBaseDir(baseDir BaseDirEnum) string {
  70. return baseDirs[baseDir]
  71. }
  72. // Use the variables from baseDirs here
  73. var locationTemplates = map[LocationEnum]string{
  74. ConfigFile: "${config}/config.xml",
  75. CertFile: "${config}/cert.pem",
  76. KeyFile: "${config}/key.pem",
  77. HTTPSCertFile: "${config}/https-cert.pem",
  78. HTTPSKeyFile: "${config}/https-key.pem",
  79. Database: "${data}/" + LevelDBDir,
  80. LogFile: "${data}/syncthing.log", // -logfile on Windows
  81. CsrfTokens: "${data}/csrftokens.txt",
  82. PanicLog: "${data}/panic-${timestamp}.log",
  83. AuditLog: "${data}/audit-${timestamp}.log",
  84. GUIAssets: "${config}/gui",
  85. DefFolder: "${userHome}/Sync",
  86. FailuresFile: "${data}/failures-unreported.txt",
  87. }
  88. var locations = make(map[LocationEnum]string)
  89. // expandLocations replaces the variables in the locations map with actual
  90. // directory locations.
  91. func expandLocations() error {
  92. newLocations := make(map[LocationEnum]string)
  93. for key, dir := range locationTemplates {
  94. for varName, value := range baseDirs {
  95. dir = strings.Replace(dir, "${"+string(varName)+"}", value, -1)
  96. }
  97. var err error
  98. dir, err = fs.ExpandTilde(dir)
  99. if err != nil {
  100. return err
  101. }
  102. newLocations[key] = filepath.Clean(dir)
  103. }
  104. locations = newLocations
  105. return nil
  106. }
  107. // defaultConfigDir returns the default configuration directory, as figured
  108. // out by various the environment variables present on each platform, or dies
  109. // trying.
  110. func defaultConfigDir(userHome string) string {
  111. switch runtime.GOOS {
  112. case "windows":
  113. if p := os.Getenv("LocalAppData"); p != "" {
  114. return filepath.Join(p, "Syncthing")
  115. }
  116. return filepath.Join(os.Getenv("AppData"), "Syncthing")
  117. case "darwin":
  118. return filepath.Join(userHome, "Library/Application Support/Syncthing")
  119. default:
  120. if xdgCfg := os.Getenv("XDG_CONFIG_HOME"); xdgCfg != "" {
  121. return filepath.Join(xdgCfg, "syncthing")
  122. }
  123. return filepath.Join(userHome, ".config/syncthing")
  124. }
  125. }
  126. // defaultDataDir returns the default data directory, which usually is the
  127. // config directory but might be something else.
  128. func defaultDataDir(userHome, config string) string {
  129. switch runtime.GOOS {
  130. case "windows", "darwin":
  131. return config
  132. default:
  133. // If a database exists at the "normal" location, use that anyway.
  134. // We look for both LevelDB and Badger variants here regardless of
  135. // what we're currently configured to use, because we might be
  136. // starting up in Badger mode with only a LevelDB database present
  137. // (will be converted), or vice versa.
  138. if _, err := os.Lstat(filepath.Join(config, LevelDBDir)); err == nil {
  139. return config
  140. }
  141. if _, err := os.Lstat(filepath.Join(config, BadgerDir)); err == nil {
  142. return config
  143. }
  144. // Always use this env var, as it's explicitly set by the user
  145. if xdgHome := os.Getenv("XDG_DATA_HOME"); xdgHome != "" {
  146. return filepath.Join(xdgHome, "syncthing")
  147. }
  148. // Only use the XDG default, if a syncthing specific dir already
  149. // exists. Existence of ~/.local/share is not deemed enough, as
  150. // it may also exist erroneously on non-XDG systems.
  151. xdgDefault := filepath.Join(userHome, ".local/share/syncthing")
  152. if _, err := os.Lstat(xdgDefault); err == nil {
  153. return xdgDefault
  154. }
  155. // FYI: XDG_DATA_DIRS is not relevant, as it is for system-wide
  156. // data dirs, not user specific ones.
  157. return config
  158. }
  159. }
  160. // userHomeDir returns the user's home directory, or dies trying.
  161. func userHomeDir() string {
  162. userHome, err := fs.ExpandTilde("~")
  163. if err != nil {
  164. fmt.Println(err)
  165. panic("Failed to get user home dir")
  166. }
  167. return userHome
  168. }
  169. func GetTimestamped(key LocationEnum) string {
  170. // We take the roundtrip via "${timestamp}" instead of passing the path
  171. // directly through time.Format() to avoid issues when the path we are
  172. // expanding contains numbers; otherwise for example
  173. // /home/user2006/.../panic-20060102-150405.log would get both instances of
  174. // 2006 replaced by 2015...
  175. tpl := locations[key]
  176. now := time.Now().Format("20060102-150405")
  177. return strings.Replace(tpl, "${timestamp}", now, -1)
  178. }