github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/sandbox/process/exec_posix.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows
     6  
     7  package process
     8  
     9  import (
    10  	"github.com/LanceLRQ/deer-common/sandbox/forkexec"
    11  	"github.com/LanceLRQ/deer-common/sandbox/process/execenv"
    12  	"github.com/pkg/errors"
    13  	"os"
    14  	"runtime"
    15  	"syscall"
    16  )
    17  
    18  // The only signal values guaranteed to be present in the os package on all
    19  // systems are os.Interrupt (send the process an interrupt) and os.Kill (force
    20  // the process to exit). On Windows, sending os.Interrupt to a process with
    21  // os.Process.Signal is not implemented; it will return an error instead of
    22  // sending a signal.
    23  var (
    24  	Interrupt Signal = syscall.SIGINT
    25  	Kill      Signal = syscall.SIGKILL
    26  )
    27  
    28  func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
    29  	// If there is no SysProcAttr (ie. no Chroot or changed
    30  	// UID/GID), double-check existence of the directory we want
    31  	// to chdir into. We can make the error clearer this way.
    32  	if attr != nil && attr.Sys == nil && attr.Dir != "" {
    33  		if _, err := os.Stat(attr.Dir); err != nil {
    34  			pe := err.(*os.PathError)
    35  			pe.Op = "chdir"
    36  			return nil, pe
    37  		}
    38  	}
    39  
    40  	sysattr := &forkexec.ProcAttr{
    41  		Dir: attr.Dir,
    42  		Env: attr.Env,
    43  		Sys: attr.Sys,
    44  	}
    45  	if sysattr.Env == nil {
    46  		sysattr.Env, err = execenv.Default(sysattr.Sys)
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  	}
    51  	sysattr.Files = make([]uintptr, 0, len(attr.Files))
    52  	for _, f := range attr.Files {
    53  		if fi, ok := f.(*os.File); ok {
    54  			sysattr.Files = append(sysattr.Files, fi.Fd())
    55  		} else if fd, ok := f.(uintptr); ok {
    56  			sysattr.Files = append(sysattr.Files, fd)
    57  		} else {
    58  			return nil, errors.Errorf("Files only allow *os.File and uintptr(file descriptor)")
    59  		}
    60  	}
    61  
    62  	pid, h, e := forkexec.StartProcess(name, argv, sysattr)
    63  
    64  	// Make sure we don't run the finalizers of attr.Files.
    65  	runtime.KeepAlive(attr)
    66  
    67  	if e != nil {
    68  		return nil, &os.PathError{Op: "fork/exec", Path: name, Err: e}
    69  	}
    70  
    71  	return newProcess(pid, h), nil
    72  }
    73  
    74  func (p *Process) kill() error {
    75  	return p.Signal(Kill)
    76  }
    77  
    78  // ProcessState stores information about a process, as reported by Wait.
    79  type ProcessState struct {
    80  	pid    int                // The process's id.
    81  	status syscall.WaitStatus // System-dependent status info.
    82  	rusage *syscall.Rusage
    83  }
    84  
    85  // Pid returns the process id of the exited process.
    86  func (p *ProcessState) Pid() int {
    87  	return p.pid
    88  }
    89  
    90  func (p *ProcessState) exited() bool {
    91  	return p.status.Exited()
    92  }
    93  
    94  func (p *ProcessState) success() bool {
    95  	return p.status.ExitStatus() == 0
    96  }
    97  
    98  func (p *ProcessState) sys() interface{} {
    99  	return p.status
   100  }
   101  
   102  func (p *ProcessState) sysUsage() interface{} {
   103  	return p.rusage
   104  }
   105  
   106  func (p *ProcessState) String() string {
   107  	if p == nil {
   108  		return "<nil>"
   109  	}
   110  	status := p.Sys().(syscall.WaitStatus)
   111  	res := ""
   112  	switch {
   113  	case status.Exited():
   114  		res = "exit status " + itoa(status.ExitStatus())
   115  	case status.Signaled():
   116  		res = "signal: " + status.Signal().String()
   117  	case status.Stopped():
   118  		res = "stop signal: " + status.StopSignal().String()
   119  		if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 {
   120  			res += " (trap " + itoa(status.TrapCause()) + ")"
   121  		}
   122  	case status.Continued():
   123  		res = "continued"
   124  	}
   125  	if status.CoreDump() {
   126  		res += " (core dumped)"
   127  	}
   128  	return res
   129  }
   130  
   131  // ExitCode returns the exit code of the exited process, or -1
   132  // if the process hasn't exited or was terminated by a signal.
   133  func (p *ProcessState) ExitCode() int {
   134  	// return -1 if the process hasn't started.
   135  	if p == nil {
   136  		return -1
   137  	}
   138  	return p.status.ExitStatus()
   139  }