mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-08 22:31:04 +00:00
cmd/syncthing: Refactor command line parsing (#7330)
This commit is contained in:
parent
0471daf771
commit
4f20c900d0
@ -15,19 +15,17 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func startBlockProfiler() {
|
||||||
if innerProcess && os.Getenv("STBLOCKPROFILE") != "" {
|
profiler := pprof.Lookup("block")
|
||||||
profiler := pprof.Lookup("block")
|
if profiler == nil {
|
||||||
if profiler == nil {
|
panic("Couldn't find block profiler")
|
||||||
panic("Couldn't find block profiler")
|
|
||||||
}
|
|
||||||
l.Debugln("Starting block profiling")
|
|
||||||
go func() {
|
|
||||||
err := saveBlockingProfiles(profiler) // Only returns on error
|
|
||||||
l.Warnln("Block profiler failed:", err)
|
|
||||||
panic("Block profiler failed")
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
l.Debugln("Starting block profiling")
|
||||||
|
go func() {
|
||||||
|
err := saveBlockingProfiles(profiler) // Only returns on error
|
||||||
|
l.Warnln("Block profiler failed:", err)
|
||||||
|
panic("Block profiler failed")
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveBlockingProfiles(profiler *pprof.Profile) error {
|
func saveBlockingProfiles(profiler *pprof.Profile) error {
|
||||||
|
@ -11,24 +11,17 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"strconv"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func startHeapProfiler() {
|
||||||
if innerProcess && os.Getenv("STHEAPPROFILE") != "" {
|
l.Debugln("Starting heap profiling")
|
||||||
rate := 1
|
go func() {
|
||||||
if i, err := strconv.Atoi(os.Getenv("STHEAPPROFILE")); err == nil {
|
err := saveHeapProfiles(1) // Only returns on error
|
||||||
rate = i
|
l.Warnln("Heap profiler failed:", err)
|
||||||
}
|
panic("Heap profiler failed")
|
||||||
l.Debugln("Starting heap profiling")
|
}()
|
||||||
go func() {
|
|
||||||
err := saveHeapProfiles(rate) // Only returns on error
|
|
||||||
l.Warnln("Heap profiler failed:", err)
|
|
||||||
panic("Heap profiler failed")
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveHeapProfiles(rate int) error {
|
func saveHeapProfiles(rate int) error {
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -22,13 +21,16 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/alecthomas/kong"
|
||||||
"github.com/syncthing/syncthing/lib/build"
|
"github.com/syncthing/syncthing/lib/build"
|
||||||
"github.com/syncthing/syncthing/lib/config"
|
"github.com/syncthing/syncthing/lib/config"
|
||||||
"github.com/syncthing/syncthing/lib/db"
|
"github.com/syncthing/syncthing/lib/db"
|
||||||
@ -55,9 +57,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
usage = "syncthing [options]"
|
|
||||||
extraUsage = `
|
extraUsage = `
|
||||||
The -logflags value is a sum of the following:
|
The --logflags value is a sum of the following:
|
||||||
|
|
||||||
1 Date
|
1 Date
|
||||||
2 Time
|
2 Time
|
||||||
@ -80,31 +81,11 @@ Development Settings
|
|||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
The following environment variables modify Syncthing's behavior in ways that
|
The following environment variables modify Syncthing's behavior in ways that
|
||||||
are mostly useful for developers. Use with care.
|
are mostly useful for developers. Use with care. See also the --debug-* options
|
||||||
|
above.
|
||||||
STNODEFAULTFOLDER Don't create a default folder when starting for the first
|
|
||||||
time. This variable will be ignored anytime after the first
|
|
||||||
run.
|
|
||||||
|
|
||||||
STGUIASSETS Directory to load GUI assets from. Overrides compiled in
|
|
||||||
assets.
|
|
||||||
|
|
||||||
STTRACE A comma separated string of facilities to trace. The valid
|
STTRACE A comma separated string of facilities to trace. The valid
|
||||||
facility strings listed below.
|
facility strings are listed below.
|
||||||
|
|
||||||
STPROFILER Set to a listen address such as "127.0.0.1:9090" to start
|
|
||||||
the profiler with HTTP access.
|
|
||||||
|
|
||||||
STCPUPROFILE Write a CPU profile to cpu-$pid.pprof on exit.
|
|
||||||
|
|
||||||
STHEAPPROFILE Write heap profiles to heap-$pid-$timestamp.pprof each time
|
|
||||||
heap usage increases.
|
|
||||||
|
|
||||||
STBLOCKPROFILE Write block profiles to block-$pid-$timestamp.pprof every 20
|
|
||||||
seconds.
|
|
||||||
|
|
||||||
STPERFSTATS Write running performance statistics to perf-$pid.csv. Not
|
|
||||||
supported on Windows.
|
|
||||||
|
|
||||||
STDEADLOCKTIMEOUT Used for debugging internal deadlocks; sets debug
|
STDEADLOCKTIMEOUT Used for debugging internal deadlocks; sets debug
|
||||||
sensitivity. Use only under direction of a developer.
|
sensitivity. Use only under direction of a developer.
|
||||||
@ -112,24 +93,11 @@ are mostly useful for developers. Use with care.
|
|||||||
STLOCKTHRESHOLD Used for debugging internal deadlocks; sets debug
|
STLOCKTHRESHOLD Used for debugging internal deadlocks; sets debug
|
||||||
sensitivity. Use only under direction of a developer.
|
sensitivity. Use only under direction of a developer.
|
||||||
|
|
||||||
STNORESTART Equivalent to the -no-restart argument.
|
|
||||||
|
|
||||||
STNOUPGRADE Disable automatic upgrades.
|
|
||||||
|
|
||||||
STHASHING Select the SHA256 hashing package to use. Possible values
|
STHASHING Select the SHA256 hashing package to use. Possible values
|
||||||
are "standard" for the Go standard library implementation,
|
are "standard" for the Go standard library implementation,
|
||||||
"minio" for the github.com/minio/sha256-simd implementation,
|
"minio" for the github.com/minio/sha256-simd implementation,
|
||||||
and blank (the default) for auto detection.
|
and blank (the default) for auto detection.
|
||||||
|
|
||||||
STRECHECKDBEVERY Set to a time interval to override the default database
|
|
||||||
check interval of 30 days (720h). The interval understands
|
|
||||||
"h", "m" and "s" abbreviations for hours minutes and seconds.
|
|
||||||
Valid values are like "720h", "30s", etc.
|
|
||||||
|
|
||||||
STGCINDIRECTEVERY Set to a time interval to override the default database
|
|
||||||
indirection GC interval of 13 hours. Same format as the
|
|
||||||
STRECHECKDBEVERY variable.
|
|
||||||
|
|
||||||
GOMAXPROCS Set the maximum number of CPU cores to use. Defaults to all
|
GOMAXPROCS Set the maximum number of CPU cores to use. Defaults to all
|
||||||
available CPU cores.
|
available CPU cores.
|
||||||
|
|
||||||
@ -143,75 +111,83 @@ Debugging Facilities
|
|||||||
|
|
||||||
The following are valid values for the STTRACE variable:
|
The following are valid values for the STTRACE variable:
|
||||||
|
|
||||||
%s`
|
%s
|
||||||
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Environment options
|
|
||||||
innerProcess = os.Getenv("STMONITORED") != ""
|
|
||||||
noDefaultFolder = os.Getenv("STNODEFAULTFOLDER") != ""
|
|
||||||
|
|
||||||
upgradeCheckInterval = 5 * time.Minute
|
upgradeCheckInterval = 5 * time.Minute
|
||||||
upgradeRetryInterval = time.Hour
|
upgradeRetryInterval = time.Hour
|
||||||
upgradeCheckKey = "lastUpgradeCheck"
|
upgradeCheckKey = "lastUpgradeCheck"
|
||||||
upgradeTimeKey = "lastUpgradeTime"
|
upgradeTimeKey = "lastUpgradeTime"
|
||||||
upgradeVersionKey = "lastUpgradeVersion"
|
upgradeVersionKey = "lastUpgradeVersion"
|
||||||
|
|
||||||
errConcurrentUpgrade = errors.New("upgrade prevented by other running Syncthing instance")
|
|
||||||
errTooEarlyUpgradeCheck = fmt.Errorf("last upgrade check happened less than %v ago, skipping", upgradeCheckInterval)
|
errTooEarlyUpgradeCheck = fmt.Errorf("last upgrade check happened less than %v ago, skipping", upgradeCheckInterval)
|
||||||
errTooEarlyUpgrade = fmt.Errorf("last upgrade happened less than %v ago, skipping", upgradeRetryInterval)
|
errTooEarlyUpgrade = fmt.Errorf("last upgrade happened less than %v ago, skipping", upgradeRetryInterval)
|
||||||
)
|
)
|
||||||
|
|
||||||
type RuntimeOptions struct {
|
// The cli struct is the main entry point for the command line parser. The
|
||||||
syncthing.Options
|
// commands and options here are top level commands to syncthing.
|
||||||
homeDir string
|
var cli struct {
|
||||||
confDir string
|
Serve serveOptions `cmd:"" help:"Run Syncthing"`
|
||||||
dataDir string
|
|
||||||
resetDatabase bool
|
|
||||||
showVersion bool
|
|
||||||
showPaths bool
|
|
||||||
showDeviceId bool
|
|
||||||
doUpgrade bool
|
|
||||||
doUpgradeCheck bool
|
|
||||||
upgradeTo string
|
|
||||||
noBrowser bool
|
|
||||||
browserOnly bool
|
|
||||||
hideConsole bool
|
|
||||||
logFile string
|
|
||||||
logMaxSize int
|
|
||||||
logMaxFiles int
|
|
||||||
auditEnabled bool
|
|
||||||
auditFile string
|
|
||||||
paused bool
|
|
||||||
unpaused bool
|
|
||||||
guiAddress string
|
|
||||||
guiAPIKey string
|
|
||||||
generateDir string
|
|
||||||
noRestart bool
|
|
||||||
cpuProfile bool
|
|
||||||
stRestarting bool
|
|
||||||
logFlags int
|
|
||||||
showHelp bool
|
|
||||||
allowNewerConfig bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultRuntimeOptions() RuntimeOptions {
|
// serveOptions are the options for the `syncthing serve` command.
|
||||||
options := RuntimeOptions{
|
type serveOptions struct {
|
||||||
Options: syncthing.Options{
|
AllowNewerConfig bool `help:"Allow loading newer than current config version"`
|
||||||
AssetDir: os.Getenv("STGUIASSETS"),
|
Audit bool `help:"Write events to audit file"`
|
||||||
NoUpgrade: os.Getenv("STNOUPGRADE") != "",
|
AuditFile string `name:"auditfile" placeholder:"PATH" help:"Specify audit file (use \"-\" for stdout, \"--\" for stderr)"`
|
||||||
ProfilerURL: os.Getenv("STPROFILER"),
|
BrowserOnly bool `help:"Open GUI in browser"`
|
||||||
},
|
ConfDir string `name:"conf" placeholder:"PATH" help:"Set configuration directory (config and keys)"`
|
||||||
noRestart: os.Getenv("STNORESTART") != "",
|
DataDir string `name:"data" placeholder:"PATH" help:"Set data directory (database and logs)"`
|
||||||
cpuProfile: os.Getenv("STCPUPROFILE") != "",
|
DeviceID bool `help:"Show the device ID"`
|
||||||
stRestarting: os.Getenv("STRESTART") != "",
|
GenerateDir string `name:"generate" placeholder:"PATH" help:"Generate key and config in specified dir, then exit"`
|
||||||
logFlags: log.Ltime,
|
GUIAddress string `name:"gui-address" placeholder:"URL" help:"Override GUI address (e.g. \"http://192.0.2.42:8443\")"`
|
||||||
logMaxSize: 10 << 20, // 10 MiB
|
GUIAPIKey string `name:"gui-apikey" placeholder:"API-KEY" help:"Override GUI API key"`
|
||||||
logMaxFiles: 3, // plus the current one
|
HideConsole bool `help:"Hide console window (Windows only)"`
|
||||||
}
|
HomeDir string `name:"home" placeholder:"PATH" help:"Set configuration and data directory"`
|
||||||
|
LogFile string `placeholder:"PATH" help:"Log file name (see below)"`
|
||||||
|
LogFlags int `placeholder:"BITS" help:"Select information in log line prefix (see below)"`
|
||||||
|
LogMaxFiles int `placeholder:"N" name:"log-max-old-files" help:"Number of old files to keep (zero to keep only current)"`
|
||||||
|
LogMaxSize int `placeholder:"BYTES" help:"Maximum size of any file (zero to disable log rotation)"`
|
||||||
|
NoBrowser bool `help:"Do not start browser"`
|
||||||
|
NoRestart bool `env:"STNORESTART" help:"Do not restart Syncthing when exiting due to API/GUI command, upgrade, or crash"`
|
||||||
|
NoDefaultFolder bool `env:"STNODEFAULTFOLDER" help:"Don't create the \"default\" folder on first startup"`
|
||||||
|
NoUpgrade bool `env:"STNOUPGRADE" help:"Disable automatic upgrades"`
|
||||||
|
Paths bool `help:"Show configuration paths"`
|
||||||
|
Paused bool `help:"Start with all devices and folders paused"`
|
||||||
|
Unpaused bool `help:"Start with all devices and folders unpaused"`
|
||||||
|
Upgrade bool `help:"Perform upgrade"`
|
||||||
|
UpgradeCheck bool `help:"Check for available upgrade"`
|
||||||
|
UpgradeTo string `placeholder:"URL" help:"Force upgrade directly from specified URL"`
|
||||||
|
Verbose bool `help:"Print verbose log output"`
|
||||||
|
Version bool `help:"Show version"`
|
||||||
|
|
||||||
|
// Debug options below
|
||||||
|
DebugDBIndirectGCInterval time.Duration `env:"STGCINDIRECTEVERY" help:"Database indirection GC interval"`
|
||||||
|
DebugDBRecheckInterval time.Duration `env:"STRECHECKDBEVERY" help:"Database metadata recalculation interval"`
|
||||||
|
DebugDeadlockTimeout int `placeholder:"SECONDS" env:"STDEADLOCKTIMEOUT" help:"Used for debugging internal deadlocks"`
|
||||||
|
DebugGUIAssetsDir string `placeholder:"PATH" help:"Directory to load GUI assets from" env:"STGUIASSETS"`
|
||||||
|
DebugPerfStats bool `env:"STPERFSTATS" help:"Write running performance statistics to perf-$pid.csv (Unix only)"`
|
||||||
|
DebugProfileBlock bool `env:"STBLOCKPROFILE" help:"Write block profiles to block-$pid-$timestamp.pprof every 20 seconds"`
|
||||||
|
DebugProfileCPU bool `help:"Write a CPU profile to cpu-$pid.pprof on exit" env:"CPUPROFILE"`
|
||||||
|
DebugProfileHeap bool `env:"STHEAPPROFILE" help:"Write heap profiles to heap-$pid-$timestamp.pprof each time heap usage increases"`
|
||||||
|
DebugProfilerListen string `placeholder:"ADDR" env:"STPROFILER" help:"Network profiler listen address"`
|
||||||
|
DebugResetDatabase bool `name:"reset-database" help:"Reset the database, forcing a full rescan and resync"`
|
||||||
|
DebugResetDeltaIdxs bool `name:"reset-deltas" help:"Reset delta index IDs, forcing a full index exchange"`
|
||||||
|
|
||||||
|
// Internal options, not shown to users
|
||||||
|
InternalRestarting bool `env:"STRESTART" hidden:"1"`
|
||||||
|
InternalInnerProcess bool `env:"STMONITORED" hidden:"1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (options *serveOptions) setDefaults() {
|
||||||
|
options.LogFlags = log.Ltime
|
||||||
|
options.LogMaxSize = 10 << 20 // 10 MiB
|
||||||
|
options.LogMaxFiles = 3 // plus the current one
|
||||||
|
|
||||||
if os.Getenv("STTRACE") != "" {
|
if os.Getenv("STTRACE") != "" {
|
||||||
options.logFlags = logger.DebugFlags
|
options.LogFlags = logger.DebugFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
// On non-Windows, we explicitly default to "-" which means stdout. On
|
// On non-Windows, we explicitly default to "-" which means stdout. On
|
||||||
@ -219,107 +195,97 @@ func defaultRuntimeOptions() RuntimeOptions {
|
|||||||
// default path, unless the user has manually specified "-" or
|
// default path, unless the user has manually specified "-" or
|
||||||
// something else.
|
// something else.
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
options.logFile = "default"
|
options.LogFile = "default"
|
||||||
} else {
|
} else {
|
||||||
options.logFile = "-"
|
options.LogFile = "-"
|
||||||
}
|
}
|
||||||
|
|
||||||
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.homeDir, "home", "", "Set configuration and data directory")
|
|
||||||
flag.StringVar(&options.confDir, "config", "", "Set configuration directory (config and keys)")
|
|
||||||
flag.StringVar(&options.dataDir, "data", "", "Set data directory (database and logs)")
|
|
||||||
flag.IntVar(&options.logFlags, "logflags", options.logFlags, "Select information in log line prefix (see below)")
|
|
||||||
flag.BoolVar(&options.noBrowser, "no-browser", false, "Do not start browser")
|
|
||||||
flag.BoolVar(&options.browserOnly, "browser-only", false, "Open GUI in browser")
|
|
||||||
flag.BoolVar(&options.noRestart, "no-restart", options.noRestart, "Do not restart Syncthing when exiting due to API/GUI command, upgrade, or crash")
|
|
||||||
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")
|
|
||||||
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")
|
|
||||||
flag.BoolVar(&options.showHelp, "help", false, "Show this help")
|
|
||||||
flag.BoolVar(&options.showPaths, "paths", false, "Show configuration paths")
|
|
||||||
flag.BoolVar(&options.showDeviceId, "device-id", false, "Show the device ID")
|
|
||||||
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")
|
|
||||||
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")
|
|
||||||
flag.StringVar(&options.logFile, "logfile", options.logFile, "Log file name (see below).")
|
|
||||||
flag.IntVar(&options.logMaxSize, "log-max-size", options.logMaxSize, "Maximum size of any file (zero to disable log rotation).")
|
|
||||||
flag.IntVar(&options.logMaxFiles, "log-max-old-files", options.logMaxFiles, "Number of old files to keep (zero to keep only current).")
|
|
||||||
flag.StringVar(&options.auditFile, "auditfile", options.auditFile, "Specify audit file (use \"-\" for stdout, \"--\" for stderr)")
|
|
||||||
flag.BoolVar(&options.allowNewerConfig, "allow-newer-config", false, "Allow loading newer than current config version")
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// Allow user to hide the console window
|
|
||||||
flag.BoolVar(&options.hideConsole, "no-console", false, "Hide console window")
|
|
||||||
}
|
|
||||||
|
|
||||||
longUsage := fmt.Sprintf(extraUsage, debugFacilities())
|
|
||||||
flag.Usage = usageFor(flag.CommandLine, usage, longUsage)
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if len(flag.Args()) > 0 {
|
|
||||||
flag.Usage()
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
func setLocation(enum locations.BaseDirEnum, loc string) error {
|
|
||||||
if !filepath.IsAbs(loc) {
|
|
||||||
var err error
|
|
||||||
loc, err = filepath.Abs(loc)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return locations.SetBaseDir(enum, loc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
options := parseCommandLineOptions()
|
// First some massaging of the raw command line to fit the new model.
|
||||||
l.SetFlags(options.logFlags)
|
// Basically this means adding the default command at the front, and
|
||||||
|
// converting -options to --options.
|
||||||
|
|
||||||
if options.guiAddress != "" {
|
args := os.Args[1:]
|
||||||
// The config picks this up from the environment.
|
switch {
|
||||||
os.Setenv("STGUIADDRESS", options.guiAddress)
|
case len(args) == 0:
|
||||||
}
|
// Empty command line is equivalent to just calling serve
|
||||||
if options.guiAPIKey != "" {
|
args = []string{"serve"}
|
||||||
// The config picks this up from the environment.
|
case args[0] == "-help":
|
||||||
os.Setenv("STGUIAPIKEY", options.guiAPIKey)
|
// For consistency, we consider this equivalent with --help even
|
||||||
|
// though kong would otherwise consider it a bad flag.
|
||||||
|
args[0] = "--help"
|
||||||
|
case args[0] == "-h", args[0] == "--help":
|
||||||
|
// Top level request for help, let it pass as-is to be handled by
|
||||||
|
// kong to list commands.
|
||||||
|
case strings.HasPrefix(args[0], "-"):
|
||||||
|
// There are flags not preceded by a command, so we tack on the
|
||||||
|
// "serve" command and convert the old style arguments (single dash)
|
||||||
|
// to new style (double dash).
|
||||||
|
args = append([]string{"serve"}, convertLegacyArgs(args)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.hideConsole {
|
cli.Serve.setDefaults()
|
||||||
|
|
||||||
|
// Create a parser with an overridden help function to print our extra
|
||||||
|
// help info.
|
||||||
|
parser, err := kong.New(&cli, kong.Help(extraHelpPrinter))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, err := parser.Parse(args)
|
||||||
|
parser.FatalIfErrorf(err)
|
||||||
|
err = ctx.Run()
|
||||||
|
parser.FatalIfErrorf(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extraHelpPrinter(options kong.HelpOptions, ctx *kong.Context) error {
|
||||||
|
if err := kong.DefaultHelpPrinter(options, ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ctx.Command() == "serve" {
|
||||||
|
// Help was requested for `syncthing serve`, so we add our extra
|
||||||
|
// usage info afte the normal options output.
|
||||||
|
fmt.Printf(extraUsage, debugFacilities())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveOptions.Run() is the entrypoint for `syncthing serve`
|
||||||
|
func (options serveOptions) Run() error {
|
||||||
|
l.SetFlags(options.LogFlags)
|
||||||
|
|
||||||
|
if options.GUIAddress != "" {
|
||||||
|
// The config picks this up from the environment.
|
||||||
|
os.Setenv("STGUIADDRESS", options.GUIAddress)
|
||||||
|
}
|
||||||
|
if options.GUIAPIKey != "" {
|
||||||
|
// The config picks this up from the environment.
|
||||||
|
os.Setenv("STGUIAPIKEY", options.GUIAPIKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.HideConsole {
|
||||||
osutil.HideConsole()
|
osutil.HideConsole()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not set as default above because the strings can be really long.
|
// Not set as default above because the strings can be really long.
|
||||||
var err error
|
var err error
|
||||||
homeSet := options.homeDir != ""
|
homeSet := options.HomeDir != ""
|
||||||
confSet := options.confDir != ""
|
confSet := options.ConfDir != ""
|
||||||
dataSet := options.dataDir != ""
|
dataSet := options.DataDir != ""
|
||||||
switch {
|
switch {
|
||||||
case dataSet != confSet:
|
case dataSet != confSet:
|
||||||
err = errors.New("either both or none of -conf and -data must be given, use -home to set both at once")
|
err = errors.New("either both or none of -conf and -data must be given, use -home to set both at once")
|
||||||
case homeSet && dataSet:
|
case homeSet && dataSet:
|
||||||
err = errors.New("-home must not be used together with -conf and -data")
|
err = errors.New("-home must not be used together with -conf and -data")
|
||||||
case homeSet:
|
case homeSet:
|
||||||
if err = setLocation(locations.ConfigBaseDir, options.homeDir); err == nil {
|
if err = setLocation(locations.ConfigBaseDir, options.HomeDir); err == nil {
|
||||||
err = setLocation(locations.DataBaseDir, options.homeDir)
|
err = setLocation(locations.DataBaseDir, options.HomeDir)
|
||||||
}
|
}
|
||||||
case dataSet:
|
case dataSet:
|
||||||
if err = setLocation(locations.ConfigBaseDir, options.confDir); err == nil {
|
if err = setLocation(locations.ConfigBaseDir, options.ConfDir); err == nil {
|
||||||
err = setLocation(locations.DataBaseDir, options.dataDir)
|
err = setLocation(locations.DataBaseDir, options.DataDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -327,34 +293,29 @@ func main() {
|
|||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.logFile == "default" || options.logFile == "" {
|
if options.LogFile == "default" || options.LogFile == "" {
|
||||||
// We must set this *after* expandLocations above.
|
// We must set this *after* expandLocations above.
|
||||||
// Handling an empty value is for backwards compatibility (<1.4.1).
|
// Handling an empty value is for backwards compatibility (<1.4.1).
|
||||||
options.logFile = locations.Get(locations.LogFile)
|
options.LogFile = locations.Get(locations.LogFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.AssetDir == "" {
|
if options.DebugGUIAssetsDir == "" {
|
||||||
// The asset dir is blank if STGUIASSETS wasn't set, in which case we
|
// The asset dir is blank if STGUIASSETS wasn't set, in which case we
|
||||||
// should look for extra assets in the default place.
|
// should look for extra assets in the default place.
|
||||||
options.AssetDir = locations.Get(locations.GUIAssets)
|
options.DebugGUIAssetsDir = locations.Get(locations.GUIAssets)
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.showVersion {
|
if options.Version {
|
||||||
fmt.Println(build.LongVersion)
|
fmt.Println(build.LongVersion)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.showHelp {
|
if options.Paths {
|
||||||
flag.Usage()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.showPaths {
|
|
||||||
showPaths(options)
|
showPaths(options)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.showDeviceId {
|
if options.DeviceID {
|
||||||
cert, err := tls.LoadX509KeyPair(
|
cert, err := tls.LoadX509KeyPair(
|
||||||
locations.Get(locations.CertFile),
|
locations.Get(locations.CertFile),
|
||||||
locations.Get(locations.KeyFile),
|
locations.Get(locations.KeyFile),
|
||||||
@ -365,23 +326,23 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(protocol.NewDeviceID(cert.Certificate[0]))
|
fmt.Println(protocol.NewDeviceID(cert.Certificate[0]))
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.browserOnly {
|
if options.BrowserOnly {
|
||||||
if err := openGUI(protocol.EmptyDeviceID); err != nil {
|
if err := openGUI(protocol.EmptyDeviceID); err != nil {
|
||||||
l.Warnln("Failed to open web UI:", err)
|
l.Warnln("Failed to open web UI:", err)
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.generateDir != "" {
|
if options.GenerateDir != "" {
|
||||||
if err := generate(options.generateDir); err != nil {
|
if err := generate(options.GenerateDir, options.NoDefaultFolder); err != nil {
|
||||||
l.Warnln("Failed to generate config and keys:", err)
|
l.Warnln("Failed to generate config and keys:", err)
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that our home directory exists.
|
// Ensure that our home directory exists.
|
||||||
@ -390,25 +351,25 @@ func main() {
|
|||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.upgradeTo != "" {
|
if options.UpgradeTo != "" {
|
||||||
err := upgrade.ToURL(options.upgradeTo)
|
err := upgrade.ToURL(options.UpgradeTo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Warnln("Error while Upgrading:", err)
|
l.Warnln("Error while Upgrading:", err)
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
l.Infoln("Upgraded from", options.upgradeTo)
|
l.Infoln("Upgraded from", options.UpgradeTo)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.doUpgradeCheck {
|
if options.UpgradeCheck {
|
||||||
if _, err := checkUpgrade(); err != nil {
|
if _, err := checkUpgrade(); err != nil {
|
||||||
l.Warnln("Checking for upgrade:", err)
|
l.Warnln("Checking for upgrade:", err)
|
||||||
os.Exit(exitCodeForUpgrade(err))
|
os.Exit(exitCodeForUpgrade(err))
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.doUpgrade {
|
if options.Upgrade {
|
||||||
release, err := checkUpgrade()
|
release, err := checkUpgrade()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Use leveldb database locks to protect against concurrent upgrades
|
// Use leveldb database locks to protect against concurrent upgrades
|
||||||
@ -428,24 +389,25 @@ func main() {
|
|||||||
os.Exit(svcutil.ExitUpgrade.AsInt())
|
os.Exit(svcutil.ExitUpgrade.AsInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.resetDatabase {
|
if options.DebugResetDatabase {
|
||||||
if err := resetDB(); err != nil {
|
if err := resetDB(); err != nil {
|
||||||
l.Warnln("Resetting database:", err)
|
l.Warnln("Resetting database:", err)
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
l.Infoln("Successfully reset database - it will be rebuilt after next start.")
|
l.Infoln("Successfully reset database - it will be rebuilt after next start.")
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if innerProcess {
|
if options.InternalInnerProcess {
|
||||||
syncthingMain(options)
|
syncthingMain(options)
|
||||||
} else {
|
} else {
|
||||||
monitorMain(options)
|
monitorMain(options)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func openGUI(myID protocol.DeviceID) error {
|
func openGUI(myID protocol.DeviceID) error {
|
||||||
cfg, err := loadOrDefaultConfig(myID, events.NoopLogger)
|
cfg, err := loadOrDefaultConfig(myID, events.NoopLogger, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -459,7 +421,7 @@ func openGUI(myID protocol.DeviceID) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generate(generateDir string) error {
|
func generate(generateDir string, noDefaultFolder bool) error {
|
||||||
dir, err := fs.ExpandTilde(generateDir)
|
dir, err := fs.ExpandTilde(generateDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -530,7 +492,7 @@ func (e *errNoUpgrade) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkUpgrade() (upgrade.Release, error) {
|
func checkUpgrade() (upgrade.Release, error) {
|
||||||
cfg, err := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
|
cfg, err := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return upgrade.Release{}, err
|
return upgrade.Release{}, err
|
||||||
}
|
}
|
||||||
@ -549,7 +511,7 @@ func checkUpgrade() (upgrade.Release, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func upgradeViaRest() error {
|
func upgradeViaRest() error {
|
||||||
cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
|
cfg, _ := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger, true)
|
||||||
u, err := url.Parse(cfg.GUI().URL())
|
u, err := url.Parse(cfg.GUI().URL())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -584,7 +546,17 @@ func upgradeViaRest() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncthingMain(runtimeOptions RuntimeOptions) {
|
func syncthingMain(options serveOptions) {
|
||||||
|
if options.DebugProfileBlock {
|
||||||
|
startBlockProfiler()
|
||||||
|
}
|
||||||
|
if options.DebugProfileHeap {
|
||||||
|
startHeapProfiler()
|
||||||
|
}
|
||||||
|
if options.DebugPerfStats {
|
||||||
|
startPerfStats()
|
||||||
|
}
|
||||||
|
|
||||||
// Set a log prefix similar to the ID we will have later on, or early log
|
// Set a log prefix similar to the ID we will have later on, or early log
|
||||||
// lines look ugly.
|
// lines look ugly.
|
||||||
l.SetPrefix("[start] ")
|
l.SetPrefix("[start] ")
|
||||||
@ -615,7 +587,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
|||||||
evLogger := events.NewLogger()
|
evLogger := events.NewLogger()
|
||||||
earlyService.Add(evLogger)
|
earlyService.Add(evLogger)
|
||||||
|
|
||||||
cfgWrapper, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, evLogger, runtimeOptions.allowNewerConfig, noDefaultFolder)
|
cfgWrapper, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, evLogger, options.AllowNewerConfig, options.NoDefaultFolder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Warnln("Failed to initialize config:", err)
|
l.Warnln("Failed to initialize config:", err)
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
@ -628,7 +600,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
|||||||
// unless we are in a build where it's disabled or the STNOUPGRADE
|
// unless we are in a build where it's disabled or the STNOUPGRADE
|
||||||
// environment variable is set.
|
// environment variable is set.
|
||||||
|
|
||||||
if build.IsCandidate && !upgrade.DisabledByCompilation && !runtimeOptions.NoUpgrade {
|
if build.IsCandidate && !upgrade.DisabledByCompilation && !options.NoUpgrade {
|
||||||
cfgWrapper.Modify(func(cfg *config.Configuration) {
|
cfgWrapper.Modify(func(cfg *config.Configuration) {
|
||||||
l.Infoln("Automatic upgrade is always enabled for candidate releases.")
|
l.Infoln("Automatic upgrade is always enabled for candidate releases.")
|
||||||
if cfg.Options.AutoUpgradeIntervalH == 0 || cfg.Options.AutoUpgradeIntervalH > 24 {
|
if cfg.Options.AutoUpgradeIntervalH == 0 || cfg.Options.AutoUpgradeIntervalH > 24 {
|
||||||
@ -652,7 +624,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
|||||||
// upgrade immedately. The auto-upgrade routine can only be started
|
// upgrade immedately. The auto-upgrade routine can only be started
|
||||||
// later after App is initialised.
|
// later after App is initialised.
|
||||||
|
|
||||||
autoUpgradePossible := autoUpgradePossible(runtimeOptions)
|
autoUpgradePossible := autoUpgradePossible(options)
|
||||||
if autoUpgradePossible && cfgWrapper.Options().AutoUpgradeEnabled() {
|
if autoUpgradePossible && cfgWrapper.Options().AutoUpgradeEnabled() {
|
||||||
// try to do upgrade directly and log the error if relevant.
|
// try to do upgrade directly and log the error if relevant.
|
||||||
release, err := initialAutoUpgradeCheck(db.NewMiscDataNamespace(ldb))
|
release, err := initialAutoUpgradeCheck(db.NewMiscDataNamespace(ldb))
|
||||||
@ -671,15 +643,24 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtimeOptions.unpaused {
|
if options.Unpaused {
|
||||||
setPauseState(cfgWrapper, false)
|
setPauseState(cfgWrapper, false)
|
||||||
} else if runtimeOptions.paused {
|
} else if options.Paused {
|
||||||
setPauseState(cfgWrapper, true)
|
setPauseState(cfgWrapper, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
appOpts := runtimeOptions.Options
|
appOpts := syncthing.Options{
|
||||||
if runtimeOptions.auditEnabled {
|
AssetDir: options.DebugGUIAssetsDir,
|
||||||
appOpts.AuditWriter = auditWriter(runtimeOptions.auditFile)
|
DeadlockTimeoutS: options.DebugDeadlockTimeout,
|
||||||
|
NoUpgrade: options.NoUpgrade,
|
||||||
|
ProfilerAddr: options.DebugProfilerListen,
|
||||||
|
ResetDeltaIdxs: options.DebugResetDeltaIdxs,
|
||||||
|
Verbose: options.Verbose,
|
||||||
|
DBRecheckInterval: options.DebugDBRecheckInterval,
|
||||||
|
DBIndirectGCInterval: options.DebugDBIndirectGCInterval,
|
||||||
|
}
|
||||||
|
if options.Audit {
|
||||||
|
appOpts.AuditWriter = auditWriter(options.AuditFile)
|
||||||
}
|
}
|
||||||
if t := os.Getenv("STDEADLOCKTIMEOUT"); t != "" {
|
if t := os.Getenv("STDEADLOCKTIMEOUT"); t != "" {
|
||||||
secs, _ := strconv.Atoi(t)
|
secs, _ := strconv.Atoi(t)
|
||||||
@ -708,7 +689,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
|||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtimeOptions.cpuProfile {
|
if options.DebugProfileCPU {
|
||||||
f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid()))
|
f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Warnln("Creating profile:", err)
|
l.Warnln("Creating profile:", err)
|
||||||
@ -728,7 +709,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
|||||||
|
|
||||||
cleanConfigDirectory()
|
cleanConfigDirectory()
|
||||||
|
|
||||||
if cfgWrapper.Options().StartBrowser && !runtimeOptions.noBrowser && !runtimeOptions.stRestarting {
|
if cfgWrapper.Options().StartBrowser && !options.NoBrowser && !options.InternalRestarting {
|
||||||
// Can potentially block if the utility we are invoking doesn't
|
// Can potentially block if the utility we are invoking doesn't
|
||||||
// fork, and just execs, hence keep it in its own routine.
|
// fork, and just execs, hence keep it in its own routine.
|
||||||
go func() { _ = openURL(cfgWrapper.GUI().URL()) }()
|
go func() { _ = openURL(cfgWrapper.GUI().URL()) }()
|
||||||
@ -740,7 +721,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
|
|||||||
l.Warnln("Syncthing stopped with error:", app.Error())
|
l.Warnln("Syncthing stopped with error:", app.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtimeOptions.cpuProfile {
|
if options.DebugProfileCPU {
|
||||||
pprof.StopCPUProfile()
|
pprof.StopCPUProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -768,7 +749,7 @@ func setupSignalHandling(app *syncthing.App) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadOrDefaultConfig(myID protocol.DeviceID, evLogger events.Logger) (config.Wrapper, error) {
|
func loadOrDefaultConfig(myID protocol.DeviceID, evLogger events.Logger, noDefaultFolder bool) (config.Wrapper, error) {
|
||||||
cfgFile := locations.Get(locations.ConfigFile)
|
cfgFile := locations.Get(locations.ConfigFile)
|
||||||
cfg, _, err := config.Load(cfgFile, myID, evLogger)
|
cfg, _, err := config.Load(cfgFile, myID, evLogger)
|
||||||
|
|
||||||
@ -858,11 +839,11 @@ func standbyMonitor(app *syncthing.App, cfg config.Wrapper) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func autoUpgradePossible(runtimeOptions RuntimeOptions) bool {
|
func autoUpgradePossible(options serveOptions) bool {
|
||||||
if upgrade.DisabledByCompilation {
|
if upgrade.DisabledByCompilation {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if runtimeOptions.NoUpgrade {
|
if options.NoUpgrade {
|
||||||
l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.")
|
l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -989,13 +970,13 @@ func cleanConfigDirectory() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func showPaths(options RuntimeOptions) {
|
func showPaths(options serveOptions) {
|
||||||
fmt.Printf("Configuration file:\n\t%s\n\n", locations.Get(locations.ConfigFile))
|
fmt.Printf("Configuration file:\n\t%s\n\n", locations.Get(locations.ConfigFile))
|
||||||
fmt.Printf("Database directory:\n\t%s\n\n", locations.Get(locations.Database))
|
fmt.Printf("Database directory:\n\t%s\n\n", locations.Get(locations.Database))
|
||||||
fmt.Printf("Device private key & certificate files:\n\t%s\n\t%s\n\n", locations.Get(locations.KeyFile), locations.Get(locations.CertFile))
|
fmt.Printf("Device private key & certificate files:\n\t%s\n\t%s\n\n", locations.Get(locations.KeyFile), locations.Get(locations.CertFile))
|
||||||
fmt.Printf("HTTPS private key & certificate files:\n\t%s\n\t%s\n\n", locations.Get(locations.HTTPSKeyFile), locations.Get(locations.HTTPSCertFile))
|
fmt.Printf("HTTPS private key & certificate files:\n\t%s\n\t%s\n\n", locations.Get(locations.HTTPSKeyFile), locations.Get(locations.HTTPSCertFile))
|
||||||
fmt.Printf("Log file:\n\t%s\n\n", options.logFile)
|
fmt.Printf("Log file:\n\t%s\n\n", options.LogFile)
|
||||||
fmt.Printf("GUI override directory:\n\t%s\n\n", options.AssetDir)
|
fmt.Printf("GUI override directory:\n\t%s\n\n", options.DebugGUIAssetsDir)
|
||||||
fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations.Get(locations.DefFolder))
|
fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations.Get(locations.DefFolder))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1020,3 +1001,32 @@ func exitCodeForUpgrade(err error) int {
|
|||||||
}
|
}
|
||||||
return svcutil.ExitError.AsInt()
|
return svcutil.ExitError.AsInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setLocation(enum locations.BaseDirEnum, loc string) error {
|
||||||
|
if !filepath.IsAbs(loc) {
|
||||||
|
var err error
|
||||||
|
loc, err = filepath.Abs(loc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return locations.SetBaseDir(enum, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertLegacyArgs returns the slice of arguments with single dash long
|
||||||
|
// flags converted to double dash long flags.
|
||||||
|
func convertLegacyArgs(args []string) []string {
|
||||||
|
// Legacy args begin with a single dash, followed by two or more characters.
|
||||||
|
legacyExp := regexp.MustCompile(`^-\w{2,}`)
|
||||||
|
|
||||||
|
res := make([]string, len(args))
|
||||||
|
for i, arg := range args {
|
||||||
|
if legacyExp.MatchString(arg) {
|
||||||
|
res[i] = "-" + arg
|
||||||
|
} else {
|
||||||
|
res[i] = arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
@ -44,12 +44,12 @@ const (
|
|||||||
panicUploadNoticeWait = 10 * time.Second
|
panicUploadNoticeWait = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func monitorMain(runtimeOptions RuntimeOptions) {
|
func monitorMain(options serveOptions) {
|
||||||
l.SetPrefix("[monitor] ")
|
l.SetPrefix("[monitor] ")
|
||||||
|
|
||||||
var dst io.Writer = os.Stdout
|
var dst io.Writer = os.Stdout
|
||||||
|
|
||||||
logFile := runtimeOptions.logFile
|
logFile := options.LogFile
|
||||||
if logFile != "-" {
|
if logFile != "-" {
|
||||||
if expanded, err := fs.ExpandTilde(logFile); err == nil {
|
if expanded, err := fs.ExpandTilde(logFile); err == nil {
|
||||||
logFile = expanded
|
logFile = expanded
|
||||||
@ -59,8 +59,8 @@ func monitorMain(runtimeOptions RuntimeOptions) {
|
|||||||
open := func(name string) (io.WriteCloser, error) {
|
open := func(name string) (io.WriteCloser, error) {
|
||||||
return newAutoclosedFile(name, logFileAutoCloseDelay, logFileMaxOpenTime)
|
return newAutoclosedFile(name, logFileAutoCloseDelay, logFileMaxOpenTime)
|
||||||
}
|
}
|
||||||
if runtimeOptions.logMaxSize > 0 {
|
if options.LogMaxSize > 0 {
|
||||||
fileDst, err = newRotatedFile(logFile, open, int64(runtimeOptions.logMaxSize), runtimeOptions.logMaxFiles)
|
fileDst, err = newRotatedFile(logFile, open, int64(options.LogMaxSize), options.LogMaxFiles)
|
||||||
} else {
|
} else {
|
||||||
fileDst, err = open(logFile)
|
fileDst, err = open(logFile)
|
||||||
}
|
}
|
||||||
@ -174,7 +174,7 @@ func monitorMain(runtimeOptions RuntimeOptions) {
|
|||||||
|
|
||||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||||
exitCode := exiterr.ExitCode()
|
exitCode := exiterr.ExitCode()
|
||||||
if stopped || runtimeOptions.noRestart {
|
if stopped || options.NoRestart {
|
||||||
os.Exit(exitCode)
|
os.Exit(exitCode)
|
||||||
}
|
}
|
||||||
if exitCode == svcutil.ExitUpgrade.AsInt() {
|
if exitCode == svcutil.ExitUpgrade.AsInt() {
|
||||||
@ -188,7 +188,7 @@ func monitorMain(runtimeOptions RuntimeOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtimeOptions.noRestart {
|
if options.NoRestart {
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,7 +563,7 @@ func childEnv() []string {
|
|||||||
// panicUploadMaxWait uploading panics...
|
// panicUploadMaxWait uploading panics...
|
||||||
func maybeReportPanics() {
|
func maybeReportPanics() {
|
||||||
// Try to get a config to see if/where panics should be reported.
|
// Try to get a config to see if/where panics should be reported.
|
||||||
cfg, err := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger)
|
cfg, err := loadOrDefaultConfig(protocol.EmptyDeviceID, events.NoopLogger, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Warnln("Couldn't load config; not reporting crash")
|
l.Warnln("Couldn't load config; not reporting crash")
|
||||||
return
|
return
|
||||||
|
@ -18,10 +18,8 @@ import (
|
|||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func startPerfStats() {
|
||||||
if innerProcess && os.Getenv("STPERFSTATS") != "" {
|
go savePerfStats(fmt.Sprintf("perfstats-%d.csv", syscall.Getpid()))
|
||||||
go savePerfStats(fmt.Sprintf("perfstats-%d.csv", syscall.Getpid()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func savePerfStats(file string) {
|
func savePerfStats(file string) {
|
||||||
|
12
cmd/syncthing/perfstats_unsupported.go
Normal file
12
cmd/syncthing/perfstats_unsupported.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (C) 2021 The Syncthing Authors.
|
||||||
|
//
|
||||||
|
// 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,
|
||||||
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
// +build solaris windows
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func startPerfStats() {
|
||||||
|
}
|
@ -1,57 +0,0 @@
|
|||||||
// Copyright (C) 2014 The Syncthing Authors.
|
|
||||||
//
|
|
||||||
// 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,
|
|
||||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"text/tabwriter"
|
|
||||||
)
|
|
||||||
|
|
||||||
func optionTable(w io.Writer, rows [][]string) {
|
|
||||||
tw := tabwriter.NewWriter(w, 2, 4, 2, ' ', 0)
|
|
||||||
for _, row := range rows {
|
|
||||||
for i, cell := range row {
|
|
||||||
if i > 0 {
|
|
||||||
tw.Write([]byte("\t"))
|
|
||||||
}
|
|
||||||
tw.Write([]byte(cell))
|
|
||||||
}
|
|
||||||
tw.Write([]byte("\n"))
|
|
||||||
}
|
|
||||||
tw.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func usageFor(fs *flag.FlagSet, usage string, extra string) func() {
|
|
||||||
return func() {
|
|
||||||
var b bytes.Buffer
|
|
||||||
b.WriteString("Usage:\n " + usage + "\n")
|
|
||||||
|
|
||||||
var options [][]string
|
|
||||||
fs.VisitAll(func(f *flag.Flag) {
|
|
||||||
var opt = " -" + f.Name
|
|
||||||
|
|
||||||
if f.DefValue != "false" {
|
|
||||||
opt += "=" + fmt.Sprintf(`"%s"`, f.DefValue)
|
|
||||||
}
|
|
||||||
options = append(options, []string{opt, f.Usage})
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(options) > 0 {
|
|
||||||
b.WriteString("\nOptions:\n")
|
|
||||||
optionTable(&b, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(b.String())
|
|
||||||
|
|
||||||
if len(extra) > 0 {
|
|
||||||
fmt.Println(extra)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module github.com/syncthing/syncthing
|
|||||||
require (
|
require (
|
||||||
github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6
|
github.com/AudriusButkevicius/pfilter v0.0.0-20190627213056-c55ef6137fc6
|
||||||
github.com/AudriusButkevicius/recli v0.0.5
|
github.com/AudriusButkevicius/recli v0.0.5
|
||||||
|
github.com/alecthomas/kong v0.2.12
|
||||||
github.com/bkaradzic/go-lz4 v0.0.0-20160924222819-7224d8d8f27e
|
github.com/bkaradzic/go-lz4 v0.0.0-20160924222819-7224d8d8f27e
|
||||||
github.com/calmh/xdr v1.1.0
|
github.com/calmh/xdr v1.1.0
|
||||||
github.com/ccding/go-stun v0.1.2
|
github.com/ccding/go-stun v0.1.2
|
||||||
|
2
go.sum
2
go.sum
@ -25,6 +25,8 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUW
|
|||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||||
|
github.com/alecthomas/kong v0.2.12 h1:X3kkCOXGUNzLmiu+nQtoxWqj4U2a39MpSJR3QdQXOwI=
|
||||||
|
github.com/alecthomas/kong v0.2.12/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
@ -57,7 +57,7 @@ type Options struct {
|
|||||||
AuditWriter io.Writer
|
AuditWriter io.Writer
|
||||||
DeadlockTimeoutS int
|
DeadlockTimeoutS int
|
||||||
NoUpgrade bool
|
NoUpgrade bool
|
||||||
ProfilerURL string
|
ProfilerAddr string
|
||||||
ResetDeltaIdxs bool
|
ResetDeltaIdxs bool
|
||||||
Verbose bool
|
Verbose bool
|
||||||
// null duration means use default value
|
// null duration means use default value
|
||||||
@ -169,11 +169,11 @@ func (a *App) startup() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(a.opts.ProfilerURL) > 0 {
|
if len(a.opts.ProfilerAddr) > 0 {
|
||||||
go func() {
|
go func() {
|
||||||
l.Debugln("Starting profiler on", a.opts.ProfilerURL)
|
l.Debugln("Starting profiler on", a.opts.ProfilerAddr)
|
||||||
runtime.SetBlockProfileRate(1)
|
runtime.SetBlockProfileRate(1)
|
||||||
err := http.ListenAndServe(a.opts.ProfilerURL, nil)
|
err := http.ListenAndServe(a.opts.ProfilerAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Warnln(err)
|
l.Warnln(err)
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user