unix: implement getitimer(2) and setitimer(2) on Linux

Wrap the low-level system calls with a more idiomatic Go API and a set of
constants to indicate which timer should be queried or modified.

man 2 getitimer indicates that these system calls are obsolete as of
POSIX.1-2008, but the code I am working on has not been ported to the modern
timer_gettime(2) and timer_settime(2) APIs as of yet.

Change-Id: I91482e141047846cadf47aa2417b8770955986bf
Reviewed-on: https://go-review.googlesource.com/c/sys/+/384054
Run-TryBot: Matt Layher <mdlayher@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dominik Honnef <dominik@honnef.co>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Matt Layher <mdlayher@gmail.com>
diff --git a/unix/linux/types.go b/unix/linux/types.go
index 71499f6..e93be95 100644
--- a/unix/linux/types.go
+++ b/unix/linux/types.go
@@ -459,6 +459,8 @@
 
 type ItimerSpec C.struct_itimerspec
 
+type Itimerval C.struct_itimerval
+
 const (
 	TIME_OK    = C.TIME_OK
 	TIME_INS   = C.TIME_INS
diff --git a/unix/mkerrors.sh b/unix/mkerrors.sh
index a47b035..e92ddea 100755
--- a/unix/mkerrors.sh
+++ b/unix/mkerrors.sh
@@ -597,6 +597,7 @@
 		$2 ~ /^DEVLINK_/ ||
 		$2 ~ /^ETHTOOL_/ ||
 		$2 ~ /^LWTUNNEL_IP/ ||
+		$2 ~ /^ITIMER_/ ||
 		$2 !~ "WMESGLEN" &&
 		$2 ~ /^W[A-Z0-9]+$/ ||
 		$2 ~/^PPPIOC/ ||
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index e52e8b9..f5915e2 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -14,6 +14,7 @@
 import (
 	"encoding/binary"
 	"syscall"
+	"time"
 	"unsafe"
 )
 
@@ -2314,6 +2315,52 @@
 //sys	shmdt(addr uintptr) (err error)
 //sys	shmget(key int, size int, flag int) (id int, err error)
 
+//sys	getitimer(which int, currValue *Itimerval) (err error)
+//sys	setitimer(which int, newValue *Itimerval, oldValue *Itimerval) (err error)
+
+// MakeItimerval creates an Itimerval from interval and value durations.
+func MakeItimerval(interval, value time.Duration) Itimerval {
+	return Itimerval{
+		Interval: NsecToTimeval(interval.Nanoseconds()),
+		Value:    NsecToTimeval(value.Nanoseconds()),
+	}
+}
+
+// A value which may be passed to the which parameter for Getitimer and
+// Setitimer.
+type ItimerWhich int
+
+// Possible which values for Getitimer and Setitimer.
+const (
+	ItimerReal    ItimerWhich = ITIMER_REAL
+	ItimerVirtual ItimerWhich = ITIMER_VIRTUAL
+	ItimerProf    ItimerWhich = ITIMER_PROF
+)
+
+// Getitimer wraps getitimer(2) to return the current value of the timer
+// specified by which.
+func Getitimer(which ItimerWhich) (Itimerval, error) {
+	var it Itimerval
+	if err := getitimer(int(which), &it); err != nil {
+		return Itimerval{}, err
+	}
+
+	return it, nil
+}
+
+// Setitimer wraps setitimer(2) to arm or disarm the timer specified by which.
+// It returns the previous value of the timer.
+//
+// If the Itimerval argument is the zero value, the timer will be disarmed.
+func Setitimer(which ItimerWhich, it Itimerval) (Itimerval, error) {
+	var prev Itimerval
+	if err := setitimer(int(which), &it, &prev); err != nil {
+		return Itimerval{}, err
+	}
+
+	return prev, nil
+}
+
 /*
  * Unimplemented
  */
@@ -2333,7 +2380,6 @@
 // GetMempolicy
 // GetRobustList
 // GetThreadArea
-// Getitimer
 // Getpmsg
 // IoCancel
 // IoDestroy
diff --git a/unix/zerrors_linux.go b/unix/zerrors_linux.go
index 6bce658..664db64 100644
--- a/unix/zerrors_linux.go
+++ b/unix/zerrors_linux.go
@@ -1268,6 +1268,9 @@
 	IP_XFRM_POLICY                              = 0x11
 	ISOFS_SUPER_MAGIC                           = 0x9660
 	ISTRIP                                      = 0x20
+	ITIMER_PROF                                 = 0x2
+	ITIMER_REAL                                 = 0x0
+	ITIMER_VIRTUAL                              = 0x1
 	IUTF8                                       = 0x4000
 	IXANY                                       = 0x800
 	JFFS2_SUPER_MAGIC                           = 0x72b6
diff --git a/unix/zsyscall_linux.go b/unix/zsyscall_linux.go
index 93edda4..30fa405 100644
--- a/unix/zsyscall_linux.go
+++ b/unix/zsyscall_linux.go
@@ -2032,3 +2032,23 @@
 	}
 	return
 }
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func getitimer(which int, currValue *Itimerval) (err error) {
+	_, _, e1 := Syscall(SYS_GETITIMER, uintptr(which), uintptr(unsafe.Pointer(currValue)), 0)
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func setitimer(which int, newValue *Itimerval, oldValue *Itimerval) (err error) {
+	_, _, e1 := Syscall(SYS_SETITIMER, uintptr(which), uintptr(unsafe.Pointer(newValue)), uintptr(unsafe.Pointer(oldValue)))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
diff --git a/unix/ztypes_linux.go b/unix/ztypes_linux.go
index 66788f1..824cadb 100644
--- a/unix/ztypes_linux.go
+++ b/unix/ztypes_linux.go
@@ -24,6 +24,11 @@
 	Value    Timespec
 }
 
+type Itimerval struct {
+	Interval Timeval
+	Value    Timeval
+}
+
 const (
 	TIME_OK    = 0x0
 	TIME_INS   = 0x1