diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index 9c12a835..23e3e5e6 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -384,7 +384,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation, // Start openvpn for the first time in a blocking call // until openvpn is launched - _, _ = openvpnLooper.SetStatus(constants.Running) // TODO option to disable with variable + _, _ = openvpnLooper.SetStatus(ctx, constants.Running) // TODO option to disable with variable <-ctx.Done() @@ -462,7 +462,7 @@ func routeReadyEvents(ctx context.Context, done chan<- struct{}, buildInfo model restartTickerContext, restartTickerCancel = context.WithCancel(ctx) // Runs the Public IP getter job once - _, _ = publicIPLooper.SetStatus(constants.Running) + _, _ = publicIPLooper.SetStatus(ctx, constants.Running) if versionInformation && first { first = false message, err := versionpkg.GetMessage(ctx, buildInfo, httpClient) diff --git a/internal/dns/state.go b/internal/dns/state.go index b534ad29..52f560d1 100644 --- a/internal/dns/state.go +++ b/internal/dns/state.go @@ -69,9 +69,15 @@ func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) ( l.state.status = constants.Stopping l.state.statusMu.Unlock() l.stop <- struct{}{} - <-l.stopped + + newStatus := constants.Stopping // for canceled context + select { + case <-ctx.Done(): + case <-l.stopped: + newStatus = constants.Stopped + } l.state.statusMu.Lock() - l.state.status = constants.Stopped + l.state.status = newStatus return status.String(), nil default: return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", diff --git a/internal/httpproxy/loop.go b/internal/httpproxy/loop.go index 36e8f76c..8c1207f9 100644 --- a/internal/httpproxy/loop.go +++ b/internal/httpproxy/loop.go @@ -15,10 +15,12 @@ import ( type Looper interface { Run(ctx context.Context, done chan<- struct{}) - SetStatus(status models.LoopStatus) (outcome string, err error) + SetStatus(ctx context.Context, status models.LoopStatus) ( + outcome string, err error) GetStatus() (status models.LoopStatus) GetSettings() (settings configuration.HTTPProxy) - SetSettings(settings configuration.HTTPProxy) (outcome string) + SetSettings(ctx context.Context, settings configuration.HTTPProxy) ( + outcome string) } type looper struct { @@ -57,7 +59,7 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) { if l.GetSettings().Enabled { go func() { - _, _ = l.SetStatus(constants.Running) + _, _ = l.SetStatus(ctx, constants.Running) }() } diff --git a/internal/httpproxy/state.go b/internal/httpproxy/state.go index 83d29677..addf11eb 100644 --- a/internal/httpproxy/state.go +++ b/internal/httpproxy/state.go @@ -1,6 +1,7 @@ package httpproxy import ( + "context" "errors" "fmt" "reflect" @@ -32,7 +33,8 @@ func (l *looper) GetStatus() (status models.LoopStatus) { var ErrInvalidStatus = errors.New("invalid status") -func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) { +func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) ( + outcome string, err error) { l.state.statusMu.Lock() defer l.state.statusMu.Unlock() existingStatus := l.state.status @@ -48,7 +50,12 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) l.state.status = constants.Starting l.state.statusMu.Unlock() l.start <- struct{}{} - newStatus := <-l.running + + newStatus := constants.Starting // for canceled context + select { + case <-ctx.Done(): + case newStatus = <-l.running: + } l.state.statusMu.Lock() l.state.status = newStatus return newStatus.String(), nil @@ -62,9 +69,15 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) l.state.status = constants.Stopping l.state.statusMu.Unlock() l.stop <- struct{}{} - <-l.stopped + + newStatus := constants.Stopping // for canceled context + select { + case <-ctx.Done(): + case <-l.stopped: + newStatus = constants.Stopped + } l.state.statusMu.Lock() - l.state.status = status + l.state.status = newStatus return status.String(), nil default: return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", @@ -78,7 +91,8 @@ func (l *looper) GetSettings() (settings configuration.HTTPProxy) { return l.state.settings } -func (l *looper) SetSettings(settings configuration.HTTPProxy) (outcome string) { +func (l *looper) SetSettings(ctx context.Context, settings configuration.HTTPProxy) ( + outcome string) { l.state.settingsMu.Lock() settingsUnchanged := reflect.DeepEqual(settings, l.state.settings) if settingsUnchanged { @@ -93,12 +107,12 @@ func (l *looper) SetSettings(settings configuration.HTTPProxy) (outcome string) switch { case !newEnabled && !previousEnabled: case newEnabled && previousEnabled: - _, _ = l.SetStatus(constants.Stopped) - _, _ = l.SetStatus(constants.Running) + _, _ = l.SetStatus(ctx, constants.Stopped) + _, _ = l.SetStatus(ctx, constants.Running) case newEnabled && !previousEnabled: - _, _ = l.SetStatus(constants.Running) + _, _ = l.SetStatus(ctx, constants.Running) case !newEnabled && previousEnabled: - _, _ = l.SetStatus(constants.Stopped) + _, _ = l.SetStatus(ctx, constants.Stopped) } return "settings updated" } diff --git a/internal/openvpn/loop.go b/internal/openvpn/loop.go index 1a3f8a6e..0e92b26f 100644 --- a/internal/openvpn/loop.go +++ b/internal/openvpn/loop.go @@ -21,9 +21,11 @@ import ( type Looper interface { Run(ctx context.Context, done chan<- struct{}) GetStatus() (status models.LoopStatus) - SetStatus(status models.LoopStatus) (outcome string, err error) + SetStatus(ctx context.Context, status models.LoopStatus) ( + outcome string, err error) GetSettings() (settings configuration.OpenVPN) - SetSettings(settings configuration.OpenVPN) (outcome string) + SetSettings(ctx context.Context, settings configuration.OpenVPN) ( + outcome string) GetServers() (servers models.AllServers) SetServers(servers models.AllServers) GetPortForwarded() (port uint16) diff --git a/internal/openvpn/state.go b/internal/openvpn/state.go index 86379d54..9bce13ed 100644 --- a/internal/openvpn/state.go +++ b/internal/openvpn/state.go @@ -1,6 +1,7 @@ package openvpn import ( + "context" "errors" "fmt" "reflect" @@ -46,7 +47,8 @@ func (l *looper) GetStatus() (status models.LoopStatus) { var ErrInvalidStatus = errors.New("invalid status") -func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) { +func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) ( + outcome string, err error) { l.state.statusMu.Lock() defer l.state.statusMu.Unlock() existingStatus := l.state.status @@ -62,7 +64,12 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) l.state.status = constants.Starting l.state.statusMu.Unlock() l.start <- struct{}{} - newStatus := <-l.running + + newStatus := constants.Starting // for canceled context + select { + case <-ctx.Done(): + case newStatus = <-l.running: + } l.state.statusMu.Lock() l.state.status = newStatus return newStatus.String(), nil @@ -76,9 +83,15 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) l.state.status = constants.Stopping l.state.statusMu.Unlock() l.stop <- struct{}{} - <-l.stopped + + newStatus := constants.Stopping // for canceled context + select { + case <-ctx.Done(): + case <-l.stopped: + newStatus = constants.Stopped + } l.state.statusMu.Lock() - l.state.status = constants.Stopped + l.state.status = newStatus return status.String(), nil default: return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", @@ -92,7 +105,8 @@ func (l *looper) GetSettings() (settings configuration.OpenVPN) { return l.state.settings } -func (l *looper) SetSettings(settings configuration.OpenVPN) (outcome string) { +func (l *looper) SetSettings(ctx context.Context, settings configuration.OpenVPN) ( + outcome string) { l.state.settingsMu.Lock() settingsUnchanged := reflect.DeepEqual(l.state.settings, settings) if settingsUnchanged { @@ -100,8 +114,8 @@ func (l *looper) SetSettings(settings configuration.OpenVPN) (outcome string) { return "settings left unchanged" } l.state.settings = settings - _, _ = l.SetStatus(constants.Stopped) - outcome, _ = l.SetStatus(constants.Running) + _, _ = l.SetStatus(ctx, constants.Stopped) + outcome, _ = l.SetStatus(ctx, constants.Running) return outcome } diff --git a/internal/publicip/loop.go b/internal/publicip/loop.go index a6607369..6b686b7f 100644 --- a/internal/publicip/loop.go +++ b/internal/publicip/loop.go @@ -18,7 +18,8 @@ type Looper interface { Run(ctx context.Context, done chan<- struct{}) RunRestartTicker(ctx context.Context, done chan<- struct{}) GetStatus() (status models.LoopStatus) - SetStatus(status models.LoopStatus) (outcome string, err error) + SetStatus(ctx context.Context, status models.LoopStatus) ( + outcome string, err error) GetSettings() (settings configuration.PublicIP) SetSettings(settings configuration.PublicIP) (outcome string) GetPublicIP() (publicIP net.IP) diff --git a/internal/publicip/state.go b/internal/publicip/state.go index 5f705e6e..3bcff774 100644 --- a/internal/publicip/state.go +++ b/internal/publicip/state.go @@ -1,6 +1,7 @@ package publicip import ( + "context" "errors" "fmt" "net" @@ -35,7 +36,8 @@ func (l *looper) GetStatus() (status models.LoopStatus) { var ErrInvalidStatus = errors.New("invalid status") -func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) { +func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) ( + outcome string, err error) { l.state.statusMu.Lock() defer l.state.statusMu.Unlock() existingStatus := l.state.status @@ -51,7 +53,12 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) l.state.status = constants.Starting l.state.statusMu.Unlock() l.start <- struct{}{} - newStatus := <-l.running + + newStatus := constants.Starting // for canceled context + select { + case <-ctx.Done(): + case newStatus = <-l.running: + } l.state.statusMu.Lock() l.state.status = newStatus return newStatus.String(), nil @@ -65,9 +72,15 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) l.state.status = constants.Stopping l.state.statusMu.Unlock() l.stop <- struct{}{} - <-l.stopped + + newStatus := constants.Stopping // for canceled context + select { + case <-ctx.Done(): + case <-l.stopped: + newStatus = constants.Stopped + } l.state.statusMu.Lock() - l.state.status = status + l.state.status = newStatus return status.String(), nil default: return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", @@ -81,7 +94,8 @@ func (l *looper) GetSettings() (settings configuration.PublicIP) { return l.state.settings } -func (l *looper) SetSettings(settings configuration.PublicIP) (outcome string) { +func (l *looper) SetSettings(settings configuration.PublicIP) ( + outcome string) { l.state.settingsMu.Lock() defer l.state.settingsMu.Unlock() settingsUnchanged := reflect.DeepEqual(settings, l.state.settings) diff --git a/internal/server/handler.go b/internal/server/handler.go index cb64acca..7afdba50 100644 --- a/internal/server/handler.go +++ b/internal/server/handler.go @@ -22,9 +22,9 @@ func newHandler(ctx context.Context, logger logging.Logger, logging bool, ) http.Handler { handler := &handler{} - openvpn := newOpenvpnHandler(openvpnLooper, logger) + openvpn := newOpenvpnHandler(ctx, openvpnLooper, logger) dns := newDNSHandler(ctx, unboundLooper, logger) - updater := newUpdaterHandler(updaterLooper, logger) + updater := newUpdaterHandler(ctx, updaterLooper, logger) publicip := newPublicIPHandler(publicIPLooper, logger) handler.v0 = newHandlerV0(ctx, logger, openvpnLooper, unboundLooper, updaterLooper) diff --git a/internal/server/handlerv0.go b/internal/server/handlerv0.go index 00ea08d5..3246bafe 100644 --- a/internal/server/handlerv0.go +++ b/internal/server/handlerv0.go @@ -39,9 +39,9 @@ func (h *handlerV0) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "/version": http.Redirect(w, r, "/v1/version", http.StatusPermanentRedirect) case "/openvpn/actions/restart": - outcome, _ := h.openvpn.SetStatus(constants.Stopped) + outcome, _ := h.openvpn.SetStatus(h.ctx, constants.Stopped) h.logger.Info("openvpn: %s", outcome) - outcome, _ = h.openvpn.SetStatus(constants.Running) + outcome, _ = h.openvpn.SetStatus(h.ctx, constants.Running) h.logger.Info("openvpn: %s", outcome) if _, err := w.Write([]byte("openvpn restarted, please consider using the /v1/ API in the future.")); err != nil { h.logger.Warn(err) @@ -59,9 +59,9 @@ func (h *handlerV0) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "/openvpn/settings": http.Redirect(w, r, "/v1/openvpn/settings", http.StatusPermanentRedirect) case "/updater/restart": - outcome, _ := h.updater.SetStatus(constants.Stopped) + outcome, _ := h.updater.SetStatus(h.ctx, constants.Stopped) h.logger.Info("updater: %s", outcome) - outcome, _ = h.updater.SetStatus(constants.Running) + outcome, _ = h.updater.SetStatus(h.ctx, constants.Running) h.logger.Info("updater: %s", outcome) if _, err := w.Write([]byte("updater restarted, please consider using the /v1/ API in the future.")); err != nil { h.logger.Warn(err) diff --git a/internal/server/openvpn.go b/internal/server/openvpn.go index 24af451c..60996ded 100644 --- a/internal/server/openvpn.go +++ b/internal/server/openvpn.go @@ -1,6 +1,7 @@ package server import ( + "context" "encoding/json" "net/http" "strings" @@ -9,14 +10,17 @@ import ( "github.com/qdm12/golibs/logging" ) -func newOpenvpnHandler(looper openvpn.Looper, logger logging.Logger) http.Handler { +func newOpenvpnHandler(ctx context.Context, looper openvpn.Looper, + logger logging.Logger) http.Handler { return &openvpnHandler{ + ctx: ctx, looper: looper, logger: logger, } } type openvpnHandler struct { + ctx context.Context looper openvpn.Looper logger logging.Logger } @@ -75,7 +79,7 @@ func (h *openvpnHandler) setStatus(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusBadRequest) return } - outcome, err := h.looper.SetStatus(status) + outcome, err := h.looper.SetStatus(h.ctx, status) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return diff --git a/internal/server/updater.go b/internal/server/updater.go index a4daefdd..2c920857 100644 --- a/internal/server/updater.go +++ b/internal/server/updater.go @@ -1,6 +1,7 @@ package server import ( + "context" "encoding/json" "net/http" "strings" @@ -10,15 +11,18 @@ import ( ) func newUpdaterHandler( + ctx context.Context, looper updater.Looper, logger logging.Logger) http.Handler { return &updaterHandler{ + ctx: ctx, looper: looper, logger: logger, } } type updaterHandler struct { + ctx context.Context looper updater.Looper logger logging.Logger } @@ -63,7 +67,7 @@ func (h *updaterHandler) setStatus(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusBadRequest) return } - outcome, err := h.looper.SetStatus(status) + outcome, err := h.looper.SetStatus(h.ctx, status) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return diff --git a/internal/shadowsocks/loop.go b/internal/shadowsocks/loop.go index 246c7def..eb0fcd8e 100644 --- a/internal/shadowsocks/loop.go +++ b/internal/shadowsocks/loop.go @@ -16,10 +16,12 @@ import ( type Looper interface { Run(ctx context.Context, done chan<- struct{}) - SetStatus(status models.LoopStatus) (outcome string, err error) + SetStatus(ctx context.Context, status models.LoopStatus) ( + outcome string, err error) GetStatus() (status models.LoopStatus) GetSettings() (settings configuration.ShadowSocks) - SetSettings(settings configuration.ShadowSocks) (outcome string) + SetSettings(ctx context.Context, settings configuration.ShadowSocks) ( + outcome string) } type looper struct { @@ -74,7 +76,7 @@ func (l *looper) Run(ctx context.Context, done chan<- struct{}) { if l.GetSettings().Enabled { go func() { - _, _ = l.SetStatus(constants.Running) + _, _ = l.SetStatus(ctx, constants.Running) }() } diff --git a/internal/shadowsocks/state.go b/internal/shadowsocks/state.go index 9871b717..afa26f95 100644 --- a/internal/shadowsocks/state.go +++ b/internal/shadowsocks/state.go @@ -1,6 +1,7 @@ package shadowsocks import ( + "context" "errors" "fmt" "reflect" @@ -32,7 +33,8 @@ func (l *looper) GetStatus() (status models.LoopStatus) { var ErrInvalidStatus = errors.New("invalid status") -func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) { +func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) ( + outcome string, err error) { l.state.statusMu.Lock() defer l.state.statusMu.Unlock() existingStatus := l.state.status @@ -48,7 +50,12 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) l.state.status = constants.Starting l.state.statusMu.Unlock() l.start <- struct{}{} - newStatus := <-l.running + + newStatus := constants.Starting // for canceled context + select { + case <-ctx.Done(): + case newStatus = <-l.running: + } l.state.statusMu.Lock() l.state.status = newStatus return newStatus.String(), nil @@ -62,9 +69,14 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) l.state.status = constants.Stopping l.state.statusMu.Unlock() l.stop <- struct{}{} - <-l.stopped + newStatus := constants.Stopping // for canceled context + select { + case <-ctx.Done(): + case <-l.stopped: + newStatus = constants.Stopped + } l.state.statusMu.Lock() - l.state.status = status + l.state.status = newStatus return status.String(), nil default: return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", @@ -78,7 +90,8 @@ func (l *looper) GetSettings() (settings configuration.ShadowSocks) { return l.state.settings } -func (l *looper) SetSettings(settings configuration.ShadowSocks) (outcome string) { +func (l *looper) SetSettings(ctx context.Context, settings configuration.ShadowSocks) ( + outcome string) { l.state.settingsMu.Lock() settingsUnchanged := reflect.DeepEqual(settings, l.state.settings) if settingsUnchanged { @@ -93,12 +106,12 @@ func (l *looper) SetSettings(settings configuration.ShadowSocks) (outcome string switch { case !newEnabled && !previousEnabled: case newEnabled && previousEnabled: - _, _ = l.SetStatus(constants.Stopped) - _, _ = l.SetStatus(constants.Running) + _, _ = l.SetStatus(ctx, constants.Stopped) + _, _ = l.SetStatus(ctx, constants.Running) case newEnabled && !previousEnabled: - _, _ = l.SetStatus(constants.Running) + _, _ = l.SetStatus(ctx, constants.Running) case !newEnabled && previousEnabled: - _, _ = l.SetStatus(constants.Stopped) + _, _ = l.SetStatus(ctx, constants.Stopped) } return "settings updated" } diff --git a/internal/updater/loop.go b/internal/updater/loop.go index d1afeb22..1332981a 100644 --- a/internal/updater/loop.go +++ b/internal/updater/loop.go @@ -17,7 +17,8 @@ type Looper interface { Run(ctx context.Context, done chan<- struct{}) RunRestartTicker(ctx context.Context, done chan<- struct{}) GetStatus() (status models.LoopStatus) - SetStatus(status models.LoopStatus) (outcome string, err error) + SetStatus(ctx context.Context, status models.LoopStatus) ( + outcome string, err error) GetSettings() (settings configuration.Updater) SetSettings(settings configuration.Updater) (outcome string) } diff --git a/internal/updater/state.go b/internal/updater/state.go index 71cd99f7..411ceb82 100644 --- a/internal/updater/state.go +++ b/internal/updater/state.go @@ -1,6 +1,7 @@ package updater import ( + "context" "errors" "fmt" "reflect" @@ -32,7 +33,7 @@ func (l *looper) GetStatus() (status models.LoopStatus) { var ErrInvalidStatus = errors.New("invalid status") -func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) { +func (l *looper) SetStatus(ctx context.Context, status models.LoopStatus) (outcome string, err error) { l.state.statusMu.Lock() defer l.state.statusMu.Unlock() existingStatus := l.state.status @@ -48,7 +49,12 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) l.state.status = constants.Starting l.state.statusMu.Unlock() l.start <- struct{}{} - newStatus := <-l.running + + newStatus := constants.Starting // for canceled context + select { + case <-ctx.Done(): + case newStatus = <-l.running: + } l.state.statusMu.Lock() l.state.status = newStatus return newStatus.String(), nil @@ -62,9 +68,15 @@ func (l *looper) SetStatus(status models.LoopStatus) (outcome string, err error) l.state.status = constants.Stopping l.state.statusMu.Unlock() l.stop <- struct{}{} - <-l.stopped + + newStatus := constants.Stopping // for canceled context + select { + case <-ctx.Done(): + case <-l.stopped: + newStatus = constants.Stopped + } l.state.statusMu.Lock() - l.state.status = status + l.state.status = newStatus return status.String(), nil default: return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s",