Source file src/pkg/syscall/exec_linux.go
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 linux
6
7 package syscall
8
9 import (
10 "unsafe"
11 )
12
13 type SysProcAttr struct {
14 Chroot string // Chroot.
15 Credential *Credential // Credential.
16 Ptrace bool // Enable tracing.
17 Setsid bool // Create session.
18 Setpgid bool // Set process group ID to new pid (SYSV setpgrp)
19 Setctty bool // Set controlling terminal to fd 0
20 Noctty bool // Detach fd 0 from controlling terminal
21 Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only)
22 }
23
24 // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
25 // If a dup or exec fails, write the errno error to pipe.
26 // (Pipe is close-on-exec so if exec succeeds, it will be closed.)
27 // In the child, this function must not acquire any locks, because
28 // they might have been locked at the time of the fork. This means
29 // no rescheduling, no malloc calls, and no new stack segments.
30 // The calls to RawSyscall are okay because they are assembly
31 // functions that do not grow the stack.
32 func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
33 // Declare all variables at top in case any
34 // declarations require heap allocation (e.g., err1).
35 var (
36 r1 uintptr
37 err1 Errno
38 nextfd int
39 i int
40 )
41
42 // guard against side effects of shuffling fds below.
43 fd := make([]int, len(attr.Files))
44 for i, ufd := range attr.Files {
45 fd[i] = int(ufd)
46 }
47
48 // About to call fork.
49 // No more allocation or calls of non-assembly functions.
50 r1, _, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
51 if err1 != 0 {
52 return 0, err1
53 }
54
55 if r1 != 0 {
56 // parent; return PID
57 return int(r1), 0
58 }
59
60 // Fork succeeded, now in child.
61
62 // Parent death signal
63 if sys.Pdeathsig != 0 {
64 _, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0)
65 if err1 != 0 {
66 goto childerror
67 }
68
69 // Signal self if parent is already dead. This might cause a
70 // duplicate signal in rare cases, but it won't matter when
71 // using SIGKILL.
72 r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
73 if r1 == 1 {
74 pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
75 _, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
76 if err1 != 0 {
77 goto childerror
78 }
79 }
80 }
81
82 // Enable tracing if requested.
83 if sys.Ptrace {
84 _, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
85 if err1 != 0 {
86 goto childerror
87 }
88 }
89
90 // Session ID
91 if sys.Setsid {
92 _, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
93 if err1 != 0 {
94 goto childerror
95 }
96 }
97
98 // Set process group
99 if sys.Setpgid {
100 _, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
101 if err1 != 0 {
102 goto childerror
103 }
104 }
105
106 // Chroot
107 if chroot != nil {
108 _, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
109 if err1 != 0 {
110 goto childerror
111 }
112 }
113
114 // User and groups
115 if cred := sys.Credential; cred != nil {
116 ngroups := uintptr(len(cred.Groups))
117 groups := uintptr(0)
118 if ngroups > 0 {
119 groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
120 }
121 _, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0)
122 if err1 != 0 {
123 goto childerror
124 }
125 _, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0)
126 if err1 != 0 {
127 goto childerror
128 }
129 _, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0)
130 if err1 != 0 {
131 goto childerror
132 }
133 }
134
135 // Chdir
136 if dir != nil {
137 _, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
138 if err1 != 0 {
139 goto childerror
140 }
141 }
142
143 // Pass 1: look for fd[i] < i and move those up above len(fd)
144 // so that pass 2 won't stomp on an fd it needs later.
145 nextfd = int(len(fd))
146 if pipe < nextfd {
147 _, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
148 if err1 != 0 {
149 goto childerror
150 }
151 RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
152 pipe = nextfd
153 nextfd++
154 }
155 for i = 0; i < len(fd); i++ {
156 if fd[i] >= 0 && fd[i] < int(i) {
157 _, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
158 if err1 != 0 {
159 goto childerror
160 }
161 RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
162 fd[i] = nextfd
163 nextfd++
164 if nextfd == pipe { // don't stomp on pipe
165 nextfd++
166 }
167 }
168 }
169
170 // Pass 2: dup fd[i] down onto i.
171 for i = 0; i < len(fd); i++ {
172 if fd[i] == -1 {
173 RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
174 continue
175 }
176 if fd[i] == int(i) {
177 // dup2(i, i) won't clear close-on-exec flag on Linux,
178 // probably not elsewhere either.
179 _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0)
180 if err1 != 0 {
181 goto childerror
182 }
183 continue
184 }
185 // The new fd is created NOT close-on-exec,
186 // which is exactly what we want.
187 _, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0)
188 if err1 != 0 {
189 goto childerror
190 }
191 }
192
193 // By convention, we don't close-on-exec the fds we are
194 // started with, so if len(fd) < 3, close 0, 1, 2 as needed.
195 // Programs that know they inherit fds >= 3 will need
196 // to set them close-on-exec.
197 for i = len(fd); i < 3; i++ {
198 RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
199 }
200
201 // Detach fd 0 from tty
202 if sys.Noctty {
203 _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0)
204 if err1 != 0 {
205 goto childerror
206 }
207 }
208
209 // Make fd 0 the tty
210 if sys.Setctty {
211 _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCSCTTY), 0)
212 if err1 != 0 {
213 goto childerror
214 }
215 }
216
217 // Time to exec.
218 _, _, err1 = RawSyscall(SYS_EXECVE,
219 uintptr(unsafe.Pointer(argv0)),
220 uintptr(unsafe.Pointer(&argv[0])),
221 uintptr(unsafe.Pointer(&envv[0])))
222
223 childerror:
224 // send error code on pipe
225 RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
226 for {
227 RawSyscall(SYS_EXIT, 253, 0, 0)
228 }
229
230 // Calling panic is not actually safe,
231 // but the for loop above won't break
232 // and this shuts up the compiler.
233 panic("unreached")
234 }