github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/syscall/exec_solaris.go (about)

     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  		if !cred.NoSetGroups {
   147  			err1 = setgroups1(ngroups, groups)
   148  			if err1 != 0 {
   149  				goto childerror
   150  			}
   151  		}
   152  		err1 = setgid(uintptr(cred.Gid))
   153  		if err1 != 0 {
   154  			goto childerror
   155  		}
   156  		err1 = setuid(uintptr(cred.Uid))
   157  		if err1 != 0 {
   158  			goto childerror
   159  		}
   160  	}
   161  
   162  	// Chdir
   163  	if dir != nil {
   164  		err1 = chdir(uintptr(unsafe.Pointer(dir)))
   165  		if err1 != 0 {
   166  			goto childerror
   167  		}
   168  	}
   169  
   170  	// Pass 1: look for fd[i] < i and move those up above len(fd)
   171  	// so that pass 2 won't stomp on an fd it needs later.
   172  	if pipe < nextfd {
   173  		_, err1 = fcntl1(uintptr(pipe), F_DUP2FD, uintptr(nextfd))
   174  		if err1 != 0 {
   175  			goto childerror
   176  		}
   177  		fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
   178  		pipe = nextfd
   179  		nextfd++
   180  	}
   181  	for i = 0; i < len(fd); i++ {
   182  		if fd[i] >= 0 && fd[i] < int(i) {
   183  			if nextfd == pipe { // don't stomp on pipe
   184  				nextfd++
   185  			}
   186  			_, err1 = fcntl1(uintptr(fd[i]), F_DUP2FD, uintptr(nextfd))
   187  			if err1 != 0 {
   188  				goto childerror
   189  			}
   190  			fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
   191  			fd[i] = nextfd
   192  			nextfd++
   193  		}
   194  	}
   195  
   196  	// Pass 2: dup fd[i] down onto i.
   197  	for i = 0; i < len(fd); i++ {
   198  		if fd[i] == -1 {
   199  			close(uintptr(i))
   200  			continue
   201  		}
   202  		if fd[i] == int(i) {
   203  			// dup2(i, i) won't clear close-on-exec flag on Linux,
   204  			// probably not elsewhere either.
   205  			_, err1 = fcntl1(uintptr(fd[i]), F_SETFD, 0)
   206  			if err1 != 0 {
   207  				goto childerror
   208  			}
   209  			continue
   210  		}
   211  		// The new fd is created NOT close-on-exec,
   212  		// which is exactly what we want.
   213  		_, err1 = fcntl1(uintptr(fd[i]), F_DUP2FD, uintptr(i))
   214  		if err1 != 0 {
   215  			goto childerror
   216  		}
   217  	}
   218  
   219  	// By convention, we don't close-on-exec the fds we are
   220  	// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
   221  	// Programs that know they inherit fds >= 3 will need
   222  	// to set them close-on-exec.
   223  	for i = len(fd); i < 3; i++ {
   224  		close(uintptr(i))
   225  	}
   226  
   227  	// Detach fd 0 from tty
   228  	if sys.Noctty {
   229  		err1 = ioctl(0, uintptr(TIOCNOTTY), 0)
   230  		if err1 != 0 {
   231  			goto childerror
   232  		}
   233  	}
   234  
   235  	// Set the controlling TTY to Ctty
   236  	if sys.Setctty {
   237  		err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
   238  		if err1 != 0 {
   239  			goto childerror
   240  		}
   241  	}
   242  
   243  	// Time to exec.
   244  	err1 = execve(
   245  		uintptr(unsafe.Pointer(argv0)),
   246  		uintptr(unsafe.Pointer(&argv[0])),
   247  		uintptr(unsafe.Pointer(&envv[0])))
   248  
   249  childerror:
   250  	// send error code on pipe
   251  	write1(uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
   252  	for {
   253  		exit(253)
   254  	}
   255  }
   256  
   257  // Try to open a pipe with O_CLOEXEC set on both file descriptors.
   258  func forkExecPipe(p []int) error {
   259  	err := Pipe(p)
   260  	if err != nil {
   261  		return err
   262  	}
   263  	_, err = fcntl(p[0], F_SETFD, FD_CLOEXEC)
   264  	if err != nil {
   265  		return err
   266  	}
   267  	_, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
   268  	return err
   269  }