diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go index 867c32ad..f83f638e 100644 --- a/libcontainer/configs/config.go +++ b/libcontainer/configs/config.go @@ -3,7 +3,9 @@ package configs import ( "bytes" "encoding/json" + "fmt" "os/exec" + "time" "github.com/Sirupsen/logrus" ) @@ -274,10 +276,11 @@ func (f FuncHook) Run(s HookState) error { } type Command struct { - Path string `json:"path"` - Args []string `json:"args"` - Env []string `json:"env"` - Dir string `json:"dir"` + Path string `json:"path"` + Args []string `json:"args"` + Env []string `json:"env"` + Dir string `json:"dir"` + Timeout *time.Duration `json:"timeout"` } // NewCommandHooks will execute the provided command when the hook is run. @@ -302,5 +305,19 @@ func (c Command) Run(s HookState) error { Env: c.Env, Stdin: bytes.NewReader(b), } - return cmd.Run() + errC := make(chan error, 1) + go func() { + errC <- cmd.Run() + }() + if c.Timeout != nil { + select { + case err := <-errC: + return err + case <-time.After(*c.Timeout): + cmd.Process.Kill() + cmd.Wait() + return fmt.Errorf("hook ran past specified timeout of %.1fs", c.Timeout.Seconds()) + } + } + return <-errC } diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index ab3c348c..5d7a2ad4 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" "syscall" + "time" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/configs" @@ -719,27 +720,28 @@ func setupSeccomp(config *specs.Seccomp) (*configs.Seccomp, error) { func createHooks(rspec *specs.Spec, config *configs.Config) { config.Hooks = &configs.Hooks{} for _, h := range rspec.Hooks.Prestart { - cmd := configs.Command{ - Path: h.Path, - Args: h.Args, - Env: h.Env, - } + cmd := createCommandHook(h) config.Hooks.Prestart = append(config.Hooks.Prestart, configs.NewCommandHook(cmd)) } for _, h := range rspec.Hooks.Poststart { - cmd := configs.Command{ - Path: h.Path, - Args: h.Args, - Env: h.Env, - } + cmd := createCommandHook(h) config.Hooks.Poststart = append(config.Hooks.Poststart, configs.NewCommandHook(cmd)) } for _, h := range rspec.Hooks.Poststop { - cmd := configs.Command{ - Path: h.Path, - Args: h.Args, - Env: h.Env, - } + cmd := createCommandHook(h) config.Hooks.Poststop = append(config.Hooks.Poststop, configs.NewCommandHook(cmd)) } } + +func createCommandHook(h specs.Hook) configs.Command { + cmd := configs.Command{ + Path: h.Path, + Args: h.Args, + Env: h.Env, + } + if h.Timeout != nil { + d := time.Duration(*h.Timeout) * time.Second + cmd.Timeout = &d + } + return cmd +}