main.go 30 KB

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