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

View as plain text