github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/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  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  }