locations.go 5.9 KB

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