This is primarily to improve the experience in environments that start Syncthing from a graphical environment, e.g., Windows. When already running, instead of printing an error we open the GUI. If the GUI is not available (but the lockfile indicates we are running), print an error and exit. If --no-browser is given or STNOBROWSER is set, act like before. Closes #10736. Marking as `fix` because this does not deserve to trigger a minor release on its own. --------- Signed-off-by: Jakob Borg <jakob@kastelo.net>
This commit is contained in:
+55
-35
@@ -313,21 +313,6 @@ func (c *serveCmd) Run() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func openGUI() error {
|
|
||||||
cfg, err := loadOrDefaultConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if guiCfg := cfg.GUI(); guiCfg.Enabled {
|
|
||||||
if err := openURL(guiCfg.URL()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
slog.Error("Browser: GUI is currently disabled")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func logPackages() string {
|
func logPackages() string {
|
||||||
packages := slogutil.PackageDescrs()
|
packages := slogutil.PackageDescrs()
|
||||||
|
|
||||||
@@ -418,6 +403,28 @@ func upgradeViaRest() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *serveCmd) syncthingMain() {
|
func (c *serveCmd) syncthingMain() {
|
||||||
|
// Ensure we are the only running instance
|
||||||
|
lf := flock.New(locations.Get(locations.LockFile))
|
||||||
|
locked, err := lf.TryLock()
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
slog.Error("Failed to acquire lock", slogutil.Error(err))
|
||||||
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
|
|
||||||
|
case !locked && c.NoBrowser:
|
||||||
|
slog.Error("Failed to acquire lock: is another Syncthing instance already running?")
|
||||||
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
|
|
||||||
|
case !locked:
|
||||||
|
slog.Info("Seems to already be running, launching GUI instead (use --no-browser to prevent)")
|
||||||
|
cmd := browserCmd{Verify: true}
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
slog.Error("Failed to open browser", slogutil.Error(err))
|
||||||
|
os.Exit(svcutil.ExitNoRestart.AsInt())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if c.DebugProfileBlock {
|
if c.DebugProfileBlock {
|
||||||
startBlockProfiler()
|
startBlockProfiler()
|
||||||
}
|
}
|
||||||
@@ -436,18 +443,7 @@ func (c *serveCmd) syncthingMain() {
|
|||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to load/generate certificate", slogutil.Error(err))
|
slog.Error("Failed to load/generate certificate", slogutil.Error(err))
|
||||||
os.Exit(1)
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we are the only running instance
|
|
||||||
lf := flock.New(locations.Get(locations.LockFile))
|
|
||||||
locked, err := lf.TryLock()
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Failed to acquire lock", slogutil.Error(err))
|
|
||||||
os.Exit(1)
|
|
||||||
} else if !locked {
|
|
||||||
slog.Error("Failed to acquire lock: is another Syncthing instance already running?")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
@@ -502,13 +498,13 @@ func (c *serveCmd) syncthingMain() {
|
|||||||
|
|
||||||
if err := syncthing.TryMigrateDatabase(ctx, c.DBDeleteRetentionInterval); err != nil {
|
if err := syncthing.TryMigrateDatabase(ctx, c.DBDeleteRetentionInterval); err != nil {
|
||||||
slog.Error("Failed to migrate old-style database", slogutil.Error(err))
|
slog.Error("Failed to migrate old-style database", slogutil.Error(err))
|
||||||
os.Exit(1)
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
sdb, err := syncthing.OpenDatabase(locations.Get(locations.Database), c.DBDeleteRetentionInterval)
|
sdb, err := syncthing.OpenDatabase(locations.Get(locations.Database), c.DBDeleteRetentionInterval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Error opening database", slogutil.Error(err))
|
slog.Error("Error opening database", slogutil.Error(err))
|
||||||
os.Exit(1)
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.DebugPerfStats {
|
if c.DebugPerfStats {
|
||||||
@@ -915,7 +911,7 @@ func (u upgradeCmd) Run() error {
|
|||||||
switch {
|
switch {
|
||||||
case err != nil && !os.IsNotExist(err):
|
case err != nil && !os.IsNotExist(err):
|
||||||
slog.Error("Failed to lock for upgrade", slogutil.Error(err))
|
slog.Error("Failed to lock for upgrade", slogutil.Error(err))
|
||||||
os.Exit(1)
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
case locked || os.IsNotExist(err):
|
case locked || os.IsNotExist(err):
|
||||||
// We got the lock, or the config directory didn't exist, so we
|
// We got the lock, or the config directory didn't exist, so we
|
||||||
// can do a direct upgrade
|
// can do a direct upgrade
|
||||||
@@ -935,14 +931,38 @@ func (u upgradeCmd) Run() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type browserCmd struct{}
|
type browserCmd struct {
|
||||||
|
Verify bool `help:"Verify that the GUI is reachable before launching browser"`
|
||||||
|
}
|
||||||
|
|
||||||
func (browserCmd) Run() error {
|
func (c browserCmd) Run() error {
|
||||||
if err := openGUI(); err != nil {
|
cfg, err := loadOrDefaultConfig()
|
||||||
slog.Error("Failed to open web UI", slogutil.Error(err))
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
guiCfg := cfg.GUI()
|
||||||
|
if !guiCfg.Enabled {
|
||||||
|
slog.Error("Browser: GUI is currently disabled")
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
return nil
|
url := guiCfg.URL()
|
||||||
|
|
||||||
|
if c.Verify {
|
||||||
|
// Do an HTTP request to verify the GUI/API is up and available
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = http.DefaultClient.Do(req) //nolint:bodyclose // we're exiting in a millisecond
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("GUI not available", slogutil.Error(err))
|
||||||
|
os.Exit(svcutil.ExitError.AsInt()) //nolint:gocritic // deferred cancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openURL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
type debugCmd struct {
|
type debugCmd struct {
|
||||||
|
|||||||
@@ -171,10 +171,11 @@ func (c *serveCmd) monitorMain() {
|
|||||||
exiterr := &exec.ExitError{}
|
exiterr := &exec.ExitError{}
|
||||||
if errors.As(err, &exiterr) {
|
if errors.As(err, &exiterr) {
|
||||||
exitCode := exiterr.ExitCode()
|
exitCode := exiterr.ExitCode()
|
||||||
if stopped || c.NoRestart {
|
switch {
|
||||||
|
case stopped || c.NoRestart:
|
||||||
os.Exit(exitCode)
|
os.Exit(exitCode)
|
||||||
}
|
|
||||||
if exitCode == svcutil.ExitUpgrade.AsInt() {
|
case exitCode == svcutil.ExitUpgrade.AsInt():
|
||||||
// Restart the monitor process to release the .old
|
// Restart the monitor process to release the .old
|
||||||
// binary as part of the upgrade process.
|
// binary as part of the upgrade process.
|
||||||
slog.Info("Restarting monitor...")
|
slog.Info("Restarting monitor...")
|
||||||
@@ -182,6 +183,10 @@ func (c *serveCmd) monitorMain() {
|
|||||||
slog.Error("Failed to restart monitor", slogutil.Error(err))
|
slog.Error("Failed to restart monitor", slogutil.Error(err))
|
||||||
}
|
}
|
||||||
os.Exit(exitCode)
|
os.Exit(exitCode)
|
||||||
|
|
||||||
|
case exitCode == svcutil.ExitNoRestart.AsInt():
|
||||||
|
// Requested to not restart the child
|
||||||
|
os.Exit(exitCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ const (
|
|||||||
ExitNoUpgradeAvailable ExitStatus = 2
|
ExitNoUpgradeAvailable ExitStatus = 2
|
||||||
ExitRestart ExitStatus = 3
|
ExitRestart ExitStatus = 3
|
||||||
ExitUpgrade ExitStatus = 4
|
ExitUpgrade ExitStatus = 4
|
||||||
|
ExitNoRestart ExitStatus = 5 // do not get restarted by monitor
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s ExitStatus) AsInt() int {
|
func (s ExitStatus) AsInt() int {
|
||||||
|
|||||||
Reference in New Issue
Block a user