mirror of
https://github.com/containers/podman.git
synced 2025-10-26 02:35:43 +08:00
This wraps os/exec to:
* Clear the environment when the hook doesn't set 'env'. The runtime
spec has [1]:
> * env (array of strings, OPTIONAL) with the same semantics as IEEE
> Std 1003.1-2008's environ.
And running execle or similar with NULL env results in an empty
environment:
$ cat test.c
#include <unistd.h>
int main()
{
return execle("/usr/bin/env", "env", NULL, NULL);
}
$ cc -o test test.c
$ ./test
...no output...
Go's Cmd.Env, on the other hand, has [2]:
> If Env is nil, the new process uses the current process's environment.
This commit works around that by setting []string{} in those cases
to avoid leaking the runtime environment into the hooks.
* Roll the 'timeout' value (if set) into the passed context. There's
no need for two separate ways to cancel hook execution.
* Add a configurable timeout on abandoning a post-kill wait. The
waiting goroutine will continue and eventually reap the process, but
this avoids blocking the Run() call when that takes inordinately
long (for example, if a GPU cleanup hook is stuck in I/O sleep [3]).
The 'env' output format is specified in POSIX [4].
[1]: https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#posix-platform-hooks
[2]: https://golang.org/pkg/os/exec/#Cmd
[3]: https://github.com/projectatomic/libpod/pull/857#discussion_r192191002
[4]: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html
Signed-off-by: W. Trevor King <wking@tremily.us>
Closes: #857
Approved by: mheon
63 lines
1.4 KiB
Go
63 lines
1.4 KiB
Go
// Package exec provides utilities for executing Open Container Initative runtime hooks.
|
|
package exec
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
osexec "os/exec"
|
|
"time"
|
|
|
|
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
|
)
|
|
|
|
// DefaultPostKillTimeout is the recommended default post-kill timeout.
|
|
const DefaultPostKillTimeout = time.Duration(10) * time.Second
|
|
|
|
// Run executes the hook and waits for it to complete or for the
|
|
// context or hook-specified timeout to expire.
|
|
func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer, stderr io.Writer, postKillTimeout time.Duration) (hookErr, err error) {
|
|
cmd := osexec.Cmd{
|
|
Path: hook.Path,
|
|
Args: hook.Args,
|
|
Env: hook.Env,
|
|
Stdin: bytes.NewReader(state),
|
|
Stdout: stdout,
|
|
Stderr: stderr,
|
|
}
|
|
if cmd.Env == nil {
|
|
cmd.Env = []string{}
|
|
}
|
|
|
|
if hook.Timeout != nil {
|
|
var cancel context.CancelFunc
|
|
ctx, cancel = context.WithTimeout(ctx, time.Duration(*hook.Timeout)*time.Second)
|
|
defer cancel()
|
|
}
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return err, err
|
|
}
|
|
exit := make(chan error, 1)
|
|
go func() {
|
|
exit <- cmd.Wait()
|
|
}()
|
|
|
|
select {
|
|
case err = <-exit:
|
|
return err, err
|
|
case <-ctx.Done():
|
|
cmd.Process.Kill()
|
|
timer := time.NewTimer(postKillTimeout)
|
|
defer timer.Stop()
|
|
select {
|
|
case <-timer.C:
|
|
err = fmt.Errorf("failed to reap process within %s of the kill signal", postKillTimeout)
|
|
case err = <-exit:
|
|
}
|
|
return err, ctx.Err()
|
|
}
|
|
}
|