unix: add PidfdSendSignal on Linux

This completes the pidfd_* family of syscalls and adds a test to verify that a
child process can be terminated using the new PidfdSendSignal function.

Change-Id: I71f6a1e26518f513731e20afef02030462e9a2c0
Reviewed-on: https://go-review.googlesource.com/c/sys/+/393794
Trust: Matt Layher <mdlayher@gmail.com>
Run-TryBot: Matt Layher <mdlayher@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/unix/linux/types.go b/unix/linux/types.go
index f9b6c46..d563a0c 100644
--- a/unix/linux/types.go
+++ b/unix/linux/types.go
@@ -934,6 +934,8 @@
 
 type SignalfdSiginfo C.struct_signalfd_siginfo
 
+type Siginfo C.siginfo_t
+
 // Terminal handling
 
 type Termios C.termios_t
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index d7a94e7..2a6affe 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -2297,6 +2297,7 @@
 
 //sys	PidfdOpen(pid int, flags int) (fd int, err error) = SYS_PIDFD_OPEN
 //sys	PidfdGetfd(pidfd int, targetfd int, flags int) (fd int, err error) = SYS_PIDFD_GETFD
+//sys	PidfdSendSignal(pidfd int, sig Signal, info *Siginfo, flags int) (err error) = SYS_PIDFD_SEND_SIGNAL
 
 //sys	shmat(id int, addr uintptr, flag int) (ret uintptr, err error)
 //sys	shmctl(id int, cmd int, buf *SysvShmDesc) (result int, err error)
diff --git a/unix/syscall_linux_test.go b/unix/syscall_linux_test.go
index 67b2f5d..cab2550 100644
--- a/unix/syscall_linux_test.go
+++ b/unix/syscall_linux_test.go
@@ -15,11 +15,13 @@
 	"io/ioutil"
 	"net"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"runtime"
 	"runtime/debug"
 	"strconv"
 	"strings"
+	"syscall"
 	"testing"
 	"time"
 	"unsafe"
@@ -217,6 +219,46 @@
 	return nil, false
 }
 
