diff options
author | W. Trevor King <wking@tremily.us> | 2018-05-30 16:53:10 -0700 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2018-05-31 21:45:27 +0000 |
commit | 81d6f082f36940f72c8a1961ac1150d7e63e415f (patch) | |
tree | bde859f752a3ebf478daa3cea4d147d0b416dff6 /pkg/hooks/exec/exec.go | |
parent | 824ea4da338891aff122f08f8f97b4d08b1a1e95 (diff) | |
download | podman-81d6f082f36940f72c8a1961ac1150d7e63e415f.tar.gz podman-81d6f082f36940f72c8a1961ac1150d7e63e415f.tar.bz2 podman-81d6f082f36940f72c8a1961ac1150d7e63e415f.zip |
pkg/hooks/exec: Add a new package for local hook execution
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
Diffstat (limited to 'pkg/hooks/exec/exec.go')
-rw-r--r-- | pkg/hooks/exec/exec.go | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/pkg/hooks/exec/exec.go b/pkg/hooks/exec/exec.go new file mode 100644 index 000000000..94469b1d2 --- /dev/null +++ b/pkg/hooks/exec/exec.go @@ -0,0 +1,62 @@ +// 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() + } +} |