main.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. // Copyright (C) 2014 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. "bytes"
  9. "crypto/tls"
  10. "flag"
  11. "fmt"
  12. "io"
  13. "io/ioutil"
  14. "log"
  15. "net/http"
  16. _ "net/http/pprof" // Need to import this to support STPROFILER.
  17. "net/url"
  18. "os"
  19. "os/signal"
  20. "path"
  21. "path/filepath"
  22. "runtime"
  23. "runtime/pprof"
  24. "sort"
  25. "strconv"
  26. "syscall"
  27. "time"
  28. "github.com/syncthing/syncthing/lib/build"
  29. "github.com/syncthing/syncthing/lib/config"
  30. "github.com/syncthing/syncthing/lib/dialer"
  31. "github.com/syncthing/syncthing/lib/events"
  32. "github.com/syncthing/syncthing/lib/fs"
  33. "github.com/syncthing/syncthing/lib/locations"
  34. "github.com/syncthing/syncthing/lib/logger"
  35. "github.com/syncthing/syncthing/lib/osutil"
  36. "github.com/syncthing/syncthing/lib/protocol"
  37. "github.com/syncthing/syncthing/lib/syncthing"
  38. "github.com/syncthing/syncthing/lib/tlsutil"
  39. "github.com/syncthing/syncthing/lib/upgrade"
  40. "github.com/pkg/errors"
  41. )
  42. const (
  43. exitSuccess = 0
  44. exitError = 1
  45. exitNoUpgradeAvailable = 2
  46. exitRestarting = 3
  47. exitUpgrading = 4
  48. )
  49. const (
  50. bepProtocolName = "bep/1.0"
  51. tlsDefaultCommonName = "syncthing"
  52. maxSystemErrors = 5
  53. initialSystemLog = 10
  54. maxSystemLog = 250
  55. )
  56. const (
  57. usage = "syncthing [options]"
  58. extraUsage = `
  59. The -logflags value is a sum of the following:
  60. 1 Date
  61. 2 Time
  62. 4 Microsecond time
  63. 8 Long filename
  64. 16 Short filename
  65. I.e. to prefix each log line with date and time, set -logflags=3 (1 + 2 from
  66. above). The value 0 is used to disable all of the above. The default is to
  67. show time only (2).
  68. Development Settings
  69. --------------------
  70. The following environment variables modify Syncthing's behavior in ways that
  71. are mostly useful for developers. Use with care.
  72. STNODEFAULTFOLDER Don't create a default folder when starting for the first
  73. time. This variable will be ignored anytime after the first
  74. run.
  75. STGUIASSETS Directory to load GUI assets from. Overrides compiled in
  76. assets.
  77. STTRACE A comma separated string of facilities to trace. The valid
  78. facility strings listed below.
  79. STPROFILER Set to a listen address such as "127.0.0.1:9090" to start
  80. the profiler with HTTP access.
  81. STCPUPROFILE Write a CPU profile to cpu-$pid.pprof on exit.
  82. STHEAPPROFILE Write heap profiles to heap-$pid-$timestamp.pprof each time
  83. heap usage increases.
  84. STBLOCKPROFILE Write block profiles to block-$pid-$timestamp.pprof every 20
  85. seconds.
  86. STPERFSTATS Write running performance statistics to perf-$pid.csv. Not
  87. supported on Windows.
  88. STDEADLOCKTIMEOUT Used for debugging internal deadlocks; sets debug
  89. sensitivity. Use only under direction of a developer.
  90. STLOCKTHRESHOLD Used for debugging internal deadlocks; sets debug
  91. sensitivity. Use only under direction of a developer.
  92. STNORESTART Equivalent to the -no-restart argument. Disable the
  93. Syncthing monitor process which handles restarts for some
  94. configuration changes, upgrades, crashes and also log file
  95. writing (stdout is still written).
  96. STNOUPGRADE Disable automatic upgrades.
  97. STHASHING Select the SHA256 hashing package to use. Possible values
  98. are "standard" for the Go standard library implementation,
  99. "minio" for the github.com/minio/sha256-simd implementation,
  100. and blank (the default) for auto detection.
  101. STRECHECKDBEVERY Set to a time interval to override the default database
  102. check interval of 30 days (720h). The interval understands
  103. "h", "m" and "s" abbreviations for hours minutes and seconds.
  104. Valid values are like "720h", "30s", etc.
  105. GOMAXPROCS Set the maximum number of CPU cores to use. Defaults to all
  106. available CPU cores.
  107. GOGC Percentage of heap growth at which to trigger GC. Default is
  108. 100. Lower numbers keep peak memory usage down, at the price
  109. of CPU usage (i.e. performance).
  110. Debugging Facilities
  111. --------------------
  112. The following are valid values for the STTRACE variable:
  113. %s`
  114. )
  115. // Environment options
  116. var (
  117. innerProcess = os.Getenv("STNORESTART") != "" || os.Getenv("STMONITORED") != ""
  118. noDefaultFolder = os.Getenv("STNODEFAULTFOLDER") != ""
  119. )
  120. type RuntimeOptions struct {
  121. syncthing.Options
  122. confDir string
  123. resetDatabase bool
  124. showVersion bool
  125. showPaths bool
  126. showDeviceId bool
  127. doUpgrade bool
  128. doUpgradeCheck bool
  129. upgradeTo string
  130. noBrowser bool
  131. browserOnly bool
  132. hideConsole bool
  133. logFile string
  134. auditEnabled bool
  135. auditFile string
  136. paused bool
  137. unpaused bool
  138. guiAddress string
  139. guiAPIKey string
  140. generateDir string
  141. noRestart bool
  142. cpuProfile bool
  143. stRestarting bool
  144. logFlags int
  145. showHelp bool
  146. allowNewerConfig bool
  147. }
  148. func defaultRuntimeOptions() RuntimeOptions {
  149. options := RuntimeOptions{
  150. Options: syncthing.Options{
  151. AssetDir: os.Getenv("STGUIASSETS"),
  152. NoUpgrade: os.Getenv("STNOUPGRADE") != "",
  153. ProfilerURL: os.Getenv("STPROFILER"),
  154. },
  155. noRestart: os.Getenv("STNORESTART") != "",
  156. cpuProfile: os.Getenv("STCPUPROFILE") != "",
  157. stRestarting: os.Getenv("STRESTART") != "",
  158. logFlags: log.Ltime,
  159. }
  160. if os.Getenv("STTRACE") != "" {
  161. options.logFlags = logger.DebugFlags
  162. }
  163. if runtime.GOOS != "windows" {
  164. // On non-Windows, we explicitly default to "-" which means stdout. On
  165. // Windows, the blank options.logFile will later be replaced with the
  166. // default path, unless the user has manually specified "-" or
  167. // something else.
  168. options.logFile = "-"
  169. }
  170. return options
  171. }
  172. func parseCommandLineOptions() RuntimeOptions {
  173. options := defaultRuntimeOptions()
  174. flag.StringVar(&options.generateDir, "generate", "", "Generate key and config in specified dir, then exit")
  175. flag.StringVar(&options.guiAddress, "gui-address", options.guiAddress, "Override GUI address (e.g. \"http://192.0.2.42:8443\")")
  176. flag.StringVar(&options.guiAPIKey, "gui-apikey", options.guiAPIKey, "Override GUI API key")
  177. flag.StringVar(&options.confDir, "home", "", "Set configuration directory")
  178. flag.IntVar(&options.logFlags, "logflags", options.logFlags, "Select information in log line prefix (see below)")
  179. flag.BoolVar(&options.noBrowser, "no-browser", false, "Do not start browser")
  180. flag.BoolVar(&options.browserOnly, "browser-only", false, "Open GUI in browser")
  181. flag.BoolVar(&options.noRestart, "no-restart", options.noRestart, "Disable monitor process, managed restarts and log file writing")
  182. flag.BoolVar(&options.resetDatabase, "reset-database", false, "Reset the database, forcing a full rescan and resync")
  183. flag.BoolVar(&options.ResetDeltaIdxs, "reset-deltas", false, "Reset delta index IDs, forcing a full index exchange")
  184. flag.BoolVar(&options.doUpgrade, "upgrade", false, "Perform upgrade")
  185. flag.BoolVar(&options.doUpgradeCheck, "upgrade-check", false, "Check for available upgrade")
  186. flag.BoolVar(&options.showVersion, "version", false, "Show version")
  187. flag.BoolVar(&options.showHelp, "help", false, "Show this help")
  188. flag.BoolVar(&options.showPaths, "paths", false, "Show configuration paths")
  189. flag.BoolVar(&options.showDeviceId, "device-id", false, "Show the device ID")
  190. flag.StringVar(&options.upgradeTo, "upgrade-to", options.upgradeTo, "Force upgrade directly from specified URL")
  191. flag.BoolVar(&options.auditEnabled, "audit", false, "Write events to audit file")
  192. flag.BoolVar(&options.Verbose, "verbose", false, "Print verbose log output")
  193. flag.BoolVar(&options.paused, "paused", false, "Start with all devices and folders paused")
  194. flag.BoolVar(&options.unpaused, "unpaused", false, "Start with all devices and folders unpaused")
  195. flag.StringVar(&options.logFile, "logfile", options.logFile, "Log file name (still always logs to stdout). Cannot be used together with -no-restart/STNORESTART environment variable.")
  196. flag.StringVar(&options.auditFile, "auditfile", options.auditFile, "Specify audit file (use \"-\" for stdout, \"--\" for stderr)")
  197. flag.BoolVar(&options.allowNewerConfig, "allow-newer-config", false, "Allow loading newer than current config version")
  198. if runtime.GOOS == "windows" {
  199. // Allow user to hide the console window
  200. flag.BoolVar(&options.hideConsole, "no-console", false, "Hide console window")
  201. }
  202. longUsage := fmt.Sprintf(extraUsage, debugFacilities())
  203. flag.Usage = usageFor(flag.CommandLine, usage, longUsage)
  204. flag.Parse()
  205. if len(flag.Args()) > 0 {
  206. flag.Usage()
  207. os.Exit(2)
  208. }
  209. return options
  210. }
  211. func main() {
  212. options := parseCommandLineOptions()
  213. l.SetFlags(options.logFlags)
  214. if options.guiAddress != "" {
  215. // The config picks this up from the environment.
  216. os.Setenv("STGUIADDRESS", options.guiAddress)
  217. }
  218. if options.guiAPIKey != "" {
  219. // The config picks this up from the environment.
  220. os.Setenv("STGUIAPIKEY", options.guiAPIKey)
  221. }
  222. // Check for options which are not compatible with each other. We have
  223. // to check logfile before it's set to the default below - we only want
  224. // to complain if they set -logfile explicitly, not if it's set to its
  225. // default location
  226. if options.noRestart && (options.logFile != "" && options.logFile != "-") {
  227. l.Warnln("-logfile may not be used with -no-restart or STNORESTART")
  228. os.Exit(exitError)
  229. }
  230. if options.hideConsole {
  231. osutil.HideConsole()
  232. }
  233. if options.confDir != "" {
  234. // Not set as default above because the string can be really long.
  235. if !filepath.IsAbs(options.confDir) {
  236. var err error
  237. options.confDir, err = filepath.Abs(options.confDir)
  238. if err != nil {
  239. l.Warnln("Failed to make options path absolute:", err)
  240. os.Exit(exitError)
  241. }
  242. }
  243. if err := locations.SetBaseDir(locations.ConfigBaseDir, options.confDir); err != nil {
  244. l.Warnln(err)
  245. os.Exit(exitError)
  246. }
  247. }
  248. if options.logFile == "" {
  249. // Blank means use the default logfile location. We must set this
  250. // *after* expandLocations above.
  251. options.logFile = locations.Get(locations.LogFile)
  252. }
  253. if options.AssetDir == "" {
  254. // The asset dir is blank if STGUIASSETS wasn't set, in which case we
  255. // should look for extra assets in the default place.
  256. options.AssetDir = locations.Get(locations.GUIAssets)
  257. }
  258. if options.showVersion {
  259. fmt.Println(build.LongVersion)
  260. return
  261. }
  262. if options.showHelp {
  263. flag.Usage()
  264. return
  265. }
  266. if options.showPaths {
  267. showPaths(options)
  268. return
  269. }
  270. if options.showDeviceId {
  271. cert, err := tls.LoadX509KeyPair(
  272. locations.Get(locations.CertFile),
  273. locations.Get(locations.KeyFile),
  274. )
  275. if err != nil {
  276. l.Warnln("Error reading device ID:", err)
  277. os.Exit(exitError)
  278. }
  279. fmt.Println(protocol.NewDeviceID(cert.Certificate[0]))
  280. return
  281. }
  282. if options.browserOnly {
  283. if err := openGUI(protocol.EmptyDeviceID); err != nil {
  284. l.Warnln("Failed to open web UI:", err)
  285. os.Exit(exitError)
  286. }
  287. return
  288. }
  289. if options.generateDir != "" {
  290. if err := generate(options.generateDir); err != nil {
  291. l.Warnln("Failed to generate config and keys:", err)
  292. os.Exit(exitError)
  293. }
  294. return
  295. }
  296. // Ensure that our home directory exists.
  297. if err := ensureDir(locations.GetBaseDir(locations.ConfigBaseDir), 0700); err != nil {
  298. l.Warnln("Failure on home directory:", err)
  299. os.Exit(exitError)
  300. }
  301. if options.upgradeTo != "" {
  302. err := upgrade.ToURL(options.upgradeTo)
  303. if err != nil {
  304. l.Warnln("Error while Upgrading:", err)
  305. os.Exit(exitError)
  306. }
  307. l.Infoln("Upgraded from", options.upgradeTo)
  308. return
  309. }
  310. if options.doUpgradeCheck {
  311. checkUpgrade()
  312. return
  313. }
  314. if options.doUpgrade {
  315. release := checkUpgrade()
  316. performUpgrade(release)
  317. return
  318. }
  319. if options.resetDatabase {
  320. if err := resetDB(); err != nil {
  321. l.Warnln("Resetting database:", err)
  322. os.Exit(exitError)
  323. }
  324. return
  325. }
  326. if innerProcess || options.noRestart {
  327. syncthingMain(options)
  328. } else {
  329. monitorMain(options)
  330. }
  331. }
  332. func openGUI(myID protocol.DeviceID) error {
  333. cfg, err := loadOrDefaultConfig(myID, events.NoopLogger)
  334. if err != nil {
  335. return err
  336. }
  337. if cfg.GUI().Enabled {
  338. if err := openURL(cfg.GUI().URL()); err != nil {
  339. return err
  340. }
  341. } else {
  342. l.Warnln("Browser: GUI is currently disabled")
  343. }
  344. return nil
  345. }
  346. func generate(generateDir string) error {
  347. dir, err := fs.ExpandTilde(generateDir)
  348. if err != nil {
  349. return err
  350. }
  351. if err := ensureDir(dir, 0700); err != nil {
  352. return err
  353. }
  354. var myID protocol.DeviceID
  355. certFile, keyFile := filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem")
  356. cert, err := tls.LoadX509KeyPair(certFile, keyFile)
  357. if err == nil {
  358. l.Warnln("Key exists; will not overwrite.")
  359. } else {
  360. cert, err = tlsutil.NewCertificate(certFile, keyFile, tlsDefaultCommonName)
  361. if err != nil {
  362. return errors.Wrap(err, "create certificate")
  363. }
  364. }
  365. myID = protocol.NewDeviceID(cert.Certificate[0])
  366. l.Infoln("Device ID:", myID)
  367. cfgFile := filepath.Join(dir, "config.xml")
  368. if _, err := os.Stat(cfgFile); err == nil {
  369. l.Warnln("Config exists; will not overwrite.")
  370. return nil
  371. }
  372. cfg, err := syncthing.DefaultConfig(cfgFile, myID, events.NoopLogger, noDefaultFolder)
  373. if err != nil {
  374. return err
  375. }
  376. err = cfg.Save()
  377. if err != nil {
  378. return errors.Wrap(err, "save config")
  379. }
  380. return nil
  381. }
  382. func debugFacilities() string {
  383. facilities := l.Facilities()
  384. // Get a sorted list of names
  385. var names []string
  386. maxLen := 0
  387. for name := range facilities {
  388. names = append(names, name)
  389. if len(name) > maxLen {
  390. maxLen = len(name)
  391. }
  392. }
  393. sort.Strings(names)
  394. // Format the choices
  395. b := new(bytes.Buffer)
  396. for _, name := range names {
  397. fmt.Fprintf(b, " %-*s - %s\n", maxLen, name, facilities[name])
  398. }
  399. return b.String()
  400. }
  401. func checkUpgrade() upgrade.Release {
  402. cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
  403. opts := cfg.Options()
  404. release, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases)
  405. if err != nil {
  406. l.Warnln("Upgrade:", err)
  407. os.Exit(exitError)
  408. }
  409. if upgrade.CompareVersions(release.Tag, build.Version) <= 0 {
  410. noUpgradeMessage := "No upgrade available (current %q >= latest %q)."
  411. l.Infof(noUpgradeMessage, build.Version, release.Tag)
  412. os.Exit(exitNoUpgradeAvailable)
  413. }
  414. l.Infof("Upgrade available (current %q < latest %q)", build.Version, release.Tag)
  415. return release
  416. }
  417. func performUpgrade(release upgrade.Release) {
  418. // Use leveldb database locks to protect against concurrent upgrades
  419. _, err := syncthing.OpenGoleveldb(locations.Get(locations.Database), config.TuningAuto)
  420. if err == nil {
  421. err = upgrade.To(release)
  422. if err != nil {
  423. l.Warnln("Upgrade:", err)
  424. os.Exit(exitError)
  425. }
  426. l.Infof("Upgraded to %q", release.Tag)
  427. } else {
  428. l.Infoln("Attempting upgrade through running Syncthing...")
  429. err = upgradeViaRest()
  430. if err != nil {
  431. l.Warnln("Upgrade:", err)
  432. os.Exit(exitError)
  433. }
  434. l.Infoln("Syncthing upgrading")
  435. os.Exit(exitUpgrading)
  436. }
  437. }
  438. func upgradeViaRest() error {
  439. cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
  440. u, err := url.Parse(cfg.GUI().URL())
  441. if err != nil {
  442. return err
  443. }
  444. u.Path = path.Join(u.Path, "rest/system/upgrade")
  445. target := u.String()
  446. r, _ := http.NewRequest("POST", target, nil)
  447. r.Header.Set("X-API-Key", cfg.GUI().APIKey)
  448. tr := &http.Transport{
  449. Dial: dialer.Dial,
  450. Proxy: http.ProxyFromEnvironment,
  451. TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  452. }
  453. client := &http.Client{
  454. Transport: tr,
  455. Timeout: 60 * time.Second,
  456. }
  457. resp, err := client.Do(r)
  458. if err != nil {
  459. return err
  460. }
  461. if resp.StatusCode != 200 {
  462. bs, err := ioutil.ReadAll(resp.Body)
  463. defer resp.Body.Close()
  464. if err != nil {
  465. return err
  466. }
  467. return errors.New(string(bs))
  468. }
  469. return err
  470. }
  471. func syncthingMain(runtimeOptions RuntimeOptions) {
  472. // Set a log prefix similar to the ID we will have later on, or early log
  473. // lines look ugly.
  474. l.SetPrefix("[start] ")
  475. // Print our version information up front, so any crash that happens
  476. // early etc. will have it available.
  477. l.Infoln(build.LongVersion)
  478. // Ensure that we have a certificate and key.
  479. cert, err := syncthing.LoadOrGenerateCertificate(
  480. locations.Get(locations.CertFile),
  481. locations.Get(locations.KeyFile),
  482. )
  483. if err != nil {
  484. l.Warnln("Failed to load/generate certificate:", err)
  485. os.Exit(1)
  486. }
  487. evLogger := events.NewLogger()
  488. go evLogger.Serve()
  489. defer evLogger.Stop()
  490. cfg, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, evLogger, runtimeOptions.allowNewerConfig, noDefaultFolder)
  491. if err != nil {
  492. l.Warnln("Failed to initialize config:", err)
  493. os.Exit(exitError)
  494. }
  495. if runtimeOptions.unpaused {
  496. setPauseState(cfg, false)
  497. } else if runtimeOptions.paused {
  498. setPauseState(cfg, true)
  499. }
  500. dbFile := locations.Get(locations.Database)
  501. ldb, err := syncthing.OpenGoleveldb(dbFile, cfg.Options().DatabaseTuning)
  502. if err != nil {
  503. l.Warnln("Error opening database:", err)
  504. os.Exit(1)
  505. }
  506. appOpts := runtimeOptions.Options
  507. if runtimeOptions.auditEnabled {
  508. appOpts.AuditWriter = auditWriter(runtimeOptions.auditFile)
  509. }
  510. if t := os.Getenv("STDEADLOCKTIMEOUT"); t != "" {
  511. secs, _ := strconv.Atoi(t)
  512. appOpts.DeadlockTimeoutS = secs
  513. }
  514. app := syncthing.New(cfg, ldb, evLogger, cert, appOpts)
  515. setupSignalHandling(app)
  516. if len(os.Getenv("GOMAXPROCS")) == 0 {
  517. runtime.GOMAXPROCS(runtime.NumCPU())
  518. }
  519. if runtimeOptions.cpuProfile {
  520. f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid()))
  521. if err != nil {
  522. l.Warnln("Creating profile:", err)
  523. os.Exit(exitError)
  524. }
  525. if err := pprof.StartCPUProfile(f); err != nil {
  526. l.Warnln("Starting profile:", err)
  527. os.Exit(exitError)
  528. }
  529. }
  530. if opts := cfg.Options(); opts.RestartOnWakeup {
  531. go standbyMonitor(app)
  532. }
  533. // Candidate builds should auto upgrade. Make sure the option is set,
  534. // unless we are in a build where it's disabled or the STNOUPGRADE
  535. // environment variable is set.
  536. if build.IsCandidate && !upgrade.DisabledByCompilation && !runtimeOptions.NoUpgrade {
  537. l.Infoln("Automatic upgrade is always enabled for candidate releases.")
  538. if opts := cfg.Options(); opts.AutoUpgradeIntervalH == 0 || opts.AutoUpgradeIntervalH > 24 {
  539. opts.AutoUpgradeIntervalH = 12
  540. // Set the option into the config as well, as the auto upgrade
  541. // loop expects to read a valid interval from there.
  542. cfg.SetOptions(opts)
  543. cfg.Save()
  544. }
  545. // We don't tweak the user's choice of upgrading to pre-releases or
  546. // not, as otherwise they cannot step off the candidate channel.
  547. }
  548. if opts := cfg.Options(); opts.AutoUpgradeIntervalH > 0 {
  549. if runtimeOptions.NoUpgrade {
  550. l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.")
  551. } else {
  552. go autoUpgrade(cfg, app, evLogger)
  553. }
  554. }
  555. app.Start()
  556. cleanConfigDirectory()
  557. if cfg.Options().StartBrowser && !runtimeOptions.noBrowser && !runtimeOptions.stRestarting {
  558. // Can potentially block if the utility we are invoking doesn't
  559. // fork, and just execs, hence keep it in its own routine.
  560. go func() { _ = openURL(cfg.GUI().URL()) }()
  561. }
  562. status := app.Wait()
  563. if runtimeOptions.cpuProfile {
  564. pprof.StopCPUProfile()
  565. }
  566. os.Exit(int(status))
  567. }
  568. func setupSignalHandling(app *syncthing.App) {
  569. // Exit cleanly with "restarting" code on SIGHUP.
  570. restartSign := make(chan os.Signal, 1)
  571. sigHup := syscall.Signal(1)
  572. signal.Notify(restartSign, sigHup)
  573. go func() {
  574. <-restartSign
  575. app.Stop(syncthing.ExitRestart)
  576. }()
  577. // Exit with "success" code (no restart) on INT/TERM
  578. stopSign := make(chan os.Signal, 1)
  579. sigTerm := syscall.Signal(15)
  580. signal.Notify(stopSign, os.Interrupt, sigTerm)
  581. go func() {
  582. <-stopSign
  583. app.Stop(syncthing.ExitSuccess)
  584. }()
  585. }
  586. func loadOrDefaultConfig(myID protocol.DeviceID, evLogger events.Logger) (config.Wrapper, error) {
  587. cfgFile := locations.Get(locations.ConfigFile)
  588. cfg, err := config.Load(cfgFile, myID, evLogger)
  589. if err != nil {
  590. cfg, err = syncthing.DefaultConfig(cfgFile, myID, evLogger, noDefaultFolder)
  591. }
  592. return cfg, err
  593. }
  594. func auditWriter(auditFile string) io.Writer {
  595. var fd io.Writer
  596. var err error
  597. var auditDest string
  598. var auditFlags int
  599. if auditFile == "-" {
  600. fd = os.Stdout
  601. auditDest = "stdout"
  602. } else if auditFile == "--" {
  603. fd = os.Stderr
  604. auditDest = "stderr"
  605. } else {
  606. if auditFile == "" {
  607. auditFile = locations.GetTimestamped(locations.AuditLog)
  608. auditFlags = os.O_WRONLY | os.O_CREATE | os.O_EXCL
  609. } else {
  610. auditFlags = os.O_WRONLY | os.O_CREATE | os.O_APPEND
  611. }
  612. fd, err = os.OpenFile(auditFile, auditFlags, 0600)
  613. if err != nil {
  614. l.Warnln("Audit:", err)
  615. os.Exit(exitError)
  616. }
  617. auditDest = auditFile
  618. }
  619. l.Infoln("Audit log in", auditDest)
  620. return fd
  621. }
  622. func resetDB() error {
  623. return os.RemoveAll(locations.Get(locations.Database))
  624. }
  625. func ensureDir(dir string, mode fs.FileMode) error {
  626. fs := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
  627. err := fs.MkdirAll(".", mode)
  628. if err != nil {
  629. return err
  630. }
  631. if fi, err := fs.Stat("."); err == nil {
  632. // Apprently the stat may fail even though the mkdirall passed. If it
  633. // does, we'll just assume things are in order and let other things
  634. // fail (like loading or creating the config...).
  635. currentMode := fi.Mode() & 0777
  636. if currentMode != mode {
  637. err := fs.Chmod(".", mode)
  638. // This can fail on crappy filesystems, nothing we can do about it.
  639. if err != nil {
  640. l.Warnln(err)
  641. }
  642. }
  643. }
  644. return nil
  645. }
  646. func standbyMonitor(app *syncthing.App) {
  647. restartDelay := 60 * time.Second
  648. now := time.Now()
  649. for {
  650. time.Sleep(10 * time.Second)
  651. if time.Since(now) > 2*time.Minute {
  652. l.Infof("Paused state detected, possibly woke up from standby. Restarting in %v.", restartDelay)
  653. // We most likely just woke from standby. If we restart
  654. // immediately chances are we won't have networking ready. Give
  655. // things a moment to stabilize.
  656. time.Sleep(restartDelay)
  657. app.Stop(syncthing.ExitRestart)
  658. return
  659. }
  660. now = time.Now()
  661. }
  662. }
  663. func autoUpgrade(cfg config.Wrapper, app *syncthing.App, evLogger events.Logger) {
  664. timer := time.NewTimer(0)
  665. sub := evLogger.Subscribe(events.DeviceConnected)
  666. for {
  667. select {
  668. case event := <-sub.C():
  669. data, ok := event.Data.(map[string]string)
  670. if !ok || data["clientName"] != "syncthing" || upgrade.CompareVersions(data["clientVersion"], build.Version) != upgrade.Newer {
  671. continue
  672. }
  673. l.Infof("Connected to device %s with a newer version (current %q < remote %q). Checking for upgrades.", data["id"], build.Version, data["clientVersion"])
  674. case <-timer.C:
  675. }
  676. opts := cfg.Options()
  677. checkInterval := time.Duration(opts.AutoUpgradeIntervalH) * time.Hour
  678. if checkInterval < time.Hour {
  679. // We shouldn't be here if AutoUpgradeIntervalH < 1, but for
  680. // safety's sake.
  681. checkInterval = time.Hour
  682. }
  683. rel, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases)
  684. if err == upgrade.ErrUpgradeUnsupported {
  685. sub.Unsubscribe()
  686. return
  687. }
  688. if err != nil {
  689. // Don't complain too loudly here; we might simply not have
  690. // internet connectivity, or the upgrade server might be down.
  691. l.Infoln("Automatic upgrade:", err)
  692. timer.Reset(checkInterval)
  693. continue
  694. }
  695. if upgrade.CompareVersions(rel.Tag, build.Version) != upgrade.Newer {
  696. // Skip equal, older or majorly newer (incompatible) versions
  697. timer.Reset(checkInterval)
  698. continue
  699. }
  700. l.Infof("Automatic upgrade (current %q < latest %q)", build.Version, rel.Tag)
  701. err = upgrade.To(rel)
  702. if err != nil {
  703. l.Warnln("Automatic upgrade:", err)
  704. timer.Reset(checkInterval)
  705. continue
  706. }
  707. sub.Unsubscribe()
  708. l.Warnf("Automatically upgraded to version %q. Restarting in 1 minute.", rel.Tag)
  709. time.Sleep(time.Minute)
  710. app.Stop(syncthing.ExitUpgrade)
  711. return
  712. }
  713. }
  714. // cleanConfigDirectory removes old, unused configuration and index formats, a
  715. // suitable time after they have gone out of fashion.
  716. func cleanConfigDirectory() {
  717. patterns := map[string]time.Duration{
  718. "panic-*.log": 7 * 24 * time.Hour, // keep panic logs for a week
  719. "audit-*.log": 7 * 24 * time.Hour, // keep audit logs for a week
  720. "index": 14 * 24 * time.Hour, // keep old index format for two weeks
  721. "index-v0.11.0.db": 14 * 24 * time.Hour, // keep old index format for two weeks
  722. "index-v0.13.0.db": 14 * 24 * time.Hour, // keep old index format for two weeks
  723. "index*.converted": 14 * 24 * time.Hour, // keep old converted indexes for two weeks
  724. "config.xml.v*": 30 * 24 * time.Hour, // old config versions for a month
  725. "*.idx.gz": 30 * 24 * time.Hour, // these should for sure no longer exist
  726. "backup-of-v0.8": 30 * 24 * time.Hour, // these neither
  727. "tmp-index-sorter.*": time.Minute, // these should never exist on startup
  728. "support-bundle-*": 30 * 24 * time.Hour, // keep old support bundle zip or folder for a month
  729. }
  730. for pat, dur := range patterns {
  731. fs := fs.NewFilesystem(fs.FilesystemTypeBasic, locations.GetBaseDir(locations.ConfigBaseDir))
  732. files, err := fs.Glob(pat)
  733. if err != nil {
  734. l.Infoln("Cleaning:", err)
  735. continue
  736. }
  737. for _, file := range files {
  738. info, err := fs.Lstat(file)
  739. if err != nil {
  740. l.Infoln("Cleaning:", err)
  741. continue
  742. }
  743. if time.Since(info.ModTime()) > dur {
  744. if err = fs.RemoveAll(file); err != nil {
  745. l.Infoln("Cleaning:", err)
  746. } else {
  747. l.Infoln("Cleaned away old file", filepath.Base(file))
  748. }
  749. }
  750. }
  751. }
  752. }
  753. func showPaths(options RuntimeOptions) {
  754. fmt.Printf("Configuration file:\n\t%s\n\n", locations.Get(locations.ConfigFile))
  755. fmt.Printf("Database directory:\n\t%s\n\n", locations.Get(locations.Database))
  756. fmt.Printf("Device private key & certificate files:\n\t%s\n\t%s\n\n", locations.Get(locations.KeyFile), locations.Get(locations.CertFile))
  757. fmt.Printf("HTTPS private key & certificate files:\n\t%s\n\t%s\n\n", locations.Get(locations.HTTPSKeyFile), locations.Get(locations.HTTPSCertFile))
  758. fmt.Printf("Log file:\n\t%s\n\n", options.logFile)
  759. fmt.Printf("GUI override directory:\n\t%s\n\n", options.AssetDir)
  760. fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations.Get(locations.DefFolder))
  761. }
  762. func setPauseState(cfg config.Wrapper, paused bool) {
  763. raw := cfg.RawCopy()
  764. for i := range raw.Devices {
  765. raw.Devices[i].Paused = paused
  766. }
  767. for i := range raw.Folders {
  768. raw.Folders[i].Paused = paused
  769. }
  770. if _, err := cfg.Replace(raw); err != nil {
  771. l.Warnln("Cannot adjust paused state:", err)
  772. os.Exit(exitError)
  773. }
  774. }