...

Source file src/github.com/Microsoft/hcsshim/internal/guest/runtime/runc/process.go

Documentation: github.com/Microsoft/hcsshim/internal/guest/runtime/runc

     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  

View as plain text