...

Source file src/syscall/exec_bsd.go

     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 dragonfly freebsd netbsd openbsd
     6	
     7	package syscall
     8	
     9	import (
    10		"unsafe"
    11	)
    12	
    13	type SysProcAttr struct {
    14		Chroot     string      // Chroot.
    15		Credential *Credential // Credential.
    16		Ptrace     bool        // Enable tracing.
    17		Setsid     bool        // Create session.
    18		Setpgid    bool        // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
    19		Setctty    bool        // Set controlling terminal to fd Ctty
    20		Noctty     bool        // Detach fd 0 from controlling terminal
    21		Ctty       int         // Controlling TTY fd
    22		Foreground bool        // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY)
    23		Pgid       int         // Child's process group ID if Setpgid.
    24	}
    25	
    26	// Implemented in runtime package.
    27	func runtime_BeforeFork()
    28	func runtime_AfterFork()
    29	func runtime_AfterForkInChild()
    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	//go:norace
    41	func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
    42		// Declare all variables at top in case any
    43		// declarations require heap allocation (e.g., err1).
    44		var (
    45			r1     uintptr
    46			err1   Errno
    47			nextfd int
    48			i      int
    49		)
    50	
    51		// guard against side effects of shuffling fds below.
    52		// Make sure that nextfd is beyond any currently open files so
    53		// that we can't run the risk of overwriting any of them.
    54		fd := make([]int, len(attr.Files))
    55		nextfd = len(attr.Files)
    56		for i, ufd := range attr.Files {
    57			if nextfd < int(ufd) {
    58				nextfd = int(ufd)
    59			}
    60			fd[i] = int(ufd)
    61		}
    62		nextfd++
    63	
    64		// About to call fork.
    65		// No more allocation or calls of non-assembly functions.
    66		runtime_BeforeFork()
    67		r1, _, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
    68		if err1 != 0 {
    69			runtime_AfterFork()
    70			return 0, err1
    71		}
    72	
    73		if r1 != 0 {
    74			// parent; return PID
    75			runtime_AfterFork()
    76			return int(r1), 0
    77		}
    78	
    79		// Fork succeeded, now in child.
    80	
    81		runtime_AfterForkInChild()
    82	
    83		// Enable tracing if requested.
    84		if sys.Ptrace {
    85			_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
    86			if err1 != 0 {
    87				goto childerror
    88			}
    89		}
    90	
    91		// Session ID
    92		if sys.Setsid {
    93			_, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
    94			if err1 != 0 {
    95				goto childerror
    96			}
    97		}
    98	
    99		// Set process group
   100		if sys.Setpgid || sys.Foreground {
   101			// Place child in process group.
   102			_, _, err1 = RawSyscall(SYS_SETPGID, 0, uintptr(sys.Pgid), 0)
   103			if err1 != 0 {
   104				goto childerror
   105			}
   106		}
   107	
   108		if sys.Foreground {
   109			pgrp := sys.Pgid
   110			if pgrp == 0 {
   111				r1, _, err1 = RawSyscall(SYS_GETPID, 0, 0, 0)
   112				if err1 != 0 {
   113					goto childerror
   114				}
   115	
   116				pgrp = int(r1)
   117			}
   118	
   119			// Place process group in foreground.
   120			_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
   121			if err1 != 0 {
   122				goto childerror
   123			}
   124		}
   125	
   126		// Chroot
   127		if chroot != nil {
   128			_, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
   129			if err1 != 0 {
   130				goto childerror
   131			}
   132		}
   133	
   134		// User and groups
   135		if cred := sys.Credential; cred != nil {
   136			ngroups := uintptr(len(cred.Groups))
   137			groups := uintptr(0)
   138			if ngroups > 0 {
   139				groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
   140			}
   141			if !cred.NoSetGroups {
   142				_, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0)
   143				if err1 != 0 {
   144					goto childerror
   145				}
   146			}
   147			_, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0)
   148			if err1 != 0 {
   149				goto childerror
   150			}
   151			_, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0)
   152			if err1 != 0 {
   153				goto childerror
   154			}
   155		}
   156	
   157		// Chdir
   158		if dir != nil {
   159			_, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
   160			if err1 != 0 {
   161				goto childerror
   162			}
   163		}
   164	
   165		// Pass 1: look for fd[i] < i and move those up above len(fd)
   166		// so that pass 2 won't stomp on an fd it needs later.
   167		if pipe < nextfd {
   168			_, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
   169			if err1 != 0 {
   170				goto childerror
   171			}
   172			RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
   173			pipe = nextfd
   174			nextfd++
   175		}
   176		for i = 0; i < len(fd); i++ {
   177			if fd[i] >= 0 && fd[i] < int(i) {
   178				if nextfd == pipe { // don't stomp on pipe
   179					nextfd++
   180				}
   181				_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
   182				if err1 != 0 {
   183					goto childerror
   184				}
   185				RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
   186				fd[i] = nextfd
   187				nextfd++
   188			}
   189		}
   190	
   191		// Pass 2: dup fd[i] down onto i.
   192		for i = 0; i < len(fd); i++ {
   193			if fd[i] == -1 {
   194				RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
   195				continue
   196			}
   197			if fd[i] == int(i) {
   198				// dup2(i, i) won't clear close-on-exec flag on Linux,
   199				// probably not elsewhere either.
   200				_, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0)
   201				if err1 != 0 {
   202					goto childerror
   203				}
   204				continue
   205			}
   206			// The new fd is created NOT close-on-exec,
   207			// which is exactly what we want.
   208			_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0)
   209			if err1 != 0 {
   210				goto childerror
   211			}
   212		}
   213	
   214		// By convention, we don't close-on-exec the fds we are
   215		// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
   216		// Programs that know they inherit fds >= 3 will need
   217		// to set them close-on-exec.
   218		for i = len(fd); i < 3; i++ {
   219			RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
   220		}
   221	
   222		// Detach fd 0 from tty
   223		if sys.Noctty {
   224			_, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0)
   225			if err1 != 0 {
   226				goto childerror
   227			}
   228		}
   229	
   230		// Set the controlling TTY to Ctty
   231		if sys.Setctty {
   232			_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
   233			if err1 != 0 {
   234				goto childerror
   235			}
   236		}
   237	
   238		// Time to exec.
   239		_, _, err1 = RawSyscall(SYS_EXECVE,
   240			uintptr(unsafe.Pointer(argv0)),
   241			uintptr(unsafe.Pointer(&argv[0])),
   242			uintptr(unsafe.Pointer(&envv[0])))
   243	
   244	childerror:
   245		// send error code on pipe
   246		RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
   247		for {
   248			RawSyscall(SYS_EXIT, 253, 0, 0)
   249		}
   250	}
   251	

View as plain text