main.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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/calmh/suture"
  20. "github.com/prometheus/client_golang/prometheus/promhttp"
  21. "github.com/syncthing/syncthing/lib/protocol"
  22. "github.com/syncthing/syncthing/lib/tlsutil"
  23. "github.com/syndtr/goleveldb/leveldb/opt"
  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. const (
  76. cleanIntv = 1 * time.Hour
  77. statsIntv = 5 * time.Minute
  78. )
  79. var listen string
  80. var dir string
  81. var metricsListen string
  82. var replicationListen string
  83. var replicationPeers string
  84. var certFile string
  85. var keyFile string
  86. var useHTTP bool
  87. log.SetOutput(os.Stdout)
  88. log.SetFlags(0)
  89. flag.StringVar(&certFile, "cert", "./cert.pem", "Certificate file")
  90. flag.StringVar(&dir, "db-dir", "./discovery.db", "Database directory")
  91. flag.BoolVar(&debug, "debug", false, "Print debug output")
  92. flag.BoolVar(&useHTTP, "http", false, "Listen on HTTP (behind an HTTPS proxy)")
  93. flag.StringVar(&listen, "listen", ":8443", "Listen address")
  94. flag.StringVar(&keyFile, "key", "./key.pem", "Key file")
  95. flag.StringVar(&metricsListen, "metrics-listen", "", "Metrics listen address")
  96. flag.StringVar(&replicationPeers, "replicate", "", "Replication peers, id@address, comma separated")
  97. flag.StringVar(&replicationListen, "replication-listen", ":19200", "Replication listen address")
  98. flag.Parse()
  99. log.Println(LongVersion)
  100. cert, err := tls.LoadX509KeyPair(certFile, keyFile)
  101. if err != nil {
  102. log.Println("Failed to load keypair. Generating one, this might take a while...")
  103. cert, err = tlsutil.NewCertificate(certFile, keyFile, "stdiscosrv", 0)
  104. if err != nil {
  105. log.Fatalln("Failed to generate X509 key pair:", err)
  106. }
  107. }
  108. devID := protocol.NewDeviceID(cert.Certificate[0])
  109. log.Println("Server device ID is", devID)
  110. // Parse the replication specs, if any.
  111. var allowedReplicationPeers []protocol.DeviceID
  112. var replicationDestinations []string
  113. parts := strings.Split(replicationPeers, ",")
  114. for _, part := range parts {
  115. fields := strings.Split(part, "@")
  116. switch len(fields) {
  117. case 2:
  118. // This is an id@address specification. Grab the address for the
  119. // destination list. Try to resolve it once to catch obvious
  120. // syntax errors here rather than having the sender service fail
  121. // repeatedly later.
  122. _, err := net.ResolveTCPAddr("tcp", fields[1])
  123. if err != nil {
  124. log.Fatalln("Resolving address:", err)
  125. }
  126. replicationDestinations = append(replicationDestinations, fields[1])
  127. fallthrough // N.B.
  128. case 1:
  129. // The first part is always a device ID.
  130. id, err := protocol.DeviceIDFromString(fields[0])
  131. if err != nil {
  132. log.Fatalln("Parsing device ID:", err)
  133. }
  134. allowedReplicationPeers = append(allowedReplicationPeers, id)
  135. default:
  136. log.Fatalln("Unrecognized replication spec:", part)
  137. }
  138. }
  139. // Root of the service tree.
  140. main := suture.New("main", suture.Spec{
  141. PanicPanics: true,
  142. })
  143. // Start the database.
  144. db, err := newLevelDBStore(dir)
  145. if err != nil {
  146. log.Fatalln("Open database:", err)
  147. }
  148. main.Add(db)
  149. // Start any replication senders.
  150. var repl replicationMultiplexer
  151. for _, dst := range replicationDestinations {
  152. rs := newReplicationSender(dst, cert, allowedReplicationPeers)
  153. main.Add(rs)
  154. repl = append(repl, rs)
  155. }
  156. // If we have replication configured, start the replication listener.
  157. if len(allowedReplicationPeers) > 0 {
  158. rl := newReplicationListener(replicationListen, cert, allowedReplicationPeers, db)
  159. main.Add(rl)
  160. }
  161. // Start the main API server.
  162. qs := newAPISrv(listen, cert, db, repl, useHTTP)
  163. main.Add(qs)
  164. // If we have a metrics port configured, start a metrics handler.
  165. if metricsListen != "" {
  166. go func() {
  167. mux := http.NewServeMux()
  168. mux.Handle("/metrics", promhttp.Handler())
  169. log.Fatal(http.ListenAndServe(metricsListen, mux))
  170. }()
  171. }
  172. // Engage!
  173. main.Serve()
  174. }