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 }