zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob d7cfb4e1 (13828B) - Raw


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