main.go 6.6 KB

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