From 866b9d8a482812a94f618dbf0b132e98907c261a Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Thu, 21 Aug 2014 16:14:51 -0700 Subject: [PATCH] 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 --- unix/exec_bsd.go | 248 ---------------------------------------- unix/exec_linux.go | 263 ------------------------------------------- unix/exec_solaris.go | 244 --------------------------------------- unix/exec_unix.go | 250 ---------------------------------------- unix/syscall_unix.go | 16 +++ 5 files changed, 16 insertions(+), 1005 deletions(-) delete mode 100644 unix/exec_bsd.go delete mode 100644 unix/exec_linux.go delete mode 100644 unix/exec_solaris.go delete mode 100644 unix/exec_unix.go diff --git a/unix/exec_bsd.go b/unix/exec_bsd.go deleted file mode 100644 index f4b0a6ca..00000000 --- a/unix/exec_bsd.go +++ /dev/null @@ -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 -} diff --git a/unix/exec_linux.go b/unix/exec_linux.go deleted file mode 100644 index c0629234..00000000 --- a/unix/exec_linux.go +++ /dev/null @@ -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 -} diff --git a/unix/exec_solaris.go b/unix/exec_solaris.go deleted file mode 100644 index f2ba9b19..00000000 --- a/unix/exec_solaris.go +++ /dev/null @@ -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 -} diff --git a/unix/exec_unix.go b/unix/exec_unix.go deleted file mode 100644 index 9f0fba37..00000000 --- a/unix/exec_unix.go +++ /dev/null @@ -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) -} diff --git a/unix/syscall_unix.go b/unix/syscall_unix.go index c18d4414..70b65f44 100644 --- a/unix/syscall_unix.go +++ b/unix/syscall_unix.go @@ -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 +}