github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/syscall/exec_libc.go (about) 1 // Copyright 2011 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 //go:build aix || solaris 6 7 // This file handles forkAndExecInChild function for OS using libc syscall like AIX or Solaris. 8 9 package syscall 10 11 import ( 12 "runtime" 13 "unsafe" 14 ) 15 16 type SysProcAttr struct { 17 Chroot string // Chroot. 18 Credential *Credential // Credential. 19 Setsid bool // Create session. 20 // Setpgid sets the process group ID of the child to Pgid, 21 // or, if Pgid == 0, to the new child's process ID. 22 Setpgid bool 23 // Setctty sets the controlling terminal of the child to 24 // file descriptor Ctty. Ctty must be a descriptor number 25 // in the child process: an index into ProcAttr.Files. 26 // This is only meaningful if Setsid is true. 27 Setctty bool 28 Noctty bool // Detach fd 0 from controlling terminal 29 Ctty int // Controlling TTY fd 30 // Foreground places the child process group in the foreground. 31 // This implies Setpgid. The Ctty field must be set to 32 // the descriptor of the controlling TTY. 33 // Unlike Setctty, in this case Ctty must be a descriptor 34 // number in the parent process. 35 Foreground bool 36 Pgid int // Child's process group ID if Setpgid. 37 } 38 39 // Implemented in runtime package. 40 func runtime_BeforeFork() 41 func runtime_AfterFork() 42 func runtime_AfterForkInChild() 43 44 func chdir(path uintptr) (err Errno) 45 func chroot1(path uintptr) (err Errno) 46 func closeFD(fd uintptr) (err Errno) 47 func dup2child(old uintptr, new uintptr) (val uintptr, err Errno) 48 func execve(path uintptr, argv uintptr, envp uintptr) (err Errno) 49 func exit(code uintptr) 50 func fcntl1(fd uintptr, cmd uintptr, arg uintptr) (val uintptr, err Errno) 51 func forkx(flags uintptr) (pid uintptr, err Errno) 52 func getpid() (pid uintptr, err Errno) 53 func ioctl(fd uintptr, req uintptr, arg uintptr) (err Errno) 54 func setgid(gid uintptr) (err Errno) 55 func setgroups1(ngid uintptr, gid uintptr) (err Errno) 56 func setsid() (pid uintptr, err Errno) 57 func setuid(uid uintptr) (err Errno) 58 func setpgid(pid uintptr, pgid uintptr) (err Errno) 59 func write1(fd uintptr, buf uintptr, nbyte uintptr) (n uintptr, err Errno) 60 61 // syscall defines this global on our behalf to avoid a build dependency on other platforms 62 func init() { 63 execveLibc = execve 64 } 65 66 // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child. 67 // If a dup or exec fails, write the errno error to pipe. 68 // (Pipe is close-on-exec so if exec succeeds, it will be closed.) 69 // In the child, this function must not acquire any locks, because 70 // they might have been locked at the time of the fork. This means 71 // no rescheduling, no malloc calls, and no new stack segments. 72 // 73 // We call hand-crafted syscalls, implemented in 74 // ../runtime/syscall_solaris.go, rather than generated libc wrappers 75 // because we need to avoid lazy-loading the functions (might malloc, 76 // split the stack, or acquire mutexes). We can't call RawSyscall 77 // because it's not safe even for BSD-subsystem calls. 78 // 79 //go:norace 80 func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { 81 // Declare all variables at top in case any 82 // declarations require heap allocation (e.g., err1). 83 var ( 84 r1 uintptr 85 err1 Errno 86 nextfd int 87 i int 88 ) 89 90 // guard against side effects of shuffling fds below. 91 // Make sure that nextfd is beyond any currently open files so 92 // that we can't run the risk of overwriting any of them. 93 fd := make([]int, len(attr.Files)) 94 nextfd = len(attr.Files) 95 for i, ufd := range attr.Files { 96 if nextfd < int(ufd) { 97 nextfd = int(ufd) 98 } 99 fd[i] = int(ufd) 100 } 101 nextfd++ 102 103 // About to call fork. 104 // No more allocation or calls of non-assembly functions. 105 runtime_BeforeFork() 106 r1, err1 = forkx(0x1) // FORK_NOSIGCHLD 107 if err1 != 0 { 108 runtime_AfterFork() 109 return 0, err1 110 } 111 112 if r1 != 0 { 113 // parent; return PID 114 runtime_AfterFork() 115 return int(r1), 0 116 } 117 118 // Fork succeeded, now in child. 119 120 // Session ID 121 if sys.Setsid { 122 _, err1 = setsid() 123 if err1 != 0 { 124 goto childerror 125 } 126 } 127 128 // Set process group 129 if sys.Setpgid || sys.Foreground { 130 // Place child in process group. 131 err1 = setpgid(0, uintptr(sys.Pgid)) 132 if err1 != 0 { 133 goto childerror 134 } 135 } 136 137 if sys.Foreground { 138 pgrp := _Pid_t(sys.Pgid) 139 if pgrp == 0 { 140 r1, err1 = getpid() 141 if err1 != 0 { 142 goto childerror 143 } 144 145 pgrp = _Pid_t(r1) 146 } 147 148 // Place process group in foreground. 149 err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp))) 150 if err1 != 0 { 151 goto childerror 152 } 153 } 154 155 // Restore the signal mask. We do this after TIOCSPGRP to avoid 156 // having the kernel send a SIGTTOU signal to the process group. 157 runtime_AfterForkInChild() 158 159 // Chroot 160 if chroot != nil { 161 err1 = chroot1(uintptr(unsafe.Pointer(chroot))) 162 if err1 != 0 { 163 goto childerror 164 } 165 } 166 167 // User and groups 168 if cred := sys.Credential; cred != nil { 169 ngroups := uintptr(len(cred.Groups)) 170 groups := uintptr(0) 171 if ngroups > 0 { 172 groups = uintptr(unsafe.Pointer(&cred.Groups[0])) 173 } 174 if !cred.NoSetGroups { 175 err1 = setgroups1(ngroups, groups) 176 if err1 != 0 { 177 goto childerror 178 } 179 } 180 err1 = setgid(uintptr(cred.Gid)) 181 if err1 != 0 { 182 goto childerror 183 } 184 err1 = setuid(uintptr(cred.Uid)) 185 if err1 != 0 { 186 goto childerror 187 } 188 } 189 190 // Chdir 191 if dir != nil { 192 err1 = chdir(uintptr(unsafe.Pointer(dir))) 193 if err1 != 0 { 194 goto childerror 195 } 196 } 197 198 // Pass 1: look for fd[i] < i and move those up above len(fd) 199 // so that pass 2 won't stomp on an fd it needs later. 200 if pipe < nextfd { 201 switch runtime.GOOS { 202 case "illumos", "solaris": 203 _, err1 = fcntl1(uintptr(pipe), _F_DUP2FD_CLOEXEC, uintptr(nextfd)) 204 default: 205 _, err1 = dup2child(uintptr(pipe), uintptr(nextfd)) 206 if err1 != 0 { 207 goto childerror 208 } 209 _, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC) 210 } 211 if err1 != 0 { 212 goto childerror 213 } 214 pipe = nextfd 215 nextfd++ 216 } 217 for i = 0; i < len(fd); i++ { 218 if fd[i] >= 0 && fd[i] < i { 219 if nextfd == pipe { // don't stomp on pipe 220 nextfd++ 221 } 222 switch runtime.GOOS { 223 case "illumos", "solaris": 224 _, err1 = fcntl1(uintptr(fd[i]), _F_DUP2FD_CLOEXEC, uintptr(nextfd)) 225 default: 226 _, err1 = dup2child(uintptr(fd[i]), uintptr(nextfd)) 227 if err1 != 0 { 228 goto childerror 229 } 230 _, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC) 231 } 232 if err1 != 0 { 233 goto childerror 234 } 235 fd[i] = nextfd 236 nextfd++ 237 } 238 } 239 240 // Pass 2: dup fd[i] down onto i. 241 for i = 0; i < len(fd); i++ { 242 if fd[i] == -1 { 243 closeFD(uintptr(i)) 244 continue 245 } 246 if fd[i] == i { 247 // dup2(i, i) won't clear close-on-exec flag on Linux, 248 // probably not elsewhere either. 249 _, err1 = fcntl1(uintptr(fd[i]), F_SETFD, 0) 250 if err1 != 0 { 251 goto childerror 252 } 253 continue 254 } 255 // The new fd is created NOT close-on-exec, 256 // which is exactly what we want. 257 _, err1 = dup2child(uintptr(fd[i]), uintptr(i)) 258 if err1 != 0 { 259 goto childerror 260 } 261 } 262 263 // By convention, we don't close-on-exec the fds we are 264 // started with, so if len(fd) < 3, close 0, 1, 2 as needed. 265 // Programs that know they inherit fds >= 3 will need 266 // to set them close-on-exec. 267 for i = len(fd); i < 3; i++ { 268 closeFD(uintptr(i)) 269 } 270 271 // Detach fd 0 from tty 272 if sys.Noctty { 273 err1 = ioctl(0, uintptr(TIOCNOTTY), 0) 274 if err1 != 0 { 275 goto childerror 276 } 277 } 278 279 // Set the controlling TTY to Ctty 280 if sys.Setctty { 281 // On AIX, TIOCSCTTY is undefined 282 if TIOCSCTTY == 0 { 283 err1 = ENOSYS 284 goto childerror 285 } 286 err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0) 287 if err1 != 0 { 288 goto childerror 289 } 290 } 291 292 // Time to exec. 293 err1 = execve( 294 uintptr(unsafe.Pointer(argv0)), 295 uintptr(unsafe.Pointer(&argv[0])), 296 uintptr(unsafe.Pointer(&envv[0]))) 297 298 childerror: 299 // send error code on pipe 300 write1(uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1)) 301 for { 302 exit(253) 303 } 304 }