Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: graceful client shutdown #1088

Merged
merged 1 commit into from
May 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 39 additions & 4 deletions app/cmd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
"net"
"net/netip"
"os"
"os/signal"
"runtime"
"slices"
"strconv"
"strings"
"syscall"
"time"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -512,23 +514,52 @@ func runClient(cmd *cobra.Command, args []string) {
})
}

runner.Run()
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
defer signal.Stop(signalChan)

runnerChan := make(chan clientModeRunnerResult, 1)
go func() {
runnerChan <- runner.Run()
}()

select {
case <-signalChan:
logger.Info("received signal, shutting down gracefully")
case r := <-runnerChan:
if r.OK {
logger.Info(r.Msg)
} else {
_ = c.Close() // Close the client here as Fatal will exit the program without running defer
if r.Err != nil {
logger.Fatal(r.Msg, zap.Error(r.Err))
} else {
logger.Fatal(r.Msg)
}
}
}
}

type clientModeRunner struct {
ModeMap map[string]func() error
}

type clientModeRunnerResult struct {
OK bool
Msg string
Err error
}

func (r *clientModeRunner) Add(name string, f func() error) {
if r.ModeMap == nil {
r.ModeMap = make(map[string]func() error)
}
r.ModeMap[name] = f
}

func (r *clientModeRunner) Run() {
func (r *clientModeRunner) Run() clientModeRunnerResult {
if len(r.ModeMap) == 0 {
logger.Fatal("no mode specified")
return clientModeRunnerResult{OK: false, Msg: "no mode specified"}
}

type modeError struct {
Expand All @@ -546,9 +577,13 @@ func (r *clientModeRunner) Run() {
for i := 0; i < len(r.ModeMap); i++ {
e := <-errChan
if e.Err != nil {
logger.Fatal("failed to run "+e.Name, zap.Error(e.Err))
return clientModeRunnerResult{OK: false, Msg: "failed to run " + e.Name, Err: e.Err}
}
}

// We don't really have any such cases, as currently none of our modes would stop on themselves without error.
// But we leave the possibility here for future expansion.
return clientModeRunnerResult{OK: true, Msg: "finished without error"}
}

func clientSOCKS5(config socks5Config, c client.Client) error {
Expand Down