src/pkg/syscall/exec_linux.go - The Go Programming Language

Golang

Source file src/pkg/syscall/exec_linux.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 linux
     6	
     7	package syscall
     8	
     9	import (
    10		"unsafe"
    11	)
    12	
    13	type SysProcAttr struct {
    14		Chroot     string      // Chroot.
    15		Credential *Credential // Credential.
    16		Ptrace     bool        // Enable tracing.
    17		Setsid     bool        // Create session.
    18		Setpgid    bool        // Set process group ID to new pid (SYSV setpgrp)
    19		Setctty    bool        // Set controlling terminal to fd 0
    20		Noctty     bool        // Detach fd 0 from controlling terminal
    21		Pdeathsig  Signal      // Signal that the process will get when its parent dies (Linux only)
    22	}
    23	
    24	// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
    25	// If a dup or exec fails, write the errno error to pipe.
    26	// (Pipe is close-on-exec so if exec succeeds, it will be closed.)
    27	// In the child, this function must not acquire any locks, because
    28	// they might have been locked at the time of the fork.  This means
    29	// no rescheduling, no malloc calls, and no new stack segments.
    30	// The calls to RawSyscall are okay because they are assembly
    31	// functions that do not grow the stack.
    32	func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
    33		// Declare all variables at top in case any
    34		// declarations require heap allocation (e.g., err1).
    35		var (
    36			r1     uintptr
    37			err1   Errno
    38			nextfd int
    39			i      int
    40		)
    41	
    42		// guard against side effects of shuffling fds below.
    43		fd := make([]int, len(attr.Files))
    44		for i, ufd := range attr.Files {
    45			fd[i] = int(ufd)
    46		}
    47	
    48		// About to call fork.
    49		// No more allocation or calls of non-assembly functions.
    50		r1, _, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
    51		if err1 != 0 {
    52			return 0, err1
    53		}
    54	
    55		if r1 != 0 {
    56			// parent; return PID
    57			return int(r1), 0
    58		}
    59	
    60		// Fork succeeded, now in child.
    61	
    62		// Parent death signal
    63		if sys.Pdeathsig != 0 {
    64			_, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0)
    65			if err1 != 0 {
    66				goto childerror
    67			}
    68	
    69			// Signal self if parent is already dead. This might cause a
    70			// duplicate signal in rare cases, but it won't matter when
    71			// using SIGKILL.
    72			r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
    73			if r1 == 1 {
    74				pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
    75				_, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
    76				if err1 != 0 {
    77					goto childerror
    78				}
    79			}
    80		}
    81	
    82		// Enable tracing if requested.
    83		if sys.Ptrace {
    84			_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
    85			if err1 != 0 {
    86				goto childerror
    87			}
    88		}
    89	
    90		// Session ID
    91		if sys.Setsid {
    92			_, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
    93			if err1 != 0 {
    94				goto childerror
    95			}
    96		}
    97	
    98		// Set process group
    99		if sys.Setpgid {
   100			_, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
   101			if err1 != 0 {
   102				goto childerror
   103			}
   104		}
   105	
   106		// Chroot
   107		if chroot != nil {
   108			_, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
   109			if err1 != 0 {
   110				goto childerror
   111			}
   112		}
   113	
   114		// User and groups
   115		if cred := sys.Credential; cred != nil {
   116			ngroups := uintptr(len(cred.Groups))
   117			groups := uintptr(0)
   118			if ngroups > 0 {
   119				groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
   120			}
   121			_, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0)
   122			if err1 != 0 {
   123				goto childerror
   124			}
   125			_, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0)
   126			if err1 != 0 {
   127				goto childerror
   128			}
   129			_, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0)
   130			if err1 != 0 {
   131				goto childerror
   132			}
   133		}
   134	
   135		// Chdir
   136		if dir != nil {
   137			_, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
   138			if err1 != 0 {
   139				goto childerror
   140			}
   141		}
   142	
   143		// Pass 1: look for fd[i] < i and move those up above len(fd)
   144		// so that pass 2 won't stomp on an fd it needs later.
   145		nextfd = int(len(fd))
   146		if pipe < nextfd {
   147			_, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
   148			if err1 != 0 {
   149				goto childerror
   150			}
   151			RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
   152			pipe = nextfd
   153			nextfd++
   154		}
   155		for i = 0; i < len(fd); i++ {
   156			if fd[i] >= 0 && fd[i] < int(i) {
   157				_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
   158				if err1 != 0 {
   159					goto childerror
   160				}
   161				RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
   162				fd[i] = nextfd
   163				nextfd++
   164				if nextfd == pipe { // don't stomp on pipe
   165					nextfd++
   166				}
   167			}
   168		}
   169	
   170		// Pass 2: dup fd[i] down onto i.
   171		for i = 0; i < len(fd); i++ {
   172			if fd[i] == -1 {
   173				RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
   174				continue
   175			}
   176			if fd[i] == int(i) {
   177				// dup2(i, i) won't clear close-on-exec flag on Linux,
   178				// probably not elsewhere either.
   179				_, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0)
   180				if err1 != 0 {
   181					goto childerror
   182				}
   183				continue
   184			}
   185			// The new fd is created NOT close-on-exec,
   186			// which is exactly what we want.
   187			_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0)
   188			if err1 != 0 {
   189				goto childerror
   190			}
   191		}
   192	
   193		// By convention, we don't close-on-exec the fds we are
   194		// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
   195		// Programs that know they inherit fds >= 3 will need
   196		// to set them close-on-exec.
   197		for i = len(fd); i < 3; i++ {
   198			RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
   199		}
   200	
   201		// Detach fd 0 from tty
   202		if sys.Noctty {
   203			_, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0)
   204			if err1 != 0 {
   205				goto childerror
   206			}
   207		}
   208	
   209		// Make fd 0 the tty
   210		if sys.Setctty {
   211			_, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCSCTTY), 0)
   212			if err1 != 0 {
   213				goto childerror
   214			}
   215		}
   216	
   217		// Time to exec.
   218		_, _, err1 = RawSyscall(SYS_EXECVE,
   219			uintptr(unsafe.Pointer(argv0)),
   220			uintptr(unsafe.Pointer(&argv[0])),
   221			uintptr(unsafe.Pointer(&envv[0])))
   222	
   223	childerror:
   224		// send error code on pipe
   225		RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
   226		for {
   227			RawSyscall(SYS_EXIT, 253, 0, 0)
   228		}
   229	
   230		// Calling panic is not actually safe,
   231		// but the for loop above won't break
   232		// and this shuts up the compiler.
   233		panic("unreached")
   234	}