1const builtin = @import("builtin");
2
3const std = @import("../../std.zig");
4const assert = std.debug.assert;
5const linux = std.os.linux;
6const mem = std.mem;
7const elf = std.elf;
8const expect = std.testing.expect;
9const expectEqual = std.testing.expectEqual;
10const fs = std.fs;
11
12test "fallocate" {
13 const io = std.testing.io;
14
15 var tmp = std.testing.tmpDir(.{});
16 defer tmp.cleanup();
17
18 const path = "test_fallocate";
19 const file = try tmp.dir.createFile(io, path, .{ .truncate = true, .permissions = .fromMode(0o666) });
20 defer file.close(io);
21
22 try expect((try file.stat(io)).size == 0);
23
24 const len: i64 = 65536;
25 switch (linux.errno(linux.fallocate(file.handle, 0, 0, len))) {
26 .SUCCESS => {},
27 .NOSYS => return error.SkipZigTest,
28 .OPNOTSUPP => return error.SkipZigTest,
29 else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),
30 }
31
32 try expect((try file.stat(io)).size == len);
33}
34
35test "getpid" {
36 try expect(linux.getpid() != 0);
37}
38
39test "getppid" {
40 try expect(linux.getppid() != 0);
41}
42
43test "timer" {
44 const epoll_fd = linux.epoll_create();
45 var err: linux.E = linux.errno(epoll_fd);
46 try expect(err == .SUCCESS);
47
48 const timer_fd = linux.timerfd_create(linux.TIMERFD_CLOCK.MONOTONIC, .{});
49 try expect(linux.errno(timer_fd) == .SUCCESS);
50
51 const time_interval = linux.timespec{
52 .sec = 0,
53 .nsec = 2000000,
54 };
55
56 const new_time = linux.itimerspec{
57 .it_interval = time_interval,
58 .it_value = time_interval,
59 };
60
61 err = linux.errno(linux.timerfd_settime(@as(i32, @intCast(timer_fd)), .{}, &new_time, null));
62 try expect(err == .SUCCESS);
63
64 var event = linux.epoll_event{
65 .events = linux.EPOLL.IN | linux.EPOLL.OUT | linux.EPOLL.ET,
66 .data = linux.epoll_data{ .ptr = 0 },
67 };
68
69 err = linux.errno(linux.epoll_ctl(@as(i32, @intCast(epoll_fd)), linux.EPOLL.CTL_ADD, @as(i32, @intCast(timer_fd)), &event));
70 try expect(err == .SUCCESS);
71
72 const events_one: linux.epoll_event = undefined;
73 var events: [8]linux.epoll_event = @splat(events_one);
74
75 err = linux.errno(linux.epoll_wait(@as(i32, @intCast(epoll_fd)), &events, 8, -1));
76 try expect(err == .SUCCESS);
77}
78
79test "statx" {
80 const io = std.testing.io;
81
82 var tmp = std.testing.tmpDir(.{});
83 defer tmp.cleanup();
84
85 const tmp_file_name = "just_a_temporary_file.txt";
86 var file = try tmp.dir.createFile(io, tmp_file_name, .{});
87 defer file.close(io);
88
89 var buf: linux.Statx = undefined;
90 switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, .BASIC_STATS, &buf))) {
91 .SUCCESS => {},
92 else => unreachable,
93 }
94
95 const uid = linux.getuid();
96 const gid = linux.getgid();
97 if (buf.mask.MODE)
98 try expectEqual(@as(linux.mode_t, linux.S.IFREG), buf.mode & linux.S.IFMT);
99 if (buf.mask.UID)
100 try expectEqual(uid, buf.uid);
101 if (buf.mask.GID)
102 try expectEqual(gid, buf.gid);
103 if (buf.mask.SIZE)
104 try expectEqual(@as(u64, 0), buf.size);
105}
106
107test "user and group ids" {
108 if (builtin.link_libc) return error.SkipZigTest;
109 try expectEqual(linux.getauxval(elf.AT_UID), linux.getuid());
110 try expectEqual(linux.getauxval(elf.AT_GID), linux.getgid());
111 try expectEqual(linux.getauxval(elf.AT_EUID), linux.geteuid());
112 try expectEqual(linux.getauxval(elf.AT_EGID), linux.getegid());
113}
114
115test "fadvise" {
116 const io = std.testing.io;
117
118 var tmp = std.testing.tmpDir(.{});
119 defer tmp.cleanup();
120
121 const tmp_file_name = "temp_posix_fadvise.txt";
122 var file = try tmp.dir.createFile(io, tmp_file_name, .{});
123 defer file.close(io);
124
125 var buf: [2048]u8 = undefined;
126 try file.writeStreamingAll(io, &buf);
127
128 const ret = linux.fadvise(file.handle, 0, 0, linux.POSIX_FADV.SEQUENTIAL);
129 try expectEqual(@as(usize, 0), ret);
130}
131
132test "sigset_t" {
133 const SIG = linux.SIG;
134 assert(@sizeOf(linux.sigset_t) == (linux.NSIG / 8));
135
136 var sigset = linux.sigemptyset();
137
138 // See that none are set, then set each one, see that they're all set, then
139 // remove them all, and then see that none are set.
140 for (1..linux.NSIG) |i| {
141 const sig = std.enums.fromInt(SIG, i) orelse continue;
142 try expectEqual(false, linux.sigismember(&sigset, sig));
143 }
144 for (1..linux.NSIG) |i| {
145 const sig = std.enums.fromInt(SIG, i) orelse continue;
146 linux.sigaddset(&sigset, sig);
147 }
148 for (1..linux.NSIG) |i| {
149 const sig = std.enums.fromInt(SIG, i) orelse continue;
150 try expectEqual(true, linux.sigismember(&sigset, sig));
151 }
152 for (1..linux.NSIG) |i| {
153 const sig = std.enums.fromInt(SIG, i) orelse continue;
154 linux.sigdelset(&sigset, sig);
155 }
156 for (1..linux.NSIG) |i| {
157 const sig = std.enums.fromInt(SIG, i) orelse continue;
158 try expectEqual(false, linux.sigismember(&sigset, sig));
159 }
160}
161
162test "sigfillset" {
163 // unlike the C library, all the signals are set in the kernel-level fillset
164 const sigset = linux.sigfillset();
165 for (1..linux.NSIG) |i| {
166 const sig = std.enums.fromInt(linux.SIG, i) orelse continue;
167 try expectEqual(true, linux.sigismember(&sigset, sig));
168 }
169}
170
171test "sigemptyset" {
172 const sigset = linux.sigemptyset();
173 for (1..linux.NSIG) |i| {
174 const sig = std.enums.fromInt(linux.SIG, i) orelse continue;
175 try expectEqual(false, linux.sigismember(&sigset, sig));
176 }
177}
178
179test "sysinfo" {
180 var info: linux.Sysinfo = undefined;
181 const result: usize = linux.sysinfo(&info);
182 try expect(std.os.linux.errno(result) == .SUCCESS);
183
184 try expect(info.mem_unit > 0);
185 try expect(info.mem_unit <= std.heap.page_size_max);
186}
187
188comptime {
189 assert(128 == @as(u32, @bitCast(linux.FUTEX_OP{ .cmd = @fromBackingInt(@intCast(0)), .private = true, .realtime = false })));
190 assert(256 == @as(u32, @bitCast(linux.FUTEX_OP{ .cmd = @fromBackingInt(@intCast(0)), .private = false, .realtime = true })));
191
192 // Check futex_param4 union is packed correctly
193 const param_union = linux.futex_param4{
194 .val2 = 0xaabbcc,
195 };
196 assert(@intFromPtr(param_union.timeout) == 0xaabbcc);
197}
198
199test "futex v1" {
200 var lock: std.atomic.Value(u32) = std.atomic.Value(u32).init(1);
201 var rc: usize = 0;
202
203 // No-op wait, lock value is not expected value
204 rc = linux.futex(&lock.raw, .{ .cmd = .WAIT, .private = true }, 2, .{ .timeout = null }, null, 0);
205 try expectEqual(.AGAIN, linux.errno(rc));
206
207 rc = linux.futex_4arg(&lock.raw, .{ .cmd = .WAIT, .private = true }, 2, null);
208 try expectEqual(.AGAIN, linux.errno(rc));
209
210 // Short-fuse wait, timeout kicks in
211 rc = linux.futex(&lock.raw, .{ .cmd = .WAIT, .private = true }, 1, .{ .timeout = &.{ .sec = 0, .nsec = 2 } }, null, 0);
212 try expectEqual(.TIMEDOUT, linux.errno(rc));
213
214 rc = linux.futex_4arg(&lock.raw, .{ .cmd = .WAIT, .private = true }, 1, &.{ .sec = 0, .nsec = 2 });
215 try expectEqual(.TIMEDOUT, linux.errno(rc));
216
217 // Wakeup (no waiters)
218 rc = linux.futex(&lock.raw, .{ .cmd = .WAKE, .private = true }, 2, .{ .timeout = null }, null, 0);
219 try expectEqual(0, rc);
220
221 rc = linux.futex_3arg(&lock.raw, .{ .cmd = .WAKE, .private = true }, 2);
222 try expectEqual(0, rc);
223
224 // CMP_REQUEUE - val3 mismatch
225 rc = linux.futex(&lock.raw, .{ .cmd = .CMP_REQUEUE, .private = true }, 2, .{ .val2 = 0 }, null, 99);
226 try expectEqual(.AGAIN, linux.errno(rc));
227
228 // CMP_REQUEUE - requeue (but no waiters, so ... not much)
229 {
230 const val3 = 1;
231 const wake_nr = 3;
232 const requeue_max = std.math.maxInt(u31);
233 var target_lock: std.atomic.Value(u32) = std.atomic.Value(u32).init(1);
234 rc = linux.futex(&lock.raw, .{ .cmd = .CMP_REQUEUE, .private = true }, wake_nr, .{ .val2 = requeue_max }, &target_lock.raw, val3);
235 try expectEqual(0, rc);
236 }
237
238 // WAKE_OP - just to see if we can construct the arguments ...
239 {
240 var lock2: std.atomic.Value(u32) = std.atomic.Value(u32).init(1);
241 const wake1_nr = 2;
242 const wake2_nr = 3;
243 const wake_op = linux.FUTEX_WAKE_OP{
244 .cmd = .ANDN,
245 .arg_shift = true,
246 .cmp = .LT,
247 .oparg = 4,
248 .cmdarg = 5,
249 };
250
251 rc = linux.futex(&lock.raw, .{ .cmd = .WAKE_OP, .private = true }, wake1_nr, .{ .val2 = wake2_nr }, &lock2.raw, @bitCast(wake_op));
252 try expectEqual(0, rc);
253 }
254
255 // WAIT_BITSET
256 {
257 // val1 return early
258 rc = linux.futex(&lock.raw, .{ .cmd = .WAIT_BITSET, .private = true }, 2, .{ .timeout = null }, null, 0xfff);
259 try expectEqual(.AGAIN, linux.errno(rc));
260
261 // timeout wait
262 const timeout: linux.timespec = .{ .sec = 0, .nsec = 2 };
263 rc = linux.futex(&lock.raw, .{ .cmd = .WAIT_BITSET, .private = true }, 1, .{ .timeout = &timeout }, null, 0xfff);
264 try expectEqual(.TIMEDOUT, linux.errno(rc));
265 }
266
267 // WAKE_BITSET
268 {
269 rc = linux.futex(&lock.raw, .{ .cmd = .WAKE_BITSET, .private = true }, 2, .{ .timeout = null }, null, 0xfff000);
270 try expectEqual(0, rc);
271
272 // bitmask must have at least 1 bit set:
273 rc = linux.futex(&lock.raw, .{ .cmd = .WAKE_BITSET, .private = true }, 2, .{ .timeout = null }, null, 0);
274 try expectEqual(.INVAL, linux.errno(rc));
275 }
276}
277
278comptime {
279 assert(2 == @as(u32, @bitCast(linux.FUTEX2_FLAGS{ .size = .U32, .private = false })));
280 assert(128 == @as(u32, @bitCast(linux.FUTEX2_FLAGS{ .size = @fromBackingInt(@intCast(0)), .private = true })));
281}
282
283test "futex2_waitv" {
284 const locks = [_]std.atomic.Value(u32){
285 std.atomic.Value(u32).init(1),
286 std.atomic.Value(u32).init(1),
287 std.atomic.Value(u32).init(1),
288 };
289
290 const futexes = [_]linux.futex2_waitone{
291 .{
292 .val = 1,
293 .uaddr = @intFromPtr(&locks[0].raw),
294 .flags = .{ .size = .U32, .private = true },
295 },
296 .{
297 .val = 1,
298 .uaddr = @intFromPtr(&locks[1].raw),
299 .flags = .{ .size = .U32, .private = true },
300 },
301 .{
302 .val = 1,
303 .uaddr = @intFromPtr(&locks[2].raw),
304 .flags = .{ .size = .U32, .private = true },
305 },
306 };
307
308 const timeout = linux.kernel_timespec{ .sec = 0, .nsec = 2 }; // absolute timeout, so this is 1970...
309 const rc = linux.futex2_waitv(&futexes, futexes.len, .{}, &timeout, .MONOTONIC);
310 switch (linux.errno(rc)) {
311 .NOSYS => return error.SkipZigTest, // futex2_waitv added in kernel v5.16
312 else => |err| try expectEqual(.TIMEDOUT, err),
313 }
314}
315
316// Futex v2 API is only supported on recent kernels (v6.7), so skip tests if the syscalls
317// return ENOSYS.
318fn futex2_skip_if_unsupported() !void {
319 const lock: u32 = 0;
320 const rc = linux.futex2_wake(&lock, 0, 1, .{ .size = .U32, .private = true });
321 if (linux.errno(rc) == .NOSYS) {
322 return error.SkipZigTest;
323 }
324}
325
326test "futex2_wait" {
327 var lock: std.atomic.Value(u32) = std.atomic.Value(u32).init(1);
328 var rc: usize = 0;
329 const mask = 0x1;
330
331 try futex2_skip_if_unsupported();
332
333 // The API for 8,16,64 bit futexes is defined, but as of kernel v6.14
334 // (at least) they're not implemented.
335 if (false) {
336 rc = linux.futex2_wait(&lock.raw, 1, mask, .{ .size = .U8, .private = true }, null, .MONOTONIC);
337 try expectEqual(.INVAL, linux.errno(rc));
338
339 rc = linux.futex2_wait(&lock.raw, 1, mask, .{ .size = .U16, .private = true }, null, .MONOTONIC);
340 try expectEqual(.INVAL, linux.errno(rc));
341
342 rc = linux.futex2_wait(&lock.raw, 1, mask, .{ .size = .U64, .private = true }, null, .MONOTONIC);
343 try expectEqual(.INVAL, linux.errno(rc));
344 }
345
346 const flags = linux.FUTEX2_FLAGS{ .size = .U32, .private = true };
347 // no-wait, lock state mismatch
348 rc = linux.futex2_wait(&lock.raw, 2, mask, flags, null, .MONOTONIC);
349 try expectEqual(.AGAIN, linux.errno(rc));
350
351 // hit timeout on wait
352 rc = linux.futex2_wait(&lock.raw, 1, mask, flags, &.{ .sec = 0, .nsec = 2 }, .MONOTONIC);
353 try expectEqual(.TIMEDOUT, linux.errno(rc));
354
355 // timeout is absolute
356 {
357 var curr: linux.timespec = undefined;
358 rc = linux.clock_gettime(.MONOTONIC, &curr); // gettime() uses platform timespec
359 try expectEqual(0, rc);
360
361 // ... but futex2_wait always uses 64-bit timespec
362 var timeout: linux.kernel_timespec = .{
363 .sec = curr.sec,
364 .nsec = curr.nsec + 2,
365 };
366 rc = linux.futex2_wait(&lock.raw, 1, mask, flags, &timeout, .MONOTONIC);
367 try expectEqual(.TIMEDOUT, linux.errno(rc));
368 }
369
370 rc = linux.futex2_wait(&lock.raw, 1, mask, flags, &.{ .sec = 0, .nsec = 2 }, .REALTIME);
371 try expectEqual(.TIMEDOUT, linux.errno(rc));
372}
373
374test "futex2_wake" {
375 var lock: std.atomic.Value(u32) = std.atomic.Value(u32).init(1);
376
377 try futex2_skip_if_unsupported();
378
379 const rc = linux.futex2_wake(&lock.raw, 0xFF, 1, .{ .size = .U32, .private = true });
380 try expectEqual(0, rc);
381}
382
383test "futex2_requeue" {
384 try futex2_skip_if_unsupported();
385
386 const locks = [_]std.atomic.Value(u32){
387 std.atomic.Value(u32).init(1),
388 std.atomic.Value(u32).init(1),
389 };
390
391 const futexes = [_]linux.futex2_waitone{
392 .{
393 .val = 1,
394 .uaddr = @intFromPtr(&locks[0].raw),
395 .flags = .{ .size = .U32, .private = true },
396 },
397 .{
398 .val = 1,
399 .uaddr = @intFromPtr(&locks[1].raw),
400 .flags = .{ .size = .U32, .private = true },
401 },
402 };
403
404 const rc = linux.futex2_requeue(&futexes, .{}, 2, 2);
405 try expectEqual(0, rc);
406}
407
408test "timerfd" {
409 const tfd: linux.fd_t = rc: {
410 const rc = linux.timerfd_create(.MONOTONIC, .{ .CLOEXEC = true });
411 switch (linux.errno(rc)) {
412 .SUCCESS => break :rc @intCast(rc),
413 else => @panic("test failed"),
414 }
415 };
416 defer _ = linux.close(tfd);
417
418 // Fire event 10_000_000ns = 10ms after the posix.timerfd_settime call.
419 var sit: linux.itimerspec = .{ .it_interval = .{ .sec = 0, .nsec = 0 }, .it_value = .{ .sec = 0, .nsec = 10 * (1000 * 1000) } };
420 const flags: linux.TFD.TIMER = .{};
421 switch (linux.errno(linux.timerfd_settime(tfd, @bitCast(flags), &sit, null))) {
422 .SUCCESS => {},
423 else => @panic("test failed"),
424 }
425
426 var fds: [1]std.posix.pollfd = .{.{ .fd = tfd, .events = linux.POLL.IN }};
427 try expectEqual(@as(usize, 1), try std.posix.poll(&fds, -1)); // -1 => infinite waiting
428
429 const git = rc: {
430 var curr_value: linux.itimerspec = undefined;
431 const rc = linux.timerfd_gettime(tfd, &curr_value);
432 switch (linux.errno(rc)) {
433 .SUCCESS => break :rc curr_value,
434 else => @panic("test failed"),
435 }
436 };
437 const expect_disarmed_timer: linux.itimerspec = .{
438 .it_interval = .{ .sec = 0, .nsec = 0 },
439 .it_value = .{ .sec = 0, .nsec = 0 },
440 };
441 try expectEqual(expect_disarmed_timer, git);
442}
443
444test {
445 _ = linux.IoUring;
446}