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, ®s) 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 }