From d05b721ca4d8bfc8d2ae70eb2f5256e177b39ef9 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 23 Jun 2025 18:17:51 +0200 Subject: [PATCH] reexec: align closer to exec.Command Make our implementation a wrapper for exec.Command to make sure our implementation keeps aligned with a regular exec.Command with the exception of the "Args" that we need to override, and (on Linux), Pdeathsig to be set. Signed-off-by: Sebastiaan van Stijn --- reexec/reexec_linux.go | 14 ++++++++------ reexec/reexec_other.go | 10 ++++++---- reexec/reexec_test.go | 37 ++++++++++++++++++++++++++----------- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/reexec/reexec_linux.go b/reexec/reexec_linux.go index 03f600e0..12b6c32e 100644 --- a/reexec/reexec_linux.go +++ b/reexec/reexec_linux.go @@ -6,11 +6,13 @@ import ( ) func command(args ...string) *exec.Cmd { - return &exec.Cmd{ - Path: Self(), - Args: args, - SysProcAttr: &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGTERM, - }, + // We try to stay close to exec.Command's behavior, but after + // constructing the cmd, we remove "Self()" from cmd.Args, which + // is prepended by exec.Command. + cmd := exec.Command(Self(), args...) + cmd.Args = cmd.Args[1:] + cmd.SysProcAttr = &syscall.SysProcAttr{ + Pdeathsig: syscall.SIGTERM, } + return cmd } diff --git a/reexec/reexec_other.go b/reexec/reexec_other.go index 498d28bc..e07dd2d6 100644 --- a/reexec/reexec_other.go +++ b/reexec/reexec_other.go @@ -7,8 +7,10 @@ import ( ) func command(args ...string) *exec.Cmd { - return &exec.Cmd{ - Path: Self(), - Args: args, - } + // We try to stay close to exec.Command's behavior, but after + // constructing the cmd, we remove "Self()" from cmd.Args, which + // is prepended by exec.Command. + cmd := exec.Command(Self(), args...) + cmd.Args = cmd.Args[1:] + return cmd } diff --git a/reexec/reexec_test.go b/reexec/reexec_test.go index 0c5a6a33..e5773548 100644 --- a/reexec/reexec_test.go +++ b/reexec/reexec_test.go @@ -5,6 +5,7 @@ import ( "os" "os/exec" "path/filepath" + "reflect" "strings" "testing" ) @@ -19,7 +20,11 @@ func init() { panic("Return Error") }) Register(testReExec2, func() { - fmt.Println("Hello " + testReExec2) + var args string + if len(os.Args) > 1 { + args = fmt.Sprintf("(args: %#v)", os.Args[1:]) + } + fmt.Println("Hello", testReExec2, args) os.Exit(0) }) Init() @@ -62,21 +67,32 @@ func TestRegister(t *testing.T) { func TestCommand(t *testing.T) { tests := []struct { - doc string - name string + doc string + cmdAndArgs []string + expOut string }{ { - doc: "basename", - name: testReExec2, + doc: "basename", + cmdAndArgs: []string{testReExec2}, + expOut: "Hello test-reexec2", }, { - doc: "full path", - name: filepath.Join("something", testReExec2), + doc: "full path", + cmdAndArgs: []string{filepath.Join("something", testReExec2)}, + expOut: `Hello test-reexec2`, + }, + { + doc: "command with args", + cmdAndArgs: []string{testReExec2, "--some-flag", "some-value", "arg1", "arg2"}, + expOut: `Hello test-reexec2 (args: []string{"--some-flag", "some-value", "arg1", "arg2"})`, }, } for _, tc := range tests { t.Run(tc.doc, func(t *testing.T) { - cmd := Command(tc.name) + cmd := Command(tc.cmdAndArgs...) + if !reflect.DeepEqual(cmd.Args, tc.cmdAndArgs) { + t.Fatalf("got %+v, want %+v", cmd.Args, tc.cmdAndArgs) + } w, err := cmd.StdinPipe() if err != nil { t.Fatalf("Error on pipe creation: %v", err) @@ -88,10 +104,9 @@ func TestCommand(t *testing.T) { t.Errorf("Error on re-exec cmd: %v, out: %v", err, string(out)) } - const expected = "Hello " + testReExec2 actual := strings.TrimSpace(string(out)) - if actual != expected { - t.Errorf("got %v, want %v", actual, expected) + if actual != tc.expOut { + t.Errorf("got %v, want %v", actual, tc.expOut) } }) }