diff --git a/cmd/cloudflared/linux_service.go b/cmd/cloudflared/linux_service.go
index fede6b23..62559bf4 100644
--- a/cmd/cloudflared/linux_service.go
+++ b/cmd/cloudflared/linux_service.go
@@ -118,10 +118,6 @@ case "$1" in
echo "Starting $name"
$cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file"
- if ! is_running; then
- echo "Unable to start, see $stdout_log and $stderr_log"
- exit 1
- fi
fi
;;
stop)
diff --git a/cmd/cloudflared/macos_service.go b/cmd/cloudflared/macos_service.go
index d4fd3f20..9ec162fa 100644
--- a/cmd/cloudflared/macos_service.go
+++ b/cmd/cloudflared/macos_service.go
@@ -60,7 +60,7 @@ func newLaunchdTemplate(installPath, stdoutPath, stderrPath string) *ServiceTemp
ThrottleInterval
- 20
+ 5
`, launchdIdentifier, stdoutPath, stderrPath),
}
diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go
index e2a6488c..3f6198ff 100644
--- a/cmd/cloudflared/tunnel/cmd.go
+++ b/cmd/cloudflared/tunnel/cmd.go
@@ -39,6 +39,7 @@ import (
"github.com/getsentry/raven-go"
"github.com/gliderlabs/ssh"
"github.com/google/uuid"
+ "github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
"gopkg.in/urfave/cli.v2"
"gopkg.in/urfave/cli.v2/altsrc"
@@ -433,7 +434,6 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
defer wg.Done()
errC <- origin.StartTunnelDaemon(ctx, tunnelConfig, connectedSignal, cloudflaredID, reconnectCh)
}()
-
return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, c.Duration("grace-period"))
}
@@ -607,9 +607,15 @@ func notifySystemd(waitForSignal *signal.Signal) {
func writePidFile(waitForSignal *signal.Signal, pidFile string) {
<-waitForSignal.Wait()
- file, err := os.Create(pidFile)
+ expandedPath, err := homedir.Expand(pidFile)
if err != nil {
- logger.WithError(err).Errorf("Unable to write pid to %s", pidFile)
+ logger.WithError(err).Errorf("Unable to expand %s, try to use absolute path in --pidfile", pidFile)
+ return
+ }
+ file, err := os.Create(expandedPath)
+ if err != nil {
+ logger.WithError(err).Errorf("Unable to write pid to %s", expandedPath)
+ return
}
defer file.Close()
fmt.Fprintf(file, "%d", os.Getpid())
diff --git a/cmd/cloudflared/tunnel/signal.go b/cmd/cloudflared/tunnel/signal.go
index afc26ccf..b1ec77e2 100644
--- a/cmd/cloudflared/tunnel/signal.go
+++ b/cmd/cloudflared/tunnel/signal.go
@@ -17,9 +17,11 @@ func waitForSignal(errC chan error, shutdownC chan struct{}) error {
select {
case err := <-errC:
+ logger.Infof("terminating due to error: %v", err)
close(shutdownC)
return err
- case <-signals:
+ case s := <-signals:
+ logger.Infof("terminating due to signal %s", s)
close(shutdownC)
case <-shutdownC:
}
@@ -44,10 +46,12 @@ func waitForSignalWithGraceShutdown(errC chan error,
select {
case err := <-errC:
+ logger.Infof("Initiating graceful shutdown due to %v ...", err)
close(graceShutdownC)
close(shutdownC)
return err
- case <-signals:
+ case s := <-signals:
+ logger.Infof("Initiating graceful shutdown due to signal %s ...", s)
close(graceShutdownC)
waitForGracePeriod(signals, errC, shutdownC, gracePeriod)
case <-graceShutdownC:
@@ -64,7 +68,6 @@ func waitForGracePeriod(signals chan os.Signal,
shutdownC chan struct{},
gracePeriod time.Duration,
) {
- logger.Infof("Initiating graceful shutdown...")
// Unregister signal handler early, so the client can send a second SIGTERM/SIGINT
// to force shutdown cloudflared
signal.Stop(signals)
diff --git a/cmd/cloudflared/updater/update.go b/cmd/cloudflared/updater/update.go
index 96cdc95b..8f9c481b 100644
--- a/cmd/cloudflared/updater/update.go
+++ b/cmd/cloudflared/updater/update.go
@@ -2,6 +2,7 @@ package updater
import (
"context"
+ "fmt"
"os"
"runtime"
"time"
@@ -32,6 +33,33 @@ EKx0BZogHSor9Wy5VztdFaAaVbsJiCbO
logger = log.CreateLogger()
)
+// BinaryUpdated implements ExitCoder interface, the app will exit with status code 11
+// https://pkg.go.dev/gopkg.in/urfave/cli.v2?tab=doc#ExitCoder
+type statusSuccess struct {
+ newVersion string
+}
+
+func (u *statusSuccess) Error() string {
+ return fmt.Sprintf("cloudflared has been updated to version %s", u.newVersion)
+}
+
+func (u *statusSuccess) ExitCode() int {
+ return 11
+}
+
+// UpdateErr implements ExitCoder interface, the app will exit with status code 10
+type statusErr struct {
+ err error
+}
+
+func (e *statusErr) Error() string {
+ return fmt.Sprintf("failed to update cloudflared: %v", e.err)
+}
+
+func (e *statusErr) ExitCode() int {
+ return 10
+}
+
type UpdateOutcome struct {
Updated bool
Version string
@@ -67,14 +95,15 @@ func checkForUpdateAndApply() UpdateOutcome {
func Update(_ *cli.Context) error {
updateOutcome := loggedUpdate()
if updateOutcome.Error != nil {
- os.Exit(10)
+ return &statusErr{updateOutcome.Error}
}
if updateOutcome.noUpdate() {
logger.Infof("cloudflared is up to date (%s)", updateOutcome.Version)
+ return nil
}
- return updateOutcome.Error
+ return &statusSuccess{newVersion: updateOutcome.Version}
}
// Checks for an update and applies it if one is available
@@ -126,15 +155,19 @@ func (a *AutoUpdater) Run(ctx context.Context) error {
updateOutcome := loggedUpdate()
if updateOutcome.Updated {
os.Args = append(os.Args, "--is-autoupdated=true")
- pid, err := a.listeners.StartProcess()
- if err != nil {
- logger.WithError(err).Error("Unable to restart server automatically")
- return err
+ if IsSysV() {
+ // SysV doesn't have a mechanism to keep service alive, we have to restart the process
+ logger.Infof("Restarting service managed by SysV...")
+ pid, err := a.listeners.StartProcess()
+ if err != nil {
+ logger.WithError(err).Error("Unable to restart server automatically")
+ return &statusErr{err: err}
+ }
+ // stop old process after autoupdate. Otherwise we create a new process
+ // after each update
+ logger.Infof("PID of the new process is %d", pid)
}
- // stop old process after autoupdate. Otherwise we create a new process
- // after each update
- logger.Infof("PID of the new process is %d", pid)
- return nil
+ return &statusSuccess{newVersion: updateOutcome.Version}
}
}
select {
@@ -187,3 +220,14 @@ func SupportAutoUpdate() bool {
func isRunningFromTerminal() bool {
return terminal.IsTerminal(int(os.Stdout.Fd()))
}
+
+func IsSysV() bool {
+ if runtime.GOOS != "linux" {
+ return false
+ }
+
+ if _, err := os.Stat("/run/systemd/system"); err == nil {
+ return false
+ }
+ return true
+}
diff --git a/go.mod b/go.mod
index da0d2528..974a0b3f 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/cloudflare/cloudflared
go 1.12
require (
+ github.com/BurntSushi/go-sumtype v0.0.0-20190304192233-fcb4a6205bdc // indirect
github.com/DATA-DOG/go-sqlmock v1.3.3
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect
github.com/aws/aws-sdk-go v1.25.8
diff --git a/go.sum b/go.sum
index 985e4821..5c41f7d0 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,8 @@
bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/go-sumtype v0.0.0-20190304192233-fcb4a6205bdc h1:nvTP+jmloR0+J4YQur/rLRdLcGVEU4SquDgH+Bo7gBY=
+github.com/BurntSushi/go-sumtype v0.0.0-20190304192233-fcb4a6205bdc/go.mod h1:7yTWMMG2vOm4ABVciEt4EgNVP7fxwtcKIb/EuiLiKqY=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
@@ -244,9 +246,11 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=