Home | History | Annotate | Download | only in syscall
      1 // Copyright 2011 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build darwin dragonfly freebsd netbsd openbsd
      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    bool        // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
     20 	Setctty    bool        // Set controlling terminal to fd Ctty
     21 	Noctty     bool        // Detach fd 0 from controlling terminal
     22 	Ctty       int         // Controlling TTY fd
     23 	Foreground bool        // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY)
     24 	Pgid       int         // Child's process group ID if Setpgid.
     25 }
     26 
     27 // Implemented in runtime package.
     28 func runtime_BeforeFork()
     29 func runtime_AfterFork()
     30 
     31 // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
     32 // If a dup or exec fails, write the errno error to pipe.
     33 // (Pipe is close-on-exec so if exec succeeds, it will be closed.)
     34 // In the child, this function must not acquire any locks, because
     35 // they might have been locked at the time of the fork.  This means
     36 // no rescheduling, no malloc calls, and no new stack segments.
     37 // For the same reason compiler does not race instrument it.
     38 // The calls to RawSyscall are okay because they are assembly
     39 // functions that do not grow the stack.
     40 func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
     41 	// Declare all variables at top in case any
     42 	// declarations require heap allocation (e.g., err1).
     43 	var (
     44 		r1, r2 uintptr
     45 		err1   Errno
     46 		nextfd int
     47 		i      int
     48 	)
     49 
     50 	// guard against side effects of shuffling fds below.
     51 	// Make sure that nextfd is beyond any currently open files so
     52 	// that we can't run the risk of overwriting any of them.
     53 	fd := make([]int, len(attr.Files))
     54 	nextfd = len(attr.Files)
     55 	for i, ufd := range attr.Files {
     56 		if nextfd < int(ufd) {
     57 			nextfd = int(ufd)
     58 		}
     59 		fd[i] = int(ufd)
     60 	}
     61 	nextfd++
     62 
     63 	darwin := runtime.GOOS == "darwin"
     64 
     65 	// About to call fork.
     66 	// No more allocation or calls of non-assembly functions.
     67 	runtime_BeforeFork()
     68 	r1, r2, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
     69 	if err1 != 0 {
     70 		runtime_AfterFork()
     71 		return 0, err1
     72 	}
     73 
     74 	// On Darwin:
     75 	//	r1 = child pid in both parent and child.
     76 	//	r2 = 0 in parent, 1 in child.
     77 	// Convert to normal Unix r1 = 0 in child.
     78 	if darwin && r2 == 1 {
     79 		r1 = 0
     80 	}
     81 
     82 	if r1 != 0 {
     83 		// parent; return PID
     84 		runtime_AfterFork()
     85 		return int(r1), 0
     86 	}
     87 
     88 	// Fork succeeded, now in child.
     89 
     90 	// Enable tracing if requested.
     91 	if sys.Ptrace {
     92 		_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
     93 		if err1 != 0 {
     94 			goto childerror
     95 		}
     96 	}
     97 
     98 	// Session ID
     99 	if sys.Setsid {
    100 		_, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
    101 		if err1 != 0 {
    102 			goto childerror
    103 		}
    104 	}
    105 
    106 	// Set process group
    107 	if sys.Setpgid || sys.Foreground {
    108 		// Place child in process group.
    109 		_, _, err1 = RawSyscall(SYS_SETPGID, 0, uintptr(sys.Pgid), 0)
    110 		if err1 != 0 {
    111 			goto childerror
    112 		}
    113 	}
    114 
    115 	if sys.Foreground {
    116 		pgrp := sys.Pgid
    117 		if pgrp == 0 {
    118 			r1, _, err1 = RawSyscall(SYS_GETPID, 0, 0, 0)
    119 			if err1 != 0 {
    120 				goto childerror
    121 			}
    122 
    123 			pgrp = int(r1)
    124 		}
    125 
    126 		// Place process group in foreground.
    127 		_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
    128 		if err1 != 0 {
    129 			goto childerror
    130 		}
    131 	}
    132 
    133 	// Chroot
    134 	if chroot != nil {
    135 		_, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
    136 		if err1 != 0 {
    137 			goto childerror
    138 		}
    139 	}
    140 
    141 	// User and groups
    142 	if cred := sys.Credential; cred != nil {
    143 		ngroups := uintptr(len(cred.Groups))
    144 		groups := uintptr(0)
    145 		if ngroups > 0 {
    146 			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
    147 		}
    148 		_, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0)
    149 		if err1 != 0 {
    150 			goto childerror
    151 		}
    152 		_, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0)
    153 		if err1 != 0 {
    154 			goto childerror
    155 		}
    156 		_, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0)
    157 		if err1 != 0 {
    158 			goto childerror
    159 		}
    160 	}
    161 
    162 	// Chdir
    163 	if dir != nil {
    164 		_, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
    165 		if err1 != 0 {
    166 			goto childerror
    167 		}
    168 	}
    169 
    170 	// Pass 1: look for fd[i] < i and move those up above len(fd)
    171 	// so that pass 2 won't stomp on an fd it needs later.
    172 	if pipe < nextfd {
    173 		_, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
    174 		if err1 != 0 {
    175 			goto childerror
    176 		}
    177 		RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
    178 		pipe = nextfd
    179 		nextfd++
    180 	}
    181 	for i = 0; i < len(fd); i++ {
    182 		if fd[i] >= 0 && fd[i] < int(i) {
    183 			_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
    184 			if err1 != 0 {
    185 				goto childerror
    186 			}
    187 			RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
    188 			fd[i] = nextfd
    189 			nextfd++
    190 			if nextfd == pipe { // don't stomp on pipe
    191 				nextfd++
    192 			}
    193 		}
    194 	}
    195 
    196 	// Pass 2: dup fd[i] down onto i.
    197 	for i = 0; i < len(fd); i++ {
    198 		if fd[i] == -1 {
    199 			RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
    200 			continue
    201 		}
    202 		if fd[i] == int(i) {
    203 			// dup2(i, i) won't clear close-on-exec flag on Linux,
    204 			// probably not elsewhere either.
    205 			_, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0)
    206 			if err1 != 0 {
    207 				goto childerror
    208 			}
    209 			continue
    210 		}
    211 		// The new fd is created NOT close-on-exec,
    212 		// which is exactly what we want.
    213 		_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0)
    214 		if err1 != 0 {
    215 			goto childerror
    216 		}
    217 	}
    218 
    219 	// By convention, we don't close-on-exec the fds we are
    220 	// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
    221 	// Programs that know they inherit fds >= 3 will need
    222 	// to set them close-on-exec.
    223 	for i = len(fd); i < 3; i++ {
    224 		RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
    225 	}
    226 
    227 	// Detach fd 0 from tty
    228 	if sys.Noctty {
    229 		_, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0)
    230 		if err1 != 0 {
    231 			goto childerror
    232 		}
    233 	}
    234 
    235 	// Set the controlling TTY to Ctty
    236 	if sys.Setctty {
    237 		_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
    238 		if err1 != 0 {
    239 			goto childerror
    240 		}
    241 	}
    242 
    243 	// Time to exec.
    244 	_, _, err1 = RawSyscall(SYS_EXECVE,
    245 		uintptr(unsafe.Pointer(argv0)),
    246 		uintptr(unsafe.Pointer(&argv[0])),
    247 		uintptr(unsafe.Pointer(&envv[0])))
    248 
    249 childerror:
    250 	// send error code on pipe
    251 	RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
    252 	for {
    253 		RawSyscall(SYS_EXIT, 253, 0, 0)
    254 	}
    255 }
    256 
    257 // Try to open a pipe with O_CLOEXEC set on both file descriptors.
    258 func forkExecPipe(p []int) error {
    259 	err := Pipe(p)
    260 	if err != nil {
    261 		return err
    262 	}
    263 	_, err = fcntl(p[0], F_SETFD, FD_CLOEXEC)
    264 	if err != nil {
    265 		return err
    266 	}
    267 	_, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
    268 	return err
    269 }
    270