main.go 5.6 KB

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