main.go 6.5 KB

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