From aa168ed2cd0614afa7503e3b0eb2b1e38b43384a Mon Sep 17 00:00:00 2001
From: Michael MacInnis <Michael.P.MacInnis@gmail.com>
Date: Thu, 11 Sep 2014 18:39:51 -0700
Subject: [PATCH] syscall: SysProcAttr job control changes

Making the child's process group the foreground process group and
placing the child in a specific process group involves co-ordination
between the parent and child that must be done post-fork but pre-exec.

LGTM=iant
R=golang-codereviews, gobot, iant, mikioh.mikioh
CC=golang-codereviews
https://golang.org/cl/131750044
---
 src/syscall/exec_bsd.go   | 40 +++++++++++++++++++++++++++++++++++++--
 src/syscall/exec_linux.go | 40 +++++++++++++++++++++++++++++++++++++--
 2 files changed, 76 insertions(+), 4 deletions(-)

diff --git a/src/syscall/exec_bsd.go b/src/syscall/exec_bsd.go
index ff78f197f1..7c2c1f7071 100644
--- a/src/syscall/exec_bsd.go
+++ b/src/syscall/exec_bsd.go
@@ -19,6 +19,8 @@ type SysProcAttr struct {
 	Setpgid    bool        // Set process group ID to new pid (SYSV setpgrp)
 	Setctty    bool        // Set controlling terminal to fd 0
 	Noctty     bool        // Detach fd 0 from controlling terminal
+	Foreground bool        // Set foreground process group to child's pid. (Implies Setpgid. Stdin should be a TTY)
+	Joinpgrp   int         // If != 0, child's process group ID. (Setpgid must not be set)
 }
 
 // Implemented in runtime package.
@@ -79,7 +81,22 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
 	if r1 != 0 {
 		// parent; return PID
 		runtime_AfterFork()
-		return int(r1), 0
+		pid = int(r1)
+
+		if sys.Joinpgrp != 0 {
+			// Place the child in the specified process group.
+			RawSyscall(SYS_SETPGID, r1, uintptr(sys.Joinpgrp), 0)
+		} else if sys.Foreground || sys.Setpgid {
+			// Place the child in a new process group.
+			RawSyscall(SYS_SETPGID, 0, 0, 0)
+
+			if sys.Foreground {
+				// Set new foreground process group.
+				RawSyscall(SYS_IOCTL, uintptr(Stdin), TIOCSPGRP, uintptr(unsafe.Pointer(&pid)))
+			}
+		}
+
+		return pid, 0
 	}
 
 	// Fork succeeded, now in child.
@@ -101,11 +118,30 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
 	}
 
 	// Set process group
-	if sys.Setpgid {
+	if sys.Joinpgrp != 0 {
+		// Place the child in the specified process group.
+		_, _, err1 = RawSyscall(SYS_SETPGID, r1, uintptr(sys.Joinpgrp), 0)
+		if err1 != 0 {
+			goto childerror
+		}
+	} else if sys.Foreground || sys.Setpgid {
+		// Place the child in a new process group.
 		_, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
 		if err1 != 0 {
 			goto childerror
 		}
+
+		if sys.Foreground {
+			r1, _, _ = RawSyscall(SYS_GETPID, 0, 0, 0)
+
+			pid := int(r1)
+
+			// Set new foreground process group.
+			_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(Stdin), TIOCSPGRP, uintptr(unsafe.Pointer(&pid)))
+			if err1 != 0 {
+				goto childerror
+			}
+		}
 	}
 
 	// Chroot
diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go
index f27950f730..f61dfc424a 100644
--- a/src/syscall/exec_linux.go
+++ b/src/syscall/exec_linux.go
@@ -21,6 +21,8 @@ type SysProcAttr struct {
 	Ctty       int         // Controlling TTY fd (Linux only)
 	Pdeathsig  Signal      // Signal that the process will get when its parent dies (Linux only)
 	Cloneflags uintptr     // Flags for clone calls (Linux only)
+	Foreground bool        // Set foreground process group to child's pid. (Implies Setpgid. Stdin should be a TTY)
+	Joinpgrp   int         // If != 0, child's process group ID. (Setpgid must not be set)
 }
 
 // Implemented in runtime package.
@@ -71,7 +73,22 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
 	if r1 != 0 {
 		// parent; return PID
 		runtime_AfterFork()
-		return int(r1), 0
+		pid = int(r1)
+
+		if sys.Joinpgrp != 0 {
+			// Place the child in the specified process group.
+			RawSyscall(SYS_SETPGID, r1, uintptr(sys.Joinpgrp), 0)
+		} else if sys.Foreground || sys.Setpgid {
+			// Place the child in a new process group.
+			RawSyscall(SYS_SETPGID, 0, 0, 0)
+
+			if sys.Foreground {
+				// Set new foreground process group.
+				RawSyscall(SYS_IOCTL, uintptr(Stdin), TIOCSPGRP, uintptr(unsafe.Pointer(&pid)))
+			}
+		}
+
+		return pid, 0
 	}
 
 	// Fork succeeded, now in child.
@@ -113,11 +130,30 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
 	}
 
 	// Set process group
-	if sys.Setpgid {
+	if sys.Joinpgrp != 0 {
+		// Place the child in the specified process group.
+		_, _, err1 = RawSyscall(SYS_SETPGID, r1, uintptr(sys.Joinpgrp), 0)
+		if err1 != 0 {
+			goto childerror
+		}
+	} else if sys.Foreground || sys.Setpgid {
+		// Place the child in a new process group.
 		_, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
 		if err1 != 0 {
 			goto childerror
 		}
+
+		if sys.Foreground {
+			r1, _, _ = RawSyscall(SYS_GETPID, 0, 0, 0)
+
+			pid := int(r1)
+
+			// Set new foreground process group.
+			_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(Stdin), TIOCSPGRP, uintptr(unsafe.Pointer(&pid)))
+			if err1 != 0 {
+				goto childerror
+			}
+		}
 	}
 
 	// Chroot
-- 
2.30.9