config.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
  2. // All rights reserved. Use of this source code is governed by an MIT-style
  3. // license that can be found in the LICENSE file.
  4. // Package config implements reading and writing of the syncthing configuration file.
  5. package config
  6. import (
  7. "encoding/xml"
  8. "fmt"
  9. "os"
  10. "reflect"
  11. "sort"
  12. "strconv"
  13. "code.google.com/p/go.crypto/bcrypt"
  14. "github.com/syncthing/syncthing/internal/events"
  15. "github.com/syncthing/syncthing/internal/logger"
  16. "github.com/syncthing/syncthing/internal/osutil"
  17. "github.com/syncthing/syncthing/internal/protocol"
  18. )
  19. var l = logger.DefaultLogger
  20. type Configuration struct {
  21. Location string `xml:"-" json:"-"`
  22. Version int `xml:"version,attr" default:"3"`
  23. Repositories []RepositoryConfiguration `xml:"repository"`
  24. Nodes []NodeConfiguration `xml:"node"`
  25. GUI GUIConfiguration `xml:"gui"`
  26. Options OptionsConfiguration `xml:"options"`
  27. XMLName xml.Name `xml:"configuration" json:"-"`
  28. }
  29. type RepositoryConfiguration struct {
  30. ID string `xml:"id,attr"`
  31. Directory string `xml:"directory,attr"`
  32. Nodes []RepositoryNodeConfiguration `xml:"node"`
  33. ReadOnly bool `xml:"ro,attr"`
  34. RescanIntervalS int `xml:"rescanIntervalS,attr" default:"60"`
  35. IgnorePerms bool `xml:"ignorePerms,attr"`
  36. Invalid string `xml:"-"` // Set at runtime when there is an error, not saved
  37. Versioning VersioningConfiguration `xml:"versioning"`
  38. nodeIDs []protocol.NodeID
  39. }
  40. type VersioningConfiguration struct {
  41. Type string `xml:"type,attr"`
  42. Params map[string]string
  43. }
  44. type InternalVersioningConfiguration struct {
  45. Type string `xml:"type,attr,omitempty"`
  46. Params []InternalParam `xml:"param"`
  47. }
  48. type InternalParam struct {
  49. Key string `xml:"key,attr"`
  50. Val string `xml:"val,attr"`
  51. }
  52. func (c *VersioningConfiguration) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  53. var tmp InternalVersioningConfiguration
  54. tmp.Type = c.Type
  55. for k, v := range c.Params {
  56. tmp.Params = append(tmp.Params, InternalParam{k, v})
  57. }
  58. return e.EncodeElement(tmp, start)
  59. }
  60. func (c *VersioningConfiguration) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  61. var tmp InternalVersioningConfiguration
  62. err := d.DecodeElement(&tmp, &start)
  63. if err != nil {
  64. return err
  65. }
  66. c.Type = tmp.Type
  67. c.Params = make(map[string]string, len(tmp.Params))
  68. for _, p := range tmp.Params {
  69. c.Params[p.Key] = p.Val
  70. }
  71. return nil
  72. }
  73. func (r *RepositoryConfiguration) NodeIDs() []protocol.NodeID {
  74. if r.nodeIDs == nil {
  75. for _, n := range r.Nodes {
  76. r.nodeIDs = append(r.nodeIDs, n.NodeID)
  77. }
  78. }
  79. return r.nodeIDs
  80. }
  81. type NodeConfiguration struct {
  82. NodeID protocol.NodeID `xml:"id,attr"`
  83. Name string `xml:"name,attr,omitempty"`
  84. Addresses []string `xml:"address,omitempty"`
  85. Compression bool `xml:"compression,attr"`
  86. CertName string `xml:"certName,attr,omitempty"`
  87. Introducer bool `xml:"introducer,attr"`
  88. }
  89. type RepositoryNodeConfiguration struct {
  90. NodeID protocol.NodeID `xml:"id,attr"`
  91. Deprecated_Name string `xml:"name,attr,omitempty" json:"-"`
  92. Deprecated_Addresses []string `xml:"address,omitempty" json:"-"`
  93. }
  94. type OptionsConfiguration struct {
  95. ListenAddress []string `xml:"listenAddress" default:"0.0.0.0:22000"`
  96. GlobalAnnServer string `xml:"globalAnnounceServer" default:"announce.syncthing.net:22026"`
  97. GlobalAnnEnabled bool `xml:"globalAnnounceEnabled" default:"true"`
  98. LocalAnnEnabled bool `xml:"localAnnounceEnabled" default:"true"`
  99. LocalAnnPort int `xml:"localAnnouncePort" default:"21025"`
  100. LocalAnnMCAddr string `xml:"localAnnounceMCAddr" default:"[ff32::5222]:21026"`
  101. ParallelRequests int `xml:"parallelRequests" default:"16"`
  102. MaxSendKbps int `xml:"maxSendKbps"`
  103. MaxRecvKbps int `xml:"maxRecvKbps"`
  104. ReconnectIntervalS int `xml:"reconnectionIntervalS" default:"60"`
  105. StartBrowser bool `xml:"startBrowser" default:"true"`
  106. UPnPEnabled bool `xml:"upnpEnabled" default:"true"`
  107. UPnPLease int `xml:"upnpLeaseMinutes" default:"0"`
  108. UPnPRenewal int `xml:"upnpRenewalMinutes" default:"30"`
  109. URAccepted int `xml:"urAccepted"` // Accepted usage reporting version; 0 for off (undecided), -1 for off (permanently)
  110. RestartOnWakeup bool `xml:"restartOnWakeup" default:"true"`
  111. Deprecated_RescanIntervalS int `xml:"rescanIntervalS,omitempty" json:"-"`
  112. Deprecated_UREnabled bool `xml:"urEnabled,omitempty" json:"-"`
  113. Deprecated_URDeclined bool `xml:"urDeclined,omitempty" json:"-"`
  114. Deprecated_ReadOnly bool `xml:"readOnly,omitempty" json:"-"`
  115. Deprecated_GUIEnabled bool `xml:"guiEnabled,omitempty" json:"-"`
  116. Deprecated_GUIAddress string `xml:"guiAddress,omitempty" json:"-"`
  117. }
  118. type GUIConfiguration struct {
  119. Enabled bool `xml:"enabled,attr" default:"true"`
  120. Address string `xml:"address" default:"127.0.0.1:8080"`
  121. User string `xml:"user,omitempty"`
  122. Password string `xml:"password,omitempty"`
  123. UseTLS bool `xml:"tls,attr"`
  124. APIKey string `xml:"apikey,omitempty"`
  125. }
  126. func (cfg *Configuration) NodeMap() map[protocol.NodeID]NodeConfiguration {
  127. m := make(map[protocol.NodeID]NodeConfiguration, len(cfg.Nodes))
  128. for _, n := range cfg.Nodes {
  129. m[n.NodeID] = n
  130. }
  131. return m
  132. }
  133. func (cfg *Configuration) GetNodeConfiguration(nodeID protocol.NodeID) *NodeConfiguration {
  134. for i, node := range cfg.Nodes {
  135. if node.NodeID == nodeID {
  136. return &cfg.Nodes[i]
  137. }
  138. }
  139. return nil
  140. }
  141. func (cfg *Configuration) GetRepoConfiguration(repoID string) *RepositoryConfiguration {
  142. for i, repo := range cfg.Repositories {
  143. if repo.ID == repoID {
  144. return &cfg.Repositories[i]
  145. }
  146. }
  147. return nil
  148. }
  149. func (cfg *Configuration) RepoMap() map[string]RepositoryConfiguration {
  150. m := make(map[string]RepositoryConfiguration, len(cfg.Repositories))
  151. for _, r := range cfg.Repositories {
  152. m[r.ID] = r
  153. }
  154. return m
  155. }
  156. func setDefaults(data interface{}) error {
  157. s := reflect.ValueOf(data).Elem()
  158. t := s.Type()
  159. for i := 0; i < s.NumField(); i++ {
  160. f := s.Field(i)
  161. tag := t.Field(i).Tag
  162. v := tag.Get("default")
  163. if len(v) > 0 {
  164. switch f.Interface().(type) {
  165. case string:
  166. f.SetString(v)
  167. case int:
  168. i, err := strconv.ParseInt(v, 10, 64)
  169. if err != nil {
  170. return err
  171. }
  172. f.SetInt(i)
  173. case bool:
  174. f.SetBool(v == "true")
  175. case []string:
  176. // We don't do anything with string slices here. Any default
  177. // we set will be appended to by the XML decoder, so we fill
  178. // those after decoding.
  179. default:
  180. panic(f.Type())
  181. }
  182. }
  183. }
  184. return nil
  185. }
  186. // fillNilSlices sets default value on slices that are still nil.
  187. func fillNilSlices(data interface{}) error {
  188. s := reflect.ValueOf(data).Elem()
  189. t := s.Type()
  190. for i := 0; i < s.NumField(); i++ {
  191. f := s.Field(i)
  192. tag := t.Field(i).Tag
  193. v := tag.Get("default")
  194. if len(v) > 0 {
  195. switch f.Interface().(type) {
  196. case []string:
  197. if f.IsNil() {
  198. rv := reflect.MakeSlice(reflect.TypeOf([]string{}), 1, 1)
  199. rv.Index(0).SetString(v)
  200. f.Set(rv)
  201. }
  202. }
  203. }
  204. }
  205. return nil
  206. }
  207. func (cfg *Configuration) Save() error {
  208. fd, err := os.Create(cfg.Location + ".tmp")
  209. if err != nil {
  210. l.Warnln("Saving config:", err)
  211. return err
  212. }
  213. e := xml.NewEncoder(fd)
  214. e.Indent("", " ")
  215. err = e.Encode(cfg)
  216. if err != nil {
  217. fd.Close()
  218. return err
  219. }
  220. _, err = fd.Write([]byte("\n"))
  221. if err != nil {
  222. l.Warnln("Saving config:", err)
  223. fd.Close()
  224. return err
  225. }
  226. err = fd.Close()
  227. if err != nil {
  228. l.Warnln("Saving config:", err)
  229. return err
  230. }
  231. err = osutil.Rename(cfg.Location+".tmp", cfg.Location)
  232. if err != nil {
  233. l.Warnln("Saving config:", err)
  234. }
  235. events.Default.Log(events.ConfigSaved, cfg)
  236. return err
  237. }
  238. func uniqueStrings(ss []string) []string {
  239. var m = make(map[string]bool, len(ss))
  240. for _, s := range ss {
  241. m[s] = true
  242. }
  243. var us = make([]string, 0, len(m))
  244. for k := range m {
  245. us = append(us, k)
  246. }
  247. return us
  248. }
  249. func (cfg *Configuration) prepare(myID protocol.NodeID) {
  250. fillNilSlices(&cfg.Options)
  251. cfg.Options.ListenAddress = uniqueStrings(cfg.Options.ListenAddress)
  252. // Initialize an empty slice for repositories if the config has none
  253. if cfg.Repositories == nil {
  254. cfg.Repositories = []RepositoryConfiguration{}
  255. }
  256. // Check for missing, bad or duplicate repository ID:s
  257. var seenRepos = map[string]*RepositoryConfiguration{}
  258. var uniqueCounter int
  259. for i := range cfg.Repositories {
  260. repo := &cfg.Repositories[i]
  261. if len(repo.Directory) == 0 {
  262. repo.Invalid = "no directory configured"
  263. continue
  264. }
  265. if repo.ID == "" {
  266. repo.ID = "default"
  267. }
  268. if seen, ok := seenRepos[repo.ID]; ok {
  269. l.Warnf("Multiple repositories with ID %q; disabling", repo.ID)
  270. seen.Invalid = "duplicate repository ID"
  271. if seen.ID == repo.ID {
  272. uniqueCounter++
  273. seen.ID = fmt.Sprintf("%s~%d", repo.ID, uniqueCounter)
  274. }
  275. repo.Invalid = "duplicate repository ID"
  276. uniqueCounter++
  277. repo.ID = fmt.Sprintf("%s~%d", repo.ID, uniqueCounter)
  278. } else {
  279. seenRepos[repo.ID] = repo
  280. }
  281. }
  282. if cfg.Options.Deprecated_URDeclined {
  283. cfg.Options.URAccepted = -1
  284. }
  285. cfg.Options.Deprecated_URDeclined = false
  286. cfg.Options.Deprecated_UREnabled = false
  287. // Upgrade to v2 configuration if appropriate
  288. if cfg.Version == 1 {
  289. convertV1V2(cfg)
  290. }
  291. // Upgrade to v3 configuration if appropriate
  292. if cfg.Version == 2 {
  293. convertV2V3(cfg)
  294. }
  295. // Upgrade to v4 configuration if appropriate
  296. if cfg.Version == 3 {
  297. convertV3V4(cfg)
  298. }
  299. // Hash old cleartext passwords
  300. if len(cfg.GUI.Password) > 0 && cfg.GUI.Password[0] != '$' {
  301. hash, err := bcrypt.GenerateFromPassword([]byte(cfg.GUI.Password), 0)
  302. if err != nil {
  303. l.Warnln("bcrypting password:", err)
  304. } else {
  305. cfg.GUI.Password = string(hash)
  306. }
  307. }
  308. // Build a list of available nodes
  309. existingNodes := make(map[protocol.NodeID]bool)
  310. existingNodes[myID] = true
  311. for _, node := range cfg.Nodes {
  312. existingNodes[node.NodeID] = true
  313. }
  314. // Ensure this node is present in all relevant places
  315. me := cfg.GetNodeConfiguration(myID)
  316. if me == nil {
  317. myName, _ := os.Hostname()
  318. cfg.Nodes = append(cfg.Nodes, NodeConfiguration{
  319. NodeID: myID,
  320. Name: myName,
  321. })
  322. }
  323. sort.Sort(NodeConfigurationList(cfg.Nodes))
  324. // Ensure that any loose nodes are not present in the wrong places
  325. // Ensure that there are no duplicate nodes
  326. for i := range cfg.Repositories {
  327. cfg.Repositories[i].Nodes = ensureNodePresent(cfg.Repositories[i].Nodes, myID)
  328. cfg.Repositories[i].Nodes = ensureExistingNodes(cfg.Repositories[i].Nodes, existingNodes)
  329. cfg.Repositories[i].Nodes = ensureNoDuplicates(cfg.Repositories[i].Nodes)
  330. sort.Sort(RepositoryNodeConfigurationList(cfg.Repositories[i].Nodes))
  331. }
  332. // An empty address list is equivalent to a single "dynamic" entry
  333. for i := range cfg.Nodes {
  334. n := &cfg.Nodes[i]
  335. if len(n.Addresses) == 0 || len(n.Addresses) == 1 && n.Addresses[0] == "" {
  336. n.Addresses = []string{"dynamic"}
  337. }
  338. }
  339. }
  340. func New(location string, myID protocol.NodeID) Configuration {
  341. var cfg Configuration
  342. cfg.Location = location
  343. setDefaults(&cfg)
  344. setDefaults(&cfg.Options)
  345. setDefaults(&cfg.GUI)
  346. cfg.prepare(myID)
  347. return cfg
  348. }
  349. func Load(location string, myID protocol.NodeID) (Configuration, error) {
  350. var cfg Configuration
  351. cfg.Location = location
  352. setDefaults(&cfg)
  353. setDefaults(&cfg.Options)
  354. setDefaults(&cfg.GUI)
  355. fd, err := os.Open(location)
  356. if err != nil {
  357. return Configuration{}, err
  358. }
  359. err = xml.NewDecoder(fd).Decode(&cfg)
  360. fd.Close()
  361. cfg.prepare(myID)
  362. return cfg, err
  363. }
  364. // ChangeRequiresRestart returns true if updating the configuration requires a
  365. // complete restart.
  366. func ChangeRequiresRestart(from, to Configuration) bool {
  367. // Adding, removing or changing repos requires restart
  368. if len(from.Repositories) != len(to.Repositories) {
  369. return true
  370. }
  371. fromRepos := from.RepoMap()
  372. toRepos := to.RepoMap()
  373. for id := range fromRepos {
  374. if !reflect.DeepEqual(fromRepos[id], toRepos[id]) {
  375. return true
  376. }
  377. }
  378. // Removing a node requires a restart. Adding one does not. Changing
  379. // address or name does not.
  380. fromNodes := from.NodeMap()
  381. toNodes := to.NodeMap()
  382. for nodeID := range fromNodes {
  383. if _, ok := toNodes[nodeID]; !ok {
  384. return true
  385. }
  386. }
  387. // All of the generic options require restart
  388. if !reflect.DeepEqual(from.Options, to.Options) || !reflect.DeepEqual(from.GUI, to.GUI) {
  389. return true
  390. }
  391. return false
  392. }
  393. func convertV3V4(cfg *Configuration) {
  394. // In previous versions, rescan interval was common for each repository.
  395. // From now, it can be set independently. We have to make sure, that after upgrade
  396. // the individual rescan interval will be defined for every existing repository.
  397. for i := range cfg.Repositories {
  398. cfg.Repositories[i].RescanIntervalS = cfg.Options.Deprecated_RescanIntervalS
  399. }
  400. cfg.Options.Deprecated_RescanIntervalS = 0
  401. // In previous versions, repositories held full node configurations.
  402. // Since that's the only place where node configs were in V1, we still have
  403. // to define the deprecated fields to be able to upgrade from V1 to V4.
  404. for i, repo := range cfg.Repositories {
  405. for j := range repo.Nodes {
  406. rncfg := cfg.Repositories[i].Nodes[j]
  407. rncfg.Deprecated_Name = ""
  408. rncfg.Deprecated_Addresses = nil
  409. }
  410. }
  411. cfg.Version = 4
  412. }
  413. func convertV2V3(cfg *Configuration) {
  414. // In previous versions, compression was always on. When upgrading, enable
  415. // compression on all existing new. New nodes will get compression on by
  416. // default by the GUI.
  417. for i := range cfg.Nodes {
  418. cfg.Nodes[i].Compression = true
  419. }
  420. // The global discovery format and port number changed in v0.9. Having the
  421. // default announce server but old port number is guaranteed to be legacy.
  422. if cfg.Options.GlobalAnnServer == "announce.syncthing.net:22025" {
  423. cfg.Options.GlobalAnnServer = "announce.syncthing.net:22026"
  424. }
  425. cfg.Version = 3
  426. }
  427. func convertV1V2(cfg *Configuration) {
  428. // Collect the list of nodes.
  429. // Replace node configs inside repositories with only a reference to the nide ID.
  430. // Set all repositories to read only if the global read only flag is set.
  431. var nodes = map[string]RepositoryNodeConfiguration{}
  432. for i, repo := range cfg.Repositories {
  433. cfg.Repositories[i].ReadOnly = cfg.Options.Deprecated_ReadOnly
  434. for j, node := range repo.Nodes {
  435. id := node.NodeID.String()
  436. if _, ok := nodes[id]; !ok {
  437. nodes[id] = node
  438. }
  439. cfg.Repositories[i].Nodes[j] = RepositoryNodeConfiguration{NodeID: node.NodeID}
  440. }
  441. }
  442. cfg.Options.Deprecated_ReadOnly = false
  443. // Set and sort the list of nodes.
  444. for _, node := range nodes {
  445. cfg.Nodes = append(cfg.Nodes, NodeConfiguration{
  446. NodeID: node.NodeID,
  447. Name: node.Deprecated_Name,
  448. Addresses: node.Deprecated_Addresses,
  449. })
  450. }
  451. sort.Sort(NodeConfigurationList(cfg.Nodes))
  452. // GUI
  453. cfg.GUI.Address = cfg.Options.Deprecated_GUIAddress
  454. cfg.GUI.Enabled = cfg.Options.Deprecated_GUIEnabled
  455. cfg.Options.Deprecated_GUIEnabled = false
  456. cfg.Options.Deprecated_GUIAddress = ""
  457. cfg.Version = 2
  458. }
  459. type NodeConfigurationList []NodeConfiguration
  460. func (l NodeConfigurationList) Less(a, b int) bool {
  461. return l[a].NodeID.Compare(l[b].NodeID) == -1
  462. }
  463. func (l NodeConfigurationList) Swap(a, b int) {
  464. l[a], l[b] = l[b], l[a]
  465. }
  466. func (l NodeConfigurationList) Len() int {
  467. return len(l)
  468. }
  469. type RepositoryNodeConfigurationList []RepositoryNodeConfiguration
  470. func (l RepositoryNodeConfigurationList) Less(a, b int) bool {
  471. return l[a].NodeID.Compare(l[b].NodeID) == -1
  472. }
  473. func (l RepositoryNodeConfigurationList) Swap(a, b int) {
  474. l[a], l[b] = l[b], l[a]
  475. }
  476. func (l RepositoryNodeConfigurationList) Len() int {
  477. return len(l)
  478. }
  479. func ensureNodePresent(nodes []RepositoryNodeConfiguration, myID protocol.NodeID) []RepositoryNodeConfiguration {
  480. for _, node := range nodes {
  481. if node.NodeID.Equals(myID) {
  482. return nodes
  483. }
  484. }
  485. nodes = append(nodes, RepositoryNodeConfiguration{
  486. NodeID: myID,
  487. })
  488. return nodes
  489. }
  490. func ensureExistingNodes(nodes []RepositoryNodeConfiguration, existingNodes map[protocol.NodeID]bool) []RepositoryNodeConfiguration {
  491. count := len(nodes)
  492. i := 0
  493. loop:
  494. for i < count {
  495. if _, ok := existingNodes[nodes[i].NodeID]; !ok {
  496. nodes[i] = nodes[count-1]
  497. count--
  498. continue loop
  499. }
  500. i++
  501. }
  502. return nodes[0:count]
  503. }
  504. func ensureNoDuplicates(nodes []RepositoryNodeConfiguration) []RepositoryNodeConfiguration {
  505. count := len(nodes)
  506. i := 0
  507. seenNodes := make(map[protocol.NodeID]bool)
  508. loop:
  509. for i < count {
  510. id := nodes[i].NodeID
  511. if _, ok := seenNodes[id]; ok {
  512. nodes[i] = nodes[count-1]
  513. count--
  514. continue loop
  515. }
  516. seenNodes[id] = true
  517. i++
  518. }
  519. return nodes[0:count]
  520. }