syscall: Add Foreground and Pgid to SysProcAttr

On Unix, when placing a child in a new process group, allow that group
to become the foreground process group. Also, allow a child process to
join a specific process group.

When setting the foreground process group, Ctty is used as the file
descriptor of the controlling terminal. Ctty has been added to the BSD
and Solaris SysProcAttr structures and the handling of Setctty changed
to match Linux.

Change-Id: I18d169a6c5ab8a6a90708c4ff52eb4aded50bc8c
Reviewed-on: https://go-review.googlesource.com/5130
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Michael MacInnis 2015-02-17 22:23:16 -05:00 committed by Ian Lance Taylor
parent 5bf9249eda
commit f7befa43a3
7 changed files with 317 additions and 17 deletions

View file

@ -0,0 +1,217 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package syscall_test
import (
"io"
"os"
"os/exec"
"os/signal"
"syscall"
"testing"
"unsafe"
)
type command struct {
pipe io.WriteCloser
proc *exec.Cmd
test *testing.T
}
func (c *command) Info() (pid, pgrp int) {
pid = c.proc.Process.Pid
pgrp, err := syscall.Getpgid(pid)
if err != nil {
c.test.Fatal(err)
}
return
}
func (c *command) Start() {
c.proc.Start()
}
func (c *command) Stop() {
c.pipe.Close()
c.proc.Wait()
}
func create(t *testing.T) *command {
proc := exec.Command("cat")
stdin, err := proc.StdinPipe()
if err != nil {
t.Fatal(err)
}
return &command{stdin, proc, t}
}
func parent() (pid, pgrp int) {
return syscall.Getpid(), syscall.Getpgrp()
}
func TestZeroSysProcAttr(t *testing.T) {
ppid, ppgrp := parent()
cmd := create(t)
cmd.Start()
defer cmd.Stop()
cpid, cpgrp := cmd.Info()
if cpid == ppid {
t.Fatalf("Parent and child have the same process ID")
}
if cpgrp != ppgrp {
t.Fatalf("Child is not in parent's process group")
}
}
func TestSetpgid(t *testing.T) {
ppid, ppgrp := parent()
cmd := create(t)
cmd.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Start()
defer cmd.Stop()
cpid, cpgrp := cmd.Info()
if cpid == ppid {
t.Fatalf("Parent and child have the same process ID")
}
if cpgrp == ppgrp {
t.Fatalf("Parent and child are in the same process group")
}
if cpid != cpgrp {
t.Fatalf("Child's process group is not the child's process ID")
}
}
func TestPgid(t *testing.T) {
ppid, ppgrp := parent()
cmd1 := create(t)
cmd1.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd1.Start()
defer cmd1.Stop()
cpid1, cpgrp1 := cmd1.Info()
if cpid1 == ppid {
t.Fatalf("Parent and child 1 have the same process ID")
}
if cpgrp1 == ppgrp {
t.Fatalf("Parent and child 1 are in the same process group")
}
if cpid1 != cpgrp1 {
t.Fatalf("Child 1's process group is not its process ID")
}
cmd2 := create(t)
cmd2.proc.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
Pgid: cpgrp1,
}
cmd2.Start()
defer cmd2.Stop()
cpid2, cpgrp2 := cmd2.Info()
if cpid2 == ppid {
t.Fatalf("Parent and child 2 have the same process ID")
}
if cpgrp2 == ppgrp {
t.Fatalf("Parent and child 2 are in the same process group")
}
if cpid2 == cpgrp2 {
t.Fatalf("Child 2's process group is its process ID")
}
if cpid1 == cpid2 {
t.Fatalf("Child 1 and 2 have the same process ID")
}
if cpgrp1 != cpgrp2 {
t.Fatalf("Child 1 and 2 are not in the same process group")
}
}
func TestForeground(t *testing.T) {
signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU)
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
t.Skipf("Can't test Foreground. Couldn't open /dev/tty: %s",
err)
}
fpgrp := 0
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
tty.Fd(),
syscall.TIOCGPGRP,
uintptr(unsafe.Pointer(&fpgrp)))
if errno != 0 {
t.Fatalf("TIOCGPGRP failed with error code: %s", errno)
}
if fpgrp == 0 {
t.Fatalf("Foreground process group is zero")
}
ppid, ppgrp := parent()
cmd := create(t)
cmd.proc.SysProcAttr = &syscall.SysProcAttr{
Ctty: int(tty.Fd()),
Foreground: true,
}
cmd.Start()
cpid, cpgrp := cmd.Info()
if cpid == ppid {
t.Fatalf("Parent and child have the same process ID")
}
if cpgrp == ppgrp {
t.Fatalf("Parent and child are in the same process group")
}
if cpid != cpgrp {
t.Fatalf("Child's process group is not the child's process ID")
}
cmd.Stop()
_, _, errno = syscall.Syscall(syscall.SYS_IOCTL,
tty.Fd(),
syscall.TIOCSPGRP,
uintptr(unsafe.Pointer(&fpgrp)))
if errno != 0 {
t.Fatalf("TIOCSPGRP failed with error code: %s", errno)
}
signal.Reset()
}