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