...

Source file src/syscall/exec_libc.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 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