1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build linux
6
7package unix_test
8
9import (
10	"bufio"
11	"bytes"
12	"errors"
13	"fmt"
14	"io/ioutil"
15	"os"
16	"runtime"
17	"runtime/debug"
18	"strconv"
19	"strings"
20	"testing"
21	"time"
22	"unsafe"
23
24	"golang.org/x/sys/unix"
25)
26
27func TestIoctlGetInt(t *testing.T) {
28	f, err := os.Open("/dev/random")
29	if err != nil {
30		t.Fatalf("failed to open device: %v", err)
31	}
32	defer f.Close()
33
34	v, err := unix.IoctlGetInt(int(f.Fd()), unix.RNDGETENTCNT)
35	if err != nil {
36		t.Fatalf("failed to perform ioctl: %v", err)
37	}
38
39	t.Logf("%d bits of entropy available", v)
40}
41
42func TestIoctlRetInt(t *testing.T) {
43	f, err := os.Open("/proc/self/ns/mnt")
44	if err != nil {
45		t.Skipf("skipping test, %v", err)
46	}
47	defer f.Close()
48
49	v, err := unix.IoctlRetInt(int(f.Fd()), unix.NS_GET_NSTYPE)
50	if err != nil {
51		if err == unix.ENOTTY {
52			t.Skipf("old kernel? (need Linux >= 4.11)")
53		}
54		t.Fatalf("failed to perform ioctl: %v", err)
55	}
56	if v != unix.CLONE_NEWNS {
57		t.Fatalf("unexpected return from ioctl; expected %v, got %v", v, unix.CLONE_NEWNS)
58	}
59}
60
61func TestIoctlGetRTCTime(t *testing.T) {
62	f, err := os.Open("/dev/rtc0")
63	if err != nil {
64		t.Skipf("skipping test, %v", err)
65	}
66	defer f.Close()
67
68	v, err := unix.IoctlGetRTCTime(int(f.Fd()))
69	if err != nil {
70		t.Fatalf("failed to perform ioctl: %v", err)
71	}
72
73	t.Logf("RTC time: %04d-%02d-%02d %02d:%02d:%02d", v.Year+1900, v.Mon+1, v.Mday, v.Hour, v.Min, v.Sec)
74}
75
76func TestIoctlGetRTCWkAlrm(t *testing.T) {
77	f, err := os.Open("/dev/rtc0")
78	if err != nil {
79		t.Skipf("skipping test, %v", err)
80	}
81	defer f.Close()
82
83	v, err := unix.IoctlGetRTCWkAlrm(int(f.Fd()))
84	if err != nil {
85		t.Fatalf("failed to perform ioctl: %v", err)
86	}
87
88	t.Logf("RTC wake alarm enabled '%d'; time: %04d-%02d-%02d %02d:%02d:%02d",
89		v.Enabled, v.Time.Year+1900, v.Time.Mon+1, v.Time.Mday, v.Time.Hour, v.Time.Min, v.Time.Sec)
90}
91
92func TestPpoll(t *testing.T) {
93	if runtime.GOOS == "android" {
94		t.Skip("mkfifo syscall is not available on android, skipping test")
95	}
96
97	defer chtmpdir(t)()
98	f, cleanup := mktmpfifo(t)
99	defer cleanup()
100
101	const timeout = 100 * time.Millisecond
102
103	ok := make(chan bool, 1)
104	go func() {
105		select {
106		case <-time.After(10 * timeout):
107			t.Errorf("Ppoll: failed to timeout after %d", 10*timeout)
108		case <-ok:
109		}
110	}()
111
112	fds := []unix.PollFd{{Fd: int32(f.Fd()), Events: unix.POLLIN}}
113	timeoutTs := unix.NsecToTimespec(int64(timeout))
114	n, err := unix.Ppoll(fds, &timeoutTs, nil)
115	ok <- true
116	if err != nil {
117		t.Errorf("Ppoll: unexpected error: %v", err)
118		return
119	}
120	if n != 0 {
121		t.Errorf("Ppoll: wrong number of events: got %v, expected %v", n, 0)
122		return
123	}
124}
125
126func TestTime(t *testing.T) {
127	var ut unix.Time_t
128	ut2, err := unix.Time(&ut)
129	if err != nil {
130		t.Fatalf("Time: %v", err)
131	}
132	if ut != ut2 {
133		t.Errorf("Time: return value %v should be equal to argument %v", ut2, ut)
134	}
135
136	var now time.Time
137
138	for i := 0; i < 10; i++ {
139		ut, err = unix.Time(nil)
140		if err != nil {
141			t.Fatalf("Time: %v", err)
142		}
143
144		now = time.Now()
145		diff := int64(ut) - now.Unix()
146		if -1 <= diff && diff <= 1 {
147			return
148		}
149	}
150
151	t.Errorf("Time: return value %v should be nearly equal to time.Now().Unix() %v±1", ut, now.Unix())
152}
153
154func TestUtime(t *testing.T) {
155	defer chtmpdir(t)()
156
157	touch(t, "file1")
158
159	buf := &unix.Utimbuf{
160		Modtime: 12345,
161	}
162
163	err := unix.Utime("file1", buf)
164	if err != nil {
165		t.Fatalf("Utime: %v", err)
166	}
167
168	fi, err := os.Stat("file1")
169	if err != nil {
170		t.Fatal(err)
171	}
172
173	if fi.ModTime().Unix() != 12345 {
174		t.Errorf("Utime: failed to change modtime: expected %v, got %v", 12345, fi.ModTime().Unix())
175	}
176}
177
178func TestRlimitAs(t *testing.T) {
179	// disable GC during to avoid flaky test
180	defer debug.SetGCPercent(debug.SetGCPercent(-1))
181
182	var rlim unix.Rlimit
183	err := unix.Getrlimit(unix.RLIMIT_AS, &rlim)
184	if err != nil {
185		t.Fatalf("Getrlimit: %v", err)
186	}
187	var zero unix.Rlimit
188	if zero == rlim {
189		t.Fatalf("Getrlimit: got zero value %#v", rlim)
190	}
191	set := rlim
192	set.Cur = uint64(unix.Getpagesize())
193	err = unix.Setrlimit(unix.RLIMIT_AS, &set)
194	if err != nil {
195		t.Fatalf("Setrlimit: set failed: %#v %v", set, err)
196	}
197
198	// RLIMIT_AS was set to the page size, so mmap()'ing twice the page size
199	// should fail. See 'man 2 getrlimit'.
200	_, err = unix.Mmap(-1, 0, 2*unix.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_PRIVATE)
201	if err == nil {
202		t.Fatal("Mmap: unexpectedly succeeded after setting RLIMIT_AS")
203	}
204
205	err = unix.Setrlimit(unix.RLIMIT_AS, &rlim)
206	if err != nil {
207		t.Fatalf("Setrlimit: restore failed: %#v %v", rlim, err)
208	}
209
210	b, err := unix.Mmap(-1, 0, 2*unix.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_PRIVATE)
211	if err != nil {
212		t.Fatalf("Mmap: %v", err)
213	}
214	err = unix.Munmap(b)
215	if err != nil {
216		t.Fatalf("Munmap: %v", err)
217	}
218}
219
220func TestPselect(t *testing.T) {
221	for {
222		n, err := unix.Pselect(0, nil, nil, nil, &unix.Timespec{Sec: 0, Nsec: 0}, nil)
223		if err == unix.EINTR {
224			t.Logf("Pselect interrupted")
225			continue
226		} else if err != nil {
227			t.Fatalf("Pselect: %v", err)
228		}
229		if n != 0 {
230			t.Fatalf("Pselect: got %v ready file descriptors, expected 0", n)
231		}
232		break
233	}
234
235	dur := 2500 * time.Microsecond
236	ts := unix.NsecToTimespec(int64(dur))
237	var took time.Duration
238	for {
239		start := time.Now()
240		n, err := unix.Pselect(0, nil, nil, nil, &ts, nil)
241		took = time.Since(start)
242		if err == unix.EINTR {
243			t.Logf("Pselect interrupted after %v", took)
244			continue
245		} else if err != nil {
246			t.Fatalf("Pselect: %v", err)
247		}
248		if n != 0 {
249			t.Fatalf("Pselect: got %v ready file descriptors, expected 0", n)
250		}
251		break
252	}
253
254	if took < dur {
255		t.Errorf("Pselect: timeout should have been at least %v, got %v", dur, took)
256	}
257}
258
259func TestSchedSetaffinity(t *testing.T) {
260	var newMask unix.CPUSet
261	newMask.Zero()
262	if newMask.Count() != 0 {
263		t.Errorf("CpuZero: didn't zero CPU set: %v", newMask)
264	}
265	cpu := 1
266	newMask.Set(cpu)
267	if newMask.Count() != 1 || !newMask.IsSet(cpu) {
268		t.Errorf("CpuSet: didn't set CPU %d in set: %v", cpu, newMask)
269	}
270	cpu = 5
271	newMask.Set(cpu)
272	if newMask.Count() != 2 || !newMask.IsSet(cpu) {
273		t.Errorf("CpuSet: didn't set CPU %d in set: %v", cpu, newMask)
274	}
275	newMask.Clear(cpu)
276	if newMask.Count() != 1 || newMask.IsSet(cpu) {
277		t.Errorf("CpuClr: didn't clear CPU %d in set: %v", cpu, newMask)
278	}
279
280	runtime.LockOSThread()
281	defer runtime.UnlockOSThread()
282
283	var oldMask unix.CPUSet
284	err := unix.SchedGetaffinity(0, &oldMask)
285	if err != nil {
286		t.Fatalf("SchedGetaffinity: %v", err)
287	}
288
289	if runtime.NumCPU() < 2 {
290		t.Skip("skipping setaffinity tests on single CPU system")
291	}
292	if runtime.GOOS == "android" {
293		t.Skip("skipping setaffinity tests on android")
294	}
295
296	// On a system like ppc64x where some cores can be disabled using ppc64_cpu,
297	// setaffinity should only be called with enabled cores. The valid cores
298	// are found from the oldMask, but if none are found then the setaffinity
299	// tests are skipped. Issue #27875.
300	cpu = 1
301	if !oldMask.IsSet(cpu) {
302		newMask.Zero()
303		for i := 0; i < len(oldMask); i++ {
304			if oldMask.IsSet(i) {
305				newMask.Set(i)
306				break
307			}
308		}
309		if newMask.Count() == 0 {
310			t.Skip("skipping setaffinity tests if CPU not available")
311		}
312	}
313
314	err = unix.SchedSetaffinity(0, &newMask)
315	if err != nil {
316		t.Fatalf("SchedSetaffinity: %v", err)
317	}
318
319	var gotMask unix.CPUSet
320	err = unix.SchedGetaffinity(0, &gotMask)
321	if err != nil {
322		t.Fatalf("SchedGetaffinity: %v", err)
323	}
324
325	if gotMask != newMask {
326		t.Errorf("SchedSetaffinity: returned affinity mask does not match set affinity mask")
327	}
328
329	// Restore old mask so it doesn't affect successive tests
330	err = unix.SchedSetaffinity(0, &oldMask)
331	if err != nil {
332		t.Fatalf("SchedSetaffinity: %v", err)
333	}
334}
335
336func TestStatx(t *testing.T) {
337	var stx unix.Statx_t
338	err := unix.Statx(unix.AT_FDCWD, ".", 0, 0, &stx)
339	if err == unix.ENOSYS || err == unix.EPERM {
340		t.Skip("statx syscall is not available, skipping test")
341	} else if err != nil {
342		t.Fatalf("Statx: %v", err)
343	}
344
345	defer chtmpdir(t)()
346	touch(t, "file1")
347
348	var st unix.Stat_t
349	err = unix.Stat("file1", &st)
350	if err != nil {
351		t.Fatalf("Stat: %v", err)
352	}
353
354	flags := unix.AT_STATX_SYNC_AS_STAT
355	err = unix.Statx(unix.AT_FDCWD, "file1", flags, unix.STATX_ALL, &stx)
356	if err != nil {
357		t.Fatalf("Statx: %v", err)
358	}
359
360	if uint32(stx.Mode) != st.Mode {
361		t.Errorf("Statx: returned stat mode does not match Stat")
362	}
363
364	ctime := unix.StatxTimestamp{Sec: int64(st.Ctim.Sec), Nsec: uint32(st.Ctim.Nsec)}
365	mtime := unix.StatxTimestamp{Sec: int64(st.Mtim.Sec), Nsec: uint32(st.Mtim.Nsec)}
366
367	if stx.Ctime != ctime {
368		t.Errorf("Statx: returned stat ctime does not match Stat")
369	}
370	if stx.Mtime != mtime {
371		t.Errorf("Statx: returned stat mtime does not match Stat")
372	}
373
374	err = os.Symlink("file1", "symlink1")
375	if err != nil {
376		t.Fatal(err)
377	}
378
379	err = unix.Lstat("symlink1", &st)
380	if err != nil {
381		t.Fatalf("Lstat: %v", err)
382	}
383
384	err = unix.Statx(unix.AT_FDCWD, "symlink1", flags, unix.STATX_BASIC_STATS, &stx)
385	if err != nil {
386		t.Fatalf("Statx: %v", err)
387	}
388
389	// follow symlink, expect a regulat file
390	if stx.Mode&unix.S_IFREG == 0 {
391		t.Errorf("Statx: didn't follow symlink")
392	}
393
394	err = unix.Statx(unix.AT_FDCWD, "symlink1", flags|unix.AT_SYMLINK_NOFOLLOW, unix.STATX_ALL, &stx)
395	if err != nil {
396		t.Fatalf("Statx: %v", err)
397	}
398
399	// follow symlink, expect a symlink
400	if stx.Mode&unix.S_IFLNK == 0 {
401		t.Errorf("Statx: unexpectedly followed symlink")
402	}
403	if uint32(stx.Mode) != st.Mode {
404		t.Errorf("Statx: returned stat mode does not match Lstat")
405	}
406
407	ctime = unix.StatxTimestamp{Sec: int64(st.Ctim.Sec), Nsec: uint32(st.Ctim.Nsec)}
408	mtime = unix.StatxTimestamp{Sec: int64(st.Mtim.Sec), Nsec: uint32(st.Mtim.Nsec)}
409
410	if stx.Ctime != ctime {
411		t.Errorf("Statx: returned stat ctime does not match Lstat")
412	}
413	if stx.Mtime != mtime {
414		t.Errorf("Statx: returned stat mtime does not match Lstat")
415	}
416}
417
418// stringsFromByteSlice converts a sequence of attributes to a []string.
419// On Linux, each entry is a NULL-terminated string.
420func stringsFromByteSlice(buf []byte) []string {
421	var result []string
422	off := 0
423	for i, b := range buf {
424		if b == 0 {
425			result = append(result, string(buf[off:i]))
426			off = i + 1
427		}
428	}
429	return result
430}
431
432func TestFaccessat(t *testing.T) {
433	defer chtmpdir(t)()
434	touch(t, "file1")
435
436	err := unix.Faccessat(unix.AT_FDCWD, "file1", unix.R_OK, 0)
437	if err != nil {
438		t.Errorf("Faccessat: unexpected error: %v", err)
439	}
440
441	err = unix.Faccessat(unix.AT_FDCWD, "file1", unix.R_OK, 2)
442	if err != unix.EINVAL {
443		t.Errorf("Faccessat: unexpected error: %v, want EINVAL", err)
444	}
445
446	err = unix.Faccessat(unix.AT_FDCWD, "file1", unix.R_OK, unix.AT_EACCESS)
447	if err != nil {
448		t.Errorf("Faccessat: unexpected error: %v", err)
449	}
450
451	err = os.Symlink("file1", "symlink1")
452	if err != nil {
453		t.Fatal(err)
454	}
455
456	err = unix.Faccessat(unix.AT_FDCWD, "symlink1", unix.R_OK, unix.AT_SYMLINK_NOFOLLOW)
457	if err != nil {
458		t.Errorf("Faccessat SYMLINK_NOFOLLOW: unexpected error %v", err)
459	}
460
461	// We can't really test AT_SYMLINK_NOFOLLOW, because there
462	// doesn't seem to be any way to change the mode of a symlink.
463	// We don't test AT_EACCESS because such tests are only
464	// meaningful if run as root.
465
466	err = unix.Fchmodat(unix.AT_FDCWD, "file1", 0, 0)
467	if err != nil {
468		t.Errorf("Fchmodat: unexpected error %v", err)
469	}
470
471	err = unix.Faccessat(unix.AT_FDCWD, "file1", unix.F_OK, unix.AT_SYMLINK_NOFOLLOW)
472	if err != nil {
473		t.Errorf("Faccessat: unexpected error: %v", err)
474	}
475
476	err = unix.Faccessat(unix.AT_FDCWD, "file1", unix.R_OK, unix.AT_SYMLINK_NOFOLLOW)
477	if err != unix.EACCES {
478		if unix.Getuid() != 0 {
479			t.Errorf("Faccessat: unexpected error: %v, want EACCES", err)
480		}
481	}
482}
483
484func TestSyncFileRange(t *testing.T) {
485	file, err := ioutil.TempFile("", "TestSyncFileRange")
486	if err != nil {
487		t.Fatal(err)
488	}
489	defer os.Remove(file.Name())
490	defer file.Close()
491
492	err = unix.SyncFileRange(int(file.Fd()), 0, 0, 0)
493	if err == unix.ENOSYS || err == unix.EPERM {
494		t.Skip("sync_file_range syscall is not available, skipping test")
495	} else if err != nil {
496		t.Fatalf("SyncFileRange: %v", err)
497	}
498
499	// invalid flags
500	flags := 0xf00
501	err = unix.SyncFileRange(int(file.Fd()), 0, 0, flags)
502	if err != unix.EINVAL {
503		t.Fatalf("SyncFileRange: unexpected error: %v, want EINVAL", err)
504	}
505}
506
507func TestClockNanosleep(t *testing.T) {
508	delay := 50 * time.Millisecond
509
510	// Relative timespec.
511	start := time.Now()
512	rel := unix.NsecToTimespec(delay.Nanoseconds())
513	remain := unix.Timespec{}
514	for {
515		err := unix.ClockNanosleep(unix.CLOCK_MONOTONIC, 0, &rel, &remain)
516		if err == unix.ENOSYS || err == unix.EPERM {
517			t.Skip("clock_nanosleep syscall is not available, skipping test")
518		} else if err == unix.EINTR {
519			t.Logf("ClockNanosleep interrupted after %v", time.Since(start))
520			rel = remain
521			continue
522		} else if err != nil {
523			t.Errorf("ClockNanosleep(CLOCK_MONOTONIC, 0, %#v, nil) = %v", &rel, err)
524		} else if slept := time.Since(start); slept < delay {
525			t.Errorf("ClockNanosleep(CLOCK_MONOTONIC, 0, %#v, nil) slept only %v", &rel, slept)
526		}
527		break
528	}
529
530	// Absolute timespec.
531	for {
532		start = time.Now()
533		until := start.Add(delay)
534		abs := unix.NsecToTimespec(until.UnixNano())
535		err := unix.ClockNanosleep(unix.CLOCK_REALTIME, unix.TIMER_ABSTIME, &abs, nil)
536		if err == unix.EINTR {
537			t.Logf("ClockNanosleep interrupted after %v", time.Since(start))
538			continue
539		} else if err != nil {
540			t.Errorf("ClockNanosleep(CLOCK_REALTIME, TIMER_ABSTIME, %#v (=%v), nil) = %v", &abs, until, err)
541		} else if slept := time.Since(start); slept < delay {
542			t.Errorf("ClockNanosleep(CLOCK_REALTIME, TIMER_ABSTIME, %#v (=%v), nil) slept only %v", &abs, until, slept)
543		}
544		break
545	}
546
547	// Invalid clock. clock_nanosleep(2) says EINVAL, but it’s actually EOPNOTSUPP.
548	err := unix.ClockNanosleep(unix.CLOCK_THREAD_CPUTIME_ID, 0, &rel, nil)
549	if err != unix.EINVAL && err != unix.EOPNOTSUPP {
550		t.Errorf("ClockNanosleep(CLOCK_THREAD_CPUTIME_ID, 0, %#v, nil) = %v, want EINVAL or EOPNOTSUPP", &rel, err)
551	}
552}
553
554func TestOpenByHandleAt(t *testing.T) {
555	skipIfNotSupported := func(t *testing.T, name string, err error) {
556		if err == unix.EPERM {
557			t.Skipf("skipping %s test without CAP_DAC_READ_SEARCH", name)
558		}
559		if err == unix.ENOSYS {
560			t.Skipf("%s system call not available", name)
561		}
562		if err == unix.EOPNOTSUPP {
563			t.Skipf("%s not supported on this filesystem", name)
564		}
565	}
566
567	h, mountID, err := unix.NameToHandleAt(unix.AT_FDCWD, "syscall_linux_test.go", 0)
568	if err != nil {
569		skipIfNotSupported(t, "name_to_handle_at", err)
570		t.Fatalf("NameToHandleAt: %v", err)
571	}
572	t.Logf("mountID: %v, handle: size=%d, type=%d, bytes=%q", mountID,
573		h.Size(), h.Type(), h.Bytes())
574	mount, err := openMountByID(mountID)
575	if err != nil {
576		t.Fatalf("openMountByID: %v", err)
577	}
578	defer mount.Close()
579
580	for _, clone := range []bool{false, true} {
581		t.Run("clone="+strconv.FormatBool(clone), func(t *testing.T) {
582			if clone {
583				h = unix.NewFileHandle(h.Type(), h.Bytes())
584			}
585			fd, err := unix.OpenByHandleAt(int(mount.Fd()), h, unix.O_RDONLY)
586			skipIfNotSupported(t, "open_by_handle_at", err)
587			if err != nil {
588				t.Fatalf("OpenByHandleAt: %v", err)
589			}
590			defer unix.Close(fd)
591
592			t.Logf("opened fd %v", fd)
593			f := os.NewFile(uintptr(fd), "")
594			slurp, err := ioutil.ReadAll(f)
595			if err != nil {
596				t.Fatal(err)
597			}
598			const substr = "Some substring for a test."
599			if !strings.Contains(string(slurp), substr) {
600				t.Errorf("didn't find substring %q in opened file; read %d bytes", substr, len(slurp))
601			}
602		})
603	}
604}
605
606func openMountByID(mountID int) (f *os.File, err error) {
607	mi, err := os.Open("/proc/self/mountinfo")
608	if err != nil {
609		return nil, err
610	}
611	defer mi.Close()
612	bs := bufio.NewScanner(mi)
613	wantPrefix := []byte(fmt.Sprintf("%v ", mountID))
614	for bs.Scan() {
615		if !bytes.HasPrefix(bs.Bytes(), wantPrefix) {
616			continue
617		}
618		fields := strings.Fields(bs.Text())
619		dev := fields[4]
620		return os.Open(dev)
621	}
622	if err := bs.Err(); err != nil {
623		return nil, err
624	}
625	return nil, errors.New("mountID not found")
626}
627
628func TestEpoll(t *testing.T) {
629	efd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC)
630	if err != nil {
631		t.Fatalf("EpollCreate1: %v", err)
632	}
633	defer unix.Close(efd)
634
635	r, w, err := os.Pipe()
636	if err != nil {
637		t.Fatal(err)
638	}
639	defer r.Close()
640	defer w.Close()
641
642	fd := int(r.Fd())
643	ev := unix.EpollEvent{Events: unix.EPOLLIN, Fd: int32(fd)}
644
645	err = unix.EpollCtl(efd, unix.EPOLL_CTL_ADD, fd, &ev)
646	if err != nil {
647		t.Fatalf("EpollCtl: %v", err)
648	}
649
650	if _, err := w.Write([]byte("HELLO GOPHER")); err != nil {
651		t.Fatal(err)
652	}
653
654	events := make([]unix.EpollEvent, 128)
655	n, err := unix.EpollWait(efd, events, 1)
656	if err != nil {
657		t.Fatalf("EpollWait: %v", err)
658	}
659
660	if n != 1 {
661		t.Errorf("EpollWait: wrong number of events: got %v, expected 1", n)
662	}
663
664	got := int(events[0].Fd)
665	if got != fd {
666		t.Errorf("EpollWait: wrong Fd in event: got %v, expected %v", got, fd)
667	}
668}
669
670func TestPrctlRetInt(t *testing.T) {
671	err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
672	if err != nil {
673		t.Skipf("Prctl: %v, skipping test", err)
674	}
675	v, err := unix.PrctlRetInt(unix.PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)
676	if err != nil {
677		t.Fatalf("failed to perform prctl: %v", err)
678	}
679	if v != 1 {
680		t.Fatalf("unexpected return from prctl; got %v, expected %v", v, 1)
681	}
682}
683
684func TestTimerfd(t *testing.T) {
685	var now unix.Timespec
686	if err := unix.ClockGettime(unix.CLOCK_REALTIME, &now); err != nil {
687		t.Fatalf("ClockGettime: %v", err)
688	}
689
690	tfd, err := unix.TimerfdCreate(unix.CLOCK_REALTIME, 0)
691	if err == unix.ENOSYS {
692		t.Skip("timerfd_create system call not implemented")
693	} else if err != nil {
694		t.Fatalf("TimerfdCreate: %v", err)
695	}
696	defer unix.Close(tfd)
697
698	var timeSpec unix.ItimerSpec
699	if err := unix.TimerfdGettime(tfd, &timeSpec); err != nil {
700		t.Fatalf("TimerfdGettime: %v", err)
701	}
702
703	if timeSpec.Value.Nsec != 0 || timeSpec.Value.Sec != 0 {
704		t.Fatalf("TimerfdGettime: timer is already set, but shouldn't be")
705	}
706
707	timeSpec = unix.ItimerSpec{
708		Interval: unix.NsecToTimespec(int64(time.Millisecond)),
709		Value:    now,
710	}
711
712	if err := unix.TimerfdSettime(tfd, unix.TFD_TIMER_ABSTIME, &timeSpec, nil); err != nil {
713		t.Fatalf("TimerfdSettime: %v", err)
714	}
715
716	const totalTicks = 10
717	const bufferLength = 8
718
719	buffer := make([]byte, bufferLength)
720
721	var count uint64 = 0
722	for count < totalTicks {
723		n, err := unix.Read(tfd, buffer)
724		if err != nil {
725			t.Fatalf("Timerfd: %v", err)
726		} else if n != bufferLength {
727			t.Fatalf("Timerfd: got %d bytes from timerfd, expected %d bytes", n, bufferLength)
728		}
729
730		count += *(*uint64)(unsafe.Pointer(&buffer))
731	}
732}
733