Black Lives Matter. Support the Equal Justice Initiative.

Source file src/syscall/exec_darwin.go

Documentation: 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  	Ptrace     bool        // Enable tracing.
    15  	Setsid     bool        // Create session.
    16  	// Setpgid sets the process group ID of the child to Pgid,
    17  	// or, if Pgid == 0, to the new child's process ID.
    18  	Setpgid bool
    19  	// Setctty sets the controlling terminal of the child to
    20  	// file descriptor Ctty. Ctty must be a descriptor number
    21  	// in the child process: an index into ProcAttr.Files.
    22  	// This is only meaningful if Setsid is true.
    23  	Setctty bool
    24  	Noctty  bool // Detach fd 0 from controlling terminal
    25  	Ctty    int  // Controlling TTY fd
    26  	// Foreground places the child process group in the foreground.
    27  	// This implies Setpgid. The Ctty field must be set to
    28  	// the descriptor of the controlling TTY.
    29  	// Unlike Setctty, in this case Ctty must be a descriptor
    30  	// number in the parent process.
    31  	Foreground bool
    32  	Pgid       int // Child's process group ID if Setpgid.
    33  }
    34  
    35  // Implemented in runtime package.
    36  func runtime_BeforeFork()
    37  func runtime_AfterFork()
    38  func runtime_AfterForkInChild()
    39  
    40  // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
    41  // If a dup or exec fails, write the errno error to pipe.
    42  // (Pipe is close-on-exec so if exec succeeds, it will be closed.)
    43  // In the child, this function must not acquire any locks, because
    44  // they might have been locked at the time of the fork. This means
    45  // no rescheduling, no malloc calls, and no new stack segments.
    46  // For the same reason compiler does not race instrument it.
    47  // The calls to rawSyscall are okay because they are assembly
    48  // functions that do not grow the stack.
    49  //go:norace
    50  func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
    51  	// Declare all variables at top in case any
    52  	// declarations require heap allocation (e.g., err1).
    53  	var (
    54  		r1     uintptr
    55  		err1   Errno
    56  		nextfd int
    57  		i      int
    58  	)
    59  
    60  	// guard against side effects of shuffling fds below.
    61  	// Make sure that nextfd is beyond any currently open files so
    62  	// that we can't run the risk of overwriting any of them.
    63  	fd := make([]int, len(attr.Files))
    64  	nextfd = len(attr.Files)
    65  	for i, ufd := range attr.Files {
    66  		if nextfd < int(ufd) {
    67  			nextfd = int(ufd)
    68  		}
    69  		fd[i] = int(ufd)
    70  	}
    71  	nextfd++
    72  
    73  	// About to call fork.
    74  	// No more allocation or calls of non-assembly functions.
    75  	runtime_BeforeFork()
    76  	r1, _, err1 = rawSyscall(funcPC(libc_fork_trampoline), 0, 0, 0)
    77  	if err1 != 0 {
    78  		runtime_AfterFork()
    79  		return 0, err1
    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  	runtime_AfterForkInChild()
    91  
    92  	// Enable tracing if requested.
    93  	if sys.Ptrace {
    94  		if err := ptrace(PTRACE_TRACEME, 0, 0, 0); err != nil {
    95  			err1 = err.(Errno)
    96  			goto childerror
    97  		}
    98  	}
    99  
   100  	// Session ID
   101  	if sys.Setsid {
   102  		_, _, err1 = rawSyscall(funcPC(libc_setsid_trampoline), 0, 0, 0)
   103  		if err1 != 0 {
   104  			goto childerror
   105  		}
   106  	}
   107  
   108  	// Set process group
   109  	if sys.Setpgid || sys.Foreground {
   110  		// Place child in process group.
   111  		_, _, err1 = rawSyscall(funcPC(libc_setpgid_trampoline), 0, uintptr(sys.Pgid), 0)
   112  		if err1 != 0 {
   113  			goto childerror
   114  		}
   115  	}
   116  
   117  	if sys.Foreground {
   118  		pgrp := sys.Pgid
   119  		if pgrp == 0 {
   120  			r1, _, err1 = rawSyscall(funcPC(libc_getpid_trampoline), 0, 0, 0)
   121  			if err1 != 0 {
   122  				goto childerror
   123  			}
   124  
   125  			pgrp = int(r1)
   126  		}
   127  
   128  		// Place process group in foreground.
   129  		_, _, err1 = rawSyscall(funcPC(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
   130  		if err1 != 0 {
   131  			goto childerror
   132  		}
   133  	}
   134  
   135  	// Chroot
   136  	if chroot != nil {
   137  		_, _, err1 = rawSyscall(funcPC(libc_chroot_trampoline), uintptr(unsafe.Pointer(chroot)), 0, 0)
   138  		if err1 != 0 {
   139  			goto childerror
   140  		}
   141  	}
   142  
   143  	// User and groups
   144  	if cred := sys.Credential; cred != nil {
   145  		ngroups := uintptr(len(cred.Groups))
   146  		groups := uintptr(0)
   147  		if ngroups > 0 {
   148  			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
   149  		}
   150  		if !cred.NoSetGroups {
   151  			_, _, err1 = rawSyscall(funcPC(libc_setgroups_trampoline), ngroups, groups, 0)
   152  			if err1 != 0 {
   153  				goto childerror
   154  			}
   155  		}
   156  		_, _, err1 = rawSyscall(funcPC(libc_setgid_trampoline), uintptr(cred.Gid), 0, 0)
   157  		if err1 != 0 {
   158  			goto childerror
   159  		}
   160  		_, _, err1 = rawSyscall(funcPC(libc_setuid_trampoline), uintptr(cred.Uid), 0, 0)
   161  		if err1 != 0 {
   162  			goto childerror
   163  		}
   164  	}
   165  
   166  	// Chdir
   167  	if dir != nil {
   168  		_, _, err1 = rawSyscall(funcPC(libc_chdir_trampoline), uintptr(unsafe.Pointer(dir)), 0, 0)
   169  		if err1 != 0 {
   170  			goto childerror
   171  		}
   172  	}
   173  
   174  	// Pass 1: look for fd[i] < i and move those up above len(fd)
   175  	// so that pass 2 won't stomp on an fd it needs later.
   176  	if pipe < nextfd {
   177  		_, _, err1 = rawSyscall(funcPC(libc_dup2_trampoline), uintptr(pipe), uintptr(nextfd), 0)
   178  		if err1 != 0 {
   179  			goto childerror
   180  		}
   181  		rawSyscall(funcPC(libc_fcntl_trampoline), uintptr(nextfd), F_SETFD, FD_CLOEXEC)
   182  		pipe = nextfd
   183  		nextfd++
   184  	}
   185  	for i = 0; i < len(fd); i++ {
   186  		if fd[i] >= 0 && fd[i] < int(i) {
   187  			if nextfd == pipe { // don't stomp on pipe
   188  				nextfd++
   189  			}
   190  			_, _, err1 = rawSyscall(funcPC(libc_dup2_trampoline), uintptr(fd[i]), uintptr(nextfd), 0)
   191  			if err1 != 0 {
   192  				goto childerror
   193  			}
   194  			rawSyscall(funcPC(libc_fcntl_trampoline), uintptr(nextfd), F_SETFD, FD_CLOEXEC)
   195  			fd[i] = nextfd
   196  			nextfd++
   197  		}
   198  	}
   199  
   200  	// Pass 2: dup fd[i] down onto i.
   201  	for i = 0; i < len(fd); i++ {
   202  		if fd[i] == -1 {
   203  			rawSyscall(funcPC(libc_close_trampoline), uintptr(i), 0, 0)
   204  			continue
   205  		}
   206  		if fd[i] == int(i) {
   207  			// dup2(i, i) won't clear close-on-exec flag on Linux,
   208  			// probably not elsewhere either.
   209  			_, _, err1 = rawSyscall(funcPC(libc_fcntl_trampoline), uintptr(fd[i]), F_SETFD, 0)
   210  			if err1 != 0 {
   211  				goto childerror
   212  			}
   213  			continue
   214  		}
   215  		// The new fd is created NOT close-on-exec,
   216  		// which is exactly what we want.
   217  		_, _, err1 = rawSyscall(funcPC(libc_dup2_trampoline), uintptr(fd[i]), uintptr(i), 0)
   218  		if err1 != 0 {
   219  			goto childerror
   220  		}
   221  	}
   222  
   223  	// By convention, we don't close-on-exec the fds we are
   224  	// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
   225  	// Programs that know they inherit fds >= 3 will need
   226  	// to set them close-on-exec.
   227  	for i = len(fd); i < 3; i++ {
   228  		rawSyscall(funcPC(libc_close_trampoline), uintptr(i), 0, 0)
   229  	}
   230  
   231  	// Detach fd 0 from tty
   232  	if sys.Noctty {
   233  		_, _, err1 = rawSyscall(funcPC(libc_ioctl_trampoline), 0, uintptr(TIOCNOTTY), 0)
   234  		if err1 != 0 {
   235  			goto childerror
   236  		}
   237  	}
   238  
   239  	// Set the controlling TTY to Ctty
   240  	if sys.Setctty {
   241  		_, _, err1 = rawSyscall(funcPC(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
   242  		if err1 != 0 {
   243  			goto childerror
   244  		}
   245  	}
   246  
   247  	// Time to exec.
   248  	_, _, err1 = rawSyscall(funcPC(libc_execve_trampoline),
   249  		uintptr(unsafe.Pointer(argv0)),
   250  		uintptr(unsafe.Pointer(&argv[0])),
   251  		uintptr(unsafe.Pointer(&envv[0])))
   252  
   253  childerror:
   254  	// send error code on pipe
   255  	rawSyscall(funcPC(libc_write_trampoline), uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
   256  	for {
   257  		rawSyscall(funcPC(libc_exit_trampoline), 253, 0, 0)
   258  	}
   259  }
   260  

View as plain text