github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/sandbox/forkexec/exec_unix.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 darwin linux
     6  
     7  // Fork, exec, wait, etc.
     8  
     9  package forkexec
    10  
    11  import (
    12  	errorspkg "errors"
    13  	"runtime"
    14  	"syscall"
    15  	"unsafe"
    16  )
    17  
    18  // ProcAttr holds attributes that will be applied to a new process started
    19  // by StartProcess.
    20  type ProcAttr struct {
    21  	Dir   string    // Current working directory.
    22  	Env   []string  // Environment.
    23  	Files []uintptr // File descriptors.
    24  	Sys   *SysProcAttr
    25  }
    26  
    27  var zeroProcAttr ProcAttr
    28  var zeroSysProcAttr SysProcAttr
    29  
    30  func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
    31  	var p [2]int
    32  	var n int
    33  	var err1 syscall.Errno
    34  	var wstatus syscall.WaitStatus
    35  
    36  	if attr == nil {
    37  		attr = &zeroProcAttr
    38  	}
    39  	sys := attr.Sys
    40  	if sys == nil {
    41  		sys = &zeroSysProcAttr
    42  	}
    43  
    44  	p[0] = -1
    45  	p[1] = -1
    46  
    47  	// Convert args to C form.
    48  	argv0p, err := syscall.BytePtrFromString(argv0)
    49  	if err != nil {
    50  		return 0, err
    51  	}
    52  	argvp, err := syscall.SlicePtrFromStrings(argv)
    53  	if err != nil {
    54  		return 0, err
    55  	}
    56  	envvp, err := syscall.SlicePtrFromStrings(attr.Env)
    57  	if err != nil {
    58  		return 0, err
    59  	}
    60  
    61  	if (runtime.GOOS == "freebsd" || runtime.GOOS == "dragonfly") && len(argv[0]) > len(argv0) {
    62  		argvp[0] = argv0p
    63  	}
    64  
    65  	var chroot *byte
    66  	if sys.Chroot != "" {
    67  		chroot, err = syscall.BytePtrFromString(sys.Chroot)
    68  		if err != nil {
    69  			return 0, err
    70  		}
    71  	}
    72  	var dir *byte
    73  	if attr.Dir != "" {
    74  		dir, err = syscall.BytePtrFromString(attr.Dir)
    75  		if err != nil {
    76  			return 0, err
    77  		}
    78  	}
    79  
    80  	// Both Setctty and Foreground use the Ctty field,
    81  	// but they give it slightly different meanings.
    82  	if sys.Setctty && sys.Foreground {
    83  		return 0, errorspkg.New("both Setctty and Foreground set in SysProcAttr")
    84  	}
    85  	if sys.Setctty && sys.Ctty >= len(attr.Files) {
    86  		return 0, errorspkg.New("Setctty set but Ctty not valid in child")
    87  	}
    88  
    89  	// Acquire the fork lock so that no other threads
    90  	// create new fds that are not yet close-on-exec
    91  	// before we fork.
    92  	syscall.ForkLock.Lock()
    93  
    94  	// Allocate child status pipe close on exec.
    95  	if err = forkExecPipe(p[:]); err != nil {
    96  		goto error
    97  	}
    98  
    99  	// Kick off child.
   100  	pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1])
   101  	if err1 != 0 {
   102  		err = syscall.Errno(err1)
   103  		goto error
   104  	}
   105  	syscall.ForkLock.Unlock()
   106  
   107  	// Read child error status from pipe.
   108  	syscall.Close(p[1])
   109  	for {
   110  		n, err = readlen(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1)))
   111  		if err != syscall.EINTR {
   112  			break
   113  		}
   114  	}
   115  	syscall.Close(p[0])
   116  	if err != nil || n != 0 {
   117  		if n == int(unsafe.Sizeof(err1)) {
   118  			err = syscall.Errno(err1)
   119  		}
   120  		if err == nil {
   121  			err = syscall.EPIPE
   122  		}
   123  		// Child failed; wait for it to exit, to make sure
   124  		// the zombies don't accumulate.
   125  		_, err1 := syscall.Wait4(pid, &wstatus, 0, nil)
   126  		for err1 == syscall.EINTR {
   127  			_, err1 = syscall.Wait4(pid, &wstatus, 0, nil)
   128  		}
   129  		return 0, err
   130  	}
   131  
   132  	// Read got EOF, so pipe closed on exec, so exec succeeded.
   133  	return pid, nil
   134  
   135  error:
   136  	if p[0] >= 0 {
   137  		syscall.Close(p[0])
   138  		syscall.Close(p[1])
   139  	}
   140  	syscall.ForkLock.Unlock()
   141  	return 0, err
   142  }
   143  
   144  // Combination of fork and exec, careful to be thread safe.
   145  func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
   146  	return forkExec(argv0, argv, attr)
   147  }
   148  
   149  // StartProcess wraps ForkExec for package os.
   150  func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
   151  	pid, err = forkExec(argv0, argv, attr)
   152  	return pid, 0, err
   153  }