From 2b1c942d561ba19e39b3ed3b33ba193eacf8849e Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Tue, 7 Feb 2017 20:48:53 +0100 Subject: [PATCH] cmd/syncthing: Environment handling for upgrade restarts (fixes #3970) --- cmd/syncthing/main.go | 28 ++++++++++++++++++++++++++++ cmd/syncthing/monitor.go | 32 ++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index 78ebdaac7..6c346c346 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -411,6 +411,34 @@ func main() { return } + // ---BEGIN TEMPORARY HACK--- + // + // Remove once v0.14.21-v0.14.22 are rare enough. Those versions, + // essentially: + // + // 1. os.Setenv("STMONITORED", "yes") + // 2. os.Setenv("STNORESTART", "") + // + // where the intention was for 2 to cancel out 1 instead of setting + // STNORESTART to the empty value. We check for exactly this combination + // and pretend that neither was set. Looking through os.Environ lets us + // distinguish. Luckily, we weren't smart enough to use os.Unsetenv. + + matches := 0 + for _, str := range os.Environ() { + if str == "STNORESTART=" { + matches++ + } + if str == "STMONITORED=yes" { + matches++ + } + } + if matches == 2 { + innerProcess = false + } + + // ---END TEMPORARY HACK--- + if innerProcess || options.noRestart { syncthingMain(options) } else { diff --git a/cmd/syncthing/monitor.go b/cmd/syncthing/monitor.go index f64ee88ac..4cc005566 100644 --- a/cmd/syncthing/monitor.go +++ b/cmd/syncthing/monitor.go @@ -35,7 +35,6 @@ const ( ) func monitorMain(runtimeOptions RuntimeOptions) { - os.Setenv("STMONITORED", "yes") l.SetPrefix("[monitor] ") var dst io.Writer = os.Stdout @@ -69,6 +68,8 @@ func monitorMain(runtimeOptions RuntimeOptions) { sigHup := syscall.Signal(1) signal.Notify(restartSign, sigHup) + childEnv := childEnv() + first := true for { if t := time.Since(restarts[0]); t < loopThreshold { l.Warnf("%d restarts in %v; not retrying further", countRestarts, t) @@ -79,6 +80,7 @@ func monitorMain(runtimeOptions RuntimeOptions) { restarts[len(restarts)-1] = time.Now() cmd := exec.Command(args[0], args[1:]...) + cmd.Env = childEnv stderr, err := cmd.StderrPipe() if err != nil { @@ -96,10 +98,6 @@ func monitorMain(runtimeOptions RuntimeOptions) { l.Fatalln(err) } - // Let the next child process know that this is not the first time - // it's starting up. - os.Setenv("STRESTART", "yes") - stdoutMut.Lock() stdoutFirstLines = make([]string, 0, 10) stdoutLastLines = make([]string, 0, 50) @@ -149,7 +147,6 @@ func monitorMain(runtimeOptions RuntimeOptions) { // Restart the monitor process to release the .old // binary as part of the upgrade process. l.Infoln("Restarting monitor...") - os.Setenv("STNORESTART", "") if err = restartMonitor(args); err != nil { l.Warnln("Restart:", err) } @@ -161,6 +158,13 @@ func monitorMain(runtimeOptions RuntimeOptions) { l.Infoln("Syncthing exited:", err) time.Sleep(1 * time.Second) + + if first { + // Let the next child process know that this is not the first time + // it's starting up. + childEnv = append(childEnv, "STRESTART=yes") + first = false + } } } @@ -409,3 +413,19 @@ func (f *autoclosedFile) closerLoop() { } } } + +// Returns the desired child environment, properly filtered and added to. +func childEnv() []string { + var env []string + for _, str := range os.Environ() { + if strings.HasPrefix("STNORESTART=", str) { + continue + } + if strings.HasPrefix("STMONITORED=", str) { + continue + } + env = append(env, str) + } + env = append(env, "STMONITORED=yes") + return env +}