//go:cgo_import_dynamic libc_execve execve "libc.so"
//go:cgo_import_dynamic libc_fcntl fcntl "libc.so"
//go:cgo_import_dynamic libc_gethostname gethostname "libc.so"
+//go:cgo_import_dynamic libc_getpid getpid "libc.so"
//go:cgo_import_dynamic libc_ioctl ioctl "libc.so"
//go:cgo_import_dynamic libc_pipe pipe "libc.so"
//go:cgo_import_dynamic libc_setgid setgid "libc.so"
//go:linkname libc_execve libc_execve
//go:linkname libc_fcntl libc_fcntl
//go:linkname libc_gethostname libc_gethostname
+//go:linkname libc_getpid libc_getpid
//go:linkname libc_ioctl libc_ioctl
//go:linkname libc_pipe libc_pipe
//go:linkname libc_setgid libc_setgid
libc_fcntl,
libc_forkx,
libc_gethostname,
+ libc_getpid,
libc_ioctl,
libc_pipe,
libc_setgid,
return gostringnocopy(&cname[0]), 0
}
+//go:nosplit
+func syscall_getpid() (pid, err uintptr) {
+ call := libcall{
+ fn: uintptr(unsafe.Pointer(libc_getpid)),
+ n: 0,
+ args: uintptr(unsafe.Pointer(libc_getpid)), // it's unused but must be non-nil, otherwise crashes
+ }
+ asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&call))
+ return call.r1, call.err
+}
+
//go:nosplit
func syscall_ioctl(fd, req, arg uintptr) (err uintptr) {
call := libcall{
TEXT ·gethostname(SB),NOSPLIT,$0
JMP runtime·syscall_gethostname(SB)
+TEXT ·getpid(SB),NOSPLIT,$0
+ JMP runtime·syscall_getpid(SB)
+
TEXT ·ioctl(SB),NOSPLIT,$0
JMP runtime·syscall_ioctl(SB)
Credential *Credential // Credential.
Ptrace bool // Enable tracing.
Setsid bool // Create session.
- Setpgid bool // Set process group ID to new pid (SYSV setpgrp)
- Setctty bool // Set controlling terminal to fd 0
+ Setpgid bool // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
+ Setctty bool // Set controlling terminal to fd Ctty
Noctty bool // Detach fd 0 from controlling terminal
+ Ctty int // Controlling TTY fd
+ Foreground bool // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY)
+ Pgid int // Child's process group ID if Setpgid.
}
// Implemented in runtime package.
}
// Set process group
- if sys.Setpgid {
- _, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
+ if sys.Setpgid || sys.Foreground {
+ // Place child in process group.
+ _, _, err1 = RawSyscall(SYS_SETPGID, 0, uintptr(sys.Pgid), 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ }
+
+ if sys.Foreground {
+ pgrp := sys.Pgid
+ if pgrp == 0 {
+ r1, _, err1 = RawSyscall(SYS_GETPID, 0, 0, 0)
+ if err1 != 0 {
+ goto childerror
+ }
+
+ pgrp = int(r1)
+ }
+
+ // Place process group in foreground.
+ _, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
if err1 != 0 {
goto childerror
}
}
}
- // Make fd 0 the tty
+ // Set the controlling TTY to Ctty
if sys.Setctty {
- _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCSCTTY), 0)
+ _, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
if err1 != 0 {
goto childerror
}
Credential *Credential // Credential.
Ptrace bool // Enable tracing.
Setsid bool // Create session.
- Setpgid bool // Set process group ID to new pid (SYSV setpgrp)
+ Setpgid bool // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
Setctty bool // Set controlling terminal to fd Ctty (only meaningful if Setsid is set)
Noctty bool // Detach fd 0 from controlling terminal
- Ctty int // Controlling TTY fd (Linux only)
+ Ctty int // Controlling TTY fd
+ Foreground bool // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY)
+ Pgid int // Child's process group ID if Setpgid.
Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only)
Cloneflags uintptr // Flags for clone calls (Linux only)
UidMappings []SysProcIDMap // User ID mappings for user namespaces.
}
// Set process group
- if sys.Setpgid {
- _, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
+ if sys.Setpgid || sys.Foreground {
+ // Place child in process group.
+ _, _, err1 = RawSyscall(SYS_SETPGID, 0, uintptr(sys.Pgid), 0)
+ if err1 != 0 {
+ goto childerror
+ }
+ }
+
+ if sys.Foreground {
+ pgrp := sys.Pgid
+ if pgrp == 0 {
+ r1, _, err1 = RawSyscall(SYS_GETPID, 0, 0, 0)
+ if err1 != 0 {
+ goto childerror
+ }
+
+ pgrp = int(r1)
+ }
+
+ // Place process group in foreground.
+ _, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
if err1 != 0 {
goto childerror
}
}
// Set the controlling TTY to Ctty
- if sys.Setctty && sys.Ctty >= 0 {
+ if sys.Setctty {
_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
if err1 != 0 {
goto childerror
Chroot string // Chroot.
Credential *Credential // Credential.
Setsid bool // Create session.
- Setpgid bool // Set process group ID to new pid (SYSV setpgrp)
- Setctty bool // Set controlling terminal to fd 0
+ Setpgid bool // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
+ Setctty bool // Set controlling terminal to fd Ctty
Noctty bool // Detach fd 0 from controlling terminal
+ Ctty int // Controlling TTY fd
+ Foreground bool // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY)
+ Pgid int // Child's process group ID if Setpgid.
}
// Implemented in runtime package.
func exit(code uintptr)
func fcntl1(fd uintptr, cmd uintptr, arg uintptr) (val uintptr, err Errno)
func forkx(flags uintptr) (pid uintptr, err Errno)
+func getpid() (pid uintptr, err Errno)
func ioctl(fd uintptr, req uintptr, arg uintptr) (err Errno)
func setgid(gid uintptr) (err Errno)
func setgroups1(ngid uintptr, gid uintptr) (err Errno)
}
// Set process group
- if sys.Setpgid {
- err1 = setpgid(0, 0)
+ if sys.Setpgid || sys.Foreground {
+ // Place child in process group.
+ err1 = setpgid(0, uintptr(sys.Pgid))
+ if err1 != 0 {
+ goto childerror
+ }
+ }
+
+ if sys.Foreground {
+ pgrp := sys.Pgid
+ if pgrp == 0 {
+ r1, err1 = getpid()
+ if err1 != 0 {
+ goto childerror
+ }
+
+ pgrp = int(r1)
+ }
+
+ // Place process group in foreground.
+ err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
if err1 != 0 {
goto childerror
}
}
}
- // Make fd 0 the tty
+ // Set the controlling TTY to Ctty
if sys.Setctty {
- err1 = ioctl(0, uintptr(TIOCSCTTY), 0)
+ err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
if err1 != 0 {
goto childerror
}
--- /dev/null
+// 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()
+}