2014-11-16 21:13:20 +01:00
// Copyright (C) 2014 The Syncthing Authors.
2014-09-29 21:43:32 +02:00
//
2015-03-07 21:36:35 +01:00
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
2017-02-09 07:52:18 +01:00
// You can obtain one at https://mozilla.org/MPL/2.0/.
2014-06-01 22:50:14 +02:00
2013-12-15 11:43:31 +01:00
package main
import (
2015-10-03 17:25:21 +02:00
"bytes"
2013-12-15 11:43:31 +01:00
"crypto/tls"
2015-10-31 12:31:25 +01:00
"errors"
2014-01-26 14:28:41 +01:00
"flag"
2014-01-08 14:37:33 +01:00
"fmt"
2017-01-03 09:54:28 +01:00
"io"
2015-04-30 21:33:32 +02:00
"io/ioutil"
2013-12-15 11:43:31 +01:00
"log"
"net"
"net/http"
2016-05-28 16:08:26 +02:00
"net/url"
2013-12-15 11:43:31 +01:00
"os"
2015-12-14 09:57:49 +01:00
"os/signal"
2016-05-28 16:08:26 +02:00
"path"
2014-03-28 14:36:57 +01:00
"path/filepath"
2014-06-23 10:38:50 +02:00
"regexp"
2014-01-10 00:09:27 +01:00
"runtime"
2014-04-14 12:13:50 +02:00
"runtime/pprof"
2015-10-03 17:25:21 +02:00
"sort"
2014-04-18 13:20:42 +02:00
"strconv"
2013-12-15 11:43:31 +01:00
"strings"
2015-12-14 09:57:49 +01:00
"syscall"
2013-12-15 11:43:31 +01:00
"time"
2014-04-08 13:45:18 +02:00
2015-08-06 11:29:25 +02:00
"github.com/syncthing/syncthing/lib/config"
2015-09-23 22:03:36 +02:00
"github.com/syncthing/syncthing/lib/connections"
2015-08-06 11:29:25 +02:00
"github.com/syncthing/syncthing/lib/db"
2015-10-13 20:52:22 +02:00
"github.com/syncthing/syncthing/lib/dialer"
2015-08-06 11:29:25 +02:00
"github.com/syncthing/syncthing/lib/discover"
"github.com/syncthing/syncthing/lib/events"
2017-08-19 16:36:56 +02:00
"github.com/syncthing/syncthing/lib/fs"
2015-10-03 17:25:21 +02:00
"github.com/syncthing/syncthing/lib/logger"
2015-08-06 11:29:25 +02:00
"github.com/syncthing/syncthing/lib/model"
"github.com/syncthing/syncthing/lib/osutil"
2015-09-22 19:38:46 +02:00
"github.com/syncthing/syncthing/lib/protocol"
2016-05-26 09:02:56 +02:00
"github.com/syncthing/syncthing/lib/rand"
2016-09-23 21:33:54 +02:00
"github.com/syncthing/syncthing/lib/sha256"
2015-09-02 22:05:54 +02:00
"github.com/syncthing/syncthing/lib/tlsutil"
2015-08-06 11:29:25 +02:00
"github.com/syncthing/syncthing/lib/upgrade"
2015-07-17 22:22:07 +02:00
2018-09-08 11:56:56 +02:00
"github.com/thejerf/suture"
2016-11-11 23:23:48 +01:00
_ "net/http/pprof" // Need to import this to support STPROFILER.
2013-12-15 11:43:31 +01:00
)
2014-04-19 16:38:11 +02:00
var (
2016-01-10 18:41:15 +01:00
Version = "unknown-dev"
2016-07-04 12:58:45 +02:00
Codename = "Dysprosium Dragonfly"
2016-01-10 18:41:15 +01:00
BuildStamp = "0"
BuildDate time . Time
BuildHost = "unknown"
BuildUser = "unknown"
IsRelease bool
2017-01-30 22:33:07 +01:00
IsCandidate bool
2016-01-10 18:41:15 +01:00
IsBeta bool
LongVersion string
2017-03-17 00:40:27 +01:00
BuildTags [ ] string
2016-03-10 13:19:00 +01:00
allowedVersionExp = regexp . MustCompile ( ` ^v\d+\.\d+\.\d+(-[a-z0-9]+)*(\.\d+)*(\+\d+-g[0-9a-f]+)?(-[^\s]+)?$ ` )
2014-04-19 16:38:11 +02:00
)
2014-09-02 13:08:24 +02:00
const (
exitSuccess = 0
exitError = 1
exitNoUpgradeAvailable = 2
exitRestarting = 3
2014-09-13 16:25:39 +02:00
exitUpgrading = 4
2014-09-02 13:08:24 +02:00
)
2015-03-26 23:26:51 +01:00
const (
2015-09-02 22:05:54 +02:00
bepProtocolName = "bep/1.0"
tlsDefaultCommonName = "syncthing"
2015-11-27 09:09:39 +01:00
httpsRSABits = 2048
bepRSABits = 0 // 384 bit ECDSA used instead
2017-01-31 13:04:29 +01:00
defaultEventTimeout = time . Minute
2015-10-03 17:25:21 +02:00
maxSystemErrors = 5
initialSystemLog = 10
maxSystemLog = 250
2015-03-26 23:26:51 +01:00
)
2015-03-05 15:58:16 +01:00
2014-04-19 16:38:11 +02:00
func init ( ) {
2014-06-23 10:38:50 +02:00
if Version != "unknown-dev" {
// If not a generic dev build, version string should come from git describe
2016-01-10 18:41:15 +01:00
if ! allowedVersionExp . MatchString ( Version ) {
l . Fatalf ( "Invalid version string %q;\n\tdoes not match regexp %v" , Version , allowedVersionExp )
2014-06-23 10:38:50 +02:00
}
}
2017-03-17 00:40:27 +01:00
}
2014-06-23 10:38:50 +02:00
2017-03-17 00:40:27 +01:00
func setBuildMetadata ( ) {
2017-01-30 22:33:07 +01:00
// Check for a clean release build. A release is something like
// "v0.1.2", with an optional suffix of letters and dot separated
// numbers like "-beta3.47". If there's more stuff, like a plus sign and
// a commit hash and so on, then it's not a release. If it has a dash in
// it, it's some sort of beta, release candidate or special build. If it
// has "-rc." in it, like "v0.14.35-rc.42", then it's a candidate build.
//
// So, every build that is not a stable release build has IsBeta = true.
// This is used to enable some extra debugging (the deadlock detector).
//
// Release candidate builds are also "betas" from this point of view and
// will have that debugging enabled. In addition, some features are
// forced for release candidates - auto upgrade, and usage reporting.
2015-04-21 02:06:10 +02:00
exp := regexp . MustCompile ( ` ^v\d+\.\d+\.\d+(-[a-z]+[\d\.]+)?$ ` )
2014-10-23 19:09:53 +02:00
IsRelease = exp . MatchString ( Version )
2017-01-30 22:33:07 +01:00
IsCandidate = strings . Contains ( Version , "-rc." )
2015-04-21 02:06:10 +02:00
IsBeta = strings . Contains ( Version , "-" )
2014-10-23 19:09:53 +02:00
2014-04-19 16:38:11 +02:00
stamp , _ := strconv . Atoi ( BuildStamp )
BuildDate = time . Unix ( int64 ( stamp ) , 0 )
2014-04-19 16:40:19 +02:00
2014-05-11 22:26:48 +02:00
date := BuildDate . UTC ( ) . Format ( "2006-01-02 15:04:05 MST" )
2015-11-24 20:46:07 +01:00
LongVersion = fmt . Sprintf ( ` syncthing %s "%s" (%s %s-%s) %s@%s %s ` , Version , Codename , runtime . Version ( ) , runtime . GOOS , runtime . GOARCH , BuildUser , BuildHost , date )
2017-03-17 00:40:27 +01:00
if len ( BuildTags ) > 0 {
LongVersion = fmt . Sprintf ( "%s [%s]" , LongVersion , strings . Join ( BuildTags , ", " ) )
}
2014-04-19 16:38:11 +02:00
}
2013-12-18 19:36:28 +01:00
2013-12-15 11:43:31 +01:00
var (
2015-12-18 17:34:39 +01:00
myID protocol . DeviceID
stop = make ( chan int )
lans [ ] * net . IPNet
2014-01-26 14:28:41 +01:00
)
2014-03-09 08:48:29 +01:00
const (
usage = "syncthing [options]"
2014-10-06 17:55:54 +02:00
extraUsage = `
The - logflags value is a sum of the following :
2014-06-04 10:24:30 +02:00
1 Date
2 Time
4 Microsecond time
8 Long filename
16 Short filename
I . e . to prefix each log line with date and time , set - logflags = 3 ( 1 + 2 from
above ) . The value 0 is used to disable all of the above . The default is to
show time only ( 2 ) .
2014-03-12 10:12:35 +01:00
2014-10-06 17:55:54 +02:00
Development Settings
-- -- -- -- -- -- -- -- -- --
2014-08-27 23:38:36 +02:00
2017-01-10 08:50:11 +01:00
The following environment variables modify Syncthing ' s behavior in ways that
2014-10-06 17:55:54 +02:00
are mostly useful for developers . Use with care .
2014-08-16 00:24:24 +02:00
2016-01-09 02:11:06 +01:00
STNODEFAULTFOLDER Don ' t create a default folder when starting for the first
2017-01-10 08:50:11 +01:00
time . This variable will be ignored anytime after the first
2016-01-09 02:11:06 +01:00
run .
2014-04-14 12:13:50 +02:00
2016-01-09 02:11:06 +01:00
STGUIASSETS Directory to load GUI assets from . Overrides compiled in
assets .
2014-04-14 12:13:50 +02:00
2016-01-09 02:11:06 +01:00
STTRACE A comma separated string of facilities to trace . The valid
facility strings listed below .
2014-08-13 14:38:23 +02:00
2017-01-10 08:50:11 +01:00
STPROFILER Set to a listen address such as "127.0.0.1:9090" to start
the profiler with HTTP access .
2014-08-13 14:38:23 +02:00
2016-01-09 02:11:06 +01:00
STCPUPROFILE Write a CPU profile to cpu - $ pid . pprof on exit .
2014-08-13 14:38:23 +02:00
2016-01-09 02:11:06 +01:00
STHEAPPROFILE Write heap profiles to heap - $ pid - $ timestamp . pprof each time
heap usage increases .
2014-08-13 14:38:23 +02:00
2016-01-09 02:11:06 +01:00
STBLOCKPROFILE Write block profiles to block - $ pid - $ timestamp . pprof every 20
seconds .
2014-11-26 19:48:31 +01:00
2016-01-09 02:11:06 +01:00
STPERFSTATS Write running performance statistics to perf - $ pid . csv . Not
supported on Windows .
2015-01-27 22:21:39 +01:00
2017-01-10 08:50:11 +01:00
STDEADLOCKTIMEOUT Used for debugging internal deadlocks ; sets debug
sensitivity . Use only under direction of a developer .
2017-12-13 20:40:12 +01:00
STLOCKTHRESHOLD Used for debugging internal deadlocks ; sets debug
sensitivity . Use only under direction of a developer .
2017-01-10 08:50:11 +01:00
STNORESTART Equivalent to the - no - restart argument . Disable the
Syncthing monitor process which handles restarts for some
configuration changes , upgrades , crashes and also log file
writing ( stdout is still written ) .
2016-01-09 02:11:06 +01:00
STNOUPGRADE Disable automatic upgrades .
2015-03-29 19:05:22 +02:00
2016-09-23 21:33:54 +02:00
STHASHING Select the SHA256 hashing package to use . Possible values
are "standard" for the Go standard library implementation ,
"minio" for the github . com / minio / sha256 - simd implementation ,
and blank ( the default ) for auto detection .
2018-02-21 08:26:57 +01:00
STRECHECKDBEVERY Set to a time interval to override the default database
2017-12-14 10:51:17 +01:00
check interval of 30 days ( 720 h ) . The interval understands
"h" , "m" and "s" abbreviations for hours minutes and seconds .
Valid values are like "720h" , "30s" , etc .
2016-01-09 02:11:06 +01:00
GOMAXPROCS Set the maximum number of CPU cores to use . Defaults to all
available CPU cores .
GOGC Percentage of heap growth at which to trigger GC . Default is
100. Lower numbers keep peak memory usage down , at the price
2017-01-10 08:50:11 +01:00
of CPU usage ( i . e . performance ) .
2015-10-03 17:25:21 +02:00
Debugging Facilities
-- -- -- -- -- -- -- -- -- --
The following are valid values for the STTRACE variable :
% s `
2014-03-09 08:48:29 +01:00
)
2015-12-17 23:40:46 +01:00
// Environment options
2014-09-02 13:08:24 +02:00
var (
2017-01-30 22:33:07 +01:00
noUpgradeFromEnv = os . Getenv ( "STNOUPGRADE" ) != ""
innerProcess = os . Getenv ( "STNORESTART" ) != "" || os . Getenv ( "STMONITORED" ) != ""
noDefaultFolder = os . Getenv ( "STNODEFAULTFOLDER" ) != ""
2015-12-17 23:40:46 +01:00
)
type RuntimeOptions struct {
confDir string
2016-12-26 13:48:54 +01:00
resetDatabase bool
resetDeltaIdxs bool
2015-09-30 09:32:17 +02:00
showVersion bool
2016-02-02 10:27:01 +01:00
showPaths bool
2017-09-25 08:05:21 +02:00
showDeviceId bool
2015-09-30 09:32:17 +02:00
doUpgrade bool
doUpgradeCheck bool
upgradeTo string
noBrowser bool
2015-12-16 16:11:01 +01:00
browserOnly bool
2015-12-18 18:28:02 +01:00
hideConsole bool
2015-09-30 09:32:17 +02:00
logFile string
auditEnabled bool
2017-01-03 09:54:28 +01:00
auditFile string
2015-09-30 09:32:17 +02:00
verbose bool
paused bool
2016-12-21 19:41:25 +01:00
unpaused bool
2015-12-16 21:33:14 +01:00
guiAddress string
guiAPIKey string
generateDir string
2015-12-17 23:40:46 +01:00
noRestart bool
profiler string
2015-12-18 18:00:59 +01:00
assetDir string
2015-12-17 23:40:46 +01:00
cpuProfile bool
stRestarting bool
2015-12-18 17:34:39 +01:00
logFlags int
2018-09-15 08:17:31 +02:00
showHelp bool
2015-12-17 23:40:46 +01:00
}
2014-09-02 13:08:24 +02:00
2015-12-17 23:40:46 +01:00
func defaultRuntimeOptions ( ) RuntimeOptions {
options := RuntimeOptions {
noRestart : os . Getenv ( "STNORESTART" ) != "" ,
profiler : os . Getenv ( "STPROFILER" ) ,
2015-12-18 18:00:59 +01:00
assetDir : os . Getenv ( "STGUIASSETS" ) ,
2015-12-17 23:40:46 +01:00
cpuProfile : os . Getenv ( "STCPUPROFILE" ) != "" ,
stRestarting : os . Getenv ( "STRESTART" ) != "" ,
2015-12-18 17:34:39 +01:00
logFlags : log . Ltime ,
2015-12-17 23:40:46 +01:00
}
2014-12-04 21:25:35 +01:00
2015-12-18 17:34:39 +01:00
if os . Getenv ( "STTRACE" ) != "" {
2017-06-02 09:04:06 +02:00
options . logFlags = logger . DebugFlags
2015-12-18 17:34:39 +01:00
}
2015-12-21 11:55:58 +01:00
if runtime . GOOS != "windows" {
// On non-Windows, we explicitly default to "-" which means stdout. On
// Windows, the blank options.logFile will later be replaced with the
// default path, unless the user has manually specified "-" or
// something else.
options . logFile = "-"
2014-10-19 14:57:03 +02:00
}
2015-12-17 23:40:46 +01:00
return options
}
func parseCommandLineOptions ( ) RuntimeOptions {
options := defaultRuntimeOptions ( )
flag . StringVar ( & options . generateDir , "generate" , "" , "Generate key and config in specified dir, then exit" )
flag . StringVar ( & options . guiAddress , "gui-address" , options . guiAddress , "Override GUI address (e.g. \"http://192.0.2.42:8443\")" )
flag . StringVar ( & options . guiAPIKey , "gui-apikey" , options . guiAPIKey , "Override GUI API key" )
flag . StringVar ( & options . confDir , "home" , "" , "Set configuration directory" )
2015-12-18 17:34:39 +01:00
flag . IntVar ( & options . logFlags , "logflags" , options . logFlags , "Select information in log line prefix (see below)" )
2015-12-17 23:40:46 +01:00
flag . BoolVar ( & options . noBrowser , "no-browser" , false , "Do not start browser" )
flag . BoolVar ( & options . browserOnly , "browser-only" , false , "Open GUI in browser" )
2017-01-10 08:50:11 +01:00
flag . BoolVar ( & options . noRestart , "no-restart" , options . noRestart , "Disable monitor process, managed restarts and log file writing" )
2016-12-26 13:48:54 +01:00
flag . BoolVar ( & options . resetDatabase , "reset-database" , false , "Reset the database, forcing a full rescan and resync" )
flag . BoolVar ( & options . resetDeltaIdxs , "reset-deltas" , false , "Reset delta index IDs, forcing a full index exchange" )
2015-12-17 23:40:46 +01:00
flag . BoolVar ( & options . doUpgrade , "upgrade" , false , "Perform upgrade" )
flag . BoolVar ( & options . doUpgradeCheck , "upgrade-check" , false , "Check for available upgrade" )
flag . BoolVar ( & options . showVersion , "version" , false , "Show version" )
2018-09-15 08:17:31 +02:00
flag . BoolVar ( & options . showHelp , "help" , false , "Show this help" )
2016-02-02 10:27:01 +01:00
flag . BoolVar ( & options . showPaths , "paths" , false , "Show configuration paths" )
2017-09-25 08:05:21 +02:00
flag . BoolVar ( & options . showDeviceId , "device-id" , false , "Show the device ID" )
2015-12-17 23:40:46 +01:00
flag . StringVar ( & options . upgradeTo , "upgrade-to" , options . upgradeTo , "Force upgrade directly from specified URL" )
flag . BoolVar ( & options . auditEnabled , "audit" , false , "Write events to audit file" )
flag . BoolVar ( & options . verbose , "verbose" , false , "Print verbose log output" )
2016-12-21 19:41:25 +01:00
flag . BoolVar ( & options . paused , "paused" , false , "Start with all devices and folders paused" )
flag . BoolVar ( & options . unpaused , "unpaused" , false , "Start with all devices and folders unpaused" )
2018-03-12 13:17:12 +01:00
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." )
2017-01-03 09:54:28 +01:00
flag . StringVar ( & options . auditFile , "auditfile" , options . auditFile , "Specify audit file (use \"-\" for stdout, \"--\" for stderr)" )
2015-12-17 23:40:46 +01:00
if runtime . GOOS == "windows" {
// Allow user to hide the console window
2015-12-18 18:28:02 +01:00
flag . BoolVar ( & options . hideConsole , "no-console" , false , "Hide console window" )
2015-12-17 23:40:46 +01:00
}
2014-10-06 17:55:54 +02:00
2016-02-02 10:27:01 +01:00
longUsage := fmt . Sprintf ( extraUsage , debugFacilities ( ) )
2015-10-03 17:25:21 +02:00
flag . Usage = usageFor ( flag . CommandLine , usage , longUsage )
2014-01-26 14:28:41 +01:00
flag . Parse ( )
2015-12-17 23:40:46 +01:00
2016-11-27 12:21:05 +01:00
if len ( flag . Args ( ) ) > 0 {
flag . Usage ( )
os . Exit ( 2 )
}
2015-12-17 23:40:46 +01:00
return options
2015-12-16 21:33:14 +01:00
}
func main ( ) {
2017-03-17 00:40:27 +01:00
setBuildMetadata ( )
2015-12-17 23:40:46 +01:00
options := parseCommandLineOptions ( )
2015-12-18 17:34:39 +01:00
l . SetFlags ( options . logFlags )
2014-01-08 14:37:33 +01:00
2015-12-17 23:40:46 +01:00
if options . guiAddress != "" {
2015-10-12 15:27:57 +02:00
// The config picks this up from the environment.
2015-12-17 23:40:46 +01:00
os . Setenv ( "STGUIADDRESS" , options . guiAddress )
2015-10-12 15:27:57 +02:00
}
2015-12-17 23:40:46 +01:00
if options . guiAPIKey != "" {
2015-10-12 15:27:57 +02:00
// The config picks this up from the environment.
2015-12-17 23:40:46 +01:00
os . Setenv ( "STGUIAPIKEY" , options . guiAPIKey )
2015-10-12 15:27:57 +02:00
}
2017-01-18 18:59:48 +01:00
// Check for options which are not compatible with each other. We have
// to check logfile before it's set to the default below - we only want
// to complain if they set -logfile explicitly, not if it's set to its
// default location
if options . noRestart && ( options . logFile != "" && options . logFile != "-" ) {
2017-01-18 13:19:22 +01:00
l . Fatalln ( "-logfile may not be used with -no-restart or STNORESTART" )
}
2015-12-18 18:28:02 +01:00
if options . hideConsole {
2014-12-04 21:25:35 +01:00
osutil . HideConsole ( )
}
2015-12-17 23:40:46 +01:00
if options . confDir != "" {
2014-10-06 17:55:54 +02:00
// Not set as default above because the string can be really long.
2017-03-04 08:49:48 +01:00
if ! filepath . IsAbs ( options . confDir ) {
2017-03-09 15:57:12 +01:00
var err error
options . confDir , err = filepath . Abs ( options . confDir )
2017-03-04 08:49:48 +01:00
if err != nil {
l . Fatalln ( err )
}
}
2017-03-09 15:57:12 +01:00
baseDirs [ "config" ] = options . confDir
2015-03-29 12:55:27 +02:00
}
2015-04-03 20:22:39 +02:00
if err := expandLocations ( ) ; err != nil {
l . Fatalln ( err )
}
2015-12-21 11:55:58 +01:00
if options . logFile == "" {
// Blank means use the default logfile location. We must set this
// *after* expandLocations above.
options . logFile = locations [ locLogFile ]
}
2015-12-26 13:31:58 +01:00
if options . assetDir == "" {
// The asset dir is blank if STGUIASSETS wasn't set, in which case we
// should look for extra assets in the default place.
options . assetDir = locations [ locGUIAssets ]
}
2015-12-17 23:40:46 +01:00
if options . showVersion {
2014-04-19 16:40:19 +02:00
fmt . Println ( LongVersion )
2014-04-14 12:13:50 +02:00
return
2013-12-15 11:43:31 +01:00
}
2014-01-08 14:37:33 +01:00
2018-09-15 08:17:31 +02:00
if options . showHelp {
flag . Usage ( )
return
}
2016-02-02 10:27:01 +01:00
if options . showPaths {
2018-03-28 19:02:43 +02:00
showPaths ( options )
2016-02-02 10:27:01 +01:00
return
}
2017-09-25 08:05:21 +02:00
if options . showDeviceId {
cert , err := tls . LoadX509KeyPair ( locations [ locCertFile ] , locations [ locKeyFile ] )
if err != nil {
l . Fatalln ( "Error reading device ID:" , err )
}
myID = protocol . NewDeviceID ( cert . Certificate [ 0 ] )
fmt . Println ( myID )
return
}
2015-12-17 23:40:46 +01:00
if options . browserOnly {
2015-12-16 16:11:01 +01:00
openGUI ( )
return
}
2015-12-17 23:40:46 +01:00
if options . generateDir != "" {
generate ( options . generateDir )
2014-08-03 09:41:08 +02:00
return
2014-07-31 21:29:44 +02:00
}
2014-09-18 00:19:23 +02:00
// Ensure that our home directory exists.
2015-03-29 12:55:27 +02:00
ensureDir ( baseDirs [ "config" ] , 0700 )
2014-09-18 00:19:23 +02:00
2015-12-17 23:40:46 +01:00
if options . upgradeTo != "" {
err := upgrade . ToURL ( options . upgradeTo )
2014-12-22 12:07:04 +01:00
if err != nil {
l . Fatalln ( "Upgrade:" , err ) // exits 1
}
2016-01-16 23:01:57 +01:00
l . Infoln ( "Upgraded from" , options . upgradeTo )
2014-12-22 12:07:04 +01:00
return
}
2015-12-17 23:40:46 +01:00
if options . doUpgradeCheck {
2015-12-16 20:29:37 +01:00
checkUpgrade ( )
return
}
2014-12-08 16:36:15 +01:00
2015-12-17 23:40:46 +01:00
if options . doUpgrade {
2015-12-16 20:29:37 +01:00
release := checkUpgrade ( )
performUpgrade ( release )
2014-12-08 16:36:15 +01:00
return
2014-07-31 10:26:45 +02:00
}
2016-12-26 13:48:54 +01:00
if options . resetDatabase {
2015-04-03 20:06:03 +02:00
resetDB ( )
2014-09-02 13:08:24 +02:00
return
2014-08-03 09:41:08 +02:00
}
2017-01-25 08:33:35 +01:00
if innerProcess || options . noRestart {
2015-12-17 23:40:46 +01:00
syncthingMain ( options )
2014-09-02 13:08:24 +02:00
} else {
2015-12-17 23:40:46 +01:00
monitorMain ( options )
2014-09-02 13:08:24 +02:00
}
}
2015-12-16 16:11:01 +01:00
func openGUI ( ) {
2017-10-21 11:00:24 +02:00
cfg , _ := loadOrDefaultConfig ( )
2015-12-16 16:11:01 +01:00
if cfg . GUI ( ) . Enabled {
openURL ( cfg . GUI ( ) . URL ( ) )
} else {
l . Warnln ( "Browser: GUI is currently disabled" )
}
}
2015-11-22 07:34:04 +01:00
func generate ( generateDir string ) {
2017-08-19 16:36:56 +02:00
dir , err := fs . ExpandTilde ( generateDir )
2015-11-22 07:34:04 +01:00
if err != nil {
l . Fatalln ( "generate:" , err )
}
2015-12-18 18:47:26 +01:00
ensureDir ( dir , 0700 )
2015-11-22 07:34:04 +01:00
certFile , keyFile := filepath . Join ( dir , "cert.pem" ) , filepath . Join ( dir , "key.pem" )
cert , err := tls . LoadX509KeyPair ( certFile , keyFile )
if err == nil {
l . Warnln ( "Key exists; will not overwrite." )
l . Infoln ( "Device ID:" , protocol . NewDeviceID ( cert . Certificate [ 0 ] ) )
} else {
2015-11-27 09:09:39 +01:00
cert , err = tlsutil . NewCertificate ( certFile , keyFile , tlsDefaultCommonName , bepRSABits )
2015-11-22 07:34:04 +01:00
if err != nil {
l . Fatalln ( "Create certificate:" , err )
}
myID = protocol . NewDeviceID ( cert . Certificate [ 0 ] )
if err != nil {
l . Fatalln ( "Load certificate:" , err )
}
if err == nil {
l . Infoln ( "Device ID:" , protocol . NewDeviceID ( cert . Certificate [ 0 ] ) )
}
}
cfgFile := filepath . Join ( dir , "config.xml" )
if _ , err := os . Stat ( cfgFile ) ; err == nil {
l . Warnln ( "Config exists; will not overwrite." )
return
}
2017-10-21 11:00:24 +02:00
var cfg = defaultConfig ( cfgFile )
2015-11-22 07:34:04 +01:00
err = cfg . Save ( )
if err != nil {
l . Warnln ( "Failed to save config" , err )
}
}
2015-10-03 17:25:21 +02:00
func debugFacilities ( ) string {
facilities := l . Facilities ( )
// Get a sorted list of names
var names [ ] string
maxLen := 0
for name := range facilities {
names = append ( names , name )
if len ( name ) > maxLen {
maxLen = len ( name )
}
}
sort . Strings ( names )
// Format the choices
b := new ( bytes . Buffer )
for _ , name := range names {
fmt . Fprintf ( b , " %-*s - %s\n" , maxLen , name , facilities [ name ] )
}
return b . String ( )
}
2015-12-16 20:29:37 +01:00
func checkUpgrade ( ) upgrade . Release {
2017-10-21 11:00:24 +02:00
cfg , _ := loadOrDefaultConfig ( )
2017-01-27 13:17:06 +01:00
opts := cfg . Options ( )
release , err := upgrade . LatestRelease ( opts . ReleasesURL , Version , opts . UpgradeToPreReleases )
2015-12-16 20:29:37 +01:00
if err != nil {
l . Fatalln ( "Upgrade:" , err )
}
if upgrade . CompareVersions ( release . Tag , Version ) <= 0 {
noUpgradeMessage := "No upgrade available (current %q >= latest %q)."
l . Infof ( noUpgradeMessage , Version , release . Tag )
os . Exit ( exitNoUpgradeAvailable )
}
l . Infof ( "Upgrade available (current %q < latest %q)" , Version , release . Tag )
return release
}
func performUpgrade ( release upgrade . Release ) {
// Use leveldb database locks to protect against concurrent upgrades
_ , err := db . Open ( locations [ locDatabase ] )
if err == nil {
err = upgrade . To ( release )
if err != nil {
l . Fatalln ( "Upgrade:" , err )
}
2016-01-16 23:01:57 +01:00
l . Infof ( "Upgraded to %q" , release . Tag )
2015-12-16 20:29:37 +01:00
} else {
l . Infoln ( "Attempting upgrade through running Syncthing..." )
err = upgradeViaRest ( )
if err != nil {
l . Fatalln ( "Upgrade:" , err )
}
2016-01-16 23:01:57 +01:00
l . Infoln ( "Syncthing upgrading" )
2015-12-16 20:29:37 +01:00
os . Exit ( exitUpgrading )
}
}
2015-04-30 21:33:32 +02:00
func upgradeViaRest ( ) error {
2017-10-21 11:00:24 +02:00
cfg , _ := loadOrDefaultConfig ( )
2016-05-28 16:08:26 +02:00
u , err := url . Parse ( cfg . GUI ( ) . URL ( ) )
if err != nil {
return err
}
u . Path = path . Join ( u . Path , "rest/system/upgrade" )
target := u . String ( )
r , _ := http . NewRequest ( "POST" , target , nil )
2016-02-02 11:12:25 +01:00
r . Header . Set ( "X-API-Key" , cfg . GUI ( ) . APIKey )
2015-04-30 21:33:32 +02:00
tr := & http . Transport {
2015-10-13 20:52:22 +02:00
Dial : dialer . Dial ,
Proxy : http . ProxyFromEnvironment ,
2015-04-30 21:33:32 +02:00
TLSClientConfig : & tls . Config { InsecureSkipVerify : true } ,
}
client := & http . Client {
Transport : tr ,
Timeout : 60 * time . Second ,
}
resp , err := client . Do ( r )
if err != nil {
return err
}
if resp . StatusCode != 200 {
bs , err := ioutil . ReadAll ( resp . Body )
defer resp . Body . Close ( )
if err != nil {
return err
}
return errors . New ( string ( bs ) )
}
return err
}
2015-12-17 23:40:46 +01:00
func syncthingMain ( runtimeOptions RuntimeOptions ) {
2015-12-14 09:57:49 +01:00
setupSignalHandling ( )
2015-04-25 11:19:53 +02:00
// Create a main service manager. We'll add things to this as we go along.
2015-07-11 03:12:20 +02:00
// We want any logging it does to go through our log system.
2015-12-23 16:31:12 +01:00
mainService := suture . New ( "main" , suture . Spec {
2015-04-25 11:19:53 +02:00
Log : func ( line string ) {
2015-10-03 17:25:21 +02:00
l . Debugln ( line )
2015-04-25 11:19:53 +02:00
} ,
2018-09-08 11:56:56 +02:00
PassThroughPanics : true ,
2015-04-25 11:19:53 +02:00
} )
2015-12-23 16:31:12 +01:00
mainService . ServeBackground ( )
2014-09-02 13:08:24 +02:00
2015-04-25 11:21:47 +02:00
// Set a log prefix similar to the ID we will have later on, or early log
// lines look ugly.
l . SetPrefix ( "[start] " )
2015-12-17 23:40:46 +01:00
if runtimeOptions . auditEnabled {
2017-01-03 09:54:28 +01:00
startAuditing ( mainService , runtimeOptions . auditFile )
2015-04-25 11:21:47 +02:00
}
2015-12-17 23:40:46 +01:00
if runtimeOptions . verbose {
2015-12-23 16:31:12 +01:00
mainService . Add ( newVerboseService ( ) )
2015-04-30 20:21:48 +02:00
}
2015-10-03 17:25:21 +02:00
errors := logger . NewRecorder ( l , logger . LevelWarn , maxSystemErrors , 0 )
systemLog := logger . NewRecorder ( l , logger . LevelDebug , maxSystemLog , initialSystemLog )
2016-07-15 16:23:20 +02:00
// Event subscription for the API; must start early to catch the early
// events. The LocalChangeDetected event might overwhelm the event
// receiver in some situations so we will not subscribe to it here.
2017-04-13 19:14:34 +02:00
defaultSub := events . NewBufferedSubscription ( events . Default . Subscribe ( defaultEventMask ) , eventSubBufferSize )
diskSub := events . NewBufferedSubscription ( events . Default . Subscribe ( diskEventMask ) , eventSubBufferSize )
2015-06-16 09:17:58 +02:00
2014-01-10 00:09:27 +01:00
if len ( os . Getenv ( "GOMAXPROCS" ) ) == 0 {
runtime . GOMAXPROCS ( runtime . NumCPU ( ) )
}
2015-08-15 15:20:58 +02:00
// Attempt to increase the limit on number of open files to the maximum
// allowed, in case we have many peers. We don't really care enough to
// report the error if there is one.
osutil . MaximizeOpenFileLimit ( )
2017-02-25 09:12:13 +01:00
// Ensure that we have a certificate and key.
2015-04-25 11:19:53 +02:00
cert , err := tls . LoadX509KeyPair ( locations [ locCertFile ] , locations [ locKeyFile ] )
2013-12-15 11:43:31 +01:00
if err != nil {
2015-11-27 09:09:39 +01:00
l . Infof ( "Generating ECDSA key and certificate for %s..." , tlsDefaultCommonName )
cert , err = tlsutil . NewCertificate ( locations [ locCertFile ] , locations [ locKeyFile ] , tlsDefaultCommonName , bepRSABits )
2014-09-20 15:42:20 +02:00
if err != nil {
2015-09-02 22:05:54 +02:00
l . Fatalln ( err )
2014-09-20 15:42:20 +02:00
}
2013-12-15 11:43:31 +01:00
}
2014-09-28 13:00:38 +02:00
myID = protocol . NewDeviceID ( cert . Certificate [ 0 ] )
2014-06-30 01:42:03 +02:00
l . SetPrefix ( fmt . Sprintf ( "[%s] " , myID . String ( ) [ : 5 ] ) )
2013-12-15 11:43:31 +01:00
2014-05-15 02:08:56 +02:00
l . Infoln ( LongVersion )
l . Infoln ( "My ID:" , myID )
2016-09-23 21:33:54 +02:00
2017-09-06 08:55:47 +02:00
// Select SHA256 implementation and report. Affected by the
// STHASHING environment variable.
2016-09-23 21:33:54 +02:00
sha256 . SelectAlgo ( )
sha256 . Report ( )
2014-02-03 16:01:17 +01:00
2015-06-18 15:22:45 +02:00
// Emit the Starting event, now that we know who we are.
events . Default . Log ( events . Starting , map [ string ] string {
"home" : baseDirs [ "config" ] ,
"myID" : myID . String ( ) ,
} )
2017-10-21 11:00:24 +02:00
cfg := loadConfigAtStartup ( )
2014-10-08 13:52:05 +02:00
2015-04-09 12:53:13 +02:00
if err := checkShortIDs ( cfg ) ; err != nil {
2015-12-20 17:58:51 +01:00
l . Fatalln ( "Short device IDs are in conflict. Unlucky!\n Regenerate the device ID of one of the following:\n " , err )
2015-04-09 12:53:13 +02:00
}
2015-12-17 23:40:46 +01:00
if len ( runtimeOptions . profiler ) > 0 {
2013-12-15 11:43:31 +01:00
go func ( ) {
2015-12-17 23:40:46 +01:00
l . Debugln ( "Starting profiler on" , runtimeOptions . profiler )
2014-05-20 18:41:01 +02:00
runtime . SetBlockProfileRate ( 1 )
2015-12-17 23:40:46 +01:00
err := http . ListenAndServe ( runtimeOptions . profiler , nil )
2013-12-18 19:36:28 +01:00
if err != nil {
2014-05-15 02:08:56 +02:00
l . Fatalln ( err )
2013-12-18 19:36:28 +01:00
}
2013-12-15 11:43:31 +01:00
} ( )
}
// The TLS configuration is used for both the listening socket and outgoing
// connections.
2014-02-01 20:23:19 +01:00
tlsCfg := & tls . Config {
2014-01-09 09:28:08 +01:00
Certificates : [ ] tls . Certificate { cert } ,
2015-03-05 15:58:16 +01:00
NextProtos : [ ] string { bepProtocolName } ,
2014-01-09 09:28:08 +01:00
ClientAuth : tls . RequestClientCert ,
SessionTicketsDisabled : true ,
InsecureSkipVerify : true ,
MinVersion : tls . VersionTLS12 ,
2014-11-12 10:47:34 +01:00
CipherSuites : [ ] uint16 {
2016-12-18 22:07:44 +01:00
0xCCA8 , // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, Go 1.8
0xCCA9 , // TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, Go 1.8
tls . TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,
tls . TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ,
2014-11-12 10:47:34 +01:00
tls . TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ,
tls . TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ,
tls . TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA ,
tls . TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA ,
tls . TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ,
tls . TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ,
} ,
2013-12-15 11:43:31 +01:00
}
2018-05-05 10:24:44 +02:00
perf := cpuBench ( 3 , 150 * time . Millisecond , true )
l . Infof ( "Hashing performance is %.02f MB/s" , perf )
2017-02-06 11:27:11 +01:00
2015-03-29 12:55:27 +02:00
dbFile := locations [ locDatabase ]
2015-10-31 12:31:25 +01:00
ldb , err := db . Open ( dbFile )
2014-07-06 14:46:48 +02:00
if err != nil {
2018-06-26 11:40:34 +02:00
l . Fatalln ( "Error opening database:" , err )
2014-07-06 14:46:48 +02:00
}
2014-08-31 13:34:17 +02:00
2016-12-26 13:48:54 +01:00
if runtimeOptions . resetDeltaIdxs {
l . Infoln ( "Reinitializing delta index IDs" )
2018-03-10 11:42:01 +01:00
ldb . DropLocalDeltaIndexIDs ( )
ldb . DropRemoteDeltaIndexIDs ( )
2016-12-26 13:48:54 +01:00
}
2015-10-19 02:13:58 +02:00
protectedFiles := [ ] string {
2015-11-08 18:06:06 +01:00
locations [ locDatabase ] ,
locations [ locConfigFile ] ,
locations [ locCertFile ] ,
locations [ locKeyFile ] ,
2015-10-19 02:13:58 +02:00
}
2014-09-28 13:00:38 +02:00
// Remove database entries for folders that no longer exist in the config
2014-10-06 09:25:45 +02:00
folders := cfg . Folders ( )
2015-10-31 12:31:25 +01:00
for _ , folder := range ldb . ListFolders ( ) {
2014-10-06 09:25:45 +02:00
if _ , ok := folders [ folder ] ; ! ok {
2014-09-28 13:00:38 +02:00
l . Infof ( "Cleaning data for dropped folder %q" , folder )
2015-01-12 14:50:30 +01:00
db . DropFolder ( ldb , folder )
2014-08-31 13:34:17 +02:00
}
}
2018-02-25 10:14:17 +01:00
// Grab the previously running version string from the database.
miscDB := db . NewNamespacedKV ( ldb , string ( db . KeyTypeMiscData ) )
prevVersion , _ := miscDB . String ( "prevVersion" )
// Strip away prerelease/beta stuff and just compare the release
// numbers. 0.14.44 to 0.14.45-banana is an upgrade, 0.14.45-banana to
// 0.14.45-pineapple is not.
prevParts := strings . Split ( prevVersion , "-" )
curParts := strings . Split ( Version , "-" )
if prevParts [ 0 ] != curParts [ 0 ] {
if prevVersion != "" {
l . Infoln ( "Detected upgrade from" , prevVersion , "to" , Version )
}
// Drop delta indexes in case we've changed random stuff we
2018-03-10 11:42:01 +01:00
// shouldn't have. We will resend our index on next connect.
ldb . DropLocalDeltaIndexIDs ( )
2018-02-25 10:14:17 +01:00
// Remember the new version.
miscDB . PutString ( "prevVersion" , Version )
2016-08-05 09:13:52 +02:00
}
2018-02-25 10:14:17 +01:00
2017-05-22 21:58:33 +02:00
m := model . NewModel ( cfg , myID , "syncthing" , Version , ldb , protectedFiles )
2014-04-01 20:36:54 +02:00
2018-01-15 14:33:52 +01:00
if t := os . Getenv ( "STDEADLOCKTIMEOUT" ) ; t != "" {
if secs , _ := strconv . Atoi ( t ) ; secs > 0 {
m . StartDeadlockDetector ( time . Duration ( secs ) * time . Second )
2015-04-08 14:35:03 +02:00
}
} else if ! IsRelease || IsBeta {
2015-07-24 20:55:52 +02:00
m . StartDeadlockDetector ( 20 * time . Minute )
2015-04-08 14:35:03 +02:00
}
2016-12-21 19:41:25 +01:00
if runtimeOptions . unpaused {
setPauseState ( cfg , false )
} else if runtimeOptions . paused {
setPauseState ( cfg , true )
2015-08-23 22:03:58 +02:00
}
2016-07-23 14:46:31 +02:00
// Add and start folders
2014-10-06 09:25:45 +02:00
for _ , folderCfg := range cfg . Folders ( ) {
2016-12-21 19:41:25 +01:00
if folderCfg . Paused {
2017-04-24 01:50:56 +02:00
folderCfg . CreateRoot ( )
2016-12-21 19:41:25 +01:00
continue
}
2015-03-28 15:25:42 +01:00
m . AddFolder ( folderCfg )
2016-05-04 12:47:33 +02:00
m . StartFolder ( folderCfg . ID )
2014-08-05 12:20:50 +02:00
}
2015-12-23 16:31:12 +01:00
mainService . Add ( m )
2015-06-20 20:04:47 +02:00
2015-09-20 15:30:25 +02:00
// Start discovery
cachedDiscovery := discover . NewCachingMux ( )
2015-12-23 16:31:12 +01:00
mainService . Add ( cachedDiscovery )
2015-09-20 15:30:25 +02:00
2016-03-25 08:35:18 +01:00
// Start connection management
2017-11-06 15:05:29 +01:00
connectionsService := connections . NewService ( cfg , myID , m , tlsCfg , cachedDiscovery , bepProtocolName , tlsDefaultCommonName )
2016-05-04 21:38:12 +02:00
mainService . Add ( connectionsService )
2016-03-25 08:35:18 +01:00
2015-09-20 15:30:25 +02:00
if cfg . Options ( ) . GlobalAnnEnabled {
for _ , srv := range cfg . GlobalDiscoveryServers ( ) {
l . Infoln ( "Using discovery server" , srv )
2016-05-04 21:38:12 +02:00
gd , err := discover . NewGlobal ( srv , cert , connectionsService )
2015-09-20 15:30:25 +02:00
if err != nil {
l . Warnln ( "Global discovery:" , err )
continue
}
// Each global discovery server gets its results cached for five
// minutes, and is not asked again for a minute when it's returned
// unsuccessfully.
2017-11-22 08:05:49 +01:00
cachedDiscovery . Add ( gd , 5 * time . Minute , time . Minute )
2015-09-20 15:30:25 +02:00
}
}
if cfg . Options ( ) . LocalAnnEnabled {
// v4 broadcasts
2016-05-04 21:38:12 +02:00
bcd , err := discover . NewLocal ( myID , fmt . Sprintf ( ":%d" , cfg . Options ( ) . LocalAnnPort ) , connectionsService )
2015-09-20 15:30:25 +02:00
if err != nil {
l . Warnln ( "IPv4 local discovery:" , err )
} else {
2017-11-22 08:05:49 +01:00
cachedDiscovery . Add ( bcd , 0 , 0 )
2015-09-20 15:30:25 +02:00
}
// v6 multicasts
2016-05-04 21:38:12 +02:00
mcd , err := discover . NewLocal ( myID , cfg . Options ( ) . LocalAnnMCAddr , connectionsService )
2015-09-20 15:30:25 +02:00
if err != nil {
l . Warnln ( "IPv6 local discovery:" , err )
} else {
2017-11-22 08:05:49 +01:00
cachedDiscovery . Add ( mcd , 0 , 0 )
2015-09-20 15:30:25 +02:00
}
}
// GUI
2017-04-13 19:14:34 +02:00
setupGUI ( mainService , cfg , m , defaultSub , diskSub , cachedDiscovery , connectionsService , errors , systemLog , runtimeOptions )
2015-09-20 15:30:25 +02:00
2015-12-17 23:40:46 +01:00
if runtimeOptions . cpuProfile {
2014-08-13 14:38:23 +02:00
f , err := os . Create ( fmt . Sprintf ( "cpu-%d.pprof" , os . Getpid ( ) ) )
2014-04-14 12:13:50 +02:00
if err != nil {
log . Fatal ( err )
}
pprof . StartCPUProfile ( f )
}
2014-10-06 09:25:45 +02:00
for _ , device := range cfg . Devices ( ) {
2014-09-28 13:00:38 +02:00
if len ( device . Name ) > 0 {
l . Infof ( "Device %s is %q at %v" , device . DeviceID , device . Name , device . Addresses )
2014-05-24 21:39:08 +02:00
}
}
2017-01-30 22:33:07 +01:00
// Candidate builds always run with usage reporting.
2017-11-07 08:20:19 +01:00
if opts := cfg . Options ( ) ; IsCandidate {
2017-01-30 22:33:07 +01:00
l . Infoln ( "Anonymous usage reporting is always enabled for candidate releases." )
opts . URAccepted = usageReportVersion
2017-11-07 08:20:19 +01:00
cfg . SetOptions ( opts )
cfg . Save ( )
2017-01-30 22:33:07 +01:00
// Unique ID will be set and config saved below if necessary.
}
2017-11-07 08:20:19 +01:00
if opts := cfg . Options ( ) ; opts . URUniqueID == "" {
2017-01-30 22:33:07 +01:00
opts . URUniqueID = rand . String ( 8 )
cfg . SetOptions ( opts )
cfg . Save ( )
2014-06-11 20:04:23 +02:00
}
2017-10-12 08:16:46 +02:00
usageReportingSvc := newUsageReportingService ( cfg , m , connectionsService )
mainService . Add ( usageReportingSvc )
2015-05-12 09:35:37 +02:00
2017-11-07 08:20:19 +01:00
if opts := cfg . Options ( ) ; opts . RestartOnWakeup {
2014-09-10 22:24:53 +02:00
go standbyMonitor ( )
}
2014-08-27 23:38:36 +02:00
2017-01-30 22:33:07 +01:00
// Candidate builds should auto upgrade. Make sure the option is set,
// unless we are in a build where it's disabled or the STNOUPGRADE
// environment variable is set.
if IsCandidate && ! upgrade . DisabledByCompilation && ! noUpgradeFromEnv {
l . Infoln ( "Automatic upgrade is always enabled for candidate releases." )
2017-11-07 08:20:19 +01:00
if opts := cfg . Options ( ) ; opts . AutoUpgradeIntervalH == 0 || opts . AutoUpgradeIntervalH > 24 {
2017-01-30 22:33:07 +01:00
opts . AutoUpgradeIntervalH = 12
2017-04-10 15:56:52 +02:00
// Set the option into the config as well, as the auto upgrade
// loop expects to read a valid interval from there.
cfg . SetOptions ( opts )
cfg . Save ( )
2017-01-30 22:33:07 +01:00
}
// We don't tweak the user's choice of upgrading to pre-releases or
// not, as otherwise they cannot step off the candidate channel.
}
2017-11-07 08:20:19 +01:00
if opts := cfg . Options ( ) ; opts . AutoUpgradeIntervalH > 0 {
2017-01-30 22:33:07 +01:00
if noUpgradeFromEnv {
2014-11-26 19:48:31 +01:00
l . Infof ( "No automatic upgrades; STNOUPGRADE environment variable defined." )
2014-10-23 19:09:53 +02:00
} else {
2016-06-02 15:01:59 +02:00
go autoUpgrade ( cfg )
2014-10-23 19:09:53 +02:00
}
2014-09-26 00:51:12 +02:00
}
2017-05-15 07:42:21 +02:00
if isSuperUser ( ) {
l . Warnln ( "Syncthing should not run as a privileged or system user. Please consider using a normal user account." )
}
2015-06-16 09:27:06 +02:00
events . Default . Log ( events . StartupComplete , map [ string ] string {
"myID" : myID . String ( ) ,
} )
2014-07-13 21:07:24 +02:00
2015-04-07 09:25:28 +02:00
cleanConfigDirectory ( )
2018-01-15 18:11:14 +01:00
if cfg . Options ( ) . SetLowPriority {
if err := osutil . SetLowPriority ( ) ; err != nil {
l . Warnln ( "Failed to lower process priority:" , err )
}
}
2014-09-02 13:08:24 +02:00
code := <- stop
2014-07-13 21:07:24 +02:00
2015-12-23 16:31:12 +01:00
mainService . Stop ( )
2015-04-25 11:19:53 +02:00
2016-01-16 23:01:57 +01:00
l . Infoln ( "Exiting" )
2015-07-20 15:34:40 +02:00
2015-12-17 23:40:46 +01:00
if runtimeOptions . cpuProfile {
2015-07-20 15:34:40 +02:00
pprof . StopCPUProfile ( )
}
2014-09-02 13:08:24 +02:00
os . Exit ( code )
2014-05-24 12:28:36 +02:00
}
2015-12-14 09:57:49 +01:00
func setupSignalHandling ( ) {
// Exit cleanly with "restarting" code on SIGHUP.
restartSign := make ( chan os . Signal , 1 )
sigHup := syscall . Signal ( 1 )
signal . Notify ( restartSign , sigHup )
go func ( ) {
<- restartSign
stop <- exitRestarting
} ( )
// Exit with "success" code (no restart) on INT/TERM
stopSign := make ( chan os . Signal , 1 )
sigTerm := syscall . Signal ( 15 )
signal . Notify ( stopSign , os . Interrupt , sigTerm )
go func ( ) {
<- stopSign
stop <- exitSuccess
} ( )
}
2017-10-21 11:00:24 +02:00
func loadOrDefaultConfig ( ) ( * config . Wrapper , error ) {
2015-12-19 00:39:36 +01:00
cfgFile := locations [ locConfigFile ]
cfg , err := config . Load ( cfgFile , myID )
2015-09-29 20:05:22 +02:00
if err != nil {
2017-10-21 11:00:24 +02:00
cfg = defaultConfig ( cfgFile )
2015-09-29 20:05:22 +02:00
}
2015-12-19 00:39:36 +01:00
return cfg , err
}
2017-10-21 11:00:24 +02:00
func loadConfigAtStartup ( ) * config . Wrapper {
cfgFile := locations [ locConfigFile ]
cfg , err := config . Load ( cfgFile , myID )
2015-12-19 00:39:36 +01:00
if os . IsNotExist ( err ) {
2017-10-28 21:14:37 +02:00
cfg = defaultConfig ( cfgFile )
2015-12-19 00:39:36 +01:00
cfg . Save ( )
2017-10-21 11:00:24 +02:00
l . Infof ( "Default config saved. Edit %s to taste or use the GUI\n" , cfg . ConfigPath ( ) )
} else if err == io . EOF {
l . Fatalln ( "Failed to load config: unexpected end of file. Truncated or empty configuration?" )
2015-12-19 00:39:36 +01:00
} else if err != nil {
2017-10-21 11:00:24 +02:00
l . Fatalln ( "Failed to load config:" , err )
2015-09-29 20:05:22 +02:00
}
2016-11-12 10:34:18 +01:00
if cfg . RawCopy ( ) . OriginalVersion != config . CurrentVersion {
2015-12-24 01:12:24 +01:00
err = archiveAndSaveConfig ( cfg )
if err != nil {
l . Fatalln ( "Config archive:" , err )
}
2015-09-29 20:05:22 +02:00
}
2015-12-19 00:39:36 +01:00
return cfg
2015-09-29 20:05:22 +02:00
}
2015-12-24 01:12:24 +01:00
func archiveAndSaveConfig ( cfg * config . Wrapper ) error {
2016-09-03 23:29:32 +02:00
// Copy the existing config to an archive copy
2016-11-12 10:34:18 +01:00
archivePath := cfg . ConfigPath ( ) + fmt . Sprintf ( ".v%d" , cfg . RawCopy ( ) . OriginalVersion )
2015-12-24 01:12:24 +01:00
l . Infoln ( "Archiving a copy of old config file format at:" , archivePath )
2016-09-03 23:29:32 +02:00
if err := copyFile ( cfg . ConfigPath ( ) , archivePath ) ; err != nil {
2015-12-24 01:12:24 +01:00
return err
2015-09-29 20:05:22 +02:00
}
2016-09-03 23:29:32 +02:00
// Do a regular atomic config sve
2015-12-24 01:12:24 +01:00
return cfg . Save ( )
2015-09-29 20:05:22 +02:00
}
2016-09-03 23:29:32 +02:00
func copyFile ( src , dst string ) error {
bs , err := ioutil . ReadFile ( src )
if err != nil {
return err
}
if err := ioutil . WriteFile ( dst , bs , 0600 ) ; err != nil {
// Attempt to clean up
os . Remove ( dst )
return err
}
return nil
}
2017-01-03 09:54:28 +01:00
func startAuditing ( mainService * suture . Supervisor , auditFile string ) {
var fd io . Writer
var err error
var auditDest string
var auditFlags int
if auditFile == "-" {
fd = os . Stdout
auditDest = "stdout"
} else if auditFile == "--" {
fd = os . Stderr
auditDest = "stderr"
} else {
if auditFile == "" {
auditFile = timestampedLoc ( locAuditLog )
auditFlags = os . O_WRONLY | os . O_CREATE | os . O_EXCL
} else {
auditFlags = os . O_WRONLY | os . O_CREATE | os . O_APPEND
}
fd , err = os . OpenFile ( auditFile , auditFlags , 0600 )
if err != nil {
l . Fatalln ( "Audit:" , err )
}
auditDest = auditFile
2015-04-25 11:21:47 +02:00
}
2015-12-23 16:31:12 +01:00
auditService := newAuditService ( fd )
mainService . Add ( auditService )
2015-04-25 11:21:47 +02:00
// We wait for the audit service to fully start before we return, to
// ensure we capture all events from the start.
2015-12-23 16:31:12 +01:00
auditService . WaitForStart ( )
2015-04-25 11:21:47 +02:00
2017-01-03 09:54:28 +01:00
l . Infoln ( "Audit log in" , auditDest )
2015-04-25 11:21:47 +02:00
}
2017-04-13 19:14:34 +02:00
func setupGUI ( mainService * suture . Supervisor , cfg * config . Wrapper , m * model . Model , defaultSub , diskSub events . BufferedSubscription , discoverer discover . CachingMux , connectionsService * connections . Service , errors , systemLog logger . Recorder , runtimeOptions RuntimeOptions ) {
2015-09-30 09:32:17 +02:00
guiCfg := cfg . GUI ( )
2014-10-10 17:32:43 +02:00
2015-09-30 09:32:17 +02:00
if ! guiCfg . Enabled {
return
}
2014-10-10 17:32:43 +02:00
2015-11-16 21:33:55 +01:00
if guiCfg . InsecureAdminAccess {
l . Warnln ( "Insecure admin access is enabled." )
}
2017-05-31 20:14:04 +02:00
cpu := newCPUService ( )
mainService . Add ( cpu )
api := newAPIService ( myID , cfg , locations [ locHTTPSCertFile ] , locations [ locHTTPSKeyFile ] , runtimeOptions . assetDir , m , defaultSub , diskSub , discoverer , connectionsService , errors , systemLog , cpu )
2015-10-12 15:27:57 +02:00
cfg . Subscribe ( api )
2015-12-23 16:31:12 +01:00
mainService . Add ( api )
2015-09-30 09:32:17 +02:00
2015-12-17 23:40:46 +01:00
if cfg . Options ( ) . StartBrowser && ! runtimeOptions . noBrowser && ! runtimeOptions . stRestarting {
2015-10-12 15:27:57 +02:00
// Can potentially block if the utility we are invoking doesn't
2017-07-20 15:10:46 +02:00
// fork, and just execs, hence keep it in its own routine.
2016-10-07 05:10:26 +02:00
<- api . startedOnce
2015-10-12 15:27:57 +02:00
go openURL ( guiCfg . URL ( ) )
2014-10-10 17:32:43 +02:00
}
}
2017-10-21 11:00:24 +02:00
func defaultConfig ( cfgFile string ) * config . Wrapper {
myName , _ := os . Hostname ( )
2016-01-09 02:11:06 +01:00
var defaultFolder config . FolderConfiguration
if ! noDefaultFolder {
l . Infoln ( "Default folder created and/or linked to new config" )
2017-12-07 08:08:24 +01:00
defaultFolder = config . NewFolderConfiguration ( myID , "default" , "Default Folder" , fs . FilesystemTypeBasic , locations [ locDefFolder ] )
2016-01-09 02:11:06 +01:00
} else {
l . Infoln ( "We will skip creation of a default folder on first start since the proper envvar is set" )
}
2015-11-07 09:47:31 +01:00
thisDevice := config . NewDeviceConfiguration ( myID , myName )
thisDevice . Addresses = [ ] string { "dynamic" }
2014-10-10 17:32:43 +02:00
newCfg := config . New ( myID )
2016-01-09 02:11:06 +01:00
if ! noDefaultFolder {
newCfg . Folders = [ ] config . FolderConfiguration { defaultFolder }
}
2015-11-07 09:47:31 +01:00
newCfg . Devices = [ ] config . DeviceConfiguration { thisDevice }
2014-10-10 17:32:43 +02:00
2015-03-26 21:36:06 +01:00
port , err := getFreePort ( "127.0.0.1" , 8384 )
2014-10-10 17:32:43 +02:00
if err != nil {
l . Fatalln ( "get free port (GUI):" , err )
}
2015-10-12 15:27:57 +02:00
newCfg . GUI . RawAddress = fmt . Sprintf ( "127.0.0.1:%d" , port )
2014-10-10 17:32:43 +02:00
port , err = getFreePort ( "0.0.0.0" , 22000 )
if err != nil {
l . Fatalln ( "get free port (BEP):" , err )
}
2016-05-04 21:38:12 +02:00
if port == 22000 {
newCfg . Options . ListenAddresses = [ ] string { "default" }
} else {
newCfg . Options . ListenAddresses = [ ] string {
fmt . Sprintf ( "tcp://%s" , net . JoinHostPort ( "0.0.0.0" , strconv . Itoa ( port ) ) ) ,
"dynamic+https://relays.syncthing.net/endpoint" ,
}
}
2017-10-21 11:00:24 +02:00
return config . Wrap ( cfgFile , newCfg )
2014-10-10 17:32:43 +02:00
}
2015-04-03 20:06:03 +02:00
func resetDB ( ) error {
return os . RemoveAll ( locations [ locDatabase ] )
2014-07-15 14:27:46 +02:00
}
2014-02-12 12:10:44 +01:00
func restart ( ) {
2014-05-15 02:08:56 +02:00
l . Infoln ( "Restarting" )
2014-09-02 13:08:24 +02:00
stop <- exitRestarting
2014-02-12 12:10:44 +01:00
}
2014-05-12 01:16:27 +02:00
func shutdown ( ) {
2014-09-02 13:08:24 +02:00
l . Infoln ( "Shutting down" )
stop <- exitSuccess
2014-05-12 01:16:27 +02:00
}
2017-08-19 16:36:56 +02:00
func ensureDir ( dir string , mode fs . FileMode ) {
fs := fs . NewFilesystem ( fs . FilesystemTypeBasic , dir )
err := fs . MkdirAll ( "." , mode )
2015-12-16 21:03:20 +01:00
if err != nil {
l . Fatalln ( err )
}
2017-08-19 16:36:56 +02:00
if fi , err := fs . Stat ( "." ) ; err == nil {
2015-12-21 08:35:24 +01:00
// Apprently the stat may fail even though the mkdirall passed. If it
// does, we'll just assume things are in order and let other things
// fail (like loading or creating the config...).
currentMode := fi . Mode ( ) & 0777
if currentMode != mode {
2017-08-19 16:36:56 +02:00
err := fs . Chmod ( "." , mode )
2015-12-21 08:35:24 +01:00
// This can fail on crappy filesystems, nothing we can do about it.
if err != nil {
l . Warnln ( err )
}
2014-09-20 15:41:52 +02:00
}
2013-12-15 11:43:31 +01:00
}
}
2014-05-11 20:21:41 +02:00
// getFreePort returns a free TCP port fort listening on. The ports given are
// tried in succession and the first to succeed is returned. If none succeed,
// a random high port is returned.
func getFreePort ( host string , ports ... int ) ( int , error ) {
for _ , port := range ports {
c , err := net . Listen ( "tcp" , fmt . Sprintf ( "%s:%d" , host , port ) )
if err == nil {
c . Close ( )
return port , nil
}
}
c , err := net . Listen ( "tcp" , host + ":0" )
if err != nil {
return 0 , err
}
2014-07-31 21:29:44 +02:00
addr := c . Addr ( ) . ( * net . TCPAddr )
2014-05-11 20:21:41 +02:00
c . Close ( )
2014-07-31 21:29:44 +02:00
return addr . Port , nil
}
2014-05-11 20:21:41 +02:00
2014-08-27 23:38:36 +02:00
func standbyMonitor ( ) {
2016-12-17 15:37:11 +01:00
restartDelay := 60 * time . Second
2014-08-27 23:38:36 +02:00
now := time . Now ( )
for {
time . Sleep ( 10 * time . Second )
if time . Since ( now ) > 2 * time . Minute {
2014-09-27 14:44:15 +02:00
l . Infof ( "Paused state detected, possibly woke up from standby. Restarting in %v." , restartDelay )
2014-09-10 22:20:03 +02:00
// We most likely just woke from standby. If we restart
// immediately chances are we won't have networking ready. Give
// things a moment to stabilize.
2014-09-11 20:25:08 +02:00
time . Sleep ( restartDelay )
2014-09-10 22:20:03 +02:00
2014-08-27 23:38:36 +02:00
restart ( )
2014-09-10 22:20:03 +02:00
return
2014-08-27 23:38:36 +02:00
}
now = time . Now ( )
}
}
2014-09-26 00:51:12 +02:00
2015-09-29 20:05:22 +02:00
func autoUpgrade ( cfg * config . Wrapper ) {
2014-12-27 00:12:12 +01:00
timer := time . NewTimer ( 0 )
sub := events . Default . Subscribe ( events . DeviceConnected )
2014-09-26 00:51:12 +02:00
for {
2014-12-27 00:12:12 +01:00
select {
case event := <- sub . C ( ) :
data , ok := event . Data . ( map [ string ] string )
if ! ok || data [ "clientName" ] != "syncthing" || upgrade . CompareVersions ( data [ "clientVersion" ] , Version ) != upgrade . Newer {
continue
}
l . Infof ( "Connected to device %s with a newer version (current %q < remote %q). Checking for upgrades." , data [ "id" ] , Version , data [ "clientVersion" ] )
case <- timer . C :
2014-09-26 00:51:12 +02:00
}
2017-04-10 15:56:52 +02:00
2017-01-27 13:17:06 +01:00
opts := cfg . Options ( )
2017-04-10 15:56:52 +02:00
checkInterval := time . Duration ( opts . AutoUpgradeIntervalH ) * time . Hour
if checkInterval < time . Hour {
// We shouldn't be here if AutoUpgradeIntervalH < 1, but for
// safety's sake.
checkInterval = time . Hour
}
2017-01-27 13:17:06 +01:00
rel , err := upgrade . LatestRelease ( opts . ReleasesURL , Version , opts . UpgradeToPreReleases )
2014-10-01 00:01:32 +02:00
if err == upgrade . ErrUpgradeUnsupported {
2014-12-27 00:12:12 +01:00
events . Default . Unsubscribe ( sub )
2014-10-01 00:01:32 +02:00
return
}
2014-09-26 00:51:12 +02:00
if err != nil {
2014-09-30 17:38:12 +02:00
// Don't complain too loudly here; we might simply not have
// internet connectivity, or the upgrade server might be down.
l . Infoln ( "Automatic upgrade:" , err )
2017-04-10 15:56:52 +02:00
timer . Reset ( checkInterval )
2014-09-26 00:51:12 +02:00
continue
}
2014-12-29 12:21:08 +01:00
if upgrade . CompareVersions ( rel . Tag , Version ) != upgrade . Newer {
// Skip equal, older or majorly newer (incompatible) versions
2017-04-10 15:56:52 +02:00
timer . Reset ( checkInterval )
2014-09-26 00:51:12 +02:00
continue
}
l . Infof ( "Automatic upgrade (current %q < latest %q)" , Version , rel . Tag )
2014-12-08 16:36:15 +01:00
err = upgrade . To ( rel )
2014-09-26 00:51:12 +02:00
if err != nil {
l . Warnln ( "Automatic upgrade:" , err )
2017-04-10 15:56:52 +02:00
timer . Reset ( checkInterval )
2014-09-26 00:51:12 +02:00
continue
}
2014-12-27 00:12:12 +01:00
events . Default . Unsubscribe ( sub )
2014-09-26 00:51:12 +02:00
l . Warnf ( "Automatically upgraded to version %q. Restarting in 1 minute." , rel . Tag )
time . Sleep ( time . Minute )
stop <- exitUpgrading
return
}
}
2015-04-07 09:25:28 +02:00
// cleanConfigDirectory removes old, unused configuration and index formats, a
// suitable time after they have gone out of fashion.
func cleanConfigDirectory ( ) {
patterns := map [ string ] time . Duration {
2016-08-22 14:19:19 +02:00
"panic-*.log" : 7 * 24 * time . Hour , // keep panic logs for a week
"audit-*.log" : 7 * 24 * time . Hour , // keep audit logs for a week
"index" : 14 * 24 * time . Hour , // keep old index format for two weeks
"index-v0.11.0.db" : 14 * 24 * time . Hour , // keep old index format for two weeks
"index-v0.13.0.db" : 14 * 24 * time . Hour , // keep old index format for two weeks
"index*.converted" : 14 * 24 * time . Hour , // keep old converted indexes for two weeks
"config.xml.v*" : 30 * 24 * time . Hour , // old config versions for a month
"*.idx.gz" : 30 * 24 * time . Hour , // these should for sure no longer exist
"backup-of-v0.8" : 30 * 24 * time . Hour , // these neither
"tmp-index-sorter.*" : time . Minute , // these should never exist on startup
2015-04-07 09:25:28 +02:00
}
for pat , dur := range patterns {
2017-08-19 16:36:56 +02:00
fs := fs . NewFilesystem ( fs . FilesystemTypeBasic , baseDirs [ "config" ] )
files , err := fs . Glob ( pat )
2015-04-07 09:25:28 +02:00
if err != nil {
l . Infoln ( "Cleaning:" , err )
continue
}
for _ , file := range files {
2017-08-19 16:36:56 +02:00
info , err := fs . Lstat ( file )
2015-04-07 09:25:28 +02:00
if err != nil {
l . Infoln ( "Cleaning:" , err )
continue
}
if time . Since ( info . ModTime ( ) ) > dur {
2017-08-19 16:36:56 +02:00
if err = fs . RemoveAll ( file ) ; err != nil {
2015-04-07 09:25:28 +02:00
l . Infoln ( "Cleaning:" , err )
} else {
l . Infoln ( "Cleaned away old file" , filepath . Base ( file ) )
}
}
}
}
}
2015-04-09 12:53:13 +02:00
// checkShortIDs verifies that the configuration won't result in duplicate
// short ID:s; that is, that the devices in the cluster all have unique
// initial 64 bits.
func checkShortIDs ( cfg * config . Wrapper ) error {
2016-01-20 20:10:22 +01:00
exists := make ( map [ protocol . ShortID ] protocol . DeviceID )
2015-04-09 12:53:13 +02:00
for deviceID := range cfg . Devices ( ) {
shortID := deviceID . Short ( )
if otherID , ok := exists [ shortID ] ; ok {
return fmt . Errorf ( "%v in conflict with %v" , deviceID , otherID )
}
exists [ shortID ] = deviceID
}
return nil
}
2016-02-02 10:27:01 +01:00
2018-03-28 19:02:43 +02:00
func showPaths ( options RuntimeOptions ) {
2016-02-02 10:27:01 +01:00
fmt . Printf ( "Configuration file:\n\t%s\n\n" , locations [ locConfigFile ] )
fmt . Printf ( "Database directory:\n\t%s\n\n" , locations [ locDatabase ] )
fmt . Printf ( "Device private key & certificate files:\n\t%s\n\t%s\n\n" , locations [ locKeyFile ] , locations [ locCertFile ] )
fmt . Printf ( "HTTPS private key & certificate files:\n\t%s\n\t%s\n\n" , locations [ locHTTPSKeyFile ] , locations [ locHTTPSCertFile ] )
2018-03-28 19:02:43 +02:00
fmt . Printf ( "Log file:\n\t%s\n\n" , options . logFile )
fmt . Printf ( "GUI override directory:\n\t%s\n\n" , options . assetDir )
2016-02-02 10:27:01 +01:00
fmt . Printf ( "Default sync folder directory:\n\t%s\n\n" , locations [ locDefFolder ] )
}
2016-12-21 19:41:25 +01:00
func setPauseState ( cfg * config . Wrapper , paused bool ) {
raw := cfg . RawCopy ( )
for i := range raw . Devices {
raw . Devices [ i ] . Paused = paused
}
for i := range raw . Folders {
raw . Folders [ i ] . Paused = paused
}
2017-12-07 08:08:24 +01:00
if _ , err := cfg . Replace ( raw ) ; err != nil {
2016-12-21 19:41:25 +01:00
l . Fatalln ( "Cannot adjust paused state:" , err )
}
}