github.com/sdibtacm/sandbox@v0.0.0-20200320120712-60470cf803dc/exec/wait.go (about)

     1  //+build linux
     2  
     3  package exec
     4  
     5  import (
     6  	"github.com/sdibtacm/sandbox/exec/log"
     7  	"os"
     8  	"runtime"
     9  	"syscall"
    10  	"unsafe"
    11  )
    12  
    13  type ptrace struct {
    14  	SyscallNo uint64
    15  }
    16  
    17  func (c *Cmd) wait() (ps *ProcessState, err error, pt *ptrace) {
    18  	if c.Sys.Ptrace {
    19  		return c.Process.ptraceWait()
    20  	}
    21  
    22  	ps, err = c.Process.Wait()
    23  	return
    24  }
    25  
    26  func (p *Process) ptraceWait() (ps *ProcessState, err error, pt *ptrace) {
    27  	log.GetLog().Debug("ptrace waiting up")
    28  
    29  	var rusage syscall.Rusage
    30  	var regs syscall.PtraceRegs
    31  	var status syscall.WaitStatus
    32  	var wpid int
    33  	ps = &ProcessState{rusage: &rusage}
    34  
    35  	wpid, err = syscall.Wait4(p.Pid, &status, 0, &rusage)
    36  	log.GetLog().Debug("wait pid: {} status:{}, err: {}, the message is tell parent the child is ready", wpid, status, err)
    37  	ps.status = status
    38  	ps.pid = wpid
    39  
    40  	// parent will trace only seccomp event
    41  	_ = syscall.PtraceSetOptions(p.Pid, PTRACE_O_TRACESECCOMP)
    42  
    43  	for {
    44  		_ = syscall.PtraceCont(wpid, 0)
    45  		wpid, err = syscall.Wait4(p.Pid, &status, 0, &rusage)
    46  		ps.status = status
    47  		ps.pid = wpid
    48  		log.GetLog().Debug("wait pid: {} status:{}, err: {}", wpid, status, err)
    49  
    50  		if err != nil {
    51  			log.GetLog().Warning("wait4 error, error msg: {}", err)
    52  			_ = p.KillGroup()
    53  			return nil, err, nil
    54  		}
    55  
    56  		if status.Exited() || status.Signaled() {
    57  			log.GetLog().Info("termination, exit status = {}, signal number = {}", status.ExitStatus(), status.Signaled())
    58  			return
    59  		}
    60  
    61  		if status.Stopped() {
    62  			log.GetLog().DebugF("child stopped, signal number=%d", status.StopSignal())
    63  			if status.TrapCause() == PTRACE_EVENT_SECCOMP {
    64  				log.GetLog().Debug("cache a seccomp event")
    65  				_ = syscall.PtraceGetRegs(wpid, &regs)
    66  				pt = &ptrace{SyscallNo: uint64(regs.Orig_rax)}
    67  				_ = p.SignalGroup(syscall.SIGSYS)
    68  			}
    69  		} else {
    70  			log.GetLog().Warning("don't know what happen, pid: {}, status: {}, err: {}", wpid, status, err)
    71  			_ = p.KillGroup()
    72  		}
    73  
    74  	}
    75  }
    76  
    77  func (p *Process) Wait() (ps *ProcessState, err error) {
    78  	if p.Pid == -1 {
    79  		return nil, syscall.EINVAL
    80  	}
    81  
    82  	// If we can block until Wait4 will succeed immediately, do so.
    83  	ready, err := p.blockUntilWaitable()
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	if ready {
    88  		// Mark the process done now, before the call to Wait4,
    89  		// so that Process.signal will not send a signal.
    90  		p.setDone()
    91  		// Acquire a write lock on sigMu to wait for any
    92  		// active call to the signal method to complete.
    93  		p.sigMu.Lock()
    94  		p.sigMu.Unlock()
    95  	}
    96  
    97  	var status syscall.WaitStatus
    98  	var rusage syscall.Rusage
    99  	pid1, e := syscall.Wait4(p.Pid, &status, 0, &rusage)
   100  	if e != nil {
   101  		return nil, os.NewSyscallError("wait", e)
   102  	}
   103  	if pid1 != 0 {
   104  		p.setDone()
   105  	}
   106  	ps = &ProcessState{
   107  		pid:    pid1,
   108  		status: status,
   109  		rusage: &rusage,
   110  	}
   111  	return ps, nil
   112  }
   113  
   114  const _P_PID = 1
   115  
   116  // blockUntilWaitable attempts to block until a call to p.Wait will
   117  // succeed immediately, and reports whether it has done so.
   118  // It does not actually call p.Wait.
   119  func (p *Process) blockUntilWaitable() (bool, error) {
   120  	// The waitid system call expects a pointer to a siginfo_t,
   121  	// which is 128 bytes on all GNU/Linux systems.
   122  	// On Darwin, it requires greater than or equal to 64 bytes
   123  	// for darwin/{386,arm} and 104 bytes for darwin/amd64.
   124  	// We don't care about the values it returns.
   125  	var siginfo [16]uint64
   126  	psig := &siginfo[0]
   127  	_, _, e := syscall.Syscall6(syscall.SYS_WAITID, _P_PID, uintptr(p.Pid), uintptr(unsafe.Pointer(psig)), syscall.WEXITED|syscall.WNOWAIT, 0, 0)
   128  	runtime.KeepAlive(p)
   129  	if e != 0 {
   130  		// waitid has been available since Linux 2.6.9, but
   131  		// reportedly is not available in Ubuntu on Windows.
   132  		// See issue 16610.
   133  		if e == syscall.ENOSYS {
   134  			return false, nil
   135  		}
   136  		return false, os.NewSyscallError("waitid", e)
   137  	}
   138  	return true, nil
   139  }