| // 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 aix darwin dragonfly freebsd linux netbsd openbsd solaris | 
 |  | 
 | package syscall_test | 
 |  | 
 | import ( | 
 | 	"internal/testenv" | 
 | 	"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() { | 
 | 	if err := c.proc.Start(); err != nil { | 
 | 		c.test.Fatal(err) | 
 | 	} | 
 | } | 
 |  | 
 | func (c *command) Stop() { | 
 | 	c.pipe.Close() | 
 | 	if err := c.proc.Wait(); err != nil { | 
 | 		c.test.Fatal(err) | 
 | 	} | 
 | } | 
 |  | 
 | func create(t *testing.T) *command { | 
 | 	testenv.MustHaveExec(t) | 
 |  | 
 | 	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.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.Ioctl(tty.Fd(), syscall.TIOCSPGRP, uintptr(unsafe.Pointer(&fpgrp))) | 
 | 	if errno != 0 { | 
 | 		t.Fatalf("TIOCSPGRP failed with error code: %s", errno) | 
 | 	} | 
 |  | 
 | 	signal.Reset() | 
 | } |