mirror of
https://github.com/golang/sys.git
synced 2026-02-08 11:46:04 +03:00
go.sys/unix: delete Fork and Exec etc.
These are being deleted from go.sys because in general they can only be implemented in close coordination with the runtime. LGTM=dave R=rsc, dave, josharian CC=golang-codereviews https://golang.org/cl/129500043
This commit is contained in:
248
unix/exec_bsd.go
248
unix/exec_bsd.go
@@ -1,248 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type SysProcAttr struct {
|
||||
Chroot string // Chroot.
|
||||
Credential *Credential // Credential.
|
||||
Ptrace bool // Enable tracing.
|
||||
Setsid bool // Create session.
|
||||
Setpgid bool // Set process group ID to new pid (SYSV setpgrp)
|
||||
Setctty bool // Set controlling terminal to fd 0
|
||||
Noctty bool // Detach fd 0 from controlling terminal
|
||||
}
|
||||
|
||||
// Implemented in runtime package.
|
||||
func runtime_BeforeFork()
|
||||
func runtime_AfterFork()
|
||||
|
||||
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
|
||||
// If a dup or exec fails, write the errno error to pipe.
|
||||
// (Pipe is close-on-exec so if exec succeeds, it will be closed.)
|
||||
// In the child, this function must not acquire any locks, because
|
||||
// they might have been locked at the time of the fork. This means
|
||||
// no rescheduling, no malloc calls, and no new stack segments.
|
||||
// For the same reason compiler does not race instrument it.
|
||||
// The calls to RawSyscall are okay because they are assembly
|
||||
// functions that do not grow the stack.
|
||||
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err syscall.Errno) {
|
||||
// Declare all variables at top in case any
|
||||
// declarations require heap allocation (e.g., err1).
|
||||
var (
|
||||
r1, r2 uintptr
|
||||
err1 syscall.Errno
|
||||
nextfd int
|
||||
i int
|
||||
)
|
||||
|
||||
// guard against side effects of shuffling fds below.
|
||||
// Make sure that nextfd is beyond any currently open files so
|
||||
// that we can't run the risk of overwriting any of them.
|
||||
fd := make([]int, len(attr.Files))
|
||||
nextfd = len(attr.Files)
|
||||
for i, ufd := range attr.Files {
|
||||
if nextfd < int(ufd) {
|
||||
nextfd = int(ufd)
|
||||
}
|
||||
fd[i] = int(ufd)
|
||||
}
|
||||
nextfd++
|
||||
|
||||
darwin := runtime.GOOS == "darwin"
|
||||
|
||||
// About to call fork.
|
||||
// No more allocation or calls of non-assembly functions.
|
||||
runtime_BeforeFork()
|
||||
r1, r2, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
runtime_AfterFork()
|
||||
return 0, err1
|
||||
}
|
||||
|
||||
// On Darwin:
|
||||
// r1 = child pid in both parent and child.
|
||||
// r2 = 0 in parent, 1 in child.
|
||||
// Convert to normal Unix r1 = 0 in child.
|
||||
if darwin && r2 == 1 {
|
||||
r1 = 0
|
||||
}
|
||||
|
||||
if r1 != 0 {
|
||||
// parent; return PID
|
||||
runtime_AfterFork()
|
||||
return int(r1), 0
|
||||
}
|
||||
|
||||
// Fork succeeded, now in child.
|
||||
|
||||
// Enable tracing if requested.
|
||||
if sys.Ptrace {
|
||||
_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Session ID
|
||||
if sys.Setsid {
|
||||
_, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Set process group
|
||||
if sys.Setpgid {
|
||||
_, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Chroot
|
||||
if chroot != nil {
|
||||
_, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// User and groups
|
||||
if cred := sys.Credential; cred != nil {
|
||||
ngroups := uintptr(len(cred.Groups))
|
||||
groups := uintptr(0)
|
||||
if ngroups > 0 {
|
||||
groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
|
||||
}
|
||||
_, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
_, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
_, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Chdir
|
||||
if dir != nil {
|
||||
_, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 1: look for fd[i] < i and move those up above len(fd)
|
||||
// so that pass 2 won't stomp on an fd it needs later.
|
||||
if pipe < nextfd {
|
||||
_, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
|
||||
pipe = nextfd
|
||||
nextfd++
|
||||
}
|
||||
for i = 0; i < len(fd); i++ {
|
||||
if fd[i] >= 0 && fd[i] < int(i) {
|
||||
_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
|
||||
fd[i] = nextfd
|
||||
nextfd++
|
||||
if nextfd == pipe { // don't stomp on pipe
|
||||
nextfd++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2: dup fd[i] down onto i.
|
||||
for i = 0; i < len(fd); i++ {
|
||||
if fd[i] == -1 {
|
||||
RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
|
||||
continue
|
||||
}
|
||||
if fd[i] == int(i) {
|
||||
// dup2(i, i) won't clear close-on-exec flag on Linux,
|
||||
// probably not elsewhere either.
|
||||
_, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
continue
|
||||
}
|
||||
// The new fd is created NOT close-on-exec,
|
||||
// which is exactly what we want.
|
||||
_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// By convention, we don't close-on-exec the fds we are
|
||||
// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
|
||||
// Programs that know they inherit fds >= 3 will need
|
||||
// to set them close-on-exec.
|
||||
for i = len(fd); i < 3; i++ {
|
||||
RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
|
||||
}
|
||||
|
||||
// Detach fd 0 from tty
|
||||
if sys.Noctty {
|
||||
_, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Make fd 0 the tty
|
||||
if sys.Setctty {
|
||||
_, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCSCTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Time to exec.
|
||||
_, _, err1 = RawSyscall(SYS_EXECVE,
|
||||
uintptr(unsafe.Pointer(argv0)),
|
||||
uintptr(unsafe.Pointer(&argv[0])),
|
||||
uintptr(unsafe.Pointer(&envv[0])))
|
||||
|
||||
childerror:
|
||||
// send error code on pipe
|
||||
RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
|
||||
for {
|
||||
RawSyscall(SYS_EXIT, 253, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to open a pipe with O_CLOEXEC set on both file descriptors.
|
||||
func forkExecPipe(p []int) error {
|
||||
err := Pipe(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fcntl(p[0], F_SETFD, FD_CLOEXEC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
|
||||
return err
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type SysProcAttr struct {
|
||||
Chroot string // Chroot.
|
||||
Credential *Credential // Credential.
|
||||
Ptrace bool // Enable tracing.
|
||||
Setsid bool // Create session.
|
||||
Setpgid bool // Set process group ID to new pid (SYSV setpgrp)
|
||||
Setctty bool // Set controlling terminal to fd Ctty (only meaningful if Setsid is set)
|
||||
Noctty bool // Detach fd 0 from controlling terminal
|
||||
Ctty int // Controlling TTY fd (Linux only)
|
||||
Pdeathsig syscall.Signal // Signal that the process will get when its parent dies (Linux only)
|
||||
Cloneflags uintptr // Flags for clone calls (Linux only)
|
||||
}
|
||||
|
||||
// Implemented in runtime package.
|
||||
func runtime_BeforeFork()
|
||||
func runtime_AfterFork()
|
||||
|
||||
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
|
||||
// If a dup or exec fails, write the errno error to pipe.
|
||||
// (Pipe is close-on-exec so if exec succeeds, it will be closed.)
|
||||
// In the child, this function must not acquire any locks, because
|
||||
// they might have been locked at the time of the fork. This means
|
||||
// no rescheduling, no malloc calls, and no new stack segments.
|
||||
// For the same reason compiler does not race instrument it.
|
||||
// The calls to RawSyscall are okay because they are assembly
|
||||
// functions that do not grow the stack.
|
||||
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err syscall.Errno) {
|
||||
// Declare all variables at top in case any
|
||||
// declarations require heap allocation (e.g., err1).
|
||||
var (
|
||||
r1 uintptr
|
||||
err1 syscall.Errno
|
||||
nextfd int
|
||||
i int
|
||||
)
|
||||
|
||||
// Guard against side effects of shuffling fds below.
|
||||
// Make sure that nextfd is beyond any currently open files so
|
||||
// that we can't run the risk of overwriting any of them.
|
||||
fd := make([]int, len(attr.Files))
|
||||
nextfd = len(attr.Files)
|
||||
for i, ufd := range attr.Files {
|
||||
if nextfd < int(ufd) {
|
||||
nextfd = int(ufd)
|
||||
}
|
||||
fd[i] = int(ufd)
|
||||
}
|
||||
nextfd++
|
||||
|
||||
// About to call fork.
|
||||
// No more allocation or calls of non-assembly functions.
|
||||
runtime_BeforeFork()
|
||||
r1, _, err1 = RawSyscall6(SYS_CLONE, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
runtime_AfterFork()
|
||||
return 0, err1
|
||||
}
|
||||
|
||||
if r1 != 0 {
|
||||
// parent; return PID
|
||||
runtime_AfterFork()
|
||||
return int(r1), 0
|
||||
}
|
||||
|
||||
// Fork succeeded, now in child.
|
||||
|
||||
// Parent death signal
|
||||
if sys.Pdeathsig != 0 {
|
||||
_, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
// Signal self if parent is already dead. This might cause a
|
||||
// duplicate signal in rare cases, but it won't matter when
|
||||
// using SIGKILL.
|
||||
r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
|
||||
if r1 == 1 {
|
||||
pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
|
||||
_, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enable tracing if requested.
|
||||
if sys.Ptrace {
|
||||
_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Session ID
|
||||
if sys.Setsid {
|
||||
_, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Set process group
|
||||
if sys.Setpgid {
|
||||
_, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Chroot
|
||||
if chroot != nil {
|
||||
_, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// User and groups
|
||||
if cred := sys.Credential; cred != nil {
|
||||
ngroups := uintptr(len(cred.Groups))
|
||||
var groups unsafe.Pointer
|
||||
if ngroups > 0 {
|
||||
groups = unsafe.Pointer(&cred.Groups[0])
|
||||
}
|
||||
_, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, uintptr(groups), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
_, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
_, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Chdir
|
||||
if dir != nil {
|
||||
_, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 1: look for fd[i] < i and move those up above len(fd)
|
||||
// so that pass 2 won't stomp on an fd it needs later.
|
||||
if pipe < nextfd {
|
||||
_, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
|
||||
pipe = nextfd
|
||||
nextfd++
|
||||
}
|
||||
for i = 0; i < len(fd); i++ {
|
||||
if fd[i] >= 0 && fd[i] < int(i) {
|
||||
_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
|
||||
fd[i] = nextfd
|
||||
nextfd++
|
||||
if nextfd == pipe { // don't stomp on pipe
|
||||
nextfd++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2: dup fd[i] down onto i.
|
||||
for i = 0; i < len(fd); i++ {
|
||||
if fd[i] == -1 {
|
||||
RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
|
||||
continue
|
||||
}
|
||||
if fd[i] == int(i) {
|
||||
// dup2(i, i) won't clear close-on-exec flag on Linux,
|
||||
// probably not elsewhere either.
|
||||
_, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
continue
|
||||
}
|
||||
// The new fd is created NOT close-on-exec,
|
||||
// which is exactly what we want.
|
||||
_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// By convention, we don't close-on-exec the fds we are
|
||||
// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
|
||||
// Programs that know they inherit fds >= 3 will need
|
||||
// to set them close-on-exec.
|
||||
for i = len(fd); i < 3; i++ {
|
||||
RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
|
||||
}
|
||||
|
||||
// Detach fd 0 from tty
|
||||
if sys.Noctty {
|
||||
_, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Set the controlling TTY to Ctty
|
||||
if sys.Setctty && sys.Ctty >= 0 {
|
||||
_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Time to exec.
|
||||
_, _, err1 = RawSyscall(SYS_EXECVE,
|
||||
uintptr(unsafe.Pointer(argv0)),
|
||||
uintptr(unsafe.Pointer(&argv[0])),
|
||||
uintptr(unsafe.Pointer(&envv[0])))
|
||||
|
||||
childerror:
|
||||
// send error code on pipe
|
||||
RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
|
||||
for {
|
||||
RawSyscall(SYS_EXIT, 253, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to open a pipe with O_CLOEXEC set on both file descriptors.
|
||||
func forkExecPipe(p []int) (err error) {
|
||||
err = Pipe2(p, O_CLOEXEC)
|
||||
// pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it
|
||||
// might not be implemented.
|
||||
if err == ENOSYS {
|
||||
if err = Pipe(p); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,244 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type SysProcAttr struct {
|
||||
Chroot string // Chroot.
|
||||
Credential *Credential // Credential.
|
||||
Setsid bool // Create session.
|
||||
Setpgid bool // Set process group ID to new pid (SYSV setpgrp)
|
||||
Setctty bool // Set controlling terminal to fd 0
|
||||
Noctty bool // Detach fd 0 from controlling terminal
|
||||
}
|
||||
|
||||
// Implemented in runtime package.
|
||||
func runtime_BeforeFork()
|
||||
func runtime_AfterFork()
|
||||
|
||||
func chdir(path uintptr) (err syscall.Errno)
|
||||
func chroot1(path uintptr) (err syscall.Errno)
|
||||
func close(fd uintptr) (err syscall.Errno)
|
||||
func execve(path uintptr, argv uintptr, envp uintptr) (err syscall.Errno)
|
||||
func exit(code uintptr)
|
||||
func fcntl1(fd uintptr, cmd uintptr, arg uintptr) (val uintptr, err syscall.Errno)
|
||||
func forkx(flags uintptr) (pid uintptr, err syscall.Errno)
|
||||
func ioctl(fd uintptr, req uintptr, arg uintptr) (err syscall.Errno)
|
||||
func setgid(gid uintptr) (err syscall.Errno)
|
||||
func setgroups1(ngid uintptr, gid uintptr) (err syscall.Errno)
|
||||
func setsid() (pid uintptr, err syscall.Errno)
|
||||
func setuid(uid uintptr) (err syscall.Errno)
|
||||
func setpgid(pid uintptr, pgid uintptr) (err syscall.Errno)
|
||||
func write1(fd uintptr, buf uintptr, nbyte uintptr) (n uintptr, err syscall.Errno)
|
||||
|
||||
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
|
||||
// If a dup or exec fails, write the errno error to pipe.
|
||||
// (Pipe is close-on-exec so if exec succeeds, it will be closed.)
|
||||
// In the child, this function must not acquire any locks, because
|
||||
// they might have been locked at the time of the fork. This means
|
||||
// no rescheduling, no malloc calls, and no new stack segments.
|
||||
//
|
||||
// We call hand-crafted syscalls, implemented in
|
||||
// runtime/syscall_solaris.goc, rather than generated libc wrappers
|
||||
// because we need to avoid lazy-loading the functions (might malloc,
|
||||
// split the stack, or acquire mutexes). We can't call RawSyscall
|
||||
// because it's not safe even for BSD-subsystem calls.
|
||||
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err syscall.Errno) {
|
||||
// Declare all variables at top in case any
|
||||
// declarations require heap allocation (e.g., err1).
|
||||
var (
|
||||
r1 uintptr
|
||||
err1 syscall.Errno
|
||||
nextfd int
|
||||
i int
|
||||
)
|
||||
|
||||
// guard against side effects of shuffling fds below.
|
||||
// Make sure that nextfd is beyond any currently open files so
|
||||
// that we can't run the risk of overwriting any of them.
|
||||
fd := make([]int, len(attr.Files))
|
||||
nextfd = len(attr.Files)
|
||||
for i, ufd := range attr.Files {
|
||||
if nextfd < int(ufd) {
|
||||
nextfd = int(ufd)
|
||||
}
|
||||
fd[i] = int(ufd)
|
||||
}
|
||||
nextfd++
|
||||
|
||||
// About to call fork.
|
||||
// No more allocation or calls of non-assembly functions.
|
||||
runtime_BeforeFork()
|
||||
r1, err1 = forkx(0x1) // FORK_NOSIGCHLD
|
||||
if err1 != 0 {
|
||||
runtime_AfterFork()
|
||||
return 0, err1
|
||||
}
|
||||
|
||||
if r1 != 0 {
|
||||
// parent; return PID
|
||||
runtime_AfterFork()
|
||||
return int(r1), 0
|
||||
}
|
||||
|
||||
// Fork succeeded, now in child.
|
||||
|
||||
// Session ID
|
||||
if sys.Setsid {
|
||||
_, err1 = setsid()
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Set process group
|
||||
if sys.Setpgid {
|
||||
err1 = setpgid(0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Chroot
|
||||
if chroot != nil {
|
||||
err1 = chroot1(uintptr(unsafe.Pointer(chroot)))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// User and groups
|
||||
if cred := sys.Credential; cred != nil {
|
||||
ngroups := uintptr(len(cred.Groups))
|
||||
groups := uintptr(0)
|
||||
if ngroups > 0 {
|
||||
groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
|
||||
}
|
||||
err1 = setgroups1(ngroups, groups)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
err1 = setgid(uintptr(cred.Gid))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
err1 = setuid(uintptr(cred.Uid))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Chdir
|
||||
if dir != nil {
|
||||
err1 = chdir(uintptr(unsafe.Pointer(dir)))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 1: look for fd[i] < i and move those up above len(fd)
|
||||
// so that pass 2 won't stomp on an fd it needs later.
|
||||
if pipe < nextfd {
|
||||
_, err1 = fcntl1(uintptr(pipe), F_DUP2FD, uintptr(nextfd))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
|
||||
pipe = nextfd
|
||||
nextfd++
|
||||
}
|
||||
for i = 0; i < len(fd); i++ {
|
||||
if fd[i] >= 0 && fd[i] < int(i) {
|
||||
_, err1 = fcntl1(uintptr(fd[i]), F_DUP2FD, uintptr(nextfd))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
|
||||
fd[i] = nextfd
|
||||
nextfd++
|
||||
if nextfd == pipe { // don't stomp on pipe
|
||||
nextfd++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2: dup fd[i] down onto i.
|
||||
for i = 0; i < len(fd); i++ {
|
||||
if fd[i] == -1 {
|
||||
close(uintptr(i))
|
||||
continue
|
||||
}
|
||||
if fd[i] == int(i) {
|
||||
// dup2(i, i) won't clear close-on-exec flag on Linux,
|
||||
// probably not elsewhere either.
|
||||
_, err1 = fcntl1(uintptr(fd[i]), F_SETFD, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
continue
|
||||
}
|
||||
// The new fd is created NOT close-on-exec,
|
||||
// which is exactly what we want.
|
||||
_, err1 = fcntl1(uintptr(fd[i]), F_DUP2FD, uintptr(i))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// By convention, we don't close-on-exec the fds we are
|
||||
// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
|
||||
// Programs that know they inherit fds >= 3 will need
|
||||
// to set them close-on-exec.
|
||||
for i = len(fd); i < 3; i++ {
|
||||
close(uintptr(i))
|
||||
}
|
||||
|
||||
// Detach fd 0 from tty
|
||||
if sys.Noctty {
|
||||
err1 = ioctl(0, uintptr(TIOCNOTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Make fd 0 the tty
|
||||
if sys.Setctty {
|
||||
err1 = ioctl(0, uintptr(TIOCSCTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Time to exec.
|
||||
err1 = execve(
|
||||
uintptr(unsafe.Pointer(argv0)),
|
||||
uintptr(unsafe.Pointer(&argv[0])),
|
||||
uintptr(unsafe.Pointer(&envv[0])))
|
||||
|
||||
childerror:
|
||||
// send error code on pipe
|
||||
write1(uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
|
||||
for {
|
||||
exit(253)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to open a pipe with O_CLOEXEC set on both file descriptors.
|
||||
func forkExecPipe(p []int) error {
|
||||
err := Pipe(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fcntl(p[0], F_SETFD, FD_CLOEXEC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
|
||||
return err
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
// Fork, exec, wait, etc.
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Lock synchronizing creation of new file descriptors with fork.
|
||||
//
|
||||
// We want the child in a fork/exec sequence to inherit only the
|
||||
// file descriptors we intend. To do that, we mark all file
|
||||
// descriptors close-on-exec and then, in the child, explicitly
|
||||
// unmark the ones we want the exec'ed program to keep.
|
||||
// Unix doesn't make this easy: there is, in general, no way to
|
||||
// allocate a new file descriptor close-on-exec. Instead you
|
||||
// have to allocate the descriptor and then mark it close-on-exec.
|
||||
// If a fork happens between those two events, the child's exec
|
||||
// will inherit an unwanted file descriptor.
|
||||
//
|
||||
// This lock solves that race: the create new fd/mark close-on-exec
|
||||
// operation is done holding ForkLock for reading, and the fork itself
|
||||
// is done holding ForkLock for writing. At least, that's the idea.
|
||||
// There are some complications.
|
||||
//
|
||||
// Some system calls that create new file descriptors can block
|
||||
// for arbitrarily long times: open on a hung NFS server or named
|
||||
// pipe, accept on a socket, and so on. We can't reasonably grab
|
||||
// the lock across those operations.
|
||||
//
|
||||
// It is worse to inherit some file descriptors than others.
|
||||
// If a non-malicious child accidentally inherits an open ordinary file,
|
||||
// that's not a big deal. On the other hand, if a long-lived child
|
||||
// accidentally inherits the write end of a pipe, then the reader
|
||||
// of that pipe will not see EOF until that child exits, potentially
|
||||
// causing the parent program to hang. This is a common problem
|
||||
// in threaded C programs that use popen.
|
||||
//
|
||||
// Luckily, the file descriptors that are most important not to
|
||||
// inherit are not the ones that can take an arbitrarily long time
|
||||
// to create: pipe returns instantly, and the net package uses
|
||||
// non-blocking I/O to accept on a listening socket.
|
||||
// The rules for which file descriptor-creating operations use the
|
||||
// ForkLock are as follows:
|
||||
//
|
||||
// 1) Pipe. Does not block. Use the ForkLock.
|
||||
// 2) Socket. Does not block. Use the ForkLock.
|
||||
// 3) Accept. If using non-blocking mode, use the ForkLock.
|
||||
// Otherwise, live with the race.
|
||||
// 4) Open. Can block. Use O_CLOEXEC if available (Linux).
|
||||
// Otherwise, live with the race.
|
||||
// 5) Dup. Does not block. Use the ForkLock.
|
||||
// On Linux, could use fcntl F_DUPFD_CLOEXEC
|
||||
// instead of the ForkLock, but only for dup(fd, -1).
|
||||
|
||||
var ForkLock sync.RWMutex
|
||||
|
||||
// SlicePtrFromStrings converts a slice of strings to a slice of
|
||||
// pointers to NUL-terminated byte slices. If any string contains
|
||||
// a NUL byte, it returns (nil, EINVAL).
|
||||
func SlicePtrFromStrings(ss []string) ([]*byte, error) {
|
||||
var err error
|
||||
bb := make([]*byte, len(ss)+1)
|
||||
for i := 0; i < len(ss); i++ {
|
||||
bb[i], err = BytePtrFromString(ss[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
bb[len(ss)] = nil
|
||||
return bb, nil
|
||||
}
|
||||
|
||||
func CloseOnExec(fd int) { fcntl(fd, F_SETFD, FD_CLOEXEC) }
|
||||
|
||||
func SetNonblock(fd int, nonblocking bool) (err error) {
|
||||
flag, err := fcntl(fd, F_GETFL, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if nonblocking {
|
||||
flag |= O_NONBLOCK
|
||||
} else {
|
||||
flag &= ^O_NONBLOCK
|
||||
}
|
||||
_, err = fcntl(fd, F_SETFL, flag)
|
||||
return err
|
||||
}
|
||||
|
||||
// Credential holds user and group identities to be assumed
|
||||
// by a child process started by StartProcess.
|
||||
type Credential struct {
|
||||
Uid uint32 // User ID.
|
||||
Gid uint32 // Group ID.
|
||||
Groups []uint32 // Supplementary group IDs.
|
||||
}
|
||||
|
||||
// ProcAttr holds attributes that will be applied to a new process started
|
||||
// by StartProcess.
|
||||
type ProcAttr struct {
|
||||
Dir string // Current working directory.
|
||||
Env []string // Environment.
|
||||
Files []uintptr // File descriptors.
|
||||
Sys *SysProcAttr
|
||||
}
|
||||
|
||||
var zeroProcAttr ProcAttr
|
||||
var zeroSysProcAttr SysProcAttr
|
||||
|
||||
func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
|
||||
var p [2]int
|
||||
var n int
|
||||
var err1 syscall.Errno
|
||||
var wstatus WaitStatus
|
||||
|
||||
if attr == nil {
|
||||
attr = &zeroProcAttr
|
||||
}
|
||||
sys := attr.Sys
|
||||
if sys == nil {
|
||||
sys = &zeroSysProcAttr
|
||||
}
|
||||
|
||||
p[0] = -1
|
||||
p[1] = -1
|
||||
|
||||
// Convert args to C form.
|
||||
argv0p, err := BytePtrFromString(argv0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
argvp, err := SlicePtrFromStrings(argv)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
envvp, err := SlicePtrFromStrings(attr.Env)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if (runtime.GOOS == "freebsd" || runtime.GOOS == "dragonfly") && len(argv[0]) > len(argv0) {
|
||||
argvp[0] = argv0p
|
||||
}
|
||||
|
||||
var chroot *byte
|
||||
if sys.Chroot != "" {
|
||||
chroot, err = BytePtrFromString(sys.Chroot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
var dir *byte
|
||||
if attr.Dir != "" {
|
||||
dir, err = BytePtrFromString(attr.Dir)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire the fork lock so that no other threads
|
||||
// create new fds that are not yet close-on-exec
|
||||
// before we fork.
|
||||
ForkLock.Lock()
|
||||
|
||||
// Allocate child status pipe close on exec.
|
||||
if err = forkExecPipe(p[:]); err != nil {
|
||||
goto error
|
||||
}
|
||||
|
||||
// Kick off child.
|
||||
pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1])
|
||||
if err1 != 0 {
|
||||
err = syscall.Errno(err1)
|
||||
goto error
|
||||
}
|
||||
ForkLock.Unlock()
|
||||
|
||||
// Read child error status from pipe.
|
||||
Close(p[1])
|
||||
n, err = readlen(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1)))
|
||||
Close(p[0])
|
||||
if err != nil || n != 0 {
|
||||
if n == int(unsafe.Sizeof(err1)) {
|
||||
err = syscall.Errno(err1)
|
||||
}
|
||||
if err == nil {
|
||||
err = EPIPE
|
||||
}
|
||||
|
||||
// Child failed; wait for it to exit, to make sure
|
||||
// the zombies don't accumulate.
|
||||
_, err1 := Wait4(pid, &wstatus, 0, nil)
|
||||
for err1 == EINTR {
|
||||
_, err1 = Wait4(pid, &wstatus, 0, nil)
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Read got EOF, so pipe closed on exec, so exec succeeded.
|
||||
return pid, nil
|
||||
|
||||
error:
|
||||
if p[0] >= 0 {
|
||||
Close(p[0])
|
||||
Close(p[1])
|
||||
}
|
||||
ForkLock.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Combination of fork and exec, careful to be thread safe.
|
||||
func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
|
||||
return forkExec(argv0, argv, attr)
|
||||
}
|
||||
|
||||
// StartProcess wraps ForkExec for package os.
|
||||
func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
|
||||
pid, err = forkExec(argv0, argv, attr)
|
||||
return pid, 0, err
|
||||
}
|
||||
|
||||
// Ordinary exec.
|
||||
func Exec(argv0 string, argv []string, envv []string) (err error) {
|
||||
argv0p, err := BytePtrFromString(argv0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
argvp, err := SlicePtrFromStrings(argv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envvp, err := SlicePtrFromStrings(envv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, err1 := RawSyscall(SYS_EXECVE,
|
||||
uintptr(unsafe.Pointer(argv0p)),
|
||||
uintptr(unsafe.Pointer(&argvp[0])),
|
||||
uintptr(unsafe.Pointer(&envvp[0])))
|
||||
return syscall.Errno(err1)
|
||||
}
|
||||
@@ -255,3 +255,19 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
||||
}
|
||||
|
||||
var ioSync int64
|
||||
|
||||
func CloseOnExec(fd int) { fcntl(fd, F_SETFD, FD_CLOEXEC) }
|
||||
|
||||
func SetNonblock(fd int, nonblocking bool) (err error) {
|
||||
flag, err := fcntl(fd, F_GETFL, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if nonblocking {
|
||||
flag |= O_NONBLOCK
|
||||
} else {
|
||||
flag &= ^O_NONBLOCK
|
||||
}
|
||||
_, err = fcntl(fd, F_SETFL, flag)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user