main.go 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274
  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 http://mozilla.org/MPL/2.0/.
  6. package main
  7. import (
  8. "bytes"
  9. "crypto/tls"
  10. "errors"
  11. "flag"
  12. "fmt"
  13. "io"
  14. "io/ioutil"
  15. "log"
  16. "net"
  17. "net/http"
  18. "net/url"
  19. "os"
  20. "os/signal"
  21. "path"
  22. "path/filepath"
  23. "regexp"
  24. "runtime"
  25. "runtime/pprof"
  26. "sort"
  27. "strconv"
  28. "strings"
  29. "syscall"
  30. "time"
  31. "github.com/syncthing/syncthing/lib/config"
  32. "github.com/syncthing/syncthing/lib/connections"
  33. "github.com/syncthing/syncthing/lib/db"
  34. "github.com/syncthing/syncthing/lib/dialer"
  35. "github.com/syncthing/syncthing/lib/discover"
  36. "github.com/syncthing/syncthing/lib/events"
  37. "github.com/syncthing/syncthing/lib/logger"
  38. "github.com/syncthing/syncthing/lib/model"
  39. "github.com/syncthing/syncthing/lib/osutil"
  40. "github.com/syncthing/syncthing/lib/protocol"
  41. "github.com/syncthing/syncthing/lib/rand"
  42. "github.com/syncthing/syncthing/lib/sha256"
  43. "github.com/syncthing/syncthing/lib/symlinks"
  44. "github.com/syncthing/syncthing/lib/tlsutil"
  45. "github.com/syncthing/syncthing/lib/upgrade"
  46. "github.com/thejerf/suture"
  47. _ "net/http/pprof" // Need to import this to support STPROFILER.
  48. )
  49. var (
  50. Version = "unknown-dev"
  51. Codename = "Dysprosium Dragonfly"
  52. BuildStamp = "0"
  53. BuildDate time.Time
  54. BuildHost = "unknown"
  55. BuildUser = "unknown"
  56. IsRelease bool
  57. IsBeta bool
  58. LongVersion string
  59. allowedVersionExp = regexp.MustCompile(`^v\d+\.\d+\.\d+(-[a-z0-9]+)*(\.\d+)*(\+\d+-g[0-9a-f]+)?(-[^\s]+)?$`)
  60. )
  61. const (
  62. exitSuccess = 0
  63. exitError = 1
  64. exitNoUpgradeAvailable = 2
  65. exitRestarting = 3
  66. exitUpgrading = 4
  67. )
  68. const (
  69. bepProtocolName = "bep/1.0"
  70. tlsDefaultCommonName = "syncthing"
  71. httpsRSABits = 2048
  72. bepRSABits = 0 // 384 bit ECDSA used instead
  73. pingEventInterval = time.Minute
  74. maxSystemErrors = 5
  75. initialSystemLog = 10
  76. maxSystemLog = 250
  77. )
  78. // The discovery results are sorted by their source priority.
  79. const (
  80. ipv6LocalDiscoveryPriority = iota
  81. ipv4LocalDiscoveryPriority
  82. globalDiscoveryPriority
  83. )
  84. func init() {
  85. if Version != "unknown-dev" {
  86. // If not a generic dev build, version string should come from git describe
  87. if !allowedVersionExp.MatchString(Version) {
  88. l.Fatalf("Invalid version string %q;\n\tdoes not match regexp %v", Version, allowedVersionExp)
  89. }
  90. }
  91. // Check for a clean release build. A release is something like "v0.1.2",
  92. // with an optional suffix of letters and dot separated numbers like
  93. // "-beta3.47". If there's more stuff, like a plus sign and a commit hash
  94. // and so on, then it's not a release. If there's a dash anywhere in
  95. // there, it's some kind of beta or prerelease version.
  96. exp := regexp.MustCompile(`^v\d+\.\d+\.\d+(-[a-z]+[\d\.]+)?$`)
  97. IsRelease = exp.MatchString(Version)
  98. IsBeta = strings.Contains(Version, "-")
  99. stamp, _ := strconv.Atoi(BuildStamp)
  100. BuildDate = time.Unix(int64(stamp), 0)
  101. date := BuildDate.UTC().Format("2006-01-02 15:04:05 MST")
  102. LongVersion = fmt.Sprintf(`syncthing %s "%s" (%s %s-%s) %s@%s %s`, Version, Codename, runtime.Version(), runtime.GOOS, runtime.GOARCH, BuildUser, BuildHost, date)
  103. }
  104. var (
  105. myID protocol.DeviceID
  106. stop = make(chan int)
  107. lans []*net.IPNet
  108. )
  109. const (
  110. usage = "syncthing [options]"
  111. extraUsage = `
  112. The -logflags value is a sum of the following:
  113. 1 Date
  114. 2 Time
  115. 4 Microsecond time
  116. 8 Long filename
  117. 16 Short filename
  118. I.e. to prefix each log line with date and time, set -logflags=3 (1 + 2 from
  119. above). The value 0 is used to disable all of the above. The default is to
  120. show time only (2).
  121. Development Settings
  122. --------------------
  123. The following environment variables modify Syncthing's behavior in ways that
  124. are mostly useful for developers. Use with care.
  125. STNODEFAULTFOLDER Don't create a default folder when starting for the first
  126. time. This variable will be ignored anytime after the first
  127. run.
  128. STGUIASSETS Directory to load GUI assets from. Overrides compiled in
  129. assets.
  130. STTRACE A comma separated string of facilities to trace. The valid
  131. facility strings listed below.
  132. STPROFILER Set to a listen address such as "127.0.0.1:9090" to start
  133. the profiler with HTTP access.
  134. STCPUPROFILE Write a CPU profile to cpu-$pid.pprof on exit.
  135. STHEAPPROFILE Write heap profiles to heap-$pid-$timestamp.pprof each time
  136. heap usage increases.
  137. STBLOCKPROFILE Write block profiles to block-$pid-$timestamp.pprof every 20
  138. seconds.
  139. STPERFSTATS Write running performance statistics to perf-$pid.csv. Not
  140. supported on Windows.
  141. STDEADLOCK Used for debugging internal deadlocks. Use only under
  142. direction of a developer.
  143. STDEADLOCKTIMEOUT Used for debugging internal deadlocks; sets debug
  144. sensitivity. Use only under direction of a developer.
  145. STDEADLOCKTHRESHOLD Used for debugging internal deadlocks; sets debug
  146. sensitivity. Use only under direction of a developer.
  147. STNORESTART Equivalent to the -no-restart argument. Disable the
  148. Syncthing monitor process which handles restarts for some
  149. configuration changes, upgrades, crashes and also log file
  150. writing (stdout is still written).
  151. STNOUPGRADE Disable automatic upgrades.
  152. STHASHING Select the SHA256 hashing package to use. Possible values
  153. are "standard" for the Go standard library implementation,
  154. "minio" for the github.com/minio/sha256-simd implementation,
  155. and blank (the default) for auto detection.
  156. GOMAXPROCS Set the maximum number of CPU cores to use. Defaults to all
  157. available CPU cores.
  158. GOGC Percentage of heap growth at which to trigger GC. Default is
  159. 100. Lower numbers keep peak memory usage down, at the price
  160. of CPU usage (i.e. performance).
  161. Debugging Facilities
  162. --------------------
  163. The following are valid values for the STTRACE variable:
  164. %s`
  165. )
  166. // Environment options
  167. var (
  168. noUpgrade = os.Getenv("STNOUPGRADE") != ""
  169. innerProcess = os.Getenv("STNORESTART") != "" || os.Getenv("STMONITORED") != ""
  170. noDefaultFolder = os.Getenv("STNODEFAULTFOLDER") != ""
  171. )
  172. type RuntimeOptions struct {
  173. confDir string
  174. resetDatabase bool
  175. resetDeltaIdxs bool
  176. showVersion bool
  177. showPaths bool
  178. doUpgrade bool
  179. doUpgradeCheck bool
  180. upgradeTo string
  181. noBrowser bool
  182. browserOnly bool
  183. hideConsole bool
  184. logFile string
  185. auditEnabled bool
  186. auditFile string
  187. verbose bool
  188. paused bool
  189. unpaused bool
  190. guiAddress string
  191. guiAPIKey string
  192. generateDir string
  193. noRestart bool
  194. profiler string
  195. assetDir string
  196. cpuProfile bool
  197. stRestarting bool
  198. logFlags int
  199. }
  200. func defaultRuntimeOptions() RuntimeOptions {
  201. options := RuntimeOptions{
  202. noRestart: os.Getenv("STNORESTART") != "",
  203. profiler: os.Getenv("STPROFILER"),
  204. assetDir: os.Getenv("STGUIASSETS"),
  205. cpuProfile: os.Getenv("STCPUPROFILE") != "",
  206. stRestarting: os.Getenv("STRESTART") != "",
  207. logFlags: log.Ltime,
  208. }
  209. if os.Getenv("STTRACE") != "" {
  210. options.logFlags = log.Ltime | log.Ldate | log.Lmicroseconds | log.Lshortfile
  211. }
  212. if runtime.GOOS != "windows" {
  213. // On non-Windows, we explicitly default to "-" which means stdout. On
  214. // Windows, the blank options.logFile will later be replaced with the
  215. // default path, unless the user has manually specified "-" or
  216. // something else.
  217. options.logFile = "-"
  218. }
  219. return options
  220. }
  221. func parseCommandLineOptions() RuntimeOptions {
  222. options := defaultRuntimeOptions()
  223. flag.StringVar(&options.generateDir, "generate", "", "Generate key and config in specified dir, then exit")
  224. flag.StringVar(&options.guiAddress, "gui-address", options.guiAddress, "Override GUI address (e.g. \"http://192.0.2.42:8443\")")
  225. flag.StringVar(&options.guiAPIKey, "gui-apikey", options.guiAPIKey, "Override GUI API key")
  226. flag.StringVar(&options.confDir, "home", "", "Set configuration directory")
  227. flag.IntVar(&options.logFlags, "logflags", options.logFlags, "Select information in log line prefix (see below)")
  228. flag.BoolVar(&options.noBrowser, "no-browser", false, "Do not start browser")
  229. flag.BoolVar(&options.browserOnly, "browser-only", false, "Open GUI in browser")
  230. flag.BoolVar(&options.noRestart, "no-restart", options.noRestart, "Disable monitor process, managed restarts and log file writing")
  231. flag.BoolVar(&options.resetDatabase, "reset-database", false, "Reset the database, forcing a full rescan and resync")
  232. flag.BoolVar(&options.resetDeltaIdxs, "reset-deltas", false, "Reset delta index IDs, forcing a full index exchange")
  233. flag.BoolVar(&options.doUpgrade, "upgrade", false, "Perform upgrade")
  234. flag.BoolVar(&options.doUpgradeCheck, "upgrade-check", false, "Check for available upgrade")
  235. flag.BoolVar(&options.showVersion, "version", false, "Show version")
  236. flag.BoolVar(&options.showPaths, "paths", false, "Show configuration paths")
  237. flag.StringVar(&options.upgradeTo, "upgrade-to", options.upgradeTo, "Force upgrade directly from specified URL")
  238. flag.BoolVar(&options.auditEnabled, "audit", false, "Write events to audit file")
  239. flag.BoolVar(&options.verbose, "verbose", false, "Print verbose log output")
  240. flag.BoolVar(&options.paused, "paused", false, "Start with all devices and folders paused")
  241. flag.BoolVar(&options.unpaused, "unpaused", false, "Start with all devices and folders unpaused")
  242. flag.StringVar(&options.logFile, "logfile", options.logFile, "Log file name (use \"-\" for stdout)")
  243. flag.StringVar(&options.auditFile, "auditfile", options.auditFile, "Specify audit file (use \"-\" for stdout, \"--\" for stderr)")
  244. if runtime.GOOS == "windows" {
  245. // Allow user to hide the console window
  246. flag.BoolVar(&options.hideConsole, "no-console", false, "Hide console window")
  247. }
  248. longUsage := fmt.Sprintf(extraUsage, debugFacilities())
  249. flag.Usage = usageFor(flag.CommandLine, usage, longUsage)
  250. flag.Parse()
  251. if len(flag.Args()) > 0 {
  252. flag.Usage()
  253. os.Exit(2)
  254. }
  255. return options
  256. }
  257. func main() {
  258. options := parseCommandLineOptions()
  259. l.SetFlags(options.logFlags)
  260. if options.guiAddress != "" {
  261. // The config picks this up from the environment.
  262. os.Setenv("STGUIADDRESS", options.guiAddress)
  263. }
  264. if options.guiAPIKey != "" {
  265. // The config picks this up from the environment.
  266. os.Setenv("STGUIAPIKEY", options.guiAPIKey)
  267. }
  268. // Check for options which are not compatible with each other. We have
  269. // to check logfile before it's set to the default below - we only want
  270. // to complain if they set -logfile explicitly, not if it's set to its
  271. // default location
  272. if options.noRestart && (options.logFile != "" && options.logFile != "-") {
  273. l.Fatalln("-logfile may not be used with -no-restart or STNORESTART")
  274. }
  275. if options.hideConsole {
  276. osutil.HideConsole()
  277. }
  278. if options.confDir != "" {
  279. // Not set as default above because the string can be really long.
  280. baseDirs["config"] = options.confDir
  281. }
  282. if err := expandLocations(); err != nil {
  283. l.Fatalln(err)
  284. }
  285. if options.logFile == "" {
  286. // Blank means use the default logfile location. We must set this
  287. // *after* expandLocations above.
  288. options.logFile = locations[locLogFile]
  289. }
  290. if options.assetDir == "" {
  291. // The asset dir is blank if STGUIASSETS wasn't set, in which case we
  292. // should look for extra assets in the default place.
  293. options.assetDir = locations[locGUIAssets]
  294. }
  295. if options.showVersion {
  296. fmt.Println(LongVersion)
  297. return
  298. }
  299. if options.showPaths {
  300. showPaths()
  301. return
  302. }
  303. if options.browserOnly {
  304. openGUI()
  305. return
  306. }
  307. if options.generateDir != "" {
  308. generate(options.generateDir)
  309. return
  310. }
  311. // Ensure that our home directory exists.
  312. ensureDir(baseDirs["config"], 0700)
  313. if options.upgradeTo != "" {
  314. err := upgrade.ToURL(options.upgradeTo)
  315. if err != nil {
  316. l.Fatalln("Upgrade:", err) // exits 1
  317. }
  318. l.Infoln("Upgraded from", options.upgradeTo)
  319. return
  320. }
  321. if options.doUpgradeCheck {
  322. checkUpgrade()
  323. return
  324. }
  325. if options.doUpgrade {
  326. release := checkUpgrade()
  327. performUpgrade(release)
  328. return
  329. }
  330. if options.resetDatabase {
  331. resetDB()
  332. return
  333. }
  334. if innerProcess || options.noRestart {
  335. syncthingMain(options)
  336. } else {
  337. monitorMain(options)
  338. }
  339. }
  340. func openGUI() {
  341. cfg, _ := loadConfig()
  342. if cfg.GUI().Enabled {
  343. openURL(cfg.GUI().URL())
  344. } else {
  345. l.Warnln("Browser: GUI is currently disabled")
  346. }
  347. }
  348. func generate(generateDir string) {
  349. dir, err := osutil.ExpandTilde(generateDir)
  350. if err != nil {
  351. l.Fatalln("generate:", err)
  352. }
  353. ensureDir(dir, 0700)
  354. certFile, keyFile := filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem")
  355. cert, err := tls.LoadX509KeyPair(certFile, keyFile)
  356. if err == nil {
  357. l.Warnln("Key exists; will not overwrite.")
  358. l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0]))
  359. } else {
  360. cert, err = tlsutil.NewCertificate(certFile, keyFile, tlsDefaultCommonName, bepRSABits)
  361. if err != nil {
  362. l.Fatalln("Create certificate:", err)
  363. }
  364. myID = protocol.NewDeviceID(cert.Certificate[0])
  365. if err != nil {
  366. l.Fatalln("Load certificate:", err)
  367. }
  368. if err == nil {
  369. l.Infoln("Device ID:", protocol.NewDeviceID(cert.Certificate[0]))
  370. }
  371. }
  372. cfgFile := filepath.Join(dir, "config.xml")
  373. if _, err := os.Stat(cfgFile); err == nil {
  374. l.Warnln("Config exists; will not overwrite.")
  375. return
  376. }
  377. var myName, _ = os.Hostname()
  378. var newCfg = defaultConfig(myName)
  379. var cfg = config.Wrap(cfgFile, newCfg)
  380. err = cfg.Save()
  381. if err != nil {
  382. l.Warnln("Failed to save config", err)
  383. }
  384. }
  385. func debugFacilities() string {
  386. facilities := l.Facilities()
  387. // Get a sorted list of names
  388. var names []string
  389. maxLen := 0
  390. for name := range facilities {
  391. names = append(names, name)
  392. if len(name) > maxLen {
  393. maxLen = len(name)
  394. }
  395. }
  396. sort.Strings(names)
  397. // Format the choices
  398. b := new(bytes.Buffer)
  399. for _, name := range names {
  400. fmt.Fprintf(b, " %-*s - %s\n", maxLen, name, facilities[name])
  401. }
  402. return b.String()
  403. }
  404. func checkUpgrade() upgrade.Release {
  405. cfg, _ := loadConfig()
  406. opts := cfg.Options()
  407. release, err := upgrade.LatestRelease(opts.ReleasesURL, Version, opts.UpgradeToPreReleases)
  408. if err != nil {
  409. l.Fatalln("Upgrade:", err)
  410. }
  411. if upgrade.CompareVersions(release.Tag, Version) <= 0 {
  412. noUpgradeMessage := "No upgrade available (current %q >= latest %q)."
  413. l.Infof(noUpgradeMessage, Version, release.Tag)
  414. os.Exit(exitNoUpgradeAvailable)
  415. }
  416. l.Infof("Upgrade available (current %q < latest %q)", Version, release.Tag)
  417. return release
  418. }
  419. func performUpgrade(release upgrade.Release) {
  420. // Use leveldb database locks to protect against concurrent upgrades
  421. _, err := db.Open(locations[locDatabase])
  422. if err == nil {
  423. err = upgrade.To(release)
  424. if err != nil {
  425. l.Fatalln("Upgrade:", err)
  426. }
  427. l.Infof("Upgraded to %q", release.Tag)
  428. } else {
  429. l.Infoln("Attempting upgrade through running Syncthing...")
  430. err = upgradeViaRest()
  431. if err != nil {
  432. l.Fatalln("Upgrade:", err)
  433. }
  434. l.Infoln("Syncthing upgrading")
  435. os.Exit(exitUpgrading)
  436. }
  437. }
  438. func upgradeViaRest() error {
  439. cfg, _ := loadConfig()
  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. setupSignalHandling()
  473. // Create a main service manager. We'll add things to this as we go along.
  474. // We want any logging it does to go through our log system.
  475. mainService := suture.New("main", suture.Spec{
  476. Log: func(line string) {
  477. l.Debugln(line)
  478. },
  479. })
  480. mainService.ServeBackground()
  481. // Set a log prefix similar to the ID we will have later on, or early log
  482. // lines look ugly.
  483. l.SetPrefix("[start] ")
  484. if runtimeOptions.auditEnabled {
  485. startAuditing(mainService, runtimeOptions.auditFile)
  486. }
  487. if runtimeOptions.verbose {
  488. mainService.Add(newVerboseService())
  489. }
  490. errors := logger.NewRecorder(l, logger.LevelWarn, maxSystemErrors, 0)
  491. systemLog := logger.NewRecorder(l, logger.LevelDebug, maxSystemLog, initialSystemLog)
  492. // Event subscription for the API; must start early to catch the early
  493. // events. The LocalChangeDetected event might overwhelm the event
  494. // receiver in some situations so we will not subscribe to it here.
  495. apiSub := events.NewBufferedSubscription(events.Default.Subscribe(events.AllEvents&^events.LocalChangeDetected&^events.RemoteChangeDetected), 1000)
  496. diskSub := events.NewBufferedSubscription(events.Default.Subscribe(events.LocalChangeDetected|events.RemoteChangeDetected|events.Ping), 1000)
  497. if len(os.Getenv("GOMAXPROCS")) == 0 {
  498. runtime.GOMAXPROCS(runtime.NumCPU())
  499. }
  500. // Attempt to increase the limit on number of open files to the maximum
  501. // allowed, in case we have many peers. We don't really care enough to
  502. // report the error if there is one.
  503. osutil.MaximizeOpenFileLimit()
  504. // Ensure that that we have a certificate and key.
  505. cert, err := tls.LoadX509KeyPair(locations[locCertFile], locations[locKeyFile])
  506. if err != nil {
  507. l.Infof("Generating ECDSA key and certificate for %s...", tlsDefaultCommonName)
  508. cert, err = tlsutil.NewCertificate(locations[locCertFile], locations[locKeyFile], tlsDefaultCommonName, bepRSABits)
  509. if err != nil {
  510. l.Fatalln(err)
  511. }
  512. }
  513. myID = protocol.NewDeviceID(cert.Certificate[0])
  514. l.SetPrefix(fmt.Sprintf("[%s] ", myID.String()[:5]))
  515. l.Infoln(LongVersion)
  516. l.Infoln("My ID:", myID)
  517. sha256.SelectAlgo()
  518. sha256.Report()
  519. perf := cpuBench(3, 150*time.Millisecond)
  520. l.Infof("Actual hashing performance is %.02f MB/s", perf)
  521. // Emit the Starting event, now that we know who we are.
  522. events.Default.Log(events.Starting, map[string]string{
  523. "home": baseDirs["config"],
  524. "myID": myID.String(),
  525. })
  526. cfg := loadOrCreateConfig()
  527. if err := checkShortIDs(cfg); err != nil {
  528. l.Fatalln("Short device IDs are in conflict. Unlucky!\n Regenerate the device ID of one of the following:\n ", err)
  529. }
  530. if len(runtimeOptions.profiler) > 0 {
  531. go func() {
  532. l.Debugln("Starting profiler on", runtimeOptions.profiler)
  533. runtime.SetBlockProfileRate(1)
  534. err := http.ListenAndServe(runtimeOptions.profiler, nil)
  535. if err != nil {
  536. l.Fatalln(err)
  537. }
  538. }()
  539. }
  540. // The TLS configuration is used for both the listening socket and outgoing
  541. // connections.
  542. tlsCfg := &tls.Config{
  543. Certificates: []tls.Certificate{cert},
  544. NextProtos: []string{bepProtocolName},
  545. ClientAuth: tls.RequestClientCert,
  546. SessionTicketsDisabled: true,
  547. InsecureSkipVerify: true,
  548. MinVersion: tls.VersionTLS12,
  549. CipherSuites: []uint16{
  550. 0xCCA8, // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, Go 1.8
  551. 0xCCA9, // TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, Go 1.8
  552. tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
  553. tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
  554. tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
  555. tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
  556. tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
  557. tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
  558. tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
  559. tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
  560. },
  561. }
  562. opts := cfg.Options()
  563. if !opts.SymlinksEnabled {
  564. symlinks.Supported = false
  565. }
  566. if (opts.MaxRecvKbps > 0 || opts.MaxSendKbps > 0) && !opts.LimitBandwidthInLan {
  567. lans, _ = osutil.GetLans()
  568. for _, lan := range opts.AlwaysLocalNets {
  569. _, ipnet, err := net.ParseCIDR(lan)
  570. if err != nil {
  571. l.Infoln("Network", lan, "is malformed:", err)
  572. continue
  573. }
  574. lans = append(lans, ipnet)
  575. }
  576. networks := make([]string, len(lans))
  577. for i, lan := range lans {
  578. networks[i] = lan.String()
  579. }
  580. l.Infoln("Local networks:", strings.Join(networks, ", "))
  581. }
  582. dbFile := locations[locDatabase]
  583. ldb, err := db.Open(dbFile)
  584. if err != nil {
  585. l.Fatalln("Cannot open database:", err, "- Is another copy of Syncthing already running?")
  586. }
  587. if runtimeOptions.resetDeltaIdxs {
  588. l.Infoln("Reinitializing delta index IDs")
  589. ldb.DropDeltaIndexIDs()
  590. }
  591. protectedFiles := []string{
  592. locations[locDatabase],
  593. locations[locConfigFile],
  594. locations[locCertFile],
  595. locations[locKeyFile],
  596. }
  597. // Remove database entries for folders that no longer exist in the config
  598. folders := cfg.Folders()
  599. for _, folder := range ldb.ListFolders() {
  600. if _, ok := folders[folder]; !ok {
  601. l.Infof("Cleaning data for dropped folder %q", folder)
  602. db.DropFolder(ldb, folder)
  603. }
  604. }
  605. if cfg.RawCopy().OriginalVersion == 15 {
  606. // The config version 15->16 migration is about handling ignores and
  607. // delta indexes and requires that we drop existing indexes that
  608. // have been incorrectly ignore filtered.
  609. ldb.DropDeltaIndexIDs()
  610. }
  611. m := model.NewModel(cfg, myID, myDeviceName(cfg), "syncthing", Version, ldb, protectedFiles)
  612. if t := os.Getenv("STDEADLOCKTIMEOUT"); len(t) > 0 {
  613. it, err := strconv.Atoi(t)
  614. if err == nil {
  615. m.StartDeadlockDetector(time.Duration(it) * time.Second)
  616. }
  617. } else if !IsRelease || IsBeta {
  618. m.StartDeadlockDetector(20 * time.Minute)
  619. }
  620. if runtimeOptions.unpaused {
  621. setPauseState(cfg, false)
  622. } else if runtimeOptions.paused {
  623. setPauseState(cfg, true)
  624. }
  625. // Add and start folders
  626. for _, folderCfg := range cfg.Folders() {
  627. if folderCfg.Paused {
  628. continue
  629. }
  630. m.AddFolder(folderCfg)
  631. m.StartFolder(folderCfg.ID)
  632. }
  633. mainService.Add(m)
  634. // Start discovery
  635. cachedDiscovery := discover.NewCachingMux()
  636. mainService.Add(cachedDiscovery)
  637. // Start connection management
  638. connectionsService := connections.NewService(cfg, myID, m, tlsCfg, cachedDiscovery, bepProtocolName, tlsDefaultCommonName, lans)
  639. mainService.Add(connectionsService)
  640. if cfg.Options().GlobalAnnEnabled {
  641. for _, srv := range cfg.GlobalDiscoveryServers() {
  642. l.Infoln("Using discovery server", srv)
  643. gd, err := discover.NewGlobal(srv, cert, connectionsService)
  644. if err != nil {
  645. l.Warnln("Global discovery:", err)
  646. continue
  647. }
  648. // Each global discovery server gets its results cached for five
  649. // minutes, and is not asked again for a minute when it's returned
  650. // unsuccessfully.
  651. cachedDiscovery.Add(gd, 5*time.Minute, time.Minute, globalDiscoveryPriority)
  652. }
  653. }
  654. if cfg.Options().LocalAnnEnabled {
  655. // v4 broadcasts
  656. bcd, err := discover.NewLocal(myID, fmt.Sprintf(":%d", cfg.Options().LocalAnnPort), connectionsService)
  657. if err != nil {
  658. l.Warnln("IPv4 local discovery:", err)
  659. } else {
  660. cachedDiscovery.Add(bcd, 0, 0, ipv4LocalDiscoveryPriority)
  661. }
  662. // v6 multicasts
  663. mcd, err := discover.NewLocal(myID, cfg.Options().LocalAnnMCAddr, connectionsService)
  664. if err != nil {
  665. l.Warnln("IPv6 local discovery:", err)
  666. } else {
  667. cachedDiscovery.Add(mcd, 0, 0, ipv6LocalDiscoveryPriority)
  668. }
  669. }
  670. // GUI
  671. setupGUI(mainService, cfg, m, apiSub, diskSub, cachedDiscovery, connectionsService, errors, systemLog, runtimeOptions)
  672. if runtimeOptions.cpuProfile {
  673. f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid()))
  674. if err != nil {
  675. log.Fatal(err)
  676. }
  677. pprof.StartCPUProfile(f)
  678. }
  679. for _, device := range cfg.Devices() {
  680. if len(device.Name) > 0 {
  681. l.Infof("Device %s is %q at %v", device.DeviceID, device.Name, device.Addresses)
  682. }
  683. }
  684. if opts.URAccepted > 0 && opts.URAccepted < usageReportVersion {
  685. l.Infoln("Anonymous usage report has changed; revoking acceptance")
  686. opts.URAccepted = 0
  687. opts.URUniqueID = ""
  688. cfg.SetOptions(opts)
  689. }
  690. if opts.URAccepted >= usageReportVersion {
  691. if opts.URUniqueID == "" {
  692. // Previously the ID was generated from the node ID. We now need
  693. // to generate a new one.
  694. opts.URUniqueID = rand.String(8)
  695. cfg.SetOptions(opts)
  696. cfg.Save()
  697. }
  698. }
  699. // The usageReportingManager registers itself to listen to configuration
  700. // changes, and there's nothing more we need to tell it from the outside.
  701. // Hence we don't keep the returned pointer.
  702. newUsageReportingManager(cfg, m)
  703. if opts.RestartOnWakeup {
  704. go standbyMonitor()
  705. }
  706. if opts.AutoUpgradeIntervalH > 0 {
  707. if noUpgrade {
  708. l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.")
  709. } else {
  710. go autoUpgrade(cfg)
  711. }
  712. }
  713. events.Default.Log(events.StartupComplete, map[string]string{
  714. "myID": myID.String(),
  715. })
  716. go generatePingEvents()
  717. cleanConfigDirectory()
  718. code := <-stop
  719. mainService.Stop()
  720. l.Infoln("Exiting")
  721. if runtimeOptions.cpuProfile {
  722. pprof.StopCPUProfile()
  723. }
  724. os.Exit(code)
  725. }
  726. func myDeviceName(cfg *config.Wrapper) string {
  727. devices := cfg.Devices()
  728. myName := devices[myID].Name
  729. if myName == "" {
  730. myName, _ = os.Hostname()
  731. }
  732. return myName
  733. }
  734. func setupSignalHandling() {
  735. // Exit cleanly with "restarting" code on SIGHUP.
  736. restartSign := make(chan os.Signal, 1)
  737. sigHup := syscall.Signal(1)
  738. signal.Notify(restartSign, sigHup)
  739. go func() {
  740. <-restartSign
  741. stop <- exitRestarting
  742. }()
  743. // Exit with "success" code (no restart) on INT/TERM
  744. stopSign := make(chan os.Signal, 1)
  745. sigTerm := syscall.Signal(15)
  746. signal.Notify(stopSign, os.Interrupt, sigTerm)
  747. go func() {
  748. <-stopSign
  749. stop <- exitSuccess
  750. }()
  751. }
  752. func loadConfig() (*config.Wrapper, error) {
  753. cfgFile := locations[locConfigFile]
  754. cfg, err := config.Load(cfgFile, myID)
  755. if err != nil {
  756. myName, _ := os.Hostname()
  757. newCfg := defaultConfig(myName)
  758. cfg = config.Wrap(cfgFile, newCfg)
  759. }
  760. return cfg, err
  761. }
  762. func loadOrCreateConfig() *config.Wrapper {
  763. cfg, err := loadConfig()
  764. if os.IsNotExist(err) {
  765. cfg.Save()
  766. l.Infof("Defaults saved. Edit %s to taste or use the GUI\n", cfg.ConfigPath())
  767. } else if err != nil {
  768. l.Fatalln("Config:", err)
  769. }
  770. if cfg.RawCopy().OriginalVersion != config.CurrentVersion {
  771. err = archiveAndSaveConfig(cfg)
  772. if err != nil {
  773. l.Fatalln("Config archive:", err)
  774. }
  775. }
  776. return cfg
  777. }
  778. func archiveAndSaveConfig(cfg *config.Wrapper) error {
  779. // Copy the existing config to an archive copy
  780. archivePath := cfg.ConfigPath() + fmt.Sprintf(".v%d", cfg.RawCopy().OriginalVersion)
  781. l.Infoln("Archiving a copy of old config file format at:", archivePath)
  782. if err := copyFile(cfg.ConfigPath(), archivePath); err != nil {
  783. return err
  784. }
  785. // Do a regular atomic config sve
  786. return cfg.Save()
  787. }
  788. func copyFile(src, dst string) error {
  789. bs, err := ioutil.ReadFile(src)
  790. if err != nil {
  791. return err
  792. }
  793. if err := ioutil.WriteFile(dst, bs, 0600); err != nil {
  794. // Attempt to clean up
  795. os.Remove(dst)
  796. return err
  797. }
  798. return nil
  799. }
  800. func startAuditing(mainService *suture.Supervisor, auditFile string) {
  801. var fd io.Writer
  802. var err error
  803. var auditDest string
  804. var auditFlags int
  805. if auditFile == "-" {
  806. fd = os.Stdout
  807. auditDest = "stdout"
  808. } else if auditFile == "--" {
  809. fd = os.Stderr
  810. auditDest = "stderr"
  811. } else {
  812. if auditFile == "" {
  813. auditFile = timestampedLoc(locAuditLog)
  814. auditFlags = os.O_WRONLY | os.O_CREATE | os.O_EXCL
  815. } else {
  816. auditFlags = os.O_WRONLY | os.O_CREATE | os.O_APPEND
  817. }
  818. fd, err = os.OpenFile(auditFile, auditFlags, 0600)
  819. if err != nil {
  820. l.Fatalln("Audit:", err)
  821. }
  822. auditDest = auditFile
  823. }
  824. auditService := newAuditService(fd)
  825. mainService.Add(auditService)
  826. // We wait for the audit service to fully start before we return, to
  827. // ensure we capture all events from the start.
  828. auditService.WaitForStart()
  829. l.Infoln("Audit log in", auditDest)
  830. }
  831. func setupGUI(mainService *suture.Supervisor, cfg *config.Wrapper, m *model.Model, apiSub events.BufferedSubscription, diskSub events.BufferedSubscription, discoverer discover.CachingMux, connectionsService *connections.Service, errors, systemLog logger.Recorder, runtimeOptions RuntimeOptions) {
  832. guiCfg := cfg.GUI()
  833. if !guiCfg.Enabled {
  834. return
  835. }
  836. if guiCfg.InsecureAdminAccess {
  837. l.Warnln("Insecure admin access is enabled.")
  838. }
  839. api := newAPIService(myID, cfg, locations[locHTTPSCertFile], locations[locHTTPSKeyFile], runtimeOptions.assetDir, m, apiSub, diskSub, discoverer, connectionsService, errors, systemLog)
  840. cfg.Subscribe(api)
  841. mainService.Add(api)
  842. if cfg.Options().StartBrowser && !runtimeOptions.noBrowser && !runtimeOptions.stRestarting {
  843. // Can potentially block if the utility we are invoking doesn't
  844. // fork, and just execs, hence keep it in it's own routine.
  845. <-api.startedOnce
  846. go openURL(guiCfg.URL())
  847. }
  848. }
  849. func defaultConfig(myName string) config.Configuration {
  850. var defaultFolder config.FolderConfiguration
  851. if !noDefaultFolder {
  852. l.Infoln("Default folder created and/or linked to new config")
  853. defaultFolder = config.NewFolderConfiguration("default", locations[locDefFolder])
  854. defaultFolder.Label = "Default Folder"
  855. defaultFolder.RescanIntervalS = 60
  856. defaultFolder.MinDiskFreePct = 1
  857. defaultFolder.Devices = []config.FolderDeviceConfiguration{{DeviceID: myID}}
  858. defaultFolder.AutoNormalize = true
  859. defaultFolder.MaxConflicts = -1
  860. } else {
  861. l.Infoln("We will skip creation of a default folder on first start since the proper envvar is set")
  862. }
  863. thisDevice := config.NewDeviceConfiguration(myID, myName)
  864. thisDevice.Addresses = []string{"dynamic"}
  865. newCfg := config.New(myID)
  866. if !noDefaultFolder {
  867. newCfg.Folders = []config.FolderConfiguration{defaultFolder}
  868. }
  869. newCfg.Devices = []config.DeviceConfiguration{thisDevice}
  870. port, err := getFreePort("127.0.0.1", 8384)
  871. if err != nil {
  872. l.Fatalln("get free port (GUI):", err)
  873. }
  874. newCfg.GUI.RawAddress = fmt.Sprintf("127.0.0.1:%d", port)
  875. port, err = getFreePort("0.0.0.0", 22000)
  876. if err != nil {
  877. l.Fatalln("get free port (BEP):", err)
  878. }
  879. if port == 22000 {
  880. newCfg.Options.ListenAddresses = []string{"default"}
  881. } else {
  882. newCfg.Options.ListenAddresses = []string{
  883. fmt.Sprintf("tcp://%s", net.JoinHostPort("0.0.0.0", strconv.Itoa(port))),
  884. "dynamic+https://relays.syncthing.net/endpoint",
  885. }
  886. }
  887. return newCfg
  888. }
  889. func generatePingEvents() {
  890. for {
  891. time.Sleep(pingEventInterval)
  892. events.Default.Log(events.Ping, nil)
  893. }
  894. }
  895. func resetDB() error {
  896. return os.RemoveAll(locations[locDatabase])
  897. }
  898. func restart() {
  899. l.Infoln("Restarting")
  900. stop <- exitRestarting
  901. }
  902. func shutdown() {
  903. l.Infoln("Shutting down")
  904. stop <- exitSuccess
  905. }
  906. func ensureDir(dir string, mode os.FileMode) {
  907. err := osutil.MkdirAll(dir, mode)
  908. if err != nil {
  909. l.Fatalln(err)
  910. }
  911. if fi, err := os.Stat(dir); err == nil {
  912. // Apprently the stat may fail even though the mkdirall passed. If it
  913. // does, we'll just assume things are in order and let other things
  914. // fail (like loading or creating the config...).
  915. currentMode := fi.Mode() & 0777
  916. if currentMode != mode {
  917. err := os.Chmod(dir, mode)
  918. // This can fail on crappy filesystems, nothing we can do about it.
  919. if err != nil {
  920. l.Warnln(err)
  921. }
  922. }
  923. }
  924. }
  925. // getFreePort returns a free TCP port fort listening on. The ports given are
  926. // tried in succession and the first to succeed is returned. If none succeed,
  927. // a random high port is returned.
  928. func getFreePort(host string, ports ...int) (int, error) {
  929. for _, port := range ports {
  930. c, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port))
  931. if err == nil {
  932. c.Close()
  933. return port, nil
  934. }
  935. }
  936. c, err := net.Listen("tcp", host+":0")
  937. if err != nil {
  938. return 0, err
  939. }
  940. addr := c.Addr().(*net.TCPAddr)
  941. c.Close()
  942. return addr.Port, nil
  943. }
  944. func standbyMonitor() {
  945. restartDelay := 60 * time.Second
  946. now := time.Now()
  947. for {
  948. time.Sleep(10 * time.Second)
  949. if time.Since(now) > 2*time.Minute {
  950. l.Infof("Paused state detected, possibly woke up from standby. Restarting in %v.", restartDelay)
  951. // We most likely just woke from standby. If we restart
  952. // immediately chances are we won't have networking ready. Give
  953. // things a moment to stabilize.
  954. time.Sleep(restartDelay)
  955. restart()
  956. return
  957. }
  958. now = time.Now()
  959. }
  960. }
  961. func autoUpgrade(cfg *config.Wrapper) {
  962. timer := time.NewTimer(0)
  963. sub := events.Default.Subscribe(events.DeviceConnected)
  964. for {
  965. select {
  966. case event := <-sub.C():
  967. data, ok := event.Data.(map[string]string)
  968. if !ok || data["clientName"] != "syncthing" || upgrade.CompareVersions(data["clientVersion"], Version) != upgrade.Newer {
  969. continue
  970. }
  971. l.Infof("Connected to device %s with a newer version (current %q < remote %q). Checking for upgrades.", data["id"], Version, data["clientVersion"])
  972. case <-timer.C:
  973. }
  974. opts := cfg.Options()
  975. rel, err := upgrade.LatestRelease(opts.ReleasesURL, Version, opts.UpgradeToPreReleases)
  976. if err == upgrade.ErrUpgradeUnsupported {
  977. events.Default.Unsubscribe(sub)
  978. return
  979. }
  980. if err != nil {
  981. // Don't complain too loudly here; we might simply not have
  982. // internet connectivity, or the upgrade server might be down.
  983. l.Infoln("Automatic upgrade:", err)
  984. timer.Reset(time.Duration(cfg.Options().AutoUpgradeIntervalH) * time.Hour)
  985. continue
  986. }
  987. if upgrade.CompareVersions(rel.Tag, Version) != upgrade.Newer {
  988. // Skip equal, older or majorly newer (incompatible) versions
  989. timer.Reset(time.Duration(cfg.Options().AutoUpgradeIntervalH) * time.Hour)
  990. continue
  991. }
  992. l.Infof("Automatic upgrade (current %q < latest %q)", Version, rel.Tag)
  993. err = upgrade.To(rel)
  994. if err != nil {
  995. l.Warnln("Automatic upgrade:", err)
  996. timer.Reset(time.Duration(cfg.Options().AutoUpgradeIntervalH) * time.Hour)
  997. continue
  998. }
  999. events.Default.Unsubscribe(sub)
  1000. l.Warnf("Automatically upgraded to version %q. Restarting in 1 minute.", rel.Tag)
  1001. time.Sleep(time.Minute)
  1002. stop <- exitUpgrading
  1003. return
  1004. }
  1005. }
  1006. // cleanConfigDirectory removes old, unused configuration and index formats, a
  1007. // suitable time after they have gone out of fashion.
  1008. func cleanConfigDirectory() {
  1009. patterns := map[string]time.Duration{
  1010. "panic-*.log": 7 * 24 * time.Hour, // keep panic logs for a week
  1011. "audit-*.log": 7 * 24 * time.Hour, // keep audit logs for a week
  1012. "index": 14 * 24 * time.Hour, // keep old index format for two weeks
  1013. "index-v0.11.0.db": 14 * 24 * time.Hour, // keep old index format for two weeks
  1014. "index-v0.13.0.db": 14 * 24 * time.Hour, // keep old index format for two weeks
  1015. "index*.converted": 14 * 24 * time.Hour, // keep old converted indexes for two weeks
  1016. "config.xml.v*": 30 * 24 * time.Hour, // old config versions for a month
  1017. "*.idx.gz": 30 * 24 * time.Hour, // these should for sure no longer exist
  1018. "backup-of-v0.8": 30 * 24 * time.Hour, // these neither
  1019. "tmp-index-sorter.*": time.Minute, // these should never exist on startup
  1020. }
  1021. for pat, dur := range patterns {
  1022. pat = filepath.Join(baseDirs["config"], pat)
  1023. files, err := osutil.Glob(pat)
  1024. if err != nil {
  1025. l.Infoln("Cleaning:", err)
  1026. continue
  1027. }
  1028. for _, file := range files {
  1029. info, err := osutil.Lstat(file)
  1030. if err != nil {
  1031. l.Infoln("Cleaning:", err)
  1032. continue
  1033. }
  1034. if time.Since(info.ModTime()) > dur {
  1035. if err = os.RemoveAll(file); err != nil {
  1036. l.Infoln("Cleaning:", err)
  1037. } else {
  1038. l.Infoln("Cleaned away old file", filepath.Base(file))
  1039. }
  1040. }
  1041. }
  1042. }
  1043. }
  1044. // checkShortIDs verifies that the configuration won't result in duplicate
  1045. // short ID:s; that is, that the devices in the cluster all have unique
  1046. // initial 64 bits.
  1047. func checkShortIDs(cfg *config.Wrapper) error {
  1048. exists := make(map[protocol.ShortID]protocol.DeviceID)
  1049. for deviceID := range cfg.Devices() {
  1050. shortID := deviceID.Short()
  1051. if otherID, ok := exists[shortID]; ok {
  1052. return fmt.Errorf("%v in conflict with %v", deviceID, otherID)
  1053. }
  1054. exists[shortID] = deviceID
  1055. }
  1056. return nil
  1057. }
  1058. func showPaths() {
  1059. fmt.Printf("Configuration file:\n\t%s\n\n", locations[locConfigFile])
  1060. fmt.Printf("Database directory:\n\t%s\n\n", locations[locDatabase])
  1061. fmt.Printf("Device private key & certificate files:\n\t%s\n\t%s\n\n", locations[locKeyFile], locations[locCertFile])
  1062. fmt.Printf("HTTPS private key & certificate files:\n\t%s\n\t%s\n\n", locations[locHTTPSKeyFile], locations[locHTTPSCertFile])
  1063. fmt.Printf("Log file:\n\t%s\n\n", locations[locLogFile])
  1064. fmt.Printf("GUI override directory:\n\t%s\n\n", locations[locGUIAssets])
  1065. fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations[locDefFolder])
  1066. }
  1067. func setPauseState(cfg *config.Wrapper, paused bool) {
  1068. raw := cfg.RawCopy()
  1069. for i := range raw.Devices {
  1070. raw.Devices[i].Paused = paused
  1071. }
  1072. for i := range raw.Folders {
  1073. raw.Folders[i].Paused = paused
  1074. }
  1075. if err := cfg.Replace(raw); err != nil {
  1076. l.Fatalln("Cannot adjust paused state:", err)
  1077. }
  1078. }