aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Borg <jakob@nym.se>2014-09-06 06:50:38 +0200
committerJakob Borg <jakob@nym.se>2014-09-06 06:50:38 +0200
commit921b90936bd9e5fb9a2a782e25a78b30b8b8557e (patch)
tree98292c55f90a3458ae6018defc34ac9e686f45c2
parent5b51f4d058c306d04cbb81dd87846e7b6d6ed55e (diff)
downloadsyncthing-v0.9-nomonitor.tar.gz
syncthing-v0.9-nomonitor.zip
Revert "Use a monitor process to handle panics and restarts (fixes #586)"v0.9.12v0.9-nomonitor
This reverts commit 10f071325726e8de1d44ec9ea1a7a9741f885c6d. Conflicts: cmd/syncthing/monitor.go
-rw-r--r--cmd/syncthing/heapprof.go3
-rw-r--r--cmd/syncthing/main.go131
-rw-r--r--cmd/syncthing/monitor.go148
-rw-r--r--cmd/syncthing/perfstats_unix.go2
4 files changed, 91 insertions, 193 deletions
diff --git a/cmd/syncthing/heapprof.go b/cmd/syncthing/heapprof.go
index a4631451f..24e0e522c 100644
--- a/cmd/syncthing/heapprof.go
+++ b/cmd/syncthing/heapprof.go
@@ -14,8 +14,7 @@ import (
)
func init() {
- if innerProcess && os.Getenv("STHEAPPROFILE") != "" {
- l.Debugln("Starting heap profiling")
+ if os.Getenv("STHEAPPROFILE") != "" {
go saveHeapProfiles()
}
}
diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go
index 61f3d6784..f04add856 100644
--- a/cmd/syncthing/main.go
+++ b/cmd/syncthing/main.go
@@ -16,6 +16,7 @@ import (
"net/http"
_ "net/http/pprof"
"os"
+ "os/exec"
"path/filepath"
"regexp"
"runtime"
@@ -52,15 +53,7 @@ var (
GoArchExtra string // "", "v5", "v6", "v7"
)
-const (
- exitSuccess = 0
- exitError = 1
- exitNoUpgradeAvailable = 2
- exitRestarting = 3
-)
-
var l = logger.DefaultLogger
-var innerProcess = os.Getenv("STNORESTART") != ""
func init() {
if Version != "unknown-dev" {
@@ -88,8 +81,10 @@ var (
confDir string
logFlags int = log.Ltime
rateBucket *ratelimit.Bucket
- stop = make(chan int)
+ stop = make(chan bool)
discoverer *discover.Discoverer
+ lockConn *net.TCPListener
+ lockPort int
externalPort int
cert tls.Certificate
)
@@ -158,20 +153,16 @@ func init() {
rand.Seed(time.Now().UnixNano())
}
-// Command line options
-var (
- reset bool
- showVersion bool
- doUpgrade bool
- doUpgradeCheck bool
- noBrowser bool
- generateDir string
- guiAddress string
- guiAuthentication string
- guiAPIKey string
-)
-
func main() {
+ var reset bool
+ var showVersion bool
+ var doUpgrade bool
+ var doUpgradeCheck bool
+ var noBrowser bool
+ var generateDir string
+ var guiAddress string
+ var guiAuthentication string
+ var guiAPIKey string
flag.StringVar(&confDir, "home", getDefaultConfDir(), "Set configuration directory")
flag.BoolVar(&reset, "reset", false, "Prepare to resync from cluster")
flag.BoolVar(&showVersion, "version", false, "Show version")
@@ -226,7 +217,7 @@ func main() {
if upgrade.CompareVersions(rel.Tag, Version) <= 0 {
l.Infof("No upgrade available (current %q >= latest %q).", Version, rel.Tag)
- os.Exit(exitNoUpgradeAvailable)
+ os.Exit(2)
}
l.Infof("Upgrade available (current %q < latest %q)", Version, rel.Tag)
@@ -243,20 +234,11 @@ func main() {
}
}
- if reset {
- resetRepositories()
- return
- }
-
- if os.Getenv("STNORESTART") != "" {
- syncthingMain()
- } else {
- monitorMain()
- }
-}
-
-func syncthingMain() {
var err error
+ lockPort, err = getLockPort()
+ if err != nil {
+ l.Fatalln("Opening lock port:", err)
+ }
if len(os.Getenv("GOGC")) == 0 {
debug.SetGCPercent(25)
@@ -270,7 +252,7 @@ func syncthingMain() {
events.Default.Log(events.Starting, map[string]string{"home": confDir})
- if _, err = os.Stat(confDir); err != nil && confDir == getDefaultConfDir() {
+ if _, err := os.Stat(confDir); err != nil && confDir == getDefaultConfDir() {
// We are supposed to use the default configuration directory. It
// doesn't exist. In the past our default has been ~/.syncthing, so if
// that directory exists we move it to the new default location and
@@ -365,6 +347,15 @@ func syncthingMain() {
l.Infof("Edit %s to taste or use the GUI\n", cfgFile)
}
+ if reset {
+ resetRepositories()
+ return
+ }
+
+ if len(os.Getenv("STRESTART")) > 0 {
+ waitForParentExit()
+ }
+
if profiler := os.Getenv("STPROFILER"); len(profiler) > 0 {
go func() {
l.Debugln("Starting profiler on", profiler)
@@ -595,10 +586,9 @@ nextRepo:
events.Default.Log(events.StartupComplete, nil)
go generateEvents()
- code := <-stop
+ <-stop
l.Okln("Exiting")
- os.Exit(code)
}
func generateEvents() {
@@ -608,6 +598,25 @@ func generateEvents() {
}
}
+func waitForParentExit() {
+ l.Infoln("Waiting for parent to exit...")
+ lockPortStr := os.Getenv("STRESTART")
+ lockPort, err := strconv.Atoi(lockPortStr)
+ if err != nil {
+ l.Warnln("Invalid lock port %q: %v", lockPortStr, err)
+ }
+ // Wait for the listen address to become free, indicating that the parent has exited.
+ for {
+ ln, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", lockPort))
+ if err == nil {
+ ln.Close()
+ break
+ }
+ time.Sleep(250 * time.Millisecond)
+ }
+ l.Infoln("Continuing")
+}
+
func setupUPnP() {
if len(cfg.Options.ListenAddress) == 1 {
_, portStr, err := net.SplitHostPort(cfg.Options.ListenAddress[0])
@@ -737,12 +746,40 @@ func archiveLegacyConfig() {
func restart() {
l.Infoln("Restarting")
- stop <- exitRestarting
+ if os.Getenv("SMF_FMRI") != "" || os.Getenv("STNORESTART") != "" {
+ // Solaris SMF
+ l.Infoln("Service manager detected; exit instead of restart")
+ stop <- true
+ return
+ }
+
+ env := os.Environ()
+ newEnv := make([]string, 0, len(env))
+ for _, s := range env {
+ if !strings.HasPrefix(s, "STRESTART=") {
+ newEnv = append(newEnv, s)
+ }
+ }
+ newEnv = append(newEnv, fmt.Sprintf("STRESTART=%d", lockPort))
+
+ pgm, err := exec.LookPath(os.Args[0])
+ if err != nil {
+ l.Warnln("Cannot restart:", err)
+ return
+ }
+ proc, err := os.StartProcess(pgm, os.Args, &os.ProcAttr{
+ Env: newEnv,
+ Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
+ })
+ if err != nil {
+ l.Fatalln(err)
+ }
+ proc.Release()
+ stop <- true
}
func shutdown() {
- l.Infoln("Shutting down")
- stop <- exitSuccess
+ stop <- true
}
var saveConfigCh = make(chan struct{})
@@ -1096,6 +1133,16 @@ func getFreePort(host string, ports ...int) (int, error) {
return addr.Port, nil
}
+func getLockPort() (int, error) {
+ var err error
+ lockConn, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}})
+ if err != nil {
+ return 0, err
+ }
+ addr := lockConn.Addr().(*net.TCPAddr)
+ return addr.Port, nil
+}
+
func overrideGUIConfig(originalCfg config.GUIConfiguration, address, authentication, apikey string) config.GUIConfiguration {
// Make a copy of the config
cfg := originalCfg
diff --git a/cmd/syncthing/monitor.go b/cmd/syncthing/monitor.go
deleted file mode 100644
index 0f3cbf2ce..000000000
--- a/cmd/syncthing/monitor.go
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
-// All rights reserved. Use of this source code is governed by an MIT-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "bufio"
- "io"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "sync"
- "time"
-)
-
-var (
- stdoutFirstLines []string // The first 10 lines of stdout
- stdoutLastLines []string // The last 50 lines of stdout
- stdoutMut sync.Mutex
-)
-
-const (
- countRestarts = 5
- loopThreshold = 15 * time.Second
-)
-
-func monitorMain() {
- os.Setenv("STNORESTART", "yes")
- l.SetPrefix("[monitor] ")
-
- args := os.Args
- var restarts [countRestarts]time.Time
-
- for {
- if t := time.Since(restarts[0]); t < loopThreshold {
- l.Warnf("%d restarts in %v; not retrying further", countRestarts, t)
- os.Exit(exitError)
- }
-
- copy(restarts[0:], restarts[1:])
- restarts[len(restarts)-1] = time.Now()
-
- cmd := exec.Command(args[0], args[1:]...)
-
- stderr, err := cmd.StderrPipe()
- if err != nil {
- l.Fatalln(err)
- }
-
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- l.Fatalln(err)
- }
-
- l.Infoln("Starting syncthing")
- err = cmd.Start()
- if err != nil {
- l.Fatalln(err)
- }
-
- stdoutMut.Lock()
- stdoutFirstLines = make([]string, 0, 10)
- stdoutLastLines = make([]string, 0, 50)
- stdoutMut.Unlock()
-
- go copyStderr(stderr)
- go copyStdout(stdout)
-
- err = cmd.Wait()
- if err == nil {
- // Successfull exit indicates an intentional shutdown
- return
- }
-
- l.Infoln("Syncthing exited:", err)
- time.Sleep(1 * time.Second)
- }
-}
-
-func copyStderr(stderr io.ReadCloser) {
- br := bufio.NewReader(stderr)
-
- var panicFd *os.File
- for {
- line, err := br.ReadString('\n')
- if err != nil {
- if err != io.EOF {
- l.Warnln("stderr:", err)
- }
- return
- }
-
- if panicFd == nil {
- os.Stderr.WriteString(line)
-
- if strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:") {
- panicFd, err = os.Create(filepath.Join(confDir, time.Now().Format("panic-20060102-150405.log")))
- if err != nil {
- l.Warnln("Create panic log:", err)
- continue
- }
-
- l.Warnf("Panic detected, writing to \"%s\"", panicFd.Name())
- l.Warnln("Please create an issue at https://github.com/syncting/syncthing/issues/ with the panic log attached")
-
- stdoutMut.Lock()
- for _, line := range stdoutFirstLines {
- panicFd.WriteString(line)
- }
- panicFd.WriteString("...\n")
- for _, line := range stdoutLastLines {
- panicFd.WriteString(line)
- }
- }
- }
-
- if panicFd != nil {
- panicFd.WriteString(line)
- }
- }
-}
-
-func copyStdout(stderr io.ReadCloser) {
- br := bufio.NewReader(stderr)
- for {
- line, err := br.ReadString('\n')
- if err != nil {
- if err != io.EOF {
- l.Warnln("stdout:", err)
- }
- return
- }
-
- stdoutMut.Lock()
- if len(stdoutFirstLines) < cap(stdoutFirstLines) {
- stdoutFirstLines = append(stdoutFirstLines, line)
- }
- if l := len(stdoutLastLines); l == cap(stdoutLastLines) {
- stdoutLastLines = stdoutLastLines[:l-1]
- }
- stdoutLastLines = append(stdoutLastLines, line)
- stdoutMut.Unlock()
-
- os.Stdout.WriteString(line)
- }
-}
diff --git a/cmd/syncthing/perfstats_unix.go b/cmd/syncthing/perfstats_unix.go
index 48746a6a8..403de2693 100644
--- a/cmd/syncthing/perfstats_unix.go
+++ b/cmd/syncthing/perfstats_unix.go
@@ -15,7 +15,7 @@ import (
)
func init() {
- if innerProcess && os.Getenv("STPERFSTATS") != "" {
+ if os.Getenv("STPERFSTATS") != "" {
go savePerfStats(fmt.Sprintf("perfstats-%d.csv", syscall.Getpid()))
}
}