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