daemon/exec: don't overwrite exit code if set

If we fail to start an exec, the deferred error-handling block in [L181-L193](c7e42d855e/daemon/exec.go (L181-L193))
would set the exit code to `126` (`EACCES`). However, if we get far enough along
attempting to start the exec, we set the exit code according to the error returned
from starting the task [L288-L291](c7e42d855e/daemon/exec.go (L288-L291)).

For some situations (such as `docker exec [some-container]
missing-binary`), the 2nd block returns the correct exit code (`127`)
but that then gets overwritten by the 1st block.

This commit changes that logic to only set the default exit code `126`
if the exit code has not been set yet.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
This commit is contained in:
Laura Brehm
2024-09-26 13:51:50 +01:00
parent 4babd72186
commit c866a7e5f8
2 changed files with 39 additions and 2 deletions

View File

@@ -183,8 +183,12 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio
ec.Lock()
ec.Container.ExecCommands.Delete(ec.ID)
ec.Running = false
exitCode := 126
ec.ExitCode = &exitCode
if ec.ExitCode == nil {
// default to `126` (`EACCES`) if we fail to start
// the exec without setting an exit code.
exitCode := exitEaccess
ec.ExitCode = &exitCode
}
if err := ec.CloseStreams(); err != nil {
log.G(ctx).Errorf("failed to cleanup exec %s streams: %s", ec.Container.ID, err)
}

View File

@@ -30,3 +30,36 @@ func TestExecConsoleSize(t *testing.T) {
assert.NilError(t, err)
assert.Equal(t, strings.TrimSpace(result.Stdout()), "57 123")
}
func TestFailedExecExitCode(t *testing.T) {
testCases := []struct {
doc string
command []string
expectedExitCode int
}{
{
doc: "executable not found",
command: []string{"nonexistent"},
expectedExitCode: 127,
},
{
doc: "executable cannot be invoked",
command: []string{"/etc"},
expectedExitCode: 126,
},
}
for _, tc := range testCases {
t.Run(tc.doc, func(t *testing.T) {
ctx := setupTest(t)
apiClient := testEnv.APIClient()
cID := container.Run(ctx, t, apiClient)
result, err := container.Exec(ctx, apiClient, cID, tc.command)
assert.NilError(t, err)
assert.Equal(t, result.ExitCode, tc.expectedExitCode)
})
}
}