]> Cypherpunks.ru repositories - gostls13.git/blob - src/syscall/exec_bsd.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / src / syscall / exec_bsd.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 //go:build dragonfly || netbsd || (openbsd && mips64)
6
7 package syscall
8
9 import (
10         "runtime"
11         "unsafe"
12 )
13
14 type SysProcAttr struct {
15         Chroot     string      // Chroot.
16         Credential *Credential // Credential.
17         Ptrace     bool        // Enable tracing.
18         Setsid     bool        // Create session.
19         // Setpgid sets the process group ID of the child to Pgid,
20         // or, if Pgid == 0, to the new child's process ID.
21         Setpgid bool
22         // Setctty sets the controlling terminal of the child to
23         // file descriptor Ctty. Ctty must be a descriptor number
24         // in the child process: an index into ProcAttr.Files.
25         // This is only meaningful if Setsid is true.
26         Setctty bool
27         Noctty  bool // Detach fd 0 from controlling terminal
28         Ctty    int  // Controlling TTY fd
29         // Foreground places the child process group in the foreground.
30         // This implies Setpgid. The Ctty field must be set to
31         // the descriptor of the controlling TTY.
32         // Unlike Setctty, in this case Ctty must be a descriptor
33         // number in the parent process.
34         Foreground bool
35         Pgid       int // Child's process group ID if Setpgid.
36 }
37
38 // Implemented in runtime package.
39 func runtime_BeforeFork()
40 func runtime_AfterFork()
41 func runtime_AfterForkInChild()
42
43 // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
44 // If a dup or exec fails, write the errno error to pipe.
45 // (Pipe is close-on-exec so if exec succeeds, it will be closed.)
46 // In the child, this function must not acquire any locks, because
47 // they might have been locked at the time of the fork. This means
48 // no rescheduling, no malloc calls, and no new stack segments.
49 // For the same reason compiler does not race instrument it.
50 // The calls to RawSyscall are okay because they are assembly
51 // functions that do not grow the stack.
52 //
53 //go:norace
54 func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
55         // Declare all variables at top in case any
56         // declarations require heap allocation (e.g., err1).
57         var (
58                 r1              uintptr
59                 err1            Errno
60                 nextfd          int
61                 i               int
62                 pgrp            _C_int
63                 cred            *Credential
64                 ngroups, groups uintptr
65         )
66
67         rlim := origRlimitNofile.Load()
68
69         // guard against side effects of shuffling fds below.
70         // Make sure that nextfd is beyond any currently open files so
71         // that we can't run the risk of overwriting any of them.
72         fd := make([]int, len(attr.Files))
73         nextfd = len(attr.Files)
74         for i, ufd := range attr.Files {
75                 if nextfd < int(ufd) {
76                         nextfd = int(ufd)
77                 }
78                 fd[i] = int(ufd)
79         }
80         nextfd++
81
82         // About to call fork.
83         // No more allocation or calls of non-assembly functions.
84         runtime_BeforeFork()
85         r1, _, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
86         if err1 != 0 {
87                 runtime_AfterFork()
88                 return 0, err1
89         }
90
91         if r1 != 0 {
92                 // parent; return PID
93                 runtime_AfterFork()
94                 return int(r1), 0
95         }
96
97         // Fork succeeded, now in child.
98
99         // Enable tracing if requested.
100         if sys.Ptrace {
101                 _, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
102                 if err1 != 0 {
103                         goto childerror
104                 }
105         }
106
107         // Session ID
108         if sys.Setsid {
109                 _, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
110                 if err1 != 0 {
111                         goto childerror
112                 }
113         }
114
115         // Set process group
116         if sys.Setpgid || sys.Foreground {
117                 // Place child in process group.
118                 _, _, err1 = RawSyscall(SYS_SETPGID, 0, uintptr(sys.Pgid), 0)
119                 if err1 != 0 {
120                         goto childerror
121                 }
122         }
123
124         if sys.Foreground {
125                 // This should really be pid_t, however _C_int (aka int32) is
126                 // generally equivalent.
127                 pgrp = _C_int(sys.Pgid)
128                 if pgrp == 0 {
129                         r1, _, err1 = RawSyscall(SYS_GETPID, 0, 0, 0)
130                         if err1 != 0 {
131                                 goto childerror
132                         }
133
134                         pgrp = _C_int(r1)
135                 }
136
137                 // Place process group in foreground.
138                 _, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
139                 if err1 != 0 {
140                         goto childerror
141                 }
142         }
143
144         // Restore the signal mask. We do this after TIOCSPGRP to avoid
145         // having the kernel send a SIGTTOU signal to the process group.
146         runtime_AfterForkInChild()
147
148         // Chroot
149         if chroot != nil {
150                 _, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
151                 if err1 != 0 {
152                         goto childerror
153                 }
154         }
155
156         // User and groups
157         if cred = sys.Credential; cred != nil {
158                 ngroups = uintptr(len(cred.Groups))
159                 groups = uintptr(0)
160                 if ngroups > 0 {
161                         groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
162                 }
163                 if !cred.NoSetGroups {
164                         _, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0)
165                         if err1 != 0 {
166                                 goto childerror
167                         }
168                 }
169                 _, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0)
170                 if err1 != 0 {
171                         goto childerror
172                 }
173                 _, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0)
174                 if err1 != 0 {
175                         goto childerror
176                 }
177         }
178
179         // Chdir
180         if dir != nil {
181                 _, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
182                 if err1 != 0 {
183                         goto childerror
184                 }
185         }
186
187         // Pass 1: look for fd[i] < i and move those up above len(fd)
188         // so that pass 2 won't stomp on an fd it needs later.
189         if pipe < nextfd {
190                 if runtime.GOOS == "netbsd" || (runtime.GOOS == "openbsd" && runtime.GOARCH == "mips64") {
191                         _, _, err1 = RawSyscall(_SYS_DUP3, uintptr(pipe), uintptr(nextfd), O_CLOEXEC)
192                 } else if runtime.GOOS == "dragonfly" {
193                         _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(pipe), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
194                 } else {
195                         _, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
196                         if err1 != 0 {
197                                 goto childerror
198                         }
199                         _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
200                 }
201                 if err1 != 0 {
202                         goto childerror
203                 }
204                 pipe = nextfd
205                 nextfd++
206         }
207         for i = 0; i < len(fd); i++ {
208                 if fd[i] >= 0 && fd[i] < i {
209                         if nextfd == pipe { // don't stomp on pipe
210                                 nextfd++
211                         }
212                         if runtime.GOOS == "netbsd" || (runtime.GOOS == "openbsd" && runtime.GOARCH == "mips64") {
213                                 _, _, err1 = RawSyscall(_SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC)
214                         } else if runtime.GOOS == "dragonfly" {
215                                 _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
216                         } else {
217                                 _, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
218                                 if err1 != 0 {
219                                         goto childerror
220                                 }
221                                 _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
222                         }
223                         if err1 != 0 {
224                                 goto childerror
225                         }
226                         fd[i] = nextfd
227                         nextfd++
228                 }
229         }
230
231         // Pass 2: dup fd[i] down onto i.
232         for i = 0; i < len(fd); i++ {
233                 if fd[i] == -1 {
234                         RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
235                         continue
236                 }
237                 if fd[i] == i {
238                         // dup2(i, i) won't clear close-on-exec flag on Linux,
239                         // probably not elsewhere either.
240                         _, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0)
241                         if err1 != 0 {
242                                 goto childerror
243                         }
244                         continue
245                 }
246                 // The new fd is created NOT close-on-exec,
247                 // which is exactly what we want.
248                 _, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0)
249                 if err1 != 0 {
250                         goto childerror
251                 }
252         }
253
254         // By convention, we don't close-on-exec the fds we are
255         // started with, so if len(fd) < 3, close 0, 1, 2 as needed.
256         // Programs that know they inherit fds >= 3 will need
257         // to set them close-on-exec.
258         for i = len(fd); i < 3; i++ {
259                 RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
260         }
261
262         // Detach fd 0 from tty
263         if sys.Noctty {
264                 _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0)
265                 if err1 != 0 {
266                         goto childerror
267                 }
268         }
269
270         // Set the controlling TTY to Ctty
271         if sys.Setctty {
272                 _, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
273                 if err1 != 0 {
274                         goto childerror
275                 }
276         }
277
278         // Restore original rlimit.
279         if rlim != nil {
280                 RawSyscall(SYS_SETRLIMIT, uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(rlim)), 0)
281         }
282
283         // Time to exec.
284         _, _, err1 = RawSyscall(SYS_EXECVE,
285                 uintptr(unsafe.Pointer(argv0)),
286                 uintptr(unsafe.Pointer(&argv[0])),
287                 uintptr(unsafe.Pointer(&envv[0])))
288
289 childerror:
290         // send error code on pipe
291         RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
292         for {
293                 RawSyscall(SYS_EXIT, 253, 0, 0)
294         }
295 }