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 //go:norace
     56 func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
     57 	// Declare all variables at top in case any
     58 	// declarations require heap allocation (e.g., err1).
     59 	var (
     60 		r1     uintptr
     61 		err1   Errno
     62 		nextfd int
     63 		i      int
     64 	)
     65 
     66 	// guard against side effects of shuffling fds below.
     67 	// Make sure that nextfd is beyond any currently open files so
     68 	// that we can't run the risk of overwriting any of them.
     69 	fd := make([]int, len(attr.Files))
     70 	nextfd = len(attr.Files)
     71 	for i, ufd := range attr.Files {
     72 		if nextfd < int(ufd) {
     73 			nextfd = int(ufd)
     74 		}
     75 		fd[i] = int(ufd)
     76 	}
     77 	nextfd++
     78 
     79 	// About to call fork.
     80 	// No more allocation or calls of non-assembly functions.
     81 	runtime_BeforeFork()
     82 	r1, err1 = forkx(0x1) // FORK_NOSIGCHLD
     83 	if err1 != 0 {
     84 		runtime_AfterFork()
     85 		return 0, err1
     86 	}
     87 
     88 	if r1 != 0 {
     89 		// parent; return PID
     90 		runtime_AfterFork()
     91 		return int(r1), 0
     92 	}
     93 
     94 	// Fork succeeded, now in child.
     95 
     96 	// Session ID
     97 	if sys.Setsid {
     98 		_, err1 = setsid()
     99 		if err1 != 0 {
    100 			goto childerror
    101 		}
    102 	}
    103 
    104 	// Set process group
    105 	if sys.Setpgid || sys.Foreground {
    106 		// Place child in process group.
    107 		err1 = setpgid(0, uintptr(sys.Pgid))
    108 		if err1 != 0 {
    109 			goto childerror
    110 		}
    111 	}
    112 
    113 	if sys.Foreground {
    114 		pgrp := sys.Pgid
    115 		if pgrp == 0 {
    116 			r1, err1 = getpid()
    117 			if err1 != 0 {
    118 				goto childerror
    119 			}
    120 
    121 			pgrp = int(r1)
    122 		}
    123 
    124 		// Place process group in foreground.
    125 		err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
    126 		if err1 != 0 {
    127 			goto childerror
    128 		}
    129 	}
    130 
    131 	// Chroot
    132 	if chroot != nil {
    133 		err1 = chroot1(uintptr(unsafe.Pointer(chroot)))
    134 		if err1 != 0 {
    135 			goto childerror
    136 		}
    137 	}
    138 
    139 	// User and groups
    140 	if cred := sys.Credential; cred != nil {
    141 		ngroups := uintptr(len(cred.Groups))
    142 		groups := uintptr(0)
    143 		if ngroups > 0 {
    144 			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
    145 		}
    146 		err1 = setgroups1(ngroups, groups)
    147 		if err1 != 0 {
    148 			goto childerror
    149 		}
    150 		err1 = setgid(uintptr(cred.Gid))
    151 		if err1 != 0 {
    152 			goto childerror
    153 		}
    154 		err1 = setuid(uintptr(cred.Uid))
    155 		if err1 != 0 {
    156 			goto childerror
    157 		}
    158 	}
    159 
    160 	// Chdir
    161 	if dir != nil {
    162 		err1 = chdir(uintptr(unsafe.Pointer(dir)))
    163 		if err1 != 0 {
    164 			goto childerror
    165 		}
    166 	}
    167 
    168 	// Pass 1: look for fd[i] < i and move those up above len(fd)
    169 	// so that pass 2 won't stomp on an fd it needs later.
    170 	if pipe < nextfd {
    171 		_, err1 = fcntl1(uintptr(pipe), F_DUP2FD, uintptr(nextfd))
    172 		if err1 != 0 {
    173 			goto childerror
    174 		}
    175 		fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
    176 		pipe = nextfd
    177 		nextfd++
    178 	}
    179 	for i = 0; i < len(fd); i++ {
    180 		if fd[i] >= 0 && fd[i] < int(i) {
    181 			if nextfd == pipe { // don't stomp on pipe
    182 				nextfd++
    183 			}
    184 			_, err1 = fcntl1(uintptr(fd[i]), F_DUP2FD, uintptr(nextfd))
    185 			if err1 != 0 {
    186 				goto childerror
    187 			}
    188 			fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
    189 			fd[i] = nextfd
    190 			nextfd++
    191 		}
    192 	}
    193 
    194 	// Pass 2: dup fd[i] down onto i.
    195 	for i = 0; i < len(fd); i++ {
    196 		if fd[i] == -1 {
    197 			close(uintptr(i))
    198 			continue
    199 		}
    200 		if fd[i] == int(i) {
    201 			// dup2(i, i) won't clear close-on-exec flag on Linux,
    202 			// probably not elsewhere either.
    203 			_, err1 = fcntl1(uintptr(fd[i]), F_SETFD, 0)
    204 			if err1 != 0 {
    205 				goto childerror
    206 			}
    207 			continue
    208 		}
    209 		// The new fd is created NOT close-on-exec,
    210 		// which is exactly what we want.
    211 		_, err1 = fcntl1(uintptr(fd[i]), F_DUP2FD, uintptr(i))
    212 		if err1 != 0 {
    213 			goto childerror
    214 		}
    215 	}
    216 
    217 	// By convention, we don't close-on-exec the fds we are
    218 	// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
    219 	// Programs that know they inherit fds >= 3 will need
    220 	// to set them close-on-exec.
    221 	for i = len(fd); i < 3; i++ {
    222 		close(uintptr(i))
    223 	}
    224 
    225 	// Detach fd 0 from tty
    226 	if sys.Noctty {
    227 		err1 = ioctl(0, uintptr(TIOCNOTTY), 0)
    228 		if err1 != 0 {
    229 			goto childerror
    230 		}
    231 	}
    232 
    233 	// Set the controlling TTY to Ctty
    234 	if sys.Setctty {
    235 		err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
    236 		if err1 != 0 {
    237 			goto childerror
    238 		}
    239 	}
    240 
    241 	// Time to exec.
    242 	err1 = execve(
    243 		uintptr(unsafe.Pointer(argv0)),
    244 		uintptr(unsafe.Pointer(&argv[0])),
    245 		uintptr(unsafe.Pointer(&envv[0])))
    246 
    247 childerror:
    248 	// send error code on pipe
    249 	write1(uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
    250 	for {
    251 		exit(253)
    252 	}
    253 }
    254 
    255 // Try to open a pipe with O_CLOEXEC set on both file descriptors.
    256 func forkExecPipe(p []int) error {
    257 	err := Pipe(p)
    258 	if err != nil {
    259 		return err
    260 	}
    261 	_, err = fcntl(p[0], F_SETFD, FD_CLOEXEC)
    262 	if err != nil {
    263 		return err
    264 	}
    265 	_, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
    266 	return err
    267 }
    268