main.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Copyright (C) 2018 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 main
  7. import (
  8. "crypto/tls"
  9. "flag"
  10. "log"
  11. "net"
  12. "net/http"
  13. "os"
  14. "strings"
  15. "time"
  16. "github.com/prometheus/client_golang/prometheus/promhttp"
  17. "github.com/syncthing/syncthing/lib/build"
  18. "github.com/syncthing/syncthing/lib/protocol"
  19. "github.com/syncthing/syncthing/lib/tlsutil"
  20. "github.com/syndtr/goleveldb/leveldb/opt"
  21. "github.com/thejerf/suture"
  22. )
  23. const (
  24. addressExpiryTime = 2 * time.Hour
  25. databaseStatisticsInterval = 5 * time.Minute
  26. // Reannounce-After is set to reannounceAfterSeconds +
  27. // random(reannounzeFuzzSeconds), similar for Retry-After
  28. reannounceAfterSeconds = 3300
  29. reannounzeFuzzSeconds = 300
  30. errorRetryAfterSeconds = 1500
  31. errorRetryFuzzSeconds = 300
  32. // Retry for not found is minSeconds + failures * incSeconds +
  33. // random(fuzz), where failures is the number of consecutive lookups
  34. // with no answer, up to maxSeconds. The fuzz is applied after capping
  35. // to maxSeconds.
  36. notFoundRetryMinSeconds = 60
  37. notFoundRetryMaxSeconds = 3540
  38. notFoundRetryIncSeconds = 10
  39. notFoundRetryFuzzSeconds = 60
  40. // How often (in requests) we serialize the missed counter to database.
  41. notFoundMissesWriteInterval = 10
  42. httpReadTimeout = 5 * time.Second
  43. httpWriteTimeout = 5 * time.Second
  44. httpMaxHeaderBytes = 1 << 10
  45. // Size of the replication outbox channel
  46. replicationOutboxSize = 10000
  47. )
  48. // These options make the database a little more optimized for writes, at
  49. // the expense of some memory usage and risk of losing writes in a (system)
  50. // crash.
  51. var levelDBOptions = &opt.Options{
  52. NoSync: true,
  53. WriteBuffer: 32 << 20, // default 4<<20
  54. }
  55. var (
  56. debug = false
  57. )
  58. func main() {
  59. var listen string
  60. var dir string
  61. var metricsListen string
  62. var replicationListen string
  63. var replicationPeers string
  64. var certFile string
  65. var keyFile string
  66. var useHTTP bool
  67. log.SetOutput(os.Stdout)
  68. log.SetFlags(0)
  69. flag.StringVar(&certFile, "cert", "./cert.pem", "Certificate file")
  70. flag.StringVar(&dir, "db-dir", "./discovery.db", "Database directory")
  71. flag.BoolVar(&debug, "debug", false, "Print debug output")
  72. flag.BoolVar(&useHTTP, "http", false, "Listen on HTTP (behind an HTTPS proxy)")
  73. flag.StringVar(&listen, "listen", ":8443", "Listen address")
  74. flag.StringVar(&keyFile, "key", "./key.pem", "Key file")
  75. flag.StringVar(&metricsListen, "metrics-listen", "", "Metrics listen address")
  76. flag.StringVar(&replicationPeers, "replicate", "", "Replication peers, id@address, comma separated")
  77. flag.StringVar(&replicationListen, "replication-listen", ":19200", "Replication listen address")
  78. showVersion := flag.Bool("version", false, "Show version")
  79. flag.Parse()
  80. log.Println(build.LongVersion)
  81. if *showVersion {
  82. return
  83. }
  84. cert, err := tls.LoadX509KeyPair(certFile, keyFile)
  85. if os.IsNotExist(err) {
  86. log.Println("Failed to load keypair. Generating one, this might take a while...")
  87. cert, err = tlsutil.NewCertificate(certFile, keyFile, "stdiscosrv", 20*365)
  88. if err != nil {
  89. log.Fatalln("Failed to generate X509 key pair:", err)
  90. }
  91. } else if err != nil {
  92. log.Fatalln("Failed to load keypair:", err)
  93. }
  94. devID := protocol.NewDeviceID(cert.Certificate[0])
  95. log.Println("Server device ID is", devID)
  96. // Parse the replication specs, if any.
  97. var allowedReplicationPeers []protocol.DeviceID
  98. var replicationDestinations []string
  99. parts := strings.Split(replicationPeers, ",")
  100. for _, part := range parts {
  101. fields := strings.Split(part, "@")
  102. switch len(fields) {
  103. case 2:
  104. // This is an id@address specification. Grab the address for the
  105. // destination list. Try to resolve it once to catch obvious
  106. // syntax errors here rather than having the sender service fail
  107. // repeatedly later.
  108. _, err := net.ResolveTCPAddr("tcp", fields[1])
  109. if err != nil {
  110. log.Fatalln("Resolving address:", err)
  111. }
  112. replicationDestinations = append(replicationDestinations, fields[1])
  113. fallthrough // N.B.
  114. case 1:
  115. // The first part is always a device ID.
  116. id, err := protocol.DeviceIDFromString(fields[0])
  117. if err != nil {
  118. log.Fatalln("Parsing device ID:", err)
  119. }
  120. allowedReplicationPeers = append(allowedReplicationPeers, id)
  121. default:
  122. log.Fatalln("Unrecognized replication spec:", part)
  123. }
  124. }
  125. // Root of the service tree.
  126. main := suture.New("main", suture.Spec{
  127. PassThroughPanics: true,
  128. })
  129. // Start the database.
  130. db, err := newLevelDBStore(dir)
  131. if err != nil {
  132. log.Fatalln("Open database:", err)
  133. }
  134. main.Add(db)
  135. // Start any replication senders.
  136. var repl replicationMultiplexer
  137. for _, dst := range replicationDestinations {
  138. rs := newReplicationSender(dst, cert, allowedReplicationPeers)
  139. main.Add(rs)
  140. repl = append(repl, rs)
  141. }
  142. // If we have replication configured, start the replication listener.
  143. if len(allowedReplicationPeers) > 0 {
  144. rl := newReplicationListener(replicationListen, cert, allowedReplicationPeers, db)
  145. main.Add(rl)
  146. }
  147. // Start the main API server.
  148. qs := newAPISrv(listen, cert, db, repl, useHTTP)
  149. main.Add(qs)
  150. // If we have a metrics port configured, start a metrics handler.
  151. if metricsListen != "" {
  152. go func() {
  153. mux := http.NewServeMux()
  154. mux.Handle("/metrics", promhttp.Handler())
  155. log.Fatal(http.ListenAndServe(metricsListen, mux))
  156. }()
  157. }
  158. // Engage!
  159. main.Serve()
  160. }