1 //go:build linux 2 // +build linux 3 4 package runc 5 6 import ( 7 "syscall" 8 9 "github.com/Microsoft/hcsshim/internal/guest/runtime" 10 "github.com/Microsoft/hcsshim/internal/guest/stdio" 11 "github.com/Microsoft/hcsshim/internal/logfields" 12 "github.com/sirupsen/logrus" 13 ) 14 15 // process represents a process running in a container. It can either be a 16 // container's init process, or an exec process in a container. 17 type process struct { 18 c *container 19 pid int 20 ttyRelay *stdio.TtyRelay 21 pipeRelay *stdio.PipeRelay 22 } 23 24 var _ runtime.Process = &process{} 25 26 func (p *process) Pid() int { 27 return p.pid 28 } 29 30 func (p *process) Tty() *stdio.TtyRelay { 31 return p.ttyRelay 32 } 33 34 func (p *process) PipeRelay() *stdio.PipeRelay { 35 return p.pipeRelay 36 } 37 38 // Delete deletes any state created for the process by either this wrapper or 39 // runC itself. 40 func (p *process) Delete() error { 41 if err := p.c.r.cleanupProcess(p.c.id, p.pid); err != nil { 42 return err 43 } 44 return nil 45 } 46 47 func (p *process) Wait() (int, error) { 48 exitCode, err := p.c.r.waitOnProcess(p.pid) 49 50 l := logrus.WithField(logfields.ContainerID, p.c.id) 51 l.WithField(logfields.ContainerID, p.pid).Debug("process wait completed") 52 53 // If the init process for the container has exited, kill everything else in 54 // the container. Runc uses the devices cgroup of the container to determine 55 // what other processes to kill. 56 // 57 // We don't issue the kill if the container owns its own pid namespace, 58 // because in that case the container kernel will kill everything in the pid 59 // namespace automatically (as the container init will be the pid namespace 60 // init). This prevents a potential issue where two containers share cgroups 61 // but have their own pid namespaces. If we didn't handle this case, runc 62 // would kill the processes in both containers when trying to kill 63 // either one of them. 64 if p == p.c.init && !p.c.ownsPidNamespace { 65 // If the init process of a pid namespace terminates, the kernel 66 // terminates all other processes in the namespace with SIGKILL. We 67 // simulate the same behavior. 68 if err := p.c.Kill(syscall.SIGKILL); err != nil { 69 l.WithError(err).Error("failed to terminate container after process wait") 70 } 71 } 72 73 // Wait on the relay to drain any output that was already buffered. 74 // 75 // At this point, if this is the init process for the container, everything 76 // else in the container has been killed, so the write ends of the stdio 77 // relay will have been closed. 78 // 79 // If this is a container exec process instead, then it is possible the 80 // relay waits will hang waiting for the write ends to close. This can occur 81 // if the exec spawned any child processes that inherited its stdio. 82 // Currently we do not do anything to avoid hanging in this case, but in the 83 // future we could add special handling. 84 if p.ttyRelay != nil { 85 p.ttyRelay.Wait() 86 } 87 if p.pipeRelay != nil { 88 p.pipeRelay.Wait() 89 } 90 91 l.WithField(logfields.ProcessID, p.pid).Debug("relay wait completed") 92 93 return exitCode, err 94 } 95