github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/container/hook.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package container 16 17 import ( 18 "bytes" 19 "encoding/json" 20 "fmt" 21 "os/exec" 22 "path/filepath" 23 "strings" 24 "time" 25 26 specs "github.com/opencontainers/runtime-spec/specs-go" 27 "github.com/SagerNet/gvisor/pkg/log" 28 ) 29 30 // This file implements hooks as defined in OCI spec: 31 // https://github.com/opencontainers/runtime-spec/blob/master/config.md#toc22 32 // 33 // "hooks":{ 34 // "prestart":[{ 35 // "path":"/usr/bin/dockerd", 36 // "args":[ 37 // "libnetwork-setkey", "arg2", 38 // ] 39 // }] 40 // }, 41 42 // executeHooksBestEffort executes hooks and logs warning in case they fail. 43 // Runs all hooks, always. 44 func executeHooksBestEffort(hooks []specs.Hook, s specs.State) { 45 for _, h := range hooks { 46 if err := executeHook(h, s); err != nil { 47 log.Warningf("Failure to execute hook %+v, err: %v", h, err) 48 } 49 } 50 } 51 52 // executeHooks executes hooks until the first one fails or they all execute. 53 func executeHooks(hooks []specs.Hook, s specs.State) error { 54 for _, h := range hooks { 55 if err := executeHook(h, s); err != nil { 56 return err 57 } 58 } 59 return nil 60 } 61 62 func executeHook(h specs.Hook, s specs.State) error { 63 log.Debugf("Executing hook %+v, state: %+v", h, s) 64 65 if strings.TrimSpace(h.Path) == "" { 66 return fmt.Errorf("empty path for hook") 67 } 68 if !filepath.IsAbs(h.Path) { 69 return fmt.Errorf("path for hook is not absolute: %q", h.Path) 70 } 71 72 b, err := json.Marshal(s) 73 if err != nil { 74 return err 75 } 76 var stdout, stderr bytes.Buffer 77 cmd := exec.Cmd{ 78 Path: h.Path, 79 Args: h.Args, 80 Env: h.Env, 81 Stdin: bytes.NewReader(b), 82 Stdout: &stdout, 83 Stderr: &stderr, 84 } 85 if err := cmd.Start(); err != nil { 86 return err 87 } 88 89 c := make(chan error, 1) 90 go func() { 91 c <- cmd.Wait() 92 }() 93 94 var timer <-chan time.Time 95 if h.Timeout != nil { 96 timer = time.After(time.Duration(*h.Timeout) * time.Second) 97 } 98 select { 99 case err := <-c: 100 if err != nil { 101 return fmt.Errorf("failure executing hook %q, err: %v\nstdout: %s\nstderr: %s", h.Path, err, stdout.String(), stderr.String()) 102 } 103 case <-timer: 104 cmd.Process.Kill() 105 cmd.Wait() 106 return fmt.Errorf("timeout executing hook %q\nstdout: %s\nstderr: %s", h.Path, stdout.String(), stderr.String()) 107 } 108 109 log.Debugf("Execute hook %q success!", h.Path) 110 return nil 111 }