+func TestPidfd(t *testing.T) {
+	// Start a child process which will sleep for 1 hour; longer than the 10
+	// minute default Go test timeout.
+	cmd := exec.Command("sleep", "1h")
+	if err := cmd.Start(); err != nil {
+		t.Fatalf("failed to exec sleep: %v", err)
+	}
+
+	fd, err := unix.PidfdOpen(cmd.Process.Pid, 0)
+	if err != nil {
+		// GOARCH arm/arm64 and GOOS android builders do not support pidfds.
+		if errors.Is(err, unix.ENOSYS) {
+			t.Skipf("skipping, pidfd_open is not implemented: %v", err)
+		}
+
+		t.Fatalf("failed to open child pidfd: %v", err)
+	}
+	defer unix.Close(fd)
+
+	const want = unix.SIGHUP
+	if err := unix.PidfdSendSignal(fd, want, nil, 0); err != nil {
+		t.Fatalf("failed to signal child process: %v", err)
+	}
+
+	// Now verify that the child process received the expected signal.
+	var eerr *exec.ExitError
+	if err := cmd.Wait(); !errors.As(err, &eerr) {
+		t.Fatalf("child process terminated but did not return an exit error: %v", err)
+	}
+
+	ws, ok := eerr.Sys().(syscall.WaitStatus)
+	if !ok {
+		t.Fatalf("expected syscall.WaitStatus value, but got: %#T", eerr.Sys())
+	}
+
+	if got := ws.Signal(); got != want {
+		t.Fatalf("unexpected child exit signal, got: %s, want: %s", got, want)
+	}
+}
+
 func TestPpoll(t *testing.T) {
 	if runtime.GOOS == "android" {
 		t.Skip("mkfifo syscall is not available on android, skipping test")
diff --git a/unix/zsyscall_linux.go b/unix/zsyscall_linux.go
index 30fa405..e529939 100644
--- a/unix/zsyscall_linux.go
+++ b/unix/zsyscall_linux.go
@@ -1992,6 +1992,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func PidfdSendSignal(pidfd int, sig Signal, info *Siginfo, flags int) (err error) {
+	_, _, e1 := Syscall6(SYS_PIDFD_SEND_SIGNAL, uintptr(pidfd), uintptr(sig), uintptr(unsafe.Pointer(info)), uintptr(flags), 0, 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func shmat(id int, addr uintptr, flag int) (ret uintptr, err error) {
 	r0, _, e1 := Syscall(SYS_SHMAT, uintptr(id), uintptr(addr), uintptr(flag))
 	ret = uintptr(r0)
diff --git a/unix/ztypes_linux_386.go b/unix/ztypes_linux_386.go
index bea2549..531aefa 100644
--- a/unix/ztypes_linux_386.go
+++ b/unix/ztypes_linux_386.go
@@ -250,6 +250,13 @@
 
 const _C__NSIG = 0x41
 
+type Siginfo struct {
+	Signo int32
+	Errno int32
+	Code  int32
+	_     [116]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_amd64.go b/unix/ztypes_linux_amd64.go
index b8c8f28..727f664 100644
--- a/unix/ztypes_linux_amd64.go
+++ b/unix/ztypes_linux_amd64.go
@@ -265,6 +265,14 @@
 
 const _C__NSIG = 0x41
 
+type Siginfo struct {
+	Signo int32
+	Errno int32
+	Code  int32
+	_     int32
+	_     [112]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_arm.go b/unix/ztypes_linux_arm.go
index 4db4430..639141b 100644
--- a/unix/ztypes_linux_arm.go
+++ b/unix/ztypes_linux_arm.go
@@ -241,6 +241,13 @@
 
 const _C__NSIG = 0x41
 
+type Siginfo struct {
+	Signo int32
+	Errno int32
+	Code  int32
+	_     [116]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_arm64.go b/unix/ztypes_linux_arm64.go
index 3ebcad8..6cb03b1 100644
--- a/unix/ztypes_linux_arm64.go
+++ b/unix/ztypes_linux_arm64.go
@@ -244,6 +244,14 @@
 
 const _C__NSIG = 0x41
 
+type Siginfo struct {
+	Signo int32
+	Errno int32
+	Code  int32
+	_     int32
+	_     [112]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_mips.go b/unix/ztypes_linux_mips.go
index 3eb33e4..4a15554 100644
--- a/unix/ztypes_linux_mips.go
+++ b/unix/ztypes_linux_mips.go
@@ -246,6 +246,13 @@
 
 const _C__NSIG = 0x80
 
+type Siginfo struct {
+	Signo int32
+	Code  int32
+	Errno int32
+	_     [116]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_mips64.go b/unix/ztypes_linux_mips64.go
index 79a9446..e108492 100644
--- a/unix/ztypes_linux_mips64.go
+++ b/unix/ztypes_linux_mips64.go
@@ -247,6 +247,14 @@
 
 const _C__NSIG = 0x80
 
+type Siginfo struct {
+	Signo int32
+	Code  int32
+	Errno int32
+	_     int32
+	_     [112]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_mips64le.go b/unix/ztypes_linux_mips64le.go
index 8f4b107..8904ac8 100644
--- a/unix/ztypes_linux_mips64le.go
+++ b/unix/ztypes_linux_mips64le.go
@@ -247,6 +247,14 @@
 
 const _C__NSIG = 0x80
 
+type Siginfo struct {
+	Signo int32
+	Code  int32
+	Errno int32
+	_     int32
+	_     [112]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_mipsle.go b/unix/ztypes_linux_mipsle.go
index e4eb217..a1a28cc 100644
--- a/unix/ztypes_linux_mipsle.go
+++ b/unix/ztypes_linux_mipsle.go
@@ -246,6 +246,13 @@
 
 const _C__NSIG = 0x80
 
+type Siginfo struct {
+	Signo int32
+	Code  int32
+	Errno int32
+	_     [116]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_ppc.go b/unix/ztypes_linux_ppc.go
index d5b21f0..abdc534 100644
--- a/unix/ztypes_linux_ppc.go
+++ b/unix/ztypes_linux_ppc.go
@@ -253,6 +253,13 @@
 
 const _C__NSIG = 0x41
 
+type Siginfo struct {
+	Signo int32
+	Errno int32
+	Code  int32
+	_     [116]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_ppc64.go b/unix/ztypes_linux_ppc64.go
index 5188d14..f4afbbe 100644
--- a/unix/ztypes_linux_ppc64.go
+++ b/unix/ztypes_linux_ppc64.go
@@ -254,6 +254,14 @@
 
 const _C__NSIG = 0x41
 
+type Siginfo struct {
+	Signo int32
+	Errno int32
+	Code  int32
+	_     int32
+	_     [112]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_ppc64le.go b/unix/ztypes_linux_ppc64le.go
index de4dd4c..ea0b840 100644
--- a/unix/ztypes_linux_ppc64le.go
+++ b/unix/ztypes_linux_ppc64le.go
@@ -254,6 +254,14 @@
 
 const _C__NSIG = 0x41
 
+type Siginfo struct {
+	Signo int32
+	Errno int32
+	Code  int32
+	_     int32
+	_     [112]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_riscv64.go b/unix/ztypes_linux_riscv64.go
index dccbf9b..85d0a0d 100644
--- a/unix/ztypes_linux_riscv64.go
+++ b/unix/ztypes_linux_riscv64.go
@@ -272,6 +272,14 @@
 
 const _C__NSIG = 0x41
 
+type Siginfo struct {
+	Signo int32
+	Errno int32
+	Code  int32
+	_     int32
+	_     [112]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_s390x.go b/unix/ztypes_linux_s390x.go
index c426c35..81dd9c2 100644
--- a/unix/ztypes_linux_s390x.go
+++ b/unix/ztypes_linux_s390x.go
@@ -267,6 +267,14 @@
 
 const _C__NSIG = 0x41
 
+type Siginfo struct {
+	Signo int32
+	Errno int32
+	Code  int32
+	_     int32
+	_     [112]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32
diff --git a/unix/ztypes_linux_sparc64.go b/unix/ztypes_linux_sparc64.go
index 765edc1..6991b00 100644
--- a/unix/ztypes_linux_sparc64.go
+++ b/unix/ztypes_linux_sparc64.go
@@ -249,6 +249,14 @@
 
 const _C__NSIG = 0x41
 
+type Siginfo struct {
+	Signo int32
+	Errno int32
+	Code  int32
+	_     int32
+	_     [112]byte
+}
+
 type Termios struct {
 	Iflag  uint32
 	Oflag  uint32