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 }