github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/shim.go (about) 1 // Copyright (c) 2017 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package virtcontainers 7 8 import ( 9 "fmt" 10 "os" 11 "os/exec" 12 "syscall" 13 "time" 14 15 ns "github.com/kata-containers/runtime/virtcontainers/pkg/nsenter" 16 "github.com/kata-containers/runtime/virtcontainers/types" 17 "github.com/mitchellh/mapstructure" 18 "github.com/sirupsen/logrus" 19 ) 20 21 // ShimType describes a shim type. 22 type ShimType string 23 24 const ( 25 // NoopShimType is the noopShim. 26 NoopShimType ShimType = "noopShim" 27 28 // KataShimType is the Kata Containers shim type. 29 KataShimType ShimType = "kataShim" 30 31 // KataBuiltInShimType is the Kata Containers builtin shim type. 32 KataBuiltInShimType ShimType = "kataBuiltInShim" 33 ) 34 35 var waitForShimTimeout = 10.0 36 var consoleFileMode = os.FileMode(0660) 37 38 // ShimParams is the structure providing specific parameters needed 39 // for the execution of the shim binary. 40 type ShimParams struct { 41 Container string 42 Token string 43 URL string 44 Console string 45 ConsoleURL string 46 Terminal bool 47 Detach bool 48 PID int 49 CreateNS []ns.NSType 50 EnterNS []ns.Namespace 51 } 52 53 // ShimConfig is the structure providing specific configuration 54 // for shim implementations. 55 type ShimConfig struct { 56 Path string 57 Debug bool 58 Trace bool 59 } 60 61 // Set sets a shim type based on the input string. 62 func (pType *ShimType) Set(value string) error { 63 switch value { 64 case "noopShim": 65 *pType = NoopShimType 66 case "kataShim": 67 *pType = KataShimType 68 case "kataBuiltInShim": 69 *pType = KataBuiltInShimType 70 default: 71 return fmt.Errorf("Unknown shim type %s", value) 72 } 73 return nil 74 } 75 76 // String converts a shim type to a string. 77 func (pType *ShimType) String() string { 78 switch *pType { 79 case NoopShimType: 80 return string(NoopShimType) 81 case KataShimType: 82 return string(KataShimType) 83 case KataBuiltInShimType: 84 return string(KataBuiltInShimType) 85 default: 86 return "" 87 } 88 } 89 90 // newShim returns a shim from a shim type. 91 func newShim(pType ShimType) (shim, error) { 92 switch pType { 93 case NoopShimType: 94 return &noopShim{}, nil 95 case KataShimType: 96 return &kataShim{}, nil 97 case KataBuiltInShimType: 98 return &kataBuiltInShim{}, nil 99 default: 100 return &noopShim{}, nil 101 } 102 } 103 104 // newShimConfig returns a shim config from a generic SandboxConfig interface. 105 func newShimConfig(config SandboxConfig) interface{} { 106 switch config.ShimType { 107 case NoopShimType, KataBuiltInShimType: 108 return nil 109 case KataShimType: 110 var shimConfig ShimConfig 111 err := mapstructure.Decode(config.ShimConfig, &shimConfig) 112 if err != nil { 113 return err 114 } 115 return shimConfig 116 default: 117 return nil 118 } 119 } 120 121 func shimLogger() *logrus.Entry { 122 return virtLog.WithField("subsystem", "shim") 123 } 124 125 func signalShim(pid int, sig syscall.Signal) error { 126 if pid <= 0 { 127 return nil 128 } 129 130 shimLogger().WithFields( 131 logrus.Fields{ 132 "shim-pid": pid, 133 "shim-signal": sig, 134 }).Info("Signalling shim") 135 136 return syscall.Kill(pid, sig) 137 } 138 139 func stopShim(pid int) error { 140 if pid <= 0 { 141 return nil 142 } 143 144 if err := signalShim(pid, syscall.SIGKILL); err != nil && err != syscall.ESRCH { 145 return err 146 } 147 148 return nil 149 } 150 151 func prepareAndStartShim(sandbox *Sandbox, shim shim, cid, token, url, consoleURL string, cmd types.Cmd, 152 createNSList []ns.NSType, enterNSList []ns.Namespace) (*Process, error) { 153 process := &Process{ 154 Token: token, 155 StartTime: time.Now().UTC(), 156 } 157 158 shimParams := ShimParams{ 159 Container: cid, 160 Token: token, 161 URL: url, 162 Console: cmd.Console, 163 Terminal: cmd.Interactive, 164 Detach: cmd.Detach, 165 CreateNS: createNSList, 166 EnterNS: enterNSList, 167 ConsoleURL: consoleURL, 168 } 169 170 pid, err := shim.start(sandbox, shimParams) 171 if err != nil { 172 return nil, err 173 } 174 175 process.Pid = pid 176 177 return process, nil 178 } 179 180 func startShim(args []string, params ShimParams) (int, error) { 181 cmd := exec.Command(args[0], args[1:]...) 182 183 if !params.Detach { 184 cmd.Stdin = os.Stdin 185 cmd.Stdout = os.Stdout 186 cmd.Stderr = os.Stderr 187 } 188 189 cloneFlags := 0 190 for _, nsType := range params.CreateNS { 191 cloneFlags |= ns.CloneFlagsTable[nsType] 192 } 193 194 cmd.SysProcAttr = &syscall.SysProcAttr{ 195 Cloneflags: uintptr(cloneFlags), 196 } 197 198 var f *os.File 199 var err error 200 if params.Console != "" { 201 f, err = os.OpenFile(params.Console, os.O_RDWR, consoleFileMode) 202 if err != nil { 203 return -1, err 204 } 205 206 cmd.Stdin = f 207 cmd.Stdout = f 208 cmd.Stderr = f 209 // Create Session 210 cmd.SysProcAttr.Setsid = true 211 } 212 defer func() { 213 if f != nil { 214 f.Close() 215 } 216 }() 217 218 if err := ns.NsEnter(params.EnterNS, func() error { 219 return cmd.Start() 220 }); err != nil { 221 return -1, err 222 } 223 224 return cmd.Process.Pid, nil 225 } 226 227 func isShimRunning(pid int) (bool, error) { 228 if pid <= 0 { 229 return false, nil 230 } 231 232 process, err := os.FindProcess(pid) 233 if err != nil { 234 return false, err 235 } 236 237 if err := process.Signal(syscall.Signal(0)); err != nil { 238 return false, nil 239 } 240 241 return true, nil 242 } 243 244 // waitForShim waits for the end of the shim unless it reaches the timeout 245 // first, returning an error in that case. 246 func waitForShim(pid int) error { 247 if pid <= 0 { 248 return nil 249 } 250 251 tInit := time.Now() 252 for { 253 running, err := isShimRunning(pid) 254 if err != nil { 255 return err 256 } 257 258 if !running { 259 break 260 } 261 262 if time.Since(tInit).Seconds() >= waitForShimTimeout { 263 return fmt.Errorf("Shim still running, timeout %f s has been reached", waitForShimTimeout) 264 } 265 266 // Let's avoid to run a too busy loop 267 time.Sleep(time.Duration(100) * time.Millisecond) 268 } 269 270 return nil 271 } 272 273 // shim is the virtcontainers shim interface. 274 type shim interface { 275 // start starts the shim relying on its configuration and on 276 // parameters provided. 277 start(sandbox *Sandbox, params ShimParams) (int, error) 278 }