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 package syscall
      6 
      7 import (
      8 	"unsafe"
      9 )
     10 
     11 type SysProcAttr struct {
     12 	Chroot     string      // Chroot.
     13 	Credential *Credential // Credential.
     14 	Setsid     bool        // Create session.
     15 	Setpgid    bool        // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
     16 	Setctty    bool        // Set controlling terminal to fd Ctty
     17 	Noctty     bool        // Detach fd 0 from controlling terminal
     18 	Ctty       int         // Controlling TTY fd
     19 	Foreground bool        // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY)
     20 	Pgid       int         // Child's process group ID if Setpgid.
     21 }
     22 
     23 // Implemented in runtime package.
     24 func runtime_BeforeFork()
     25 func runtime_AfterFork()
     26 
     27 func chdir(path uintptr) (err Errno)
     28 func chroot1(path uintptr) (err Errno)
     29 func close(fd uintptr) (err Errno)
     30 func execve(path uintptr, argv uintptr, envp uintptr) (err Errno)
     31 func exit(code uintptr)
     32 func fcntl1(fd uintptr, cmd uintptr, arg uintptr) (val uintptr, err Errno)
     33 func forkx(flags uintptr) (pid uintptr, err Errno)
     34 func getpid() (pid uintptr, err Errno)
     35 func ioctl(fd uintptr, req uintptr, arg uintptr) (err Errno)
     36 func setgid(gid uintptr) (err Errno)
     37 func setgroups1(ngid uintptr, gid uintptr) (err Errno)
     38 func setsid() (pid uintptr, err Errno)
     39 func setuid(uid uintptr) (err Errno)
     40 func setpgid(pid uintptr, pgid uintptr) (err Errno)
     41 func write1(fd uintptr, buf uintptr, nbyte uintptr) (n uintptr, err Errno)
     42 
     43 // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
     44 // If a dup or exec fails, write the errno error to pipe.
     45 // (Pipe is close-on-exec so if exec succeeds, it will be closed.)
     46 // In the child, this function must not acquire any locks, because
     47 // they might have been locked at the time of the fork.  This means
     48 // no rescheduling, no malloc calls, and no new stack segments.
     49 //
     50 // We call hand-crafted syscalls, implemented in
     51 // ../runtime/syscall_solaris.go, rather than generated libc wrappers
     52 // because we need to avoid lazy-loading the functions (might malloc,
     53 // split the stack, or acquire mutexes). We can't call RawSyscall
     54 // because it's not safe even for BSD-subsystem calls.
     55 func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
     56 	// Declare all variables at top in case any
     57 	// declarations require heap allocation (e.g., err1).
     58 	var (
     59 		r1     uintptr
     60 		err1   Errno
     61 		nextfd int
     62 		i      int
     63 	)
     64 
     65 	// guard against side effects of shuffling fds below.
     66 	// Make sure that nextfd is beyond any currently open files so
     67 	// that we can't run the risk of overwriting any of them.
     68 	fd := make([]int, len(attr.Files))
     69 	nextfd = len(attr.Files)
     70 	for i, ufd := range attr.Files {
     71 		if nextfd < int(ufd) {
     72 			nextfd = int(ufd)
     73 		}
     74 		fd[i] = int(ufd)
     75 	}
     76 	nextfd++
     77 
     78 	// About to call fork.
     79 	// No more allocation or calls of non-assembly functions.
     80 	runtime_BeforeFork()
     81 	r1, err1 = forkx(0x1) // FORK_NOSIGCHLD
     82 	if err1 != 0 {
     83 		runtime_AfterFork()
     84 		return 0, err1
     85 	}
     86 
     87 	if r1 != 0 {
     88 		// parent; return PID
     89 		runtime_AfterFork()
     90 		return int(r1), 0
     91 	}
     92 
     93 	// Fork succeeded, now in child.
     94 
     95 	// Session ID
     96 	if sys.Setsid {
     97 		_, err1 = setsid()
     98 		if err1 != 0 {
     99 			goto childerror
    100 		}
    101 	}
    102 
    103 	// Set process group
    104 	if sys.Setpgid || sys.Foreground {
    105 		// Place child in process group.
    106 		err1 = setpgid(0, uintptr(sys.Pgid))
    107 		if err1 != 0 {
    108 			goto childerror
    109 		}
    110 	}
    111 
    112 	if sys.Foreground {
    113 		pgrp := sys.Pgid
    114 		if pgrp == 0 {
    115 			r1, err1 = getpid()
    116 			if err1 != 0 {
    117 				goto childerror
    118 			}
    119 
    120 			pgrp = int(r1)
    121 		}
    122 
    123 		// Place process group in foreground.
    124 		err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
    125 		if err1 != 0 {
    126 			goto childerror
    127 		}
    128 	}
    129 
    130 	// Chroot
    131 	if chroot != nil {
    132 		err1 = chroot1(uintptr(unsafe.Pointer(chroot)))
    133 		if err1 != 0 {
    134 			goto childerror
    135 		}
    136 	}
    137 
    138 	// User and groups
    139 	if cred := sys.Credential; cred != nil {
    140 		ngroups := uintptr(len(cred.Groups))
    141 		groups := uintptr(0)
    142 		if ngroups > 0 {
    143 			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
    144 		}
    145 		err1 = setgroups1(ngroups, groups)
    146 		if err1 != 0 {
    147 			goto childerror
    148 		}
    149 		err1 = setgid(uintptr(cred.Gid))
    150 		if err1 != 0 {
    151 			goto childerror
    152 		}
    153 		err1 = setuid(uintptr(cred.Uid))
    154 		if err1 != 0 {
    155 			goto childerror
    156 		}
    157 	}
    158 
    159 	// Chdir
    160 	if dir != nil {
    161 		err1 = chdir(uintptr(unsafe.Pointer(dir)))
    162 		if err1 != 0 {
    163 			goto childerror
    164 		}
    165 	}
    166 
    167 	// Pass 1: look for fd[i] < i and move those up above len(fd)
    168 	// so that pass 2 won't stomp on an fd it needs later.
    169 	if pipe < nextfd {
    170 		_, err1 = fcntl1(uintptr(pipe), F_DUP2FD, uintptr(nextfd))
    171 		if err1 != 0 {
    172 			goto childerror
    173 		}
    174 		fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
    175 		pipe = nextfd
    176 		nextfd++
    177 	}
    178 	for i = 0; i < len(fd); i++ {
    179 		if fd[i] >= 0 && fd[i] < int(i) {
    180 			_, err1 = fcntl1(uintptr(fd[i]), F_DUP2FD, uintptr(nextfd))
    181 			if err1 != 0 {
    182 				goto childerror
    183 			}
    184 			fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
    185 			fd[i] = nextfd
    186 			nextfd++
    187 			if nextfd == pipe { // don't stomp on pipe
    188 				nextfd++
    189 			}
    190 		}
    191 	}
    192 
    193 	// Pass 2: dup fd[i] down onto i.
    194 	for i = 0; i < len(fd); i++ {
    195 		if fd[i] == -1 {
    196 			close(uintptr(i))
    197 			continue
    198 		}
    199 		if fd[i] == int(i) {
    200 			// dup2(i, i) won't clear close-on-exec flag on Linux,
    201 			// probably not elsewhere either.
    202 			_, err1 = fcntl1(uintptr(fd[i]), F_SETFD, 0)
    203 			if err1 != 0 {
    204 				goto childerror
    205 			}
    206 			continue
    207 		}
    208 		// The new fd is created NOT close-on-exec,
    209 		// which is exactly what we want.
    210 		_, err1 = fcntl1(uintptr(fd[i]), F_DUP2FD, uintptr(i))
    211 		if err1 != 0 {
    212 			goto childerror
    213 		}
    214 	}
    215 
    216 	// By convention, we don't close-on-exec the fds we are
    217 	// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
    218 	// Programs that know they inherit fds >= 3 will need
    219 	// to set them close-on-exec.
    220 	for i = len(fd); i < 3; i++ {
    221 		close(uintptr(i))
    222 	}
    223 
    224 	// Detach fd 0 from tty
    225 	if sys.Noctty {
    226 		err1 = ioctl(0, uintptr(TIOCNOTTY), 0)
    227 		if err1 != 0 {
    228 			goto childerror
    229 		}
    230 	}
    231 
    232 	// Set the controlling TTY to Ctty
    233 	if sys.Setctty {
    234 		err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
    235 		if err1 != 0 {
    236 			goto childerror
    237 		}
    238 	}
    239 
    240 	// Time to exec.
    241 	err1 = execve(
    242 		uintptr(unsafe.Pointer(argv0)),
    243 		uintptr(unsafe.Pointer(&argv[0])),
    244 		uintptr(unsafe.Pointer(&envv[0])))
    245 
    246 childerror:
    247 	// send error code on pipe
    248 	write1(uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
    249 	for {
    250 		exit(253)
    251 	}
    252 }
    253 
    254 // Try to open a pipe with O_CLOEXEC set on both file descriptors.
    255 func forkExecPipe(p []int) error {
    256 	err := Pipe(p)
    257 	if err != nil {
    258 		return err
    259 	}
    260 	_, err = fcntl(p[0], F_SETFD, FD_CLOEXEC)
    261 	if err != nil {
    262 		return err
    263 	}
    264 	_, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
    265 	return err
    266 }
    267