master
  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    if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23809
 14
 15    var tmp = std.testing.tmpDir(.{});
 16    defer tmp.cleanup();
 17
 18    const path = "test_fallocate";
 19    const file = try tmp.dir.createFile(path, .{ .truncate = true, .mode = 0o666 });
 20    defer file.close();
 21
 22    try expect((try file.stat()).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()).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 = [_]linux.epoll_event{events_one} ** 8;
 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    var tmp = std.testing.tmpDir(.{});
 81    defer tmp.cleanup();
 82
 83    const tmp_file_name = "just_a_temporary_file.txt";
 84    var file = try tmp.dir.createFile(tmp_file_name, .{});
 85    defer file.close();
 86
 87    var statx_buf: linux.Statx = undefined;
 88    switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) {
 89        .SUCCESS => {},
 90        else => unreachable,
 91    }
 92
 93    if (builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) return error.SkipZigTest; // No fstatat, so the rest of the test is meaningless.
 94
 95    var stat_buf: linux.Stat = undefined;
 96    switch (linux.errno(linux.fstatat(file.handle, "", &stat_buf, linux.AT.EMPTY_PATH))) {
 97        .SUCCESS => {},
 98        else => unreachable,
 99    }
100
101    try expect(stat_buf.mode == statx_buf.mode);
102    try expect(@as(u32, @bitCast(stat_buf.uid)) == statx_buf.uid);
103    try expect(@as(u32, @bitCast(stat_buf.gid)) == statx_buf.gid);
104    try expect(@as(u64, @bitCast(@as(i64, stat_buf.size))) == statx_buf.size);
105    try expect(@as(u64, @bitCast(@as(i64, stat_buf.blksize))) == statx_buf.blksize);
106    try expect(@as(u64, @bitCast(@as(i64, stat_buf.blocks))) == statx_buf.blocks);
107}
108
109test "user and group ids" {
110    if (builtin.link_libc) return error.SkipZigTest;
111    try expectEqual(linux.getauxval(elf.AT_UID), linux.getuid());
112    try expectEqual(linux.getauxval(elf.AT_GID), linux.getgid());
113    try expectEqual(linux.getauxval(elf.AT_EUID), linux.geteuid());
114    try expectEqual(linux.getauxval(elf.AT_EGID), linux.getegid());
115}
116
117test "fadvise" {
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(tmp_file_name, .{});
123    defer file.close();
124
125    var buf: [2048]u8 = undefined;
126    try file.writeAll(&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.meta.intToEnum(SIG, i) catch continue;
142        try expectEqual(false, linux.sigismember(&sigset, sig));
143    }
144    for (1..linux.NSIG) |i| {
145        const sig = std.meta.intToEnum(SIG, i) catch continue;
146        linux.sigaddset(&sigset, sig);
147    }
148    for (1..linux.NSIG) |i| {
149        const sig = std.meta.intToEnum(SIG, i) catch continue;
150        try expectEqual(true, linux.sigismember(&sigset, sig));
151    }
152    for (1..linux.NSIG) |i| {
153        const sig = std.meta.intToEnum(SIG, i) catch continue;
154        linux.sigdelset(&sigset, sig);
155    }
156    for (1..linux.NSIG) |i| {
157        const sig = std.meta.intToEnum(SIG, i) catch 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.meta.intToEnum(linux.SIG, i) catch 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.meta.intToEnum(linux.SIG, i) catch 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 = @enumFromInt(0), .private = true, .realtime = false })));
190    assert(256 == @as(u32, @bitCast(linux.FUTEX_OP{ .cmd = @enumFromInt(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 = @enumFromInt(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 "copy_file_range error" {
409    const fds = try std.posix.pipe();
410    defer std.posix.close(fds[0]);
411    defer std.posix.close(fds[1]);
412
413    try std.testing.expectError(error.InvalidArguments, linux.wrapped.copy_file_range(fds[0], null, fds[1], null, 1, 0));
414}
415
416test {
417    _ = linux.IoUring;
418}