main.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. "log"
  11. "net/http"
  12. "os"
  13. "os/signal"
  14. "runtime"
  15. "time"
  16. _ "net/http/pprof"
  17. "github.com/alecthomas/kong"
  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/rand"
  23. "github.com/syncthing/syncthing/lib/tlsutil"
  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 notFoundRetrySeenSeconds for records we have
  36. // seen an announcement for (but it's not active right now) and
  37. // notFoundRetryUnknownSeconds for records we have never seen (or not
  38. // seen within the last week).
  39. notFoundRetryUnknownMinSeconds = 60
  40. notFoundRetryUnknownMaxSeconds = 3600
  41. httpReadTimeout = 5 * time.Second
  42. httpWriteTimeout = 5 * time.Second
  43. httpMaxHeaderBytes = 1 << 10
  44. // Size of the replication outbox channel
  45. replicationOutboxSize = 10000
  46. )
  47. var debug = false
  48. type CLI struct {
  49. Cert string `group:"Listen" help:"Certificate file" default:"./cert.pem" env:"DISCOVERY_CERT_FILE"`
  50. Key string `group:"Listen" help:"Key file" default:"./key.pem" env:"DISCOVERY_KEY_FILE"`
  51. HTTP bool `group:"Listen" help:"Listen on HTTP (behind an HTTPS proxy)" env:"DISCOVERY_HTTP"`
  52. Compression bool `group:"Listen" help:"Enable GZIP compression of responses" env:"DISCOVERY_COMPRESSION"`
  53. Listen string `group:"Listen" help:"Listen address" default:":8443" env:"DISCOVERY_LISTEN"`
  54. MetricsListen string `group:"Listen" help:"Metrics listen address" env:"DISCOVERY_METRICS_LISTEN"`
  55. DBDir string `group:"Database" help:"Database directory" default:"." env:"DISCOVERY_DB_DIR"`
  56. DBFlushInterval time.Duration `group:"Database" help:"Interval between database flushes" default:"5m" env:"DISCOVERY_DB_FLUSH_INTERVAL"`
  57. DBS3Endpoint string `name:"db-s3-endpoint" group:"Database (S3 backup)" hidden:"true" help:"S3 endpoint for database" env:"DISCOVERY_DB_S3_ENDPOINT"`
  58. DBS3Region string `name:"db-s3-region" group:"Database (S3 backup)" hidden:"true" help:"S3 region for database" env:"DISCOVERY_DB_S3_REGION"`
  59. DBS3Bucket string `name:"db-s3-bucket" group:"Database (S3 backup)" hidden:"true" help:"S3 bucket for database" env:"DISCOVERY_DB_S3_BUCKET"`
  60. DBS3AccessKeyID string `name:"db-s3-access-key-id" group:"Database (S3 backup)" hidden:"true" help:"S3 access key ID for database" env:"DISCOVERY_DB_S3_ACCESS_KEY_ID"`
  61. DBS3SecretKey string `name:"db-s3-secret-key" group:"Database (S3 backup)" hidden:"true" help:"S3 secret key for database" env:"DISCOVERY_DB_S3_SECRET_KEY"`
  62. AMQPAddress string `group:"AMQP replication" hidden:"true" help:"Address to AMQP broker" env:"DISCOVERY_AMQP_ADDRESS"`
  63. Debug bool `short:"d" help:"Print debug output" env:"DISCOVERY_DEBUG"`
  64. Version bool `short:"v" help:"Print version and exit"`
  65. }
  66. func main() {
  67. log.SetOutput(os.Stdout)
  68. var cli CLI
  69. kong.Parse(&cli)
  70. debug = cli.Debug
  71. log.Println(build.LongVersionFor("stdiscosrv"))
  72. if cli.Version {
  73. return
  74. }
  75. buildInfo.WithLabelValues(build.Version, runtime.Version(), build.User, build.Date.UTC().Format("2006-01-02T15:04:05Z")).Set(1)
  76. var cert tls.Certificate
  77. if !cli.HTTP {
  78. var err error
  79. cert, err = tls.LoadX509KeyPair(cli.Cert, cli.Key)
  80. if os.IsNotExist(err) {
  81. log.Println("Failed to load keypair. Generating one, this might take a while...")
  82. cert, err = tlsutil.NewCertificate(cli.Cert, cli.Key, "stdiscosrv", 20*365)
  83. if err != nil {
  84. log.Fatalln("Failed to generate X509 key pair:", err)
  85. }
  86. } else if err != nil {
  87. log.Fatalln("Failed to load keypair:", err)
  88. }
  89. devID := protocol.NewDeviceID(cert.Certificate[0])
  90. log.Println("Server device ID is", devID)
  91. }
  92. // Root of the service tree.
  93. main := suture.New("main", suture.Spec{
  94. PassThroughPanics: true,
  95. Timeout: 2 * time.Minute,
  96. })
  97. // If configured, use S3 for database backups.
  98. var s3c *s3Copier
  99. if cli.DBS3Endpoint != "" {
  100. hostname, err := os.Hostname()
  101. if err != nil {
  102. log.Fatalf("Failed to get hostname: %v", err)
  103. }
  104. key := hostname + ".db"
  105. s3c = newS3Copier(cli.DBS3Endpoint, cli.DBS3Region, cli.DBS3Bucket, key, cli.DBS3AccessKeyID, cli.DBS3SecretKey)
  106. }
  107. // Start the database.
  108. db := newInMemoryStore(cli.DBDir, cli.DBFlushInterval, s3c)
  109. main.Add(db)
  110. // If we have an AMQP broker for replication, start that
  111. var repl replicator
  112. if cli.AMQPAddress != "" {
  113. clientID := rand.String(10)
  114. kr := newAMQPReplicator(cli.AMQPAddress, clientID, db)
  115. main.Add(kr)
  116. repl = kr
  117. }
  118. // Start the main API server.
  119. qs := newAPISrv(cli.Listen, cert, db, repl, cli.HTTP, cli.Compression)
  120. main.Add(qs)
  121. // If we have a metrics port configured, start a metrics handler.
  122. if cli.MetricsListen != "" {
  123. go func() {
  124. mux := http.NewServeMux()
  125. mux.Handle("/metrics", promhttp.Handler())
  126. log.Fatal(http.ListenAndServe(cli.MetricsListen, mux))
  127. }()
  128. }
  129. ctx, cancel := context.WithCancel(context.Background())
  130. defer cancel()
  131. // Cancel on signal
  132. signalChan := make(chan os.Signal, 1)
  133. signal.Notify(signalChan, os.Interrupt)
  134. go func() {
  135. sig := <-signalChan
  136. log.Printf("Received signal %s; shutting down", sig)
  137. cancel()
  138. }()
  139. // Engage!
  140. main.Serve(ctx)
  141. }