diff options
Diffstat (limited to 'cmd/syncthing/main.go')
-rw-r--r-- | cmd/syncthing/main.go | 131 |
1 files changed, 89 insertions, 42 deletions
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 |