Przeglądaj źródła

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

Rob Pike 11 lat temu
rodzic
commit
866b9d8a48
5 zmienionych plików z 16 dodań i 1005 usunięć
  1. 0 248
      unix/exec_bsd.go
  2. 0 263
      unix/exec_linux.go
  3. 0 244
      unix/exec_solaris.go
  4. 0 250
      unix/exec_unix.go
  5. 16 0
      unix/syscall_unix.go

+ 0 - 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
-}

+ 0 - 263
unix/exec_linux.go

@@ -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
-}

+ 0 - 244
unix/exec_solaris.go

@@ -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
-}

+ 0 - 250
unix/exec_unix.go

@@ -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)
-}

+ 16 - 0
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
+}