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

     1  // +build linux
     2  
     3  package exec
     4  
     5  import (
     6  	"errors"
     7  	"os"
     8  	"runtime"
     9  	"sync"
    10  	"sync/atomic"
    11  	"syscall"
    12  )
    13  
    14  // process helper
    15  // because will use ptrace to trace process, the golang function have some insufficient
    16  
    17  // ProcessState stores information about a process, as reported by Wait.
    18  type ProcessState struct {
    19  	pid    int                // The process's id.
    20  	status syscall.WaitStatus // System-dependent status info.
    21  	rusage *syscall.Rusage
    22  }
    23  
    24  // Pid returns the process id of the exited process.
    25  func (p *ProcessState) Pid() int {
    26  	return p.pid
    27  }
    28  
    29  func (p *ProcessState) Exited() bool {
    30  	return p.status.Exited()
    31  }
    32  
    33  func (p *ProcessState) Success() bool {
    34  	return p.status.ExitStatus() == 0
    35  }
    36  
    37  func (p *ProcessState) Sys() syscall.WaitStatus {
    38  	return p.status
    39  }
    40  
    41  func (p *ProcessState) SysUsage() *syscall.Rusage {
    42  	return p.rusage
    43  }
    44  
    45  func (p *ProcessState) String() string {
    46  	if p == nil {
    47  		return "<nil>"
    48  	}
    49  	status := p.status
    50  	res := ""
    51  	switch {
    52  	case status.Exited():
    53  		res = "exit status " + itoa(status.ExitStatus())
    54  	case status.Signaled():
    55  		res = "signal: " + status.Signal().String()
    56  	case status.Stopped():
    57  		res = "stop signal: " + status.StopSignal().String()
    58  		if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 {
    59  			res += " (trap " + itoa(status.TrapCause()) + ")"
    60  		}
    61  	case status.Continued():
    62  		res = "continued"
    63  	}
    64  	if status.CoreDump() {
    65  		res += " (core dumped)"
    66  	}
    67  	return res
    68  }
    69  
    70  // ExitCode returns the exit code of the exited process, or -1
    71  // if the process hasn't exited or was terminated by a signal.
    72  func (p *ProcessState) ExitCode() int {
    73  	// return -1 if the process hasn't started.
    74  	if p == nil {
    75  		return -1
    76  	}
    77  	return p.status.ExitStatus()
    78  }
    79  
    80  // Process stores the information about a process created by StartProcess.
    81  type Process struct {
    82  	Pid    int
    83  	handle uintptr      // handle is accessed atomically on Windows
    84  	isdone uint32       // process has been successfully waited on, non zero if true
    85  	sigMu  sync.RWMutex // avoid race between wait and signal
    86  }
    87  
    88  var errFinished = errors.New("os: process already finished")
    89  
    90  func (p *Process) SignalGroup(sig os.Signal) error {
    91  	if p.Pid == -1 {
    92  		return errors.New("os: process already released")
    93  	}
    94  	if p.Pid == 0 {
    95  		return errors.New("os: process not initialized")
    96  	}
    97  	p.sigMu.RLock()
    98  	defer p.sigMu.RUnlock()
    99  	if p.done() {
   100  		return errFinished
   101  	}
   102  	s, ok := sig.(syscall.Signal)
   103  	if !ok {
   104  		return errors.New("os: unsupported signal type")
   105  	}
   106  	if e := syscall.Kill(-p.Pid, s); e != nil {
   107  		if e == syscall.ESRCH {
   108  			return errFinished
   109  		}
   110  		return e
   111  	}
   112  	return nil
   113  }
   114  
   115  func (p *Process) Signal(sig os.Signal) error {
   116  	if p.Pid == -1 {
   117  		return errors.New("os: process already released")
   118  	}
   119  	if p.Pid == 0 {
   120  		return errors.New("os: process not initialized")
   121  	}
   122  	p.sigMu.RLock()
   123  	defer p.sigMu.RUnlock()
   124  	if p.done() {
   125  		return errFinished
   126  	}
   127  	s, ok := sig.(syscall.Signal)
   128  	if !ok {
   129  		return errors.New("os: unsupported signal type")
   130  	}
   131  	if e := syscall.Kill(p.Pid, s); e != nil {
   132  		if e == syscall.ESRCH {
   133  			return errFinished
   134  		}
   135  		return e
   136  	}
   137  	return nil
   138  }
   139  
   140  func newProcess(pid int, handle uintptr) *Process {
   141  	p := &Process{Pid: pid, handle: handle}
   142  	runtime.SetFinalizer(p, (*Process).Release)
   143  	return p
   144  }
   145  
   146  func (p *Process) Release() error {
   147  	// NOOP for unix.
   148  	p.Pid = -1
   149  	// no need for a finalizer anymore
   150  	runtime.SetFinalizer(p, nil)
   151  	return nil
   152  }
   153  
   154  func (p *Process) Kill() error {
   155  	return p.Signal(os.Kill)
   156  }
   157  
   158  func (p *Process) KillGroup() error {
   159  	return p.Signal(os.Kill)
   160  }
   161  
   162  func (p *Process) setDone() {
   163  	atomic.StoreUint32(&p.isdone, 1)
   164  }
   165  
   166  func (p *Process) done() bool {
   167  	return atomic.LoadUint32(&p.isdone) > 0
   168  }
   169  
   170  func (p *Process) SetDone() {
   171  	p.setDone()
   172  }
   173  
   174  func (p *Process) Done() bool {
   175  	return p.done()
   176  }