github.com/runcom/containerd@v0.0.0-20160708090337-9bff9f934c0d/containerd-shim/process.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "strconv" 13 "sync" 14 "syscall" 15 "time" 16 17 "github.com/docker/containerd/specs" 18 ) 19 20 var errRuntime = errors.New("shim: runtime execution error") 21 22 type checkpoint struct { 23 // Timestamp is the time that checkpoint happened 24 Created time.Time `json:"created"` 25 // Name is the name of the checkpoint 26 Name string `json:"name"` 27 // TCP checkpoints open tcp connections 28 TCP bool `json:"tcp"` 29 // UnixSockets persists unix sockets in the checkpoint 30 UnixSockets bool `json:"unixSockets"` 31 // Shell persists tty sessions in the checkpoint 32 Shell bool `json:"shell"` 33 // Exit exits the container after the checkpoint is finished 34 Exit bool `json:"exit"` 35 // EmptyNS tells CRIU not to restore a particular namespace 36 EmptyNS []string `json:"emptyNS,omitempty"` 37 } 38 39 type processState struct { 40 specs.ProcessSpec 41 Exec bool `json:"exec"` 42 Stdin string `json:"containerdStdin"` 43 Stdout string `json:"containerdStdout"` 44 Stderr string `json:"containerdStderr"` 45 RuntimeArgs []string `json:"runtimeArgs"` 46 NoPivotRoot bool `json:"noPivotRoot"` 47 CheckpointPath string `json:"checkpoint"` 48 RootUID int `json:"rootUID"` 49 RootGID int `json:"rootGID"` 50 } 51 52 type process struct { 53 sync.WaitGroup 54 id string 55 bundle string 56 stdio *stdio 57 exec bool 58 containerPid int 59 checkpoint *checkpoint 60 checkpointPath string 61 shimIO *IO 62 stdinCloser io.Closer 63 console *os.File 64 consolePath string 65 state *processState 66 runtime string 67 } 68 69 func newProcess(id, bundle, runtimeName string) (*process, error) { 70 p := &process{ 71 id: id, 72 bundle: bundle, 73 runtime: runtimeName, 74 } 75 s, err := loadProcess() 76 if err != nil { 77 return nil, err 78 } 79 p.state = s 80 if s.CheckpointPath != "" { 81 cpt, err := loadCheckpoint(s.CheckpointPath) 82 if err != nil { 83 return nil, err 84 } 85 p.checkpoint = cpt 86 p.checkpointPath = s.CheckpointPath 87 } 88 if err := p.openIO(); err != nil { 89 return nil, err 90 } 91 return p, nil 92 } 93 94 func loadProcess() (*processState, error) { 95 f, err := os.Open("process.json") 96 if err != nil { 97 return nil, err 98 } 99 defer f.Close() 100 var s processState 101 if err := json.NewDecoder(f).Decode(&s); err != nil { 102 return nil, err 103 } 104 return &s, nil 105 } 106 107 func loadCheckpoint(checkpointPath string) (*checkpoint, error) { 108 f, err := os.Open(filepath.Join(checkpointPath, "config.json")) 109 if err != nil { 110 return nil, err 111 } 112 defer f.Close() 113 var cpt checkpoint 114 if err := json.NewDecoder(f).Decode(&cpt); err != nil { 115 return nil, err 116 } 117 return &cpt, nil 118 } 119 120 func (p *process) create() error { 121 cwd, err := os.Getwd() 122 if err != nil { 123 return err 124 } 125 logPath := filepath.Join(cwd, "log.json") 126 args := append([]string{ 127 "--log", logPath, 128 "--log-format", "json", 129 }, p.state.RuntimeArgs...) 130 if p.state.Exec { 131 args = append(args, "exec", 132 "-d", 133 "--process", filepath.Join(cwd, "process.json"), 134 "--console", p.consolePath, 135 ) 136 } else if p.checkpoint != nil { 137 args = append(args, "restore", 138 "--image-path", p.checkpointPath, 139 "--work-path", filepath.Join(p.checkpointPath, "criu.work", "restore-"+time.Now().Format(time.RFC3339)), 140 ) 141 add := func(flags ...string) { 142 args = append(args, flags...) 143 } 144 if p.checkpoint.Shell { 145 add("--shell-job") 146 } 147 if p.checkpoint.TCP { 148 add("--tcp-established") 149 } 150 if p.checkpoint.UnixSockets { 151 add("--ext-unix-sk") 152 } 153 if p.state.NoPivotRoot { 154 add("--no-pivot") 155 } 156 for _, ns := range p.checkpoint.EmptyNS { 157 add("--empty-ns", ns) 158 } 159 160 } else { 161 args = append(args, "create", 162 "--bundle", p.bundle, 163 "--console", p.consolePath, 164 ) 165 if p.state.NoPivotRoot { 166 args = append(args, "--no-pivot") 167 } 168 } 169 args = append(args, 170 "--pid-file", filepath.Join(cwd, "pid"), 171 p.id, 172 ) 173 cmd := exec.Command(p.runtime, args...) 174 cmd.Dir = p.bundle 175 cmd.Stdin = p.stdio.stdin 176 cmd.Stdout = p.stdio.stdout 177 cmd.Stderr = p.stdio.stderr 178 // Call out to setPDeathSig to set SysProcAttr as elements are platform specific 179 cmd.SysProcAttr = setPDeathSig() 180 181 if err := cmd.Start(); err != nil { 182 if exErr, ok := err.(*exec.Error); ok { 183 if exErr.Err == exec.ErrNotFound || exErr.Err == os.ErrNotExist { 184 return fmt.Errorf("%s not installed on system", p.runtime) 185 } 186 } 187 return err 188 } 189 p.stdio.stdout.Close() 190 p.stdio.stderr.Close() 191 if err := cmd.Wait(); err != nil { 192 if _, ok := err.(*exec.ExitError); ok { 193 return errRuntime 194 } 195 return err 196 } 197 data, err := ioutil.ReadFile("pid") 198 if err != nil { 199 return err 200 } 201 pid, err := strconv.Atoi(string(data)) 202 if err != nil { 203 return err 204 } 205 p.containerPid = pid 206 return nil 207 } 208 209 func (p *process) pid() int { 210 return p.containerPid 211 } 212 213 func (p *process) delete() error { 214 if !p.state.Exec { 215 cmd := exec.Command(p.runtime, append(p.state.RuntimeArgs, "delete", p.id)...) 216 cmd.SysProcAttr = setPDeathSig() 217 out, err := cmd.CombinedOutput() 218 if err != nil { 219 return fmt.Errorf("%s: %v", out, err) 220 } 221 } 222 return nil 223 } 224 225 // openIO opens the pre-created fifo's for use with the container 226 // in RDWR so that they remain open if the other side stops listening 227 func (p *process) openIO() error { 228 p.stdio = &stdio{} 229 var ( 230 uid = p.state.RootUID 231 gid = p.state.RootGID 232 ) 233 go func() { 234 if stdinCloser, err := os.OpenFile(p.state.Stdin, syscall.O_WRONLY, 0); err == nil { 235 p.stdinCloser = stdinCloser 236 } 237 }() 238 239 if p.state.Terminal { 240 master, console, err := newConsole(uid, gid) 241 if err != nil { 242 return err 243 } 244 p.console = master 245 p.consolePath = console 246 stdin, err := os.OpenFile(p.state.Stdin, syscall.O_RDONLY, 0) 247 if err != nil { 248 return err 249 } 250 go io.Copy(master, stdin) 251 stdout, err := os.OpenFile(p.state.Stdout, syscall.O_RDWR, 0) 252 if err != nil { 253 return err 254 } 255 p.Add(1) 256 go func() { 257 io.Copy(stdout, master) 258 master.Close() 259 p.Done() 260 }() 261 return nil 262 } 263 i, err := p.initializeIO(uid) 264 if err != nil { 265 return err 266 } 267 p.shimIO = i 268 // non-tty 269 for name, dest := range map[string]func(f *os.File){ 270 p.state.Stdout: func(f *os.File) { 271 p.Add(1) 272 go func() { 273 io.Copy(f, i.Stdout) 274 p.Done() 275 }() 276 }, 277 p.state.Stderr: func(f *os.File) { 278 p.Add(1) 279 go func() { 280 io.Copy(f, i.Stderr) 281 p.Done() 282 }() 283 }, 284 } { 285 f, err := os.OpenFile(name, syscall.O_RDWR, 0) 286 if err != nil { 287 return err 288 } 289 dest(f) 290 } 291 292 f, err := os.OpenFile(p.state.Stdin, syscall.O_RDONLY, 0) 293 if err != nil { 294 return err 295 } 296 go func() { 297 io.Copy(i.Stdin, f) 298 i.Stdin.Close() 299 }() 300 301 return nil 302 } 303 304 // IO holds all 3 standard io Reader/Writer (stdin,stdout,stderr) 305 type IO struct { 306 Stdin io.WriteCloser 307 Stdout io.ReadCloser 308 Stderr io.ReadCloser 309 } 310 311 func (p *process) initializeIO(rootuid int) (i *IO, err error) { 312 var fds []uintptr 313 i = &IO{} 314 // cleanup in case of an error 315 defer func() { 316 if err != nil { 317 for _, fd := range fds { 318 syscall.Close(int(fd)) 319 } 320 } 321 }() 322 // STDIN 323 r, w, err := os.Pipe() 324 if err != nil { 325 return nil, err 326 } 327 fds = append(fds, r.Fd(), w.Fd()) 328 p.stdio.stdin, i.Stdin = r, w 329 // STDOUT 330 if r, w, err = os.Pipe(); err != nil { 331 return nil, err 332 } 333 fds = append(fds, r.Fd(), w.Fd()) 334 p.stdio.stdout, i.Stdout = w, r 335 // STDERR 336 if r, w, err = os.Pipe(); err != nil { 337 return nil, err 338 } 339 fds = append(fds, r.Fd(), w.Fd()) 340 p.stdio.stderr, i.Stderr = w, r 341 // change ownership of the pipes in case we are in a user namespace 342 for _, fd := range fds { 343 if err := syscall.Fchown(int(fd), rootuid, rootuid); err != nil { 344 return nil, err 345 } 346 } 347 return i, nil 348 } 349 func (p *process) Close() error { 350 return p.stdio.Close() 351 } 352 353 type stdio struct { 354 stdin *os.File 355 stdout *os.File 356 stderr *os.File 357 } 358 359 func (s *stdio) Close() error { 360 err := s.stdin.Close() 361 if oerr := s.stdout.Close(); err == nil { 362 err = oerr 363 } 364 if oerr := s.stderr.Close(); err == nil { 365 err = oerr 366 } 367 return err 368 }