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