blob 2f19ee2a (118862B) - Raw
1 // This file contains thin wrappers around OS-specific APIs, with these 2 // specific goals in mind: 3 // * Convert "errno"-style error codes into Zig errors. 4 // * When null-terminated byte buffers are required, provide APIs which accept 5 // slices as well as APIs which accept null-terminated byte buffers. Same goes 6 // for UTF-16LE encoding. 7 // * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide 8 // cross platform abstracting. 9 // * When there exists a corresponding libc function and linking libc, the libc 10 // implementation is used. Exceptions are made for known buggy areas of libc. 11 // On Linux libc can be side-stepped by using `std.os.linux` directly. 12 // * For Windows, this file represents the API that libc would provide for 13 // Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`. 14 // Note: The Zig standard library does not support POSIX thread cancellation, and 15 // in general EINTR is handled by trying again. 16 17 const std = @import("std.zig"); 18 const builtin = @import("builtin"); 19 const assert = std.debug.assert; 20 const math = std.math; 21 const mem = std.mem; 22 const elf = std.elf; 23 const dl = @import("dynamic_library.zig"); 24 const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES; 25 26 pub const darwin = @import("os/darwin.zig"); 27 pub const dragonfly = @import("os/dragonfly.zig"); 28 pub const freebsd = @import("os/freebsd.zig"); 29 pub const netbsd = @import("os/netbsd.zig"); 30 pub const linux = @import("os/linux.zig"); 31 pub const uefi = @import("os/uefi.zig"); 32 pub const wasi = @import("os/wasi.zig"); 33 pub const windows = @import("os/windows.zig"); 34 pub const zen = @import("os/zen.zig"); 35 36 comptime { 37 assert(@import("std") == std); // std lib tests require --override-lib-dir 38 } 39 40 test "" { 41 _ = darwin; 42 _ = freebsd; 43 _ = linux; 44 _ = netbsd; 45 _ = uefi; 46 _ = wasi; 47 _ = windows; 48 _ = zen; 49 50 _ = @import("os/test.zig"); 51 } 52 53 /// When linking libc, this is the C API. Otherwise, it is the OS-specific system interface. 54 pub const system = if (builtin.link_libc) std.c else switch (builtin.os) { 55 .macosx, .ios, .watchos, .tvos => darwin, 56 .freebsd => freebsd, 57 .linux => linux, 58 .netbsd => netbsd, 59 .dragonfly => dragonfly, 60 .wasi => wasi, 61 .windows => windows, 62 .zen => zen, 63 else => struct {}, 64 }; 65 66 pub usingnamespace @import("os/bits.zig"); 67 68 /// See also `getenv`. Populated by startup code before main(). 69 pub var environ: [][*]u8 = undefined; 70 71 /// Populated by startup code before main(). 72 /// Not available on Windows. See `std.process.args` 73 /// for obtaining the process arguments. 74 pub var argv: [][*]u8 = undefined; 75 76 /// To obtain errno, call this function with the return value of the 77 /// system function call. For some systems this will obtain the value directly 78 /// from the return code; for others it will use a thread-local errno variable. 79 /// Therefore, this function only returns a well-defined value when it is called 80 /// directly after the system function call which one wants to learn the errno 81 /// value of. 82 pub const errno = system.getErrno; 83 84 /// Closes the file descriptor. 85 /// This function is not capable of returning any indication of failure. An 86 /// application which wants to ensure writes have succeeded before closing 87 /// must call `fsync` before `close`. 88 /// Note: The Zig standard library does not support POSIX thread cancellation. 89 pub fn close(fd: fd_t) void { 90 if (builtin.os == .windows) { 91 return windows.CloseHandle(fd); 92 } 93 if (builtin.os == .wasi) { 94 _ = wasi.fd_close(fd); 95 } 96 if (comptime std.Target.current.isDarwin()) { 97 // This avoids the EINTR problem. 98 switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) { 99 EBADF => unreachable, // Always a race condition. 100 else => return, 101 } 102 } 103 switch (errno(system.close(fd))) { 104 EBADF => unreachable, // Always a race condition. 105 EINTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425 106 else => return, 107 } 108 } 109 110 pub const GetRandomError = OpenError; 111 112 /// Obtain a series of random bytes. These bytes can be used to seed user-space 113 /// random number generators or for cryptographic purposes. 114 /// When linking against libc, this calls the 115 /// appropriate OS-specific library call. Otherwise it uses the zig standard 116 /// library implementation. 117 pub fn getrandom(buffer: []u8) GetRandomError!void { 118 if (builtin.os == .windows) { 119 return windows.RtlGenRandom(buffer); 120 } 121 if (builtin.os == .linux or builtin.os == .freebsd) { 122 var buf = buffer; 123 const use_c = builtin.os != .linux or 124 std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok; 125 126 while (buf.len != 0) { 127 var err: u16 = undefined; 128 129 const num_read = if (use_c) blk: { 130 const rc = std.c.getrandom(buf.ptr, buf.len, 0); 131 err = std.c.getErrno(rc); 132 break :blk @bitCast(usize, rc); 133 } else blk: { 134 const rc = linux.getrandom(buf.ptr, buf.len, 0); 135 err = linux.getErrno(rc); 136 break :blk rc; 137 }; 138 139 switch (err) { 140 0 => buf = buf[num_read..], 141 EINVAL => unreachable, 142 EFAULT => unreachable, 143 EINTR => continue, 144 ENOSYS => return getRandomBytesDevURandom(buf), 145 else => return unexpectedErrno(err), 146 } 147 } 148 return; 149 } 150 if (builtin.os == .wasi) { 151 switch (wasi.random_get(buffer.ptr, buffer.len)) { 152 0 => return, 153 else => |err| return unexpectedErrno(err), 154 } 155 } 156 return getRandomBytesDevURandom(buffer); 157 } 158 159 fn getRandomBytesDevURandom(buf: []u8) !void { 160 const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0); 161 defer close(fd); 162 163 const st = try fstat(fd); 164 if (!S_ISCHR(st.mode)) { 165 return error.NoDevice; 166 } 167 168 const stream = &std.fs.File.openHandle(fd).inStream().stream; 169 stream.readNoEof(buf) catch return error.Unexpected; 170 } 171 172 /// Causes abnormal process termination. 173 /// If linking against libc, this calls the abort() libc function. Otherwise 174 /// it raises SIGABRT followed by SIGKILL and finally lo 175 pub fn abort() noreturn { 176 @setCold(true); 177 // MSVCRT abort() sometimes opens a popup window which is undesirable, so 178 // even when linking libc on Windows we use our own abort implementation. 179 // See https://github.com/ziglang/zig/issues/2071 for more details. 180 if (builtin.os == .windows) { 181 if (builtin.mode == .Debug) { 182 @breakpoint(); 183 } 184 windows.kernel32.ExitProcess(3); 185 } 186 if (builtin.link_libc) { 187 system.abort(); 188 } 189 if (builtin.os == .uefi) { 190 exit(0); // TODO choose appropriate exit code 191 } 192 193 raise(SIGABRT) catch {}; 194 195 // TODO the rest of the implementation of abort() from musl libc here 196 197 raise(SIGKILL) catch {}; 198 exit(127); 199 } 200 201 pub const RaiseError = UnexpectedError; 202 203 pub fn raise(sig: u8) RaiseError!void { 204 if (builtin.link_libc) { 205 switch (errno(system.raise(sig))) { 206 0 => return, 207 else => |err| return unexpectedErrno(err), 208 } 209 } 210 211 if (builtin.os == .wasi) { 212 switch (wasi.proc_raise(SIGABRT)) { 213 0 => return, 214 else => |err| return unexpectedErrno(err), 215 } 216 } 217 218 if (builtin.os == .linux) { 219 var set: linux.sigset_t = undefined; 220 linux.blockAppSignals(&set); 221 const tid = linux.syscall0(linux.SYS_gettid); 222 const rc = linux.syscall2(linux.SYS_tkill, tid, sig); 223 linux.restoreSignals(&set); 224 switch (errno(rc)) { 225 0 => return, 226 else => |err| return unexpectedErrno(err), 227 } 228 } 229 230 @compileError("std.os.raise unimplemented for this target"); 231 } 232 233 pub const KillError = error{PermissionDenied} || UnexpectedError; 234 235 pub fn kill(pid: pid_t, sig: u8) KillError!void { 236 switch (errno(system.kill(pid, sig))) { 237 0 => return, 238 EINVAL => unreachable, // invalid signal 239 EPERM => return error.PermissionDenied, 240 ESRCH => unreachable, // always a race condition 241 else => |err| return unexpectedErrno(err), 242 } 243 } 244 245 /// Exits the program cleanly with the specified status code. 246 pub fn exit(status: u8) noreturn { 247 if (builtin.link_libc) { 248 system.exit(status); 249 } 250 if (builtin.os == .windows) { 251 windows.kernel32.ExitProcess(status); 252 } 253 if (builtin.os == .wasi) { 254 wasi.proc_exit(status); 255 } 256 if (builtin.os == .linux and !builtin.single_threaded) { 257 linux.exit_group(status); 258 } 259 if (builtin.os == .uefi) { 260 // exit() is only avaliable if exitBootServices() has not been called yet. 261 // This call to exit should not fail, so we don't care about its return value. 262 if (uefi.system_table.boot_services) |bs| { 263 _ = bs.exit(uefi.handle, status, 0, null); 264 } 265 // If we can't exit, reboot the system instead. 266 uefi.system_table.runtime_services.resetSystem(uefi.tables.ResetType.ResetCold, status, 0, null); 267 } 268 system.exit(status); 269 } 270 271 pub const ReadError = error{ 272 InputOutput, 273 SystemResources, 274 IsDir, 275 OperationAborted, 276 BrokenPipe, 277 278 /// This error occurs when no global event loop is configured, 279 /// and reading from the file descriptor would block. 280 WouldBlock, 281 } || UnexpectedError; 282 283 /// Returns the number of bytes that were read, which can be less than 284 /// buf.len. If 0 bytes were read, that means EOF. 285 /// If the application has a global event loop enabled, EAGAIN is handled 286 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. 287 pub fn read(fd: fd_t, buf: []u8) ReadError!usize { 288 if (builtin.os == .windows) { 289 return windows.ReadFile(fd, buf); 290 } 291 292 if (builtin.os == .wasi and !builtin.link_libc) { 293 const iovs = [1]iovec{iovec{ 294 .iov_base = buf.ptr, 295 .iov_len = buf.len, 296 }}; 297 298 var nread: usize = undefined; 299 switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) { 300 0 => return nread, 301 else => |err| return unexpectedErrno(err), 302 } 303 } 304 305 while (true) { 306 const rc = system.read(fd, buf.ptr, buf.len); 307 switch (errno(rc)) { 308 0 => return @intCast(usize, rc), 309 EINTR => continue, 310 EINVAL => unreachable, 311 EFAULT => unreachable, 312 EAGAIN => if (std.event.Loop.instance) |loop| { 313 loop.waitUntilFdReadable(fd); 314 continue; 315 } else { 316 return error.WouldBlock; 317 }, 318 EBADF => unreachable, // Always a race condition. 319 EIO => return error.InputOutput, 320 EISDIR => return error.IsDir, 321 ENOBUFS => return error.SystemResources, 322 ENOMEM => return error.SystemResources, 323 else => |err| return unexpectedErrno(err), 324 } 325 } 326 return index; 327 } 328 329 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. 330 /// If the application has a global event loop enabled, EAGAIN is handled 331 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. 332 pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { 333 while (true) { 334 // TODO handle the case when iov_len is too large and get rid of this @intCast 335 const rc = system.readv(fd, iov.ptr, @intCast(u32, iov.len)); 336 switch (errno(rc)) { 337 0 => return @bitCast(usize, rc), 338 EINTR => continue, 339 EINVAL => unreachable, 340 EFAULT => unreachable, 341 EAGAIN => if (std.event.Loop.instance) |loop| { 342 loop.waitUntilFdReadable(fd); 343 continue; 344 } else { 345 return error.WouldBlock; 346 }, 347 EBADF => unreachable, // always a race condition 348 EIO => return error.InputOutput, 349 EISDIR => return error.IsDir, 350 ENOBUFS => return error.SystemResources, 351 ENOMEM => return error.SystemResources, 352 else => |err| return unexpectedErrno(err), 353 } 354 } 355 } 356 357 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. 358 /// If the application has a global event loop enabled, EAGAIN is handled 359 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. 360 pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { 361 if (comptime std.Target.current.isDarwin()) { 362 // Darwin does not have preadv but it does have pread. 363 var off: usize = 0; 364 var iov_i: usize = 0; 365 var inner_off: usize = 0; 366 while (true) { 367 const v = iov[iov_i]; 368 const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); 369 const err = darwin.getErrno(rc); 370 switch (err) { 371 0 => { 372 const amt_read = @bitCast(usize, rc); 373 off += amt_read; 374 inner_off += amt_read; 375 if (inner_off == v.iov_len) { 376 iov_i += 1; 377 inner_off = 0; 378 if (iov_i == iov.len) { 379 return off; 380 } 381 } 382 if (rc == 0) return off; // EOF 383 continue; 384 }, 385 EINTR => continue, 386 EINVAL => unreachable, 387 EFAULT => unreachable, 388 ESPIPE => unreachable, // fd is not seekable 389 EAGAIN => if (std.event.Loop.instance) |loop| { 390 loop.waitUntilFdReadable(fd); 391 continue; 392 } else { 393 return error.WouldBlock; 394 }, 395 EBADF => unreachable, // always a race condition 396 EIO => return error.InputOutput, 397 EISDIR => return error.IsDir, 398 ENOBUFS => return error.SystemResources, 399 ENOMEM => return error.SystemResources, 400 else => return unexpectedErrno(err), 401 } 402 } 403 } 404 while (true) { 405 // TODO handle the case when iov_len is too large and get rid of this @intCast 406 const rc = system.preadv(fd, iov.ptr, @intCast(u32, iov.len), offset); 407 switch (errno(rc)) { 408 0 => return @bitCast(usize, rc), 409 EINTR => continue, 410 EINVAL => unreachable, 411 EFAULT => unreachable, 412 EAGAIN => if (std.event.Loop.instance) |loop| { 413 loop.waitUntilFdReadable(fd); 414 continue; 415 } else { 416 return error.WouldBlock; 417 }, 418 EBADF => unreachable, // always a race condition 419 EIO => return error.InputOutput, 420 EISDIR => return error.IsDir, 421 ENOBUFS => return error.SystemResources, 422 ENOMEM => return error.SystemResources, 423 else => |err| return unexpectedErrno(err), 424 } 425 } 426 } 427 428 pub const WriteError = error{ 429 DiskQuota, 430 FileTooBig, 431 InputOutput, 432 NoSpaceLeft, 433 AccessDenied, 434 BrokenPipe, 435 SystemResources, 436 OperationAborted, 437 438 /// This error occurs when no global event loop is configured, 439 /// and reading from the file descriptor would block. 440 WouldBlock, 441 } || UnexpectedError; 442 443 /// Write to a file descriptor. Keeps trying if it gets interrupted. 444 /// If the application has a global event loop enabled, EAGAIN is handled 445 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. 446 /// TODO evented I/O integration is disabled until 447 /// https://github.com/ziglang/zig/issues/3557 is solved. 448 pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { 449 if (builtin.os == .windows) { 450 return windows.WriteFile(fd, bytes); 451 } 452 453 if (builtin.os == .wasi and !builtin.link_libc) { 454 const ciovs = [1]iovec_const{iovec_const{ 455 .iov_base = bytes.ptr, 456 .iov_len = bytes.len, 457 }}; 458 var nwritten: usize = undefined; 459 switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) { 460 0 => return, 461 else => |err| return unexpectedErrno(err), 462 } 463 } 464 465 // Linux can return EINVAL when write amount is > 0x7ffff000 466 // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856 467 // TODO audit this. Shawn Landden says that this is not actually true. 468 // if this logic should stay, move it to std.os.linux 469 const max_bytes_len = 0x7ffff000; 470 471 var index: usize = 0; 472 while (index < bytes.len) { 473 const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); 474 const rc = system.write(fd, bytes.ptr + index, amt_to_write); 475 switch (errno(rc)) { 476 0 => { 477 index += @intCast(usize, rc); 478 continue; 479 }, 480 EINTR => continue, 481 EINVAL => unreachable, 482 EFAULT => unreachable, 483 // TODO https://github.com/ziglang/zig/issues/3557 484 EAGAIN => return error.WouldBlock, 485 //EAGAIN => if (std.event.Loop.instance) |loop| { 486 // loop.waitUntilFdWritable(fd); 487 // continue; 488 //} else { 489 // return error.WouldBlock; 490 //}, 491 EBADF => unreachable, // Always a race condition. 492 EDESTADDRREQ => unreachable, // `connect` was never called. 493 EDQUOT => return error.DiskQuota, 494 EFBIG => return error.FileTooBig, 495 EIO => return error.InputOutput, 496 ENOSPC => return error.NoSpaceLeft, 497 EPERM => return error.AccessDenied, 498 EPIPE => return error.BrokenPipe, 499 else => |err| return unexpectedErrno(err), 500 } 501 } 502 } 503 504 /// Write multiple buffers to a file descriptor. 505 /// If the application has a global event loop enabled, EAGAIN is handled 506 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. 507 pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { 508 while (true) { 509 // TODO handle the case when iov_len is too large and get rid of this @intCast 510 const rc = system.writev(fd, iov.ptr, @intCast(u32, iov.len)); 511 switch (errno(rc)) { 512 0 => return, 513 EINTR => continue, 514 EINVAL => unreachable, 515 EFAULT => unreachable, 516 EAGAIN => if (std.event.Loop.instance) |loop| { 517 loop.waitUntilFdWritable(fd); 518 continue; 519 } else { 520 return error.WouldBlock; 521 }, 522 EBADF => unreachable, // Always a race condition. 523 EDESTADDRREQ => unreachable, // `connect` was never called. 524 EDQUOT => return error.DiskQuota, 525 EFBIG => return error.FileTooBig, 526 EIO => return error.InputOutput, 527 ENOSPC => return error.NoSpaceLeft, 528 EPERM => return error.AccessDenied, 529 EPIPE => return error.BrokenPipe, 530 else => |err| return unexpectedErrno(err), 531 } 532 } 533 } 534 535 /// Write multiple buffers to a file descriptor, with a position offset. 536 /// Keeps trying if it gets interrupted. 537 pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void { 538 if (comptime std.Target.current.isDarwin()) { 539 // Darwin does not have pwritev but it does have pwrite. 540 var off: usize = 0; 541 var iov_i: usize = 0; 542 var inner_off: usize = 0; 543 while (true) { 544 const v = iov[iov_i]; 545 const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); 546 const err = darwin.getErrno(rc); 547 switch (err) { 548 0 => { 549 const amt_written = @bitCast(usize, rc); 550 off += amt_written; 551 inner_off += amt_written; 552 if (inner_off == v.iov_len) { 553 iov_i += 1; 554 inner_off = 0; 555 if (iov_i == iov.len) { 556 return; 557 } 558 } 559 continue; 560 }, 561 EINTR => continue, 562 ESPIPE => unreachable, // `fd` is not seekable. 563 EINVAL => unreachable, 564 EFAULT => unreachable, 565 EAGAIN => if (std.event.Loop.instance) |loop| { 566 loop.waitUntilFdWritable(fd); 567 continue; 568 } else { 569 return error.WouldBlock; 570 }, 571 EBADF => unreachable, // Always a race condition. 572 EDESTADDRREQ => unreachable, // `connect` was never called. 573 EDQUOT => return error.DiskQuota, 574 EFBIG => return error.FileTooBig, 575 EIO => return error.InputOutput, 576 ENOSPC => return error.NoSpaceLeft, 577 EPERM => return error.AccessDenied, 578 EPIPE => return error.BrokenPipe, 579 else => return unexpectedErrno(err), 580 } 581 } 582 } 583 584 while (true) { 585 // TODO handle the case when iov_len is too large and get rid of this @intCast 586 const rc = system.pwritev(fd, iov.ptr, @intCast(u32, iov.len), offset); 587 switch (errno(rc)) { 588 0 => return, 589 EINTR => continue, 590 EINVAL => unreachable, 591 EFAULT => unreachable, 592 EAGAIN => if (std.event.Loop.instance) |loop| { 593 loop.waitUntilFdWritable(fd); 594 continue; 595 } else { 596 return error.WouldBlock; 597 }, 598 EBADF => unreachable, // Always a race condition. 599 EDESTADDRREQ => unreachable, // `connect` was never called. 600 EDQUOT => return error.DiskQuota, 601 EFBIG => return error.FileTooBig, 602 EIO => return error.InputOutput, 603 ENOSPC => return error.NoSpaceLeft, 604 EPERM => return error.AccessDenied, 605 EPIPE => return error.BrokenPipe, 606 else => |err| return unexpectedErrno(err), 607 } 608 } 609 } 610 611 pub const OpenError = error{ 612 AccessDenied, 613 SymLinkLoop, 614 ProcessFdQuotaExceeded, 615 SystemFdQuotaExceeded, 616 NoDevice, 617 FileNotFound, 618 619 /// The path exceeded `MAX_PATH_BYTES` bytes. 620 NameTooLong, 621 622 /// Insufficient kernel memory was available, or 623 /// the named file is a FIFO and per-user hard limit on 624 /// memory allocation for pipes has been reached. 625 SystemResources, 626 627 /// The file is too large to be opened. This error is unreachable 628 /// for 64-bit targets, as well as when opening directories. 629 FileTooBig, 630 631 /// The path refers to directory but the `O_DIRECTORY` flag was not provided. 632 IsDir, 633 634 /// A new path cannot be created because the device has no room for the new file. 635 /// This error is only reachable when the `O_CREAT` flag is provided. 636 NoSpaceLeft, 637 638 /// A component used as a directory in the path was not, in fact, a directory, or 639 /// `O_DIRECTORY` was specified and the path was not a directory. 640 NotDir, 641 642 /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided. 643 PathAlreadyExists, 644 DeviceBusy, 645 } || UnexpectedError; 646 647 /// Open and possibly create a file. Keeps trying if it gets interrupted. 648 /// See also `openC`. 649 pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t { 650 const file_path_c = try toPosixPath(file_path); 651 return openC(&file_path_c, flags, perm); 652 } 653 654 /// Open and possibly create a file. Keeps trying if it gets interrupted. 655 /// See also `open`. 656 /// TODO https://github.com/ziglang/zig/issues/265 657 pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t { 658 while (true) { 659 const rc = system.open(file_path, flags, perm); 660 switch (errno(rc)) { 661 0 => return @intCast(fd_t, rc), 662 EINTR => continue, 663 664 EFAULT => unreachable, 665 EINVAL => unreachable, 666 EACCES => return error.AccessDenied, 667 EFBIG => return error.FileTooBig, 668 EOVERFLOW => return error.FileTooBig, 669 EISDIR => return error.IsDir, 670 ELOOP => return error.SymLinkLoop, 671 EMFILE => return error.ProcessFdQuotaExceeded, 672 ENAMETOOLONG => return error.NameTooLong, 673 ENFILE => return error.SystemFdQuotaExceeded, 674 ENODEV => return error.NoDevice, 675 ENOENT => return error.FileNotFound, 676 ENOMEM => return error.SystemResources, 677 ENOSPC => return error.NoSpaceLeft, 678 ENOTDIR => return error.NotDir, 679 EPERM => return error.AccessDenied, 680 EEXIST => return error.PathAlreadyExists, 681 EBUSY => return error.DeviceBusy, 682 else => |err| return unexpectedErrno(err), 683 } 684 } 685 } 686 687 /// Open and possibly create a file. Keeps trying if it gets interrupted. 688 /// `file_path` is relative to the open directory handle `dir_fd`. 689 /// See also `openatC`. 690 pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: usize) OpenError!fd_t { 691 const file_path_c = try toPosixPath(file_path); 692 return openatC(dir_fd, &file_path_c, flags, mode); 693 } 694 695 /// Open and possibly create a file. Keeps trying if it gets interrupted. 696 /// `file_path` is relative to the open directory handle `dir_fd`. 697 /// See also `openat`. 698 pub fn openatC(dir_fd: fd_t, file_path: [*]const u8, flags: u32, mode: usize) OpenError!fd_t { 699 while (true) { 700 const rc = system.openat(dir_fd, file_path, flags, mode); 701 switch (errno(rc)) { 702 0 => return @intCast(fd_t, rc), 703 EINTR => continue, 704 705 EFAULT => unreachable, 706 EINVAL => unreachable, 707 EACCES => return error.AccessDenied, 708 EFBIG => return error.FileTooBig, 709 EOVERFLOW => return error.FileTooBig, 710 EISDIR => return error.IsDir, 711 ELOOP => return error.SymLinkLoop, 712 EMFILE => return error.ProcessFdQuotaExceeded, 713 ENAMETOOLONG => return error.NameTooLong, 714 ENFILE => return error.SystemFdQuotaExceeded, 715 ENODEV => return error.NoDevice, 716 ENOENT => return error.FileNotFound, 717 ENOMEM => return error.SystemResources, 718 ENOSPC => return error.NoSpaceLeft, 719 ENOTDIR => return error.NotDir, 720 EPERM => return error.AccessDenied, 721 EEXIST => return error.PathAlreadyExists, 722 EBUSY => return error.DeviceBusy, 723 else => |err| return unexpectedErrno(err), 724 } 725 } 726 } 727 728 pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void { 729 while (true) { 730 switch (errno(system.dup2(old_fd, new_fd))) { 731 0 => return, 732 EBUSY, EINTR => continue, 733 EMFILE => return error.ProcessFdQuotaExceeded, 734 EINVAL => unreachable, // invalid parameters passed to dup2 735 EBADF => unreachable, // always a race condition 736 else => |err| return unexpectedErrno(err), 737 } 738 } 739 } 740 741 pub const ExecveError = error{ 742 SystemResources, 743 AccessDenied, 744 InvalidExe, 745 FileSystem, 746 IsDir, 747 FileNotFound, 748 NotDir, 749 FileBusy, 750 ProcessFdQuotaExceeded, 751 SystemFdQuotaExceeded, 752 NameTooLong, 753 } || UnexpectedError; 754 755 /// Like `execve` except the parameters are null-terminated, 756 /// matching the syscall API on all targets. This removes the need for an allocator. 757 /// This function ignores PATH environment variable. See `execvpeC` for that. 758 pub fn execveC(path: [*]const u8, child_argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) ExecveError { 759 switch (errno(system.execve(path, child_argv, envp))) { 760 0 => unreachable, 761 EFAULT => unreachable, 762 E2BIG => return error.SystemResources, 763 EMFILE => return error.ProcessFdQuotaExceeded, 764 ENAMETOOLONG => return error.NameTooLong, 765 ENFILE => return error.SystemFdQuotaExceeded, 766 ENOMEM => return error.SystemResources, 767 EACCES => return error.AccessDenied, 768 EPERM => return error.AccessDenied, 769 EINVAL => return error.InvalidExe, 770 ENOEXEC => return error.InvalidExe, 771 EIO => return error.FileSystem, 772 ELOOP => return error.FileSystem, 773 EISDIR => return error.IsDir, 774 ENOENT => return error.FileNotFound, 775 ENOTDIR => return error.NotDir, 776 ETXTBSY => return error.FileBusy, 777 else => |err| return unexpectedErrno(err), 778 } 779 } 780 781 /// Like `execvpe` except the parameters are null-terminated, 782 /// matching the syscall API on all targets. This removes the need for an allocator. 783 /// This function also uses the PATH environment variable to get the full path to the executable. 784 /// If `file` is an absolute path, this is the same as `execveC`. 785 pub fn execvpeC(file: [*]const u8, child_argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) ExecveError { 786 const file_slice = mem.toSliceConst(u8, file); 787 if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveC(file, child_argv, envp); 788 789 const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; 790 var path_buf: [MAX_PATH_BYTES]u8 = undefined; 791 var it = mem.tokenize(PATH, ":"); 792 var seen_eacces = false; 793 var err: ExecveError = undefined; 794 while (it.next()) |search_path| { 795 if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong; 796 mem.copy(u8, &path_buf, search_path); 797 path_buf[search_path.len] = '/'; 798 mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice); 799 path_buf[search_path.len + file_slice.len + 1] = 0; 800 err = execveC(&path_buf, child_argv, envp); 801 switch (err) { 802 error.AccessDenied => seen_eacces = true, 803 error.FileNotFound, error.NotDir => {}, 804 else => |e| return e, 805 } 806 } 807 if (seen_eacces) return error.AccessDenied; 808 return err; 809 } 810 811 /// This function must allocate memory to add a null terminating bytes on path and each arg. 812 /// It must also convert to KEY=VALUE\0 format for environment variables, and include null 813 /// pointers after the args and after the environment variables. 814 /// `argv_slice[0]` is the executable path. 815 /// This function also uses the PATH environment variable to get the full path to the executable. 816 pub fn execvpe( 817 allocator: *mem.Allocator, 818 argv_slice: []const []const u8, 819 env_map: *const std.BufMap, 820 ) (ExecveError || error{OutOfMemory}) { 821 const argv_buf = try allocator.alloc(?[*]u8, argv_slice.len + 1); 822 mem.set(?[*]u8, argv_buf, null); 823 defer { 824 for (argv_buf) |arg| { 825 const arg_buf = if (arg) |ptr| mem.toSlice(u8, ptr) else break; 826 allocator.free(arg_buf); 827 } 828 allocator.free(argv_buf); 829 } 830 for (argv_slice) |arg, i| { 831 const arg_buf = try allocator.alloc(u8, arg.len + 1); 832 @memcpy(arg_buf.ptr, arg.ptr, arg.len); 833 arg_buf[arg.len] = 0; 834 835 argv_buf[i] = arg_buf.ptr; 836 } 837 argv_buf[argv_slice.len] = null; 838 839 const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); 840 defer freeNullDelimitedEnvMap(allocator, envp_buf); 841 842 return execvpeC(argv_buf.ptr[0].?, argv_buf.ptr, envp_buf.ptr); 843 } 844 845 pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![]?[*]u8 { 846 const envp_count = env_map.count(); 847 const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); 848 mem.set(?[*]u8, envp_buf, null); 849 errdefer freeNullDelimitedEnvMap(allocator, envp_buf); 850 { 851 var it = env_map.iterator(); 852 var i: usize = 0; 853 while (it.next()) |pair| : (i += 1) { 854 const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); 855 @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); 856 env_buf[pair.key.len] = '='; 857 @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); 858 env_buf[env_buf.len - 1] = 0; 859 860 envp_buf[i] = env_buf.ptr; 861 } 862 assert(i == envp_count); 863 } 864 assert(envp_buf[envp_count] == null); 865 return envp_buf; 866 } 867 868 pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*]u8) void { 869 for (envp_buf) |env| { 870 const env_buf = if (env) |ptr| ptr[0 .. mem.len(u8, ptr) + 1] else break; 871 allocator.free(env_buf); 872 } 873 allocator.free(envp_buf); 874 } 875 876 /// Get an environment variable. 877 /// See also `getenvC`. 878 /// TODO make this go through libc when we have it 879 pub fn getenv(key: []const u8) ?[]const u8 { 880 for (environ) |ptr| { 881 var line_i: usize = 0; 882 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} 883 const this_key = ptr[0..line_i]; 884 if (!mem.eql(u8, key, this_key)) continue; 885 886 var end_i: usize = line_i; 887 while (ptr[end_i] != 0) : (end_i += 1) {} 888 const this_value = ptr[line_i + 1 .. end_i]; 889 890 return this_value; 891 } 892 return null; 893 } 894 895 /// Get an environment variable with a null-terminated name. 896 /// See also `getenv`. 897 /// TODO https://github.com/ziglang/zig/issues/265 898 pub fn getenvC(key: [*]const u8) ?[]const u8 { 899 if (builtin.link_libc) { 900 const value = system.getenv(key) orelse return null; 901 return mem.toSliceConst(u8, value); 902 } 903 return getenv(mem.toSliceConst(u8, key)); 904 } 905 906 pub const GetCwdError = error{ 907 NameTooLong, 908 CurrentWorkingDirectoryUnlinked, 909 } || UnexpectedError; 910 911 /// The result is a slice of out_buffer, indexed from 0. 912 pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { 913 if (builtin.os == .windows) { 914 return windows.GetCurrentDirectory(out_buffer); 915 } 916 917 const err = if (builtin.link_libc) blk: { 918 break :blk if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*; 919 } else blk: { 920 break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len)); 921 }; 922 switch (err) { 923 0 => return mem.toSlice(u8, out_buffer.ptr), 924 EFAULT => unreachable, 925 EINVAL => unreachable, 926 ENOENT => return error.CurrentWorkingDirectoryUnlinked, 927 ERANGE => return error.NameTooLong, 928 else => return unexpectedErrno(@intCast(usize, err)), 929 } 930 } 931 932 pub const SymLinkError = error{ 933 AccessDenied, 934 DiskQuota, 935 PathAlreadyExists, 936 FileSystem, 937 SymLinkLoop, 938 FileNotFound, 939 SystemResources, 940 NoSpaceLeft, 941 ReadOnlyFileSystem, 942 NotDir, 943 NameTooLong, 944 InvalidUtf8, 945 BadPathName, 946 } || UnexpectedError; 947 948 /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`. 949 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent 950 /// one; the latter case is known as a dangling link. 951 /// If `sym_link_path` exists, it will not be overwritten. 952 /// See also `symlinkC` and `symlinkW`. 953 pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { 954 if (builtin.os == .windows) { 955 const target_path_w = try windows.sliceToPrefixedFileW(target_path); 956 const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path); 957 return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); 958 } else { 959 const target_path_c = try toPosixPath(target_path); 960 const sym_link_path_c = try toPosixPath(sym_link_path); 961 return symlinkC(&target_path_c, &sym_link_path_c); 962 } 963 } 964 965 /// This is the same as `symlink` except the parameters are null-terminated pointers. 966 /// See also `symlink`. 967 pub fn symlinkC(target_path: [*]const u8, sym_link_path: [*]const u8) SymLinkError!void { 968 if (builtin.os == .windows) { 969 const target_path_w = try windows.cStrToPrefixedFileW(target_path); 970 const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); 971 return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0); 972 } 973 switch (errno(system.symlink(target_path, sym_link_path))) { 974 0 => return, 975 EFAULT => unreachable, 976 EINVAL => unreachable, 977 EACCES => return error.AccessDenied, 978 EPERM => return error.AccessDenied, 979 EDQUOT => return error.DiskQuota, 980 EEXIST => return error.PathAlreadyExists, 981 EIO => return error.FileSystem, 982 ELOOP => return error.SymLinkLoop, 983 ENAMETOOLONG => return error.NameTooLong, 984 ENOENT => return error.FileNotFound, 985 ENOTDIR => return error.NotDir, 986 ENOMEM => return error.SystemResources, 987 ENOSPC => return error.NoSpaceLeft, 988 EROFS => return error.ReadOnlyFileSystem, 989 else => |err| return unexpectedErrno(err), 990 } 991 } 992 993 pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { 994 const target_path_c = try toPosixPath(target_path); 995 const sym_link_path_c = try toPosixPath(sym_link_path); 996 return symlinkatC(target_path_c, newdirfd, sym_link_path_c); 997 } 998 999 pub fn symlinkatC(target_path: [*]const u8, newdirfd: fd_t, sym_link_path: [*]const u8) SymLinkError!void { 1000 switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) { 1001 0 => return, 1002 EFAULT => unreachable, 1003 EINVAL => unreachable, 1004 EACCES => return error.AccessDenied, 1005 EPERM => return error.AccessDenied, 1006 EDQUOT => return error.DiskQuota, 1007 EEXIST => return error.PathAlreadyExists, 1008 EIO => return error.FileSystem, 1009 ELOOP => return error.SymLinkLoop, 1010 ENAMETOOLONG => return error.NameTooLong, 1011 ENOENT => return error.FileNotFound, 1012 ENOTDIR => return error.NotDir, 1013 ENOMEM => return error.SystemResources, 1014 ENOSPC => return error.NoSpaceLeft, 1015 EROFS => return error.ReadOnlyFileSystem, 1016 else => |err| return unexpectedErrno(err), 1017 } 1018 } 1019 1020 pub const UnlinkError = error{ 1021 FileNotFound, 1022 AccessDenied, 1023 FileBusy, 1024 FileSystem, 1025 IsDir, 1026 SymLinkLoop, 1027 NameTooLong, 1028 NotDir, 1029 SystemResources, 1030 ReadOnlyFileSystem, 1031 1032 /// On Windows, file paths must be valid Unicode. 1033 InvalidUtf8, 1034 1035 /// On Windows, file paths cannot contain these characters: 1036 /// '/', '*', '?', '"', '<', '>', '|' 1037 BadPathName, 1038 } || UnexpectedError; 1039 1040 /// Delete a name and possibly the file it refers to. 1041 /// See also `unlinkC`. 1042 pub fn unlink(file_path: []const u8) UnlinkError!void { 1043 if (builtin.os == .windows) { 1044 const file_path_w = try windows.sliceToPrefixedFileW(file_path); 1045 return windows.DeleteFileW(&file_path_w); 1046 } else { 1047 const file_path_c = try toPosixPath(file_path); 1048 return unlinkC(&file_path_c); 1049 } 1050 } 1051 1052 /// Same as `unlink` except the parameter is a null terminated UTF8-encoded string. 1053 pub fn unlinkC(file_path: [*]const u8) UnlinkError!void { 1054 if (builtin.os == .windows) { 1055 const file_path_w = try windows.cStrToPrefixedFileW(file_path); 1056 return windows.DeleteFileW(&file_path_w); 1057 } 1058 switch (errno(system.unlink(file_path))) { 1059 0 => return, 1060 EACCES => return error.AccessDenied, 1061 EPERM => return error.AccessDenied, 1062 EBUSY => return error.FileBusy, 1063 EFAULT => unreachable, 1064 EINVAL => unreachable, 1065 EIO => return error.FileSystem, 1066 EISDIR => return error.IsDir, 1067 ELOOP => return error.SymLinkLoop, 1068 ENAMETOOLONG => return error.NameTooLong, 1069 ENOENT => return error.FileNotFound, 1070 ENOTDIR => return error.NotDir, 1071 ENOMEM => return error.SystemResources, 1072 EROFS => return error.ReadOnlyFileSystem, 1073 else => |err| return unexpectedErrno(err), 1074 } 1075 } 1076 1077 pub const UnlinkatError = UnlinkError || error{ 1078 /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty. 1079 DirNotEmpty, 1080 }; 1081 1082 /// Delete a file name and possibly the file it refers to, based on an open directory handle. 1083 pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { 1084 if (builtin.os == .windows) { 1085 const file_path_w = try windows.sliceToPrefixedFileW(file_path); 1086 return unlinkatW(dirfd, &file_path_w, flags); 1087 } 1088 const file_path_c = try toPosixPath(file_path); 1089 return unlinkatC(dirfd, &file_path_c, flags); 1090 } 1091 1092 /// Same as `unlinkat` but `file_path` is a null-terminated string. 1093 pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatError!void { 1094 if (builtin.os == .windows) { 1095 const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); 1096 return unlinkatW(dirfd, &file_path_w, flags); 1097 } 1098 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) { 1099 0 => return, 1100 EACCES => return error.AccessDenied, 1101 EPERM => return error.AccessDenied, 1102 EBUSY => return error.FileBusy, 1103 EFAULT => unreachable, 1104 EIO => return error.FileSystem, 1105 EISDIR => return error.IsDir, 1106 ELOOP => return error.SymLinkLoop, 1107 ENAMETOOLONG => return error.NameTooLong, 1108 ENOENT => return error.FileNotFound, 1109 ENOTDIR => return error.NotDir, 1110 ENOMEM => return error.SystemResources, 1111 EROFS => return error.ReadOnlyFileSystem, 1112 ENOTEMPTY => return error.DirNotEmpty, 1113 1114 EINVAL => unreachable, // invalid flags, or pathname has . as last component 1115 EBADF => unreachable, // always a race condition 1116 1117 else => |err| return unexpectedErrno(err), 1118 } 1119 } 1120 1121 /// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only. 1122 pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*]const u16, flags: u32) UnlinkatError!void { 1123 const w = windows; 1124 1125 const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0; 1126 const create_options_flags = if (want_rmdir_behavior) 1127 w.ULONG(w.FILE_DELETE_ON_CLOSE) 1128 else 1129 w.ULONG(w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE); 1130 1131 const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2); 1132 var nt_name = w.UNICODE_STRING{ 1133 .Length = path_len_bytes, 1134 .MaximumLength = path_len_bytes, 1135 // The Windows API makes this mutable, but it will not mutate here. 1136 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), 1137 }; 1138 1139 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { 1140 // Windows does not recognize this, but it does work with empty string. 1141 nt_name.Length = 0; 1142 } 1143 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { 1144 // Can't remove the parent directory with an open handle. 1145 return error.FileBusy; 1146 } 1147 1148 var attr = w.OBJECT_ATTRIBUTES{ 1149 .Length = @sizeOf(w.OBJECT_ATTRIBUTES), 1150 .RootDirectory = dirfd, 1151 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. 1152 .ObjectName = &nt_name, 1153 .SecurityDescriptor = null, 1154 .SecurityQualityOfService = null, 1155 }; 1156 var io: w.IO_STATUS_BLOCK = undefined; 1157 var tmp_handle: w.HANDLE = undefined; 1158 var rc = w.ntdll.NtCreateFile( 1159 &tmp_handle, 1160 w.SYNCHRONIZE | w.DELETE, 1161 &attr, 1162 &io, 1163 null, 1164 0, 1165 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE, 1166 w.FILE_OPEN, 1167 create_options_flags, 1168 null, 1169 0, 1170 ); 1171 if (rc == w.STATUS.SUCCESS) { 1172 rc = w.ntdll.NtClose(tmp_handle); 1173 } 1174 switch (rc) { 1175 w.STATUS.SUCCESS => return, 1176 w.STATUS.OBJECT_NAME_INVALID => unreachable, 1177 w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound, 1178 w.STATUS.INVALID_PARAMETER => unreachable, 1179 w.STATUS.FILE_IS_A_DIRECTORY => return error.IsDir, 1180 else => return w.unexpectedStatus(rc), 1181 } 1182 } 1183 1184 const RenameError = error{ 1185 AccessDenied, 1186 FileBusy, 1187 DiskQuota, 1188 IsDir, 1189 SymLinkLoop, 1190 LinkQuotaExceeded, 1191 NameTooLong, 1192 FileNotFound, 1193 NotDir, 1194 SystemResources, 1195 NoSpaceLeft, 1196 PathAlreadyExists, 1197 ReadOnlyFileSystem, 1198 RenameAcrossMountPoints, 1199 InvalidUtf8, 1200 BadPathName, 1201 } || UnexpectedError; 1202 1203 /// Change the name or location of a file. 1204 pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { 1205 if (builtin.os == .windows) { 1206 const old_path_w = try windows.sliceToPrefixedFileW(old_path); 1207 const new_path_w = try windows.sliceToPrefixedFileW(new_path); 1208 return renameW(&old_path_w, &new_path_w); 1209 } else { 1210 const old_path_c = try toPosixPath(old_path); 1211 const new_path_c = try toPosixPath(new_path); 1212 return renameC(&old_path_c, &new_path_c); 1213 } 1214 } 1215 1216 /// Same as `rename` except the parameters are null-terminated byte arrays. 1217 pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void { 1218 if (builtin.os == .windows) { 1219 const old_path_w = try windows.cStrToPrefixedFileW(old_path); 1220 const new_path_w = try windows.cStrToPrefixedFileW(new_path); 1221 return renameW(&old_path_w, &new_path_w); 1222 } 1223 switch (errno(system.rename(old_path, new_path))) { 1224 0 => return, 1225 EACCES => return error.AccessDenied, 1226 EPERM => return error.AccessDenied, 1227 EBUSY => return error.FileBusy, 1228 EDQUOT => return error.DiskQuota, 1229 EFAULT => unreachable, 1230 EINVAL => unreachable, 1231 EISDIR => return error.IsDir, 1232 ELOOP => return error.SymLinkLoop, 1233 EMLINK => return error.LinkQuotaExceeded, 1234 ENAMETOOLONG => return error.NameTooLong, 1235 ENOENT => return error.FileNotFound, 1236 ENOTDIR => return error.NotDir, 1237 ENOMEM => return error.SystemResources, 1238 ENOSPC => return error.NoSpaceLeft, 1239 EEXIST => return error.PathAlreadyExists, 1240 ENOTEMPTY => return error.PathAlreadyExists, 1241 EROFS => return error.ReadOnlyFileSystem, 1242 EXDEV => return error.RenameAcrossMountPoints, 1243 else => |err| return unexpectedErrno(err), 1244 } 1245 } 1246 1247 /// Same as `rename` except the parameters are null-terminated UTF16LE encoded byte arrays. 1248 /// Assumes target is Windows. 1249 pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) RenameError!void { 1250 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; 1251 return windows.MoveFileExW(old_path, new_path, flags); 1252 } 1253 1254 pub const MakeDirError = error{ 1255 AccessDenied, 1256 DiskQuota, 1257 PathAlreadyExists, 1258 SymLinkLoop, 1259 LinkQuotaExceeded, 1260 NameTooLong, 1261 FileNotFound, 1262 SystemResources, 1263 NoSpaceLeft, 1264 NotDir, 1265 ReadOnlyFileSystem, 1266 InvalidUtf8, 1267 BadPathName, 1268 } || UnexpectedError; 1269 1270 /// Create a directory. 1271 /// `mode` is ignored on Windows. 1272 pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { 1273 if (builtin.os == .windows) { 1274 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); 1275 return windows.CreateDirectoryW(&dir_path_w, null); 1276 } else { 1277 const dir_path_c = try toPosixPath(dir_path); 1278 return mkdirC(&dir_path_c, mode); 1279 } 1280 } 1281 1282 /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string. 1283 pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void { 1284 if (builtin.os == .windows) { 1285 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); 1286 return windows.CreateDirectoryW(&dir_path_w, null); 1287 } 1288 switch (errno(system.mkdir(dir_path, mode))) { 1289 0 => return, 1290 EACCES => return error.AccessDenied, 1291 EPERM => return error.AccessDenied, 1292 EDQUOT => return error.DiskQuota, 1293 EEXIST => return error.PathAlreadyExists, 1294 EFAULT => unreachable, 1295 ELOOP => return error.SymLinkLoop, 1296 EMLINK => return error.LinkQuotaExceeded, 1297 ENAMETOOLONG => return error.NameTooLong, 1298 ENOENT => return error.FileNotFound, 1299 ENOMEM => return error.SystemResources, 1300 ENOSPC => return error.NoSpaceLeft, 1301 ENOTDIR => return error.NotDir, 1302 EROFS => return error.ReadOnlyFileSystem, 1303 else => |err| return unexpectedErrno(err), 1304 } 1305 } 1306 1307 pub const DeleteDirError = error{ 1308 AccessDenied, 1309 FileBusy, 1310 SymLinkLoop, 1311 NameTooLong, 1312 FileNotFound, 1313 SystemResources, 1314 NotDir, 1315 DirNotEmpty, 1316 ReadOnlyFileSystem, 1317 InvalidUtf8, 1318 BadPathName, 1319 } || UnexpectedError; 1320 1321 /// Deletes an empty directory. 1322 pub fn rmdir(dir_path: []const u8) DeleteDirError!void { 1323 if (builtin.os == .windows) { 1324 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); 1325 return windows.RemoveDirectoryW(&dir_path_w); 1326 } else { 1327 const dir_path_c = try toPosixPath(dir_path); 1328 return rmdirC(&dir_path_c); 1329 } 1330 } 1331 1332 /// Same as `rmdir` except the parameter is null-terminated. 1333 pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void { 1334 if (builtin.os == .windows) { 1335 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); 1336 return windows.RemoveDirectoryW(&dir_path_w); 1337 } 1338 switch (errno(system.rmdir(dir_path))) { 1339 0 => return, 1340 EACCES => return error.AccessDenied, 1341 EPERM => return error.AccessDenied, 1342 EBUSY => return error.FileBusy, 1343 EFAULT => unreachable, 1344 EINVAL => unreachable, 1345 ELOOP => return error.SymLinkLoop, 1346 ENAMETOOLONG => return error.NameTooLong, 1347 ENOENT => return error.FileNotFound, 1348 ENOMEM => return error.SystemResources, 1349 ENOTDIR => return error.NotDir, 1350 EEXIST => return error.DirNotEmpty, 1351 ENOTEMPTY => return error.DirNotEmpty, 1352 EROFS => return error.ReadOnlyFileSystem, 1353 else => |err| return unexpectedErrno(err), 1354 } 1355 } 1356 1357 pub const ChangeCurDirError = error{ 1358 AccessDenied, 1359 FileSystem, 1360 SymLinkLoop, 1361 NameTooLong, 1362 FileNotFound, 1363 SystemResources, 1364 NotDir, 1365 } || UnexpectedError; 1366 1367 /// Changes the current working directory of the calling process. 1368 /// `dir_path` is recommended to be a UTF-8 encoded string. 1369 pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { 1370 if (builtin.os == .windows) { 1371 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); 1372 @compileError("TODO implement chdir for Windows"); 1373 } else { 1374 const dir_path_c = try toPosixPath(dir_path); 1375 return chdirC(&dir_path_c); 1376 } 1377 } 1378 1379 /// Same as `chdir` except the parameter is null-terminated. 1380 pub fn chdirC(dir_path: [*]const u8) ChangeCurDirError!void { 1381 if (builtin.os == .windows) { 1382 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); 1383 @compileError("TODO implement chdir for Windows"); 1384 } 1385 switch (errno(system.chdir(dir_path))) { 1386 0 => return, 1387 EACCES => return error.AccessDenied, 1388 EFAULT => unreachable, 1389 EIO => return error.FileSystem, 1390 ELOOP => return error.SymLinkLoop, 1391 ENAMETOOLONG => return error.NameTooLong, 1392 ENOENT => return error.FileNotFound, 1393 ENOMEM => return error.SystemResources, 1394 ENOTDIR => return error.NotDir, 1395 else => |err| return unexpectedErrno(err), 1396 } 1397 } 1398 1399 pub const ReadLinkError = error{ 1400 AccessDenied, 1401 FileSystem, 1402 SymLinkLoop, 1403 NameTooLong, 1404 FileNotFound, 1405 SystemResources, 1406 NotDir, 1407 } || UnexpectedError; 1408 1409 /// Read value of a symbolic link. 1410 /// The return value is a slice of `out_buffer` from index 0. 1411 pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { 1412 if (builtin.os == .windows) { 1413 const file_path_w = try windows.sliceToPrefixedFileW(file_path); 1414 @compileError("TODO implement readlink for Windows"); 1415 } else { 1416 const file_path_c = try toPosixPath(file_path); 1417 return readlinkC(&file_path_c, out_buffer); 1418 } 1419 } 1420 1421 /// Same as `readlink` except `file_path` is null-terminated. 1422 pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 { 1423 if (builtin.os == .windows) { 1424 const file_path_w = try windows.cStrToPrefixedFileW(file_path); 1425 @compileError("TODO implement readlink for Windows"); 1426 } 1427 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len); 1428 switch (errno(rc)) { 1429 0 => return out_buffer[0..@bitCast(usize, rc)], 1430 EACCES => return error.AccessDenied, 1431 EFAULT => unreachable, 1432 EINVAL => unreachable, 1433 EIO => return error.FileSystem, 1434 ELOOP => return error.SymLinkLoop, 1435 ENAMETOOLONG => return error.NameTooLong, 1436 ENOENT => return error.FileNotFound, 1437 ENOMEM => return error.SystemResources, 1438 ENOTDIR => return error.NotDir, 1439 else => |err| return unexpectedErrno(err), 1440 } 1441 } 1442 1443 pub fn readlinkatC(dirfd: fd_t, file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 { 1444 if (builtin.os == .windows) { 1445 const file_path_w = try windows.cStrToPrefixedFileW(file_path); 1446 @compileError("TODO implement readlink for Windows"); 1447 } 1448 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len); 1449 switch (errno(rc)) { 1450 0 => return out_buffer[0..@bitCast(usize, rc)], 1451 EACCES => return error.AccessDenied, 1452 EFAULT => unreachable, 1453 EINVAL => unreachable, 1454 EIO => return error.FileSystem, 1455 ELOOP => return error.SymLinkLoop, 1456 ENAMETOOLONG => return error.NameTooLong, 1457 ENOENT => return error.FileNotFound, 1458 ENOMEM => return error.SystemResources, 1459 ENOTDIR => return error.NotDir, 1460 else => |err| return unexpectedErrno(err), 1461 } 1462 } 1463 1464 pub const SetIdError = error{ 1465 ResourceLimitReached, 1466 InvalidUserId, 1467 PermissionDenied, 1468 } || UnexpectedError; 1469 1470 pub fn setuid(uid: u32) SetIdError!void { 1471 switch (errno(system.setuid(uid))) { 1472 0 => return, 1473 EAGAIN => return error.ResourceLimitReached, 1474 EINVAL => return error.InvalidUserId, 1475 EPERM => return error.PermissionDenied, 1476 else => |err| return unexpectedErrno(err), 1477 } 1478 } 1479 1480 pub fn setreuid(ruid: u32, euid: u32) SetIdError!void { 1481 switch (errno(system.setreuid(ruid, euid))) { 1482 0 => return, 1483 EAGAIN => return error.ResourceLimitReached, 1484 EINVAL => return error.InvalidUserId, 1485 EPERM => return error.PermissionDenied, 1486 else => |err| return unexpectedErrno(err), 1487 } 1488 } 1489 1490 pub fn setgid(gid: u32) SetIdError!void { 1491 switch (errno(system.setgid(gid))) { 1492 0 => return, 1493 EAGAIN => return error.ResourceLimitReached, 1494 EINVAL => return error.InvalidUserId, 1495 EPERM => return error.PermissionDenied, 1496 else => |err| return unexpectedErrno(err), 1497 } 1498 } 1499 1500 pub fn setregid(rgid: u32, egid: u32) SetIdError!void { 1501 switch (errno(system.setregid(rgid, egid))) { 1502 0 => return, 1503 EAGAIN => return error.ResourceLimitReached, 1504 EINVAL => return error.InvalidUserId, 1505 EPERM => return error.PermissionDenied, 1506 else => |err| return unexpectedErrno(err), 1507 } 1508 } 1509 1510 /// Test whether a file descriptor refers to a terminal. 1511 pub fn isatty(handle: fd_t) bool { 1512 if (builtin.os == .windows) { 1513 if (isCygwinPty(handle)) 1514 return true; 1515 1516 var out: windows.DWORD = undefined; 1517 return windows.kernel32.GetConsoleMode(handle, &out) != 0; 1518 } 1519 if (builtin.link_libc) { 1520 return system.isatty(handle) != 0; 1521 } 1522 if (builtin.os == .wasi) { 1523 @compileError("TODO implement std.os.isatty for WASI"); 1524 } 1525 if (builtin.os == .linux) { 1526 var wsz: linux.winsize = undefined; 1527 return linux.syscall3(linux.SYS_ioctl, @bitCast(usize, isize(handle)), linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0; 1528 } 1529 unreachable; 1530 } 1531 1532 pub fn isCygwinPty(handle: fd_t) bool { 1533 if (builtin.os != .windows) return false; 1534 1535 const size = @sizeOf(windows.FILE_NAME_INFO); 1536 var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH); 1537 1538 if (windows.kernel32.GetFileInformationByHandleEx( 1539 handle, 1540 windows.FileNameInfo, 1541 @ptrCast(*c_void, &name_info_bytes), 1542 name_info_bytes.len, 1543 ) == 0) { 1544 return false; 1545 } 1546 1547 const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); 1548 const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; 1549 const name_wide = @bytesToSlice(u16, name_bytes); 1550 return mem.indexOf(u16, name_wide, [_]u16{ 'm', 's', 'y', 's', '-' }) != null or 1551 mem.indexOf(u16, name_wide, [_]u16{ '-', 'p', 't', 'y' }) != null; 1552 } 1553 1554 pub const SocketError = error{ 1555 /// Permission to create a socket of the specified type and/or 1556 /// pro‐tocol is denied. 1557 PermissionDenied, 1558 1559 /// The implementation does not support the specified address family. 1560 AddressFamilyNotSupported, 1561 1562 /// Unknown protocol, or protocol family not available. 1563 ProtocolFamilyNotAvailable, 1564 1565 /// The per-process limit on the number of open file descriptors has been reached. 1566 ProcessFdQuotaExceeded, 1567 1568 /// The system-wide limit on the total number of open files has been reached. 1569 SystemFdQuotaExceeded, 1570 1571 /// Insufficient memory is available. The socket cannot be created until sufficient 1572 /// resources are freed. 1573 SystemResources, 1574 1575 /// The protocol type or the specified protocol is not supported within this domain. 1576 ProtocolNotSupported, 1577 } || UnexpectedError; 1578 1579 pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t { 1580 const rc = system.socket(domain, socket_type, protocol); 1581 switch (errno(rc)) { 1582 0 => return @intCast(fd_t, rc), 1583 EACCES => return error.PermissionDenied, 1584 EAFNOSUPPORT => return error.AddressFamilyNotSupported, 1585 EINVAL => return error.ProtocolFamilyNotAvailable, 1586 EMFILE => return error.ProcessFdQuotaExceeded, 1587 ENFILE => return error.SystemFdQuotaExceeded, 1588 ENOBUFS => return error.SystemResources, 1589 ENOMEM => return error.SystemResources, 1590 EPROTONOSUPPORT => return error.ProtocolNotSupported, 1591 else => |err| return unexpectedErrno(err), 1592 } 1593 } 1594 1595 pub const BindError = error{ 1596 /// The address is protected, and the user is not the superuser. 1597 /// For UNIX domain sockets: Search permission is denied on a component 1598 /// of the path prefix. 1599 AccessDenied, 1600 1601 /// The given address is already in use, or in the case of Internet domain sockets, 1602 /// The port number was specified as zero in the socket 1603 /// address structure, but, upon attempting to bind to an ephemeral port, it was 1604 /// determined that all port numbers in the ephemeral port range are currently in 1605 /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7). 1606 AddressInUse, 1607 1608 /// A nonexistent interface was requested or the requested address was not local. 1609 AddressNotAvailable, 1610 1611 /// Too many symbolic links were encountered in resolving addr. 1612 SymLinkLoop, 1613 1614 /// addr is too long. 1615 NameTooLong, 1616 1617 /// A component in the directory prefix of the socket pathname does not exist. 1618 FileNotFound, 1619 1620 /// Insufficient kernel memory was available. 1621 SystemResources, 1622 1623 /// A component of the path prefix is not a directory. 1624 NotDir, 1625 1626 /// The socket inode would reside on a read-only filesystem. 1627 ReadOnlyFileSystem, 1628 } || UnexpectedError; 1629 1630 /// addr is `*const T` where T is one of the sockaddr 1631 pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void { 1632 const rc = system.bind(sockfd, addr, len); 1633 switch (errno(rc)) { 1634 0 => return, 1635 EACCES => return error.AccessDenied, 1636 EADDRINUSE => return error.AddressInUse, 1637 EBADF => unreachable, // always a race condition if this error is returned 1638 EINVAL => unreachable, // invalid parameters 1639 ENOTSOCK => unreachable, // invalid `sockfd` 1640 EADDRNOTAVAIL => return error.AddressNotAvailable, 1641 EFAULT => unreachable, // invalid `addr` pointer 1642 ELOOP => return error.SymLinkLoop, 1643 ENAMETOOLONG => return error.NameTooLong, 1644 ENOENT => return error.FileNotFound, 1645 ENOMEM => return error.SystemResources, 1646 ENOTDIR => return error.NotDir, 1647 EROFS => return error.ReadOnlyFileSystem, 1648 else => |err| return unexpectedErrno(err), 1649 } 1650 } 1651 1652 const ListenError = error{ 1653 /// Another socket is already listening on the same port. 1654 /// For Internet domain sockets, the socket referred to by sockfd had not previously 1655 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it 1656 /// was determined that all port numbers in the ephemeral port range are currently in 1657 /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). 1658 AddressInUse, 1659 1660 /// The file descriptor sockfd does not refer to a socket. 1661 FileDescriptorNotASocket, 1662 1663 /// The socket is not of a type that supports the listen() operation. 1664 OperationNotSupported, 1665 } || UnexpectedError; 1666 1667 pub fn listen(sockfd: i32, backlog: u32) ListenError!void { 1668 const rc = system.listen(sockfd, backlog); 1669 switch (errno(rc)) { 1670 0 => return, 1671 EADDRINUSE => return error.AddressInUse, 1672 EBADF => unreachable, 1673 ENOTSOCK => return error.FileDescriptorNotASocket, 1674 EOPNOTSUPP => return error.OperationNotSupported, 1675 else => |err| return unexpectedErrno(err), 1676 } 1677 } 1678 1679 pub const AcceptError = error{ 1680 ConnectionAborted, 1681 1682 /// The per-process limit on the number of open file descriptors has been reached. 1683 ProcessFdQuotaExceeded, 1684 1685 /// The system-wide limit on the total number of open files has been reached. 1686 SystemFdQuotaExceeded, 1687 1688 /// Not enough free memory. This often means that the memory allocation is limited 1689 /// by the socket buffer limits, not by the system memory. 1690 SystemResources, 1691 1692 ProtocolFailure, 1693 1694 /// Firewall rules forbid connection. 1695 BlockedByFirewall, 1696 1697 /// This error occurs when no global event loop is configured, 1698 /// and accepting from the socket would block. 1699 WouldBlock, 1700 } || UnexpectedError; 1701 1702 /// Accept a connection on a socket. 1703 /// If the application has a global event loop enabled, EAGAIN is handled 1704 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. 1705 pub fn accept4( 1706 /// This argument is a socket that has been created with `socket`, bound to a local address 1707 /// with `bind`, and is listening for connections after a `listen`. 1708 sockfd: fd_t, 1709 /// This argument is a pointer to a sockaddr structure. This structure is filled in with the 1710 /// address of the peer socket, as known to the communications layer. The exact format of the 1711 /// address returned addr is determined by the socket's address family (see `socket` and the 1712 /// respective protocol man pages). 1713 addr: *sockaddr, 1714 /// This argument is a value-result argument: the caller must initialize it to contain the 1715 /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size 1716 /// of the peer address. 1717 /// 1718 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size` 1719 /// will return a value greater than was supplied to the call. 1720 addr_size: *socklen_t, 1721 /// If flags is 0, then `accept4` is the same as `accept`. The following values can be bitwise 1722 /// ORed in flags to obtain different behavior: 1723 /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`) 1724 /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve 1725 /// the same result. 1726 /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the 1727 /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful. 1728 flags: u32, 1729 ) AcceptError!fd_t { 1730 while (true) { 1731 const rc = system.accept4(sockfd, addr, addr_size, flags); 1732 switch (errno(rc)) { 1733 0 => return @intCast(fd_t, rc), 1734 EINTR => continue, 1735 1736 EAGAIN => if (std.event.Loop.instance) |loop| { 1737 loop.waitUntilFdReadable(sockfd); 1738 continue; 1739 } else { 1740 return error.WouldBlock; 1741 }, 1742 EBADF => unreachable, // always a race condition 1743 ECONNABORTED => return error.ConnectionAborted, 1744 EFAULT => unreachable, 1745 EINVAL => unreachable, 1746 ENOTSOCK => unreachable, 1747 EMFILE => return error.ProcessFdQuotaExceeded, 1748 ENFILE => return error.SystemFdQuotaExceeded, 1749 ENOBUFS => return error.SystemResources, 1750 ENOMEM => return error.SystemResources, 1751 EOPNOTSUPP => unreachable, 1752 EPROTO => return error.ProtocolFailure, 1753 EPERM => return error.BlockedByFirewall, 1754 1755 else => |err| return unexpectedErrno(err), 1756 } 1757 } 1758 } 1759 1760 pub const EpollCreateError = error{ 1761 /// The per-user limit on the number of epoll instances imposed by 1762 /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further 1763 /// details. 1764 /// Or, The per-process limit on the number of open file descriptors has been reached. 1765 ProcessFdQuotaExceeded, 1766 1767 /// The system-wide limit on the total number of open files has been reached. 1768 SystemFdQuotaExceeded, 1769 1770 /// There was insufficient memory to create the kernel object. 1771 SystemResources, 1772 } || UnexpectedError; 1773 1774 pub fn epoll_create1(flags: u32) EpollCreateError!i32 { 1775 const rc = system.epoll_create1(flags); 1776 switch (errno(rc)) { 1777 0 => return @intCast(i32, rc), 1778 else => |err| return unexpectedErrno(err), 1779 1780 EINVAL => unreachable, 1781 EMFILE => return error.ProcessFdQuotaExceeded, 1782 ENFILE => return error.SystemFdQuotaExceeded, 1783 ENOMEM => return error.SystemResources, 1784 } 1785 } 1786 1787 pub const EpollCtlError = error{ 1788 /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered 1789 /// with this epoll instance. 1790 FileDescriptorAlreadyPresentInSet, 1791 1792 /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a 1793 /// circular loop of epoll instances monitoring one another. 1794 OperationCausesCircularLoop, 1795 1796 /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll 1797 /// instance. 1798 FileDescriptorNotRegistered, 1799 1800 /// There was insufficient memory to handle the requested op control operation. 1801 SystemResources, 1802 1803 /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while 1804 /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. 1805 /// See epoll(7) for further details. 1806 UserResourceLimitReached, 1807 1808 /// The target file fd does not support epoll. This error can occur if fd refers to, 1809 /// for example, a regular file or a directory. 1810 FileDescriptorIncompatibleWithEpoll, 1811 } || UnexpectedError; 1812 1813 pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*epoll_event) EpollCtlError!void { 1814 const rc = system.epoll_ctl(epfd, op, fd, event); 1815 switch (errno(rc)) { 1816 0 => return, 1817 else => |err| return unexpectedErrno(err), 1818 1819 EBADF => unreachable, // always a race condition if this happens 1820 EEXIST => return error.FileDescriptorAlreadyPresentInSet, 1821 EINVAL => unreachable, 1822 ELOOP => return error.OperationCausesCircularLoop, 1823 ENOENT => return error.FileDescriptorNotRegistered, 1824 ENOMEM => return error.SystemResources, 1825 ENOSPC => return error.UserResourceLimitReached, 1826 EPERM => return error.FileDescriptorIncompatibleWithEpoll, 1827 } 1828 } 1829 1830 /// Waits for an I/O event on an epoll file descriptor. 1831 /// Returns the number of file descriptors ready for the requested I/O, 1832 /// or zero if no file descriptor became ready during the requested timeout milliseconds. 1833 pub fn epoll_wait(epfd: i32, events: []epoll_event, timeout: i32) usize { 1834 while (true) { 1835 // TODO get rid of the @intCast 1836 const rc = system.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); 1837 switch (errno(rc)) { 1838 0 => return @intCast(usize, rc), 1839 EINTR => continue, 1840 EBADF => unreachable, 1841 EFAULT => unreachable, 1842 EINVAL => unreachable, 1843 else => unreachable, 1844 } 1845 } 1846 } 1847 1848 pub const EventFdError = error{ 1849 SystemResources, 1850 ProcessFdQuotaExceeded, 1851 SystemFdQuotaExceeded, 1852 } || UnexpectedError; 1853 1854 pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 { 1855 const rc = system.eventfd(initval, flags); 1856 switch (errno(rc)) { 1857 0 => return @intCast(i32, rc), 1858 else => |err| return unexpectedErrno(err), 1859 1860 EINVAL => unreachable, // invalid parameters 1861 EMFILE => return error.ProcessFdQuotaExceeded, 1862 ENFILE => return error.SystemFdQuotaExceeded, 1863 ENODEV => return error.SystemResources, 1864 ENOMEM => return error.SystemResources, 1865 } 1866 } 1867 1868 pub const GetSockNameError = error{ 1869 /// Insufficient resources were available in the system to perform the operation. 1870 SystemResources, 1871 } || UnexpectedError; 1872 1873 pub fn getsockname(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void { 1874 switch (errno(system.getsockname(sockfd, addr, addrlen))) { 1875 0 => return, 1876 else => |err| return unexpectedErrno(err), 1877 1878 EBADF => unreachable, // always a race condition 1879 EFAULT => unreachable, 1880 EINVAL => unreachable, // invalid parameters 1881 ENOTSOCK => unreachable, 1882 ENOBUFS => return error.SystemResources, 1883 } 1884 } 1885 1886 pub const ConnectError = error{ 1887 /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket 1888 /// file, or search permission is denied for one of the directories in the path prefix. 1889 /// or 1890 /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or 1891 /// the connection request failed because of a local firewall rule. 1892 PermissionDenied, 1893 1894 /// Local address is already in use. 1895 AddressInUse, 1896 1897 /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an 1898 /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers 1899 /// in the ephemeral port range are currently in use. See the discussion of 1900 /// /proc/sys/net/ipv4/ip_local_port_range in ip(7). 1901 AddressNotAvailable, 1902 1903 /// The passed address didn't have the correct address family in its sa_family field. 1904 AddressFamilyNotSupported, 1905 1906 /// Insufficient entries in the routing cache. 1907 SystemResources, 1908 1909 /// A connect() on a stream socket found no one listening on the remote address. 1910 ConnectionRefused, 1911 1912 /// Network is unreachable. 1913 NetworkUnreachable, 1914 1915 /// Timeout while attempting connection. The server may be too busy to accept new connections. Note 1916 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. 1917 ConnectionTimedOut, 1918 1919 /// This error occurs when no global event loop is configured, 1920 /// and connecting to the socket would block. 1921 WouldBlock, 1922 1923 /// The given path for the unix socket does not exist. 1924 FileNotFound, 1925 } || UnexpectedError; 1926 1927 /// Initiate a connection on a socket. 1928 pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void { 1929 while (true) { 1930 switch (errno(system.connect(sockfd, sock_addr, len))) { 1931 0 => return, 1932 EACCES => return error.PermissionDenied, 1933 EPERM => return error.PermissionDenied, 1934 EADDRINUSE => return error.AddressInUse, 1935 EADDRNOTAVAIL => return error.AddressNotAvailable, 1936 EAFNOSUPPORT => return error.AddressFamilyNotSupported, 1937 EAGAIN, EINPROGRESS => { 1938 const loop = std.event.Loop.instance orelse return error.WouldBlock; 1939 loop.waitUntilFdWritableOrReadable(sockfd); 1940 return getsockoptError(sockfd); 1941 }, 1942 EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. 1943 EBADF => unreachable, // sockfd is not a valid open file descriptor. 1944 ECONNREFUSED => return error.ConnectionRefused, 1945 EFAULT => unreachable, // The socket structure address is outside the user's address space. 1946 EINTR => continue, 1947 EISCONN => unreachable, // The socket is already connected. 1948 ENETUNREACH => return error.NetworkUnreachable, 1949 ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. 1950 EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. 1951 ETIMEDOUT => return error.ConnectionTimedOut, 1952 ENOENT => return error.FileNotFound, // Returned when socket is AF_UNIX and the given path does not exist. 1953 else => |err| return unexpectedErrno(err), 1954 } 1955 } 1956 } 1957 1958 pub fn getsockoptError(sockfd: i32) ConnectError!void { 1959 var err_code: u32 = undefined; 1960 var size: u32 = @sizeOf(u32); 1961 const rc = system.getsockopt(sockfd, SOL_SOCKET, SO_ERROR, @ptrCast([*]u8, &err_code), &size); 1962 assert(size == 4); 1963 switch (errno(rc)) { 1964 0 => switch (err_code) { 1965 0 => return, 1966 EACCES => return error.PermissionDenied, 1967 EPERM => return error.PermissionDenied, 1968 EADDRINUSE => return error.AddressInUse, 1969 EADDRNOTAVAIL => return error.AddressNotAvailable, 1970 EAFNOSUPPORT => return error.AddressFamilyNotSupported, 1971 EAGAIN => return error.SystemResources, 1972 EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. 1973 EBADF => unreachable, // sockfd is not a valid open file descriptor. 1974 ECONNREFUSED => return error.ConnectionRefused, 1975 EFAULT => unreachable, // The socket structure address is outside the user's address space. 1976 EISCONN => unreachable, // The socket is already connected. 1977 ENETUNREACH => return error.NetworkUnreachable, 1978 ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. 1979 EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. 1980 ETIMEDOUT => return error.ConnectionTimedOut, 1981 else => |err| return unexpectedErrno(err), 1982 }, 1983 EBADF => unreachable, // The argument sockfd is not a valid file descriptor. 1984 EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. 1985 EINVAL => unreachable, 1986 ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. 1987 ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. 1988 else => |err| return unexpectedErrno(err), 1989 } 1990 } 1991 1992 pub fn waitpid(pid: i32, flags: u32) u32 { 1993 // TODO allow implicit pointer cast from *u32 to *c_uint ? 1994 const Status = if (builtin.link_libc) c_uint else u32; 1995 var status: Status = undefined; 1996 while (true) { 1997 switch (errno(system.waitpid(pid, &status, flags))) { 1998 0 => return @bitCast(u32, status), 1999 EINTR => continue, 2000 ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. 2001 EINVAL => unreachable, // The options argument was invalid 2002 else => unreachable, 2003 } 2004 } 2005 } 2006 2007 pub const FStatError = error{SystemResources} || UnexpectedError; 2008 2009 pub fn fstat(fd: fd_t) FStatError!Stat { 2010 var stat: Stat = undefined; 2011 if (comptime std.Target.current.isDarwin()) { 2012 switch (darwin.getErrno(darwin.@"fstat$INODE64"(fd, &stat))) { 2013 0 => return stat, 2014 EINVAL => unreachable, 2015 EBADF => unreachable, // Always a race condition. 2016 ENOMEM => return error.SystemResources, 2017 else => |err| return unexpectedErrno(err), 2018 } 2019 } 2020 2021 switch (errno(system.fstat(fd, &stat))) { 2022 0 => return stat, 2023 EINVAL => unreachable, 2024 EBADF => unreachable, // Always a race condition. 2025 ENOMEM => return error.SystemResources, 2026 else => |err| return unexpectedErrno(err), 2027 } 2028 } 2029 2030 pub const KQueueError = error{ 2031 /// The per-process limit on the number of open file descriptors has been reached. 2032 ProcessFdQuotaExceeded, 2033 2034 /// The system-wide limit on the total number of open files has been reached. 2035 SystemFdQuotaExceeded, 2036 } || UnexpectedError; 2037 2038 pub fn kqueue() KQueueError!i32 { 2039 const rc = system.kqueue(); 2040 switch (errno(rc)) { 2041 0 => return @intCast(i32, rc), 2042 EMFILE => return error.ProcessFdQuotaExceeded, 2043 ENFILE => return error.SystemFdQuotaExceeded, 2044 else => |err| return unexpectedErrno(err), 2045 } 2046 } 2047 2048 pub const KEventError = error{ 2049 /// The process does not have permission to register a filter. 2050 AccessDenied, 2051 2052 /// The event could not be found to be modified or deleted. 2053 EventNotFound, 2054 2055 /// No memory was available to register the event. 2056 SystemResources, 2057 2058 /// The specified process to attach to does not exist. 2059 ProcessNotFound, 2060 2061 /// changelist or eventlist had too many items on it. 2062 /// TODO remove this possibility 2063 Overflow, 2064 }; 2065 2066 pub fn kevent( 2067 kq: i32, 2068 changelist: []const Kevent, 2069 eventlist: []Kevent, 2070 timeout: ?*const timespec, 2071 ) KEventError!usize { 2072 while (true) { 2073 const rc = system.kevent( 2074 kq, 2075 changelist.ptr, 2076 try math.cast(c_int, changelist.len), 2077 eventlist.ptr, 2078 try math.cast(c_int, eventlist.len), 2079 timeout, 2080 ); 2081 switch (errno(rc)) { 2082 0 => return @intCast(usize, rc), 2083 EACCES => return error.AccessDenied, 2084 EFAULT => unreachable, 2085 EBADF => unreachable, // Always a race condition. 2086 EINTR => continue, 2087 EINVAL => unreachable, 2088 ENOENT => return error.EventNotFound, 2089 ENOMEM => return error.SystemResources, 2090 ESRCH => return error.ProcessNotFound, 2091 else => unreachable, 2092 } 2093 } 2094 } 2095 2096 pub const INotifyInitError = error{ 2097 ProcessFdQuotaExceeded, 2098 SystemFdQuotaExceeded, 2099 SystemResources, 2100 } || UnexpectedError; 2101 2102 /// initialize an inotify instance 2103 pub fn inotify_init1(flags: u32) INotifyInitError!i32 { 2104 const rc = system.inotify_init1(flags); 2105 switch (errno(rc)) { 2106 0 => return @intCast(i32, rc), 2107 EINVAL => unreachable, 2108 EMFILE => return error.ProcessFdQuotaExceeded, 2109 ENFILE => return error.SystemFdQuotaExceeded, 2110 ENOMEM => return error.SystemResources, 2111 else => |err| return unexpectedErrno(err), 2112 } 2113 } 2114 2115 pub const INotifyAddWatchError = error{ 2116 AccessDenied, 2117 NameTooLong, 2118 FileNotFound, 2119 SystemResources, 2120 UserResourceLimitReached, 2121 } || UnexpectedError; 2122 2123 /// add a watch to an initialized inotify instance 2124 pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 { 2125 const pathname_c = try toPosixPath(pathname); 2126 return inotify_add_watchC(inotify_fd, &pathname_c, mask); 2127 } 2128 2129 /// Same as `inotify_add_watch` except pathname is null-terminated. 2130 pub fn inotify_add_watchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) INotifyAddWatchError!i32 { 2131 const rc = system.inotify_add_watch(inotify_fd, pathname, mask); 2132 switch (errno(rc)) { 2133 0 => return @intCast(i32, rc), 2134 EACCES => return error.AccessDenied, 2135 EBADF => unreachable, 2136 EFAULT => unreachable, 2137 EINVAL => unreachable, 2138 ENAMETOOLONG => return error.NameTooLong, 2139 ENOENT => return error.FileNotFound, 2140 ENOMEM => return error.SystemResources, 2141 ENOSPC => return error.UserResourceLimitReached, 2142 else => |err| return unexpectedErrno(err), 2143 } 2144 } 2145 2146 /// remove an existing watch from an inotify instance 2147 pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void { 2148 switch (errno(system.inotify_rm_watch(inotify_fd, wd))) { 2149 0 => return, 2150 EBADF => unreachable, 2151 EINVAL => unreachable, 2152 else => unreachable, 2153 } 2154 } 2155 2156 pub const MProtectError = error{ 2157 /// The memory cannot be given the specified access. This can happen, for example, if you 2158 /// mmap(2) a file to which you have read-only access, then ask mprotect() to mark it 2159 /// PROT_WRITE. 2160 AccessDenied, 2161 2162 /// Changing the protection of a memory region would result in the total number of map‐ 2163 /// pings with distinct attributes (e.g., read versus read/write protection) exceeding the 2164 /// allowed maximum. (For example, making the protection of a range PROT_READ in the mid‐ 2165 /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐ 2166 /// pings: two read/write mappings at each end and a read-only mapping in the middle.) 2167 OutOfMemory, 2168 } || UnexpectedError; 2169 2170 /// `memory.len` must be page-aligned. 2171 pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void { 2172 assert(mem.isAligned(memory.len, mem.page_size)); 2173 switch (errno(system.mprotect(memory.ptr, memory.len, protection))) { 2174 0 => return, 2175 EINVAL => unreachable, 2176 EACCES => return error.AccessDenied, 2177 ENOMEM => return error.OutOfMemory, 2178 else => |err| return unexpectedErrno(err), 2179 } 2180 } 2181 2182 pub const ForkError = error{SystemResources} || UnexpectedError; 2183 2184 pub fn fork() ForkError!pid_t { 2185 const rc = system.fork(); 2186 switch (errno(rc)) { 2187 0 => return @intCast(pid_t, rc), 2188 EAGAIN => return error.SystemResources, 2189 ENOMEM => return error.SystemResources, 2190 else => |err| return unexpectedErrno(err), 2191 } 2192 } 2193 2194 pub const MMapError = error{ 2195 /// The underlying filesystem of the specified file does not support memory mapping. 2196 MemoryMappingNotSupported, 2197 2198 /// A file descriptor refers to a non-regular file. Or a file mapping was requested, 2199 /// but the file descriptor is not open for reading. Or `MAP_SHARED` was requested 2200 /// and `PROT_WRITE` is set, but the file descriptor is not open in `O_RDWR` mode. 2201 /// Or `PROT_WRITE` is set, but the file is append-only. 2202 AccessDenied, 2203 2204 /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on 2205 /// a filesystem that was mounted no-exec. 2206 PermissionDenied, 2207 LockedMemoryLimitExceeded, 2208 OutOfMemory, 2209 } || UnexpectedError; 2210 2211 /// Map files or devices into memory. 2212 /// Use of a mapped region can result in these signals: 2213 /// * SIGSEGV - Attempted write into a region mapped as read-only. 2214 /// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file 2215 pub fn mmap( 2216 ptr: ?[*]align(mem.page_size) u8, 2217 length: usize, 2218 prot: u32, 2219 flags: u32, 2220 fd: fd_t, 2221 offset: u64, 2222 ) MMapError![]align(mem.page_size) u8 { 2223 const err = if (builtin.link_libc) blk: { 2224 const rc = std.c.mmap(ptr, length, prot, flags, fd, offset); 2225 if (rc != std.c.MAP_FAILED) return @ptrCast([*]align(mem.page_size) u8, @alignCast(mem.page_size, rc))[0..length]; 2226 break :blk @intCast(usize, system._errno().*); 2227 } else blk: { 2228 const rc = system.mmap(ptr, length, prot, flags, fd, offset); 2229 const err = errno(rc); 2230 if (err == 0) return @intToPtr([*]align(mem.page_size) u8, rc)[0..length]; 2231 break :blk err; 2232 }; 2233 switch (err) { 2234 ETXTBSY => return error.AccessDenied, 2235 EACCES => return error.AccessDenied, 2236 EPERM => return error.PermissionDenied, 2237 EAGAIN => return error.LockedMemoryLimitExceeded, 2238 EBADF => unreachable, // Always a race condition. 2239 EOVERFLOW => unreachable, // The number of pages used for length + offset would overflow. 2240 ENODEV => return error.MemoryMappingNotSupported, 2241 EINVAL => unreachable, // Invalid parameters to mmap() 2242 ENOMEM => return error.OutOfMemory, 2243 else => return unexpectedErrno(err), 2244 } 2245 } 2246 2247 /// Deletes the mappings for the specified address range, causing 2248 /// further references to addresses within the range to generate invalid memory references. 2249 /// Note that while POSIX allows unmapping a region in the middle of an existing mapping, 2250 /// Zig's munmap function does not, for two reasons: 2251 /// * It violates the Zig principle that resource deallocation must succeed. 2252 /// * The Windows function, VirtualFree, has this restriction. 2253 pub fn munmap(memory: []align(mem.page_size) u8) void { 2254 switch (errno(system.munmap(memory.ptr, memory.len))) { 2255 0 => return, 2256 EINVAL => unreachable, // Invalid parameters. 2257 ENOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping. 2258 else => unreachable, 2259 } 2260 } 2261 2262 pub const AccessError = error{ 2263 PermissionDenied, 2264 FileNotFound, 2265 NameTooLong, 2266 InputOutput, 2267 SystemResources, 2268 BadPathName, 2269 2270 /// On Windows, file paths must be valid Unicode. 2271 InvalidUtf8, 2272 } || UnexpectedError; 2273 2274 /// check user's permissions for a file 2275 /// TODO currently this assumes `mode` is `F_OK` on Windows. 2276 pub fn access(path: []const u8, mode: u32) AccessError!void { 2277 if (builtin.os == .windows) { 2278 const path_w = try windows.sliceToPrefixedFileW(path); 2279 _ = try windows.GetFileAttributesW(&path_w); 2280 return; 2281 } 2282 const path_c = try toPosixPath(path); 2283 return accessC(&path_c, mode); 2284 } 2285 2286 /// Same as `access` except `path` is null-terminated. 2287 pub fn accessC(path: [*]const u8, mode: u32) AccessError!void { 2288 if (builtin.os == .windows) { 2289 const path_w = try windows.cStrToPrefixedFileW(path); 2290 _ = try windows.GetFileAttributesW(&path_w); 2291 return; 2292 } 2293 switch (errno(system.access(path, mode))) { 2294 0 => return, 2295 EACCES => return error.PermissionDenied, 2296 EROFS => return error.PermissionDenied, 2297 ELOOP => return error.PermissionDenied, 2298 ETXTBSY => return error.PermissionDenied, 2299 ENOTDIR => return error.FileNotFound, 2300 ENOENT => return error.FileNotFound, 2301 2302 ENAMETOOLONG => return error.NameTooLong, 2303 EINVAL => unreachable, 2304 EFAULT => unreachable, 2305 EIO => return error.InputOutput, 2306 ENOMEM => return error.SystemResources, 2307 else => |err| return unexpectedErrno(err), 2308 } 2309 } 2310 2311 /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string. 2312 /// Otherwise use `access` or `accessC`. 2313 /// TODO currently this ignores `mode`. 2314 pub fn accessW(path: [*]const u16, mode: u32) windows.GetFileAttributesError!void { 2315 const ret = try windows.GetFileAttributesW(path); 2316 if (ret != windows.INVALID_FILE_ATTRIBUTES) { 2317 return; 2318 } 2319 switch (windows.kernel32.GetLastError()) { 2320 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, 2321 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, 2322 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, 2323 else => |err| return windows.unexpectedError(err), 2324 } 2325 } 2326 2327 pub const PipeError = error{ 2328 SystemFdQuotaExceeded, 2329 ProcessFdQuotaExceeded, 2330 } || UnexpectedError; 2331 2332 /// Creates a unidirectional data channel that can be used for interprocess communication. 2333 pub fn pipe() PipeError![2]fd_t { 2334 var fds: [2]fd_t = undefined; 2335 switch (errno(system.pipe(&fds))) { 2336 0 => return fds, 2337 EINVAL => unreachable, // Invalid parameters to pipe() 2338 EFAULT => unreachable, // Invalid fds pointer 2339 ENFILE => return error.SystemFdQuotaExceeded, 2340 EMFILE => return error.ProcessFdQuotaExceeded, 2341 else => |err| return unexpectedErrno(err), 2342 } 2343 } 2344 2345 pub fn pipe2(flags: u32) PipeError![2]fd_t { 2346 var fds: [2]fd_t = undefined; 2347 switch (errno(system.pipe2(&fds, flags))) { 2348 0 => return fds, 2349 EINVAL => unreachable, // Invalid flags 2350 EFAULT => unreachable, // Invalid fds pointer 2351 ENFILE => return error.SystemFdQuotaExceeded, 2352 EMFILE => return error.ProcessFdQuotaExceeded, 2353 else => |err| return unexpectedErrno(err), 2354 } 2355 } 2356 2357 pub const SysCtlError = error{ 2358 PermissionDenied, 2359 SystemResources, 2360 NameTooLong, 2361 } || UnexpectedError; 2362 2363 pub fn sysctl( 2364 name: []const c_int, 2365 oldp: ?*c_void, 2366 oldlenp: ?*usize, 2367 newp: ?*c_void, 2368 newlen: usize, 2369 ) SysCtlError!void { 2370 const name_len = math.cast(c_uint, name.len) catch return error.NameTooLong; 2371 switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) { 2372 0 => return, 2373 EFAULT => unreachable, 2374 EPERM => return error.PermissionDenied, 2375 ENOMEM => return error.SystemResources, 2376 else => |err| return unexpectedErrno(err), 2377 } 2378 } 2379 2380 pub fn sysctlbynameC( 2381 name: [*]const u8, 2382 oldp: ?*c_void, 2383 oldlenp: ?*usize, 2384 newp: ?*c_void, 2385 newlen: usize, 2386 ) SysCtlError!void { 2387 switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) { 2388 0 => return, 2389 EFAULT => unreachable, 2390 EPERM => return error.PermissionDenied, 2391 ENOMEM => return error.SystemResources, 2392 else => |err| return unexpectedErrno(err), 2393 } 2394 } 2395 2396 pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void { 2397 switch (errno(system.gettimeofday(tv, tz))) { 2398 0 => return, 2399 EINVAL => unreachable, 2400 else => unreachable, 2401 } 2402 } 2403 2404 pub const SeekError = error{Unseekable} || UnexpectedError; 2405 2406 /// Repositions read/write file offset relative to the beginning. 2407 pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { 2408 if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { 2409 var result: u64 = undefined; 2410 switch (errno(system.llseek(fd, offset, &result, SEEK_SET))) { 2411 0 => return, 2412 EBADF => unreachable, // always a race condition 2413 EINVAL => return error.Unseekable, 2414 EOVERFLOW => return error.Unseekable, 2415 ESPIPE => return error.Unseekable, 2416 ENXIO => return error.Unseekable, 2417 else => |err| return unexpectedErrno(err), 2418 } 2419 } 2420 if (builtin.os == .windows) { 2421 return windows.SetFilePointerEx_BEGIN(fd, offset); 2422 } 2423 const ipos = @bitCast(i64, offset); // the OS treats this as unsigned 2424 switch (errno(system.lseek(fd, ipos, SEEK_SET))) { 2425 0 => return, 2426 EBADF => unreachable, // always a race condition 2427 EINVAL => return error.Unseekable, 2428 EOVERFLOW => return error.Unseekable, 2429 ESPIPE => return error.Unseekable, 2430 ENXIO => return error.Unseekable, 2431 else => |err| return unexpectedErrno(err), 2432 } 2433 } 2434 2435 /// Repositions read/write file offset relative to the current offset. 2436 pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { 2437 if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { 2438 var result: u64 = undefined; 2439 switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_CUR))) { 2440 0 => return, 2441 EBADF => unreachable, // always a race condition 2442 EINVAL => return error.Unseekable, 2443 EOVERFLOW => return error.Unseekable, 2444 ESPIPE => return error.Unseekable, 2445 ENXIO => return error.Unseekable, 2446 else => |err| return unexpectedErrno(err), 2447 } 2448 } 2449 if (builtin.os == .windows) { 2450 return windows.SetFilePointerEx_CURRENT(fd, offset); 2451 } 2452 switch (errno(system.lseek(fd, offset, SEEK_CUR))) { 2453 0 => return, 2454 EBADF => unreachable, // always a race condition 2455 EINVAL => return error.Unseekable, 2456 EOVERFLOW => return error.Unseekable, 2457 ESPIPE => return error.Unseekable, 2458 ENXIO => return error.Unseekable, 2459 else => |err| return unexpectedErrno(err), 2460 } 2461 } 2462 2463 /// Repositions read/write file offset relative to the end. 2464 pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { 2465 if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { 2466 var result: u64 = undefined; 2467 switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_END))) { 2468 0 => return, 2469 EBADF => unreachable, // always a race condition 2470 EINVAL => return error.Unseekable, 2471 EOVERFLOW => return error.Unseekable, 2472 ESPIPE => return error.Unseekable, 2473 ENXIO => return error.Unseekable, 2474 else => |err| return unexpectedErrno(err), 2475 } 2476 } 2477 if (builtin.os == .windows) { 2478 return windows.SetFilePointerEx_END(fd, offset); 2479 } 2480 switch (errno(system.lseek(fd, offset, SEEK_END))) { 2481 0 => return, 2482 EBADF => unreachable, // always a race condition 2483 EINVAL => return error.Unseekable, 2484 EOVERFLOW => return error.Unseekable, 2485 ESPIPE => return error.Unseekable, 2486 ENXIO => return error.Unseekable, 2487 else => |err| return unexpectedErrno(err), 2488 } 2489 } 2490 2491 /// Returns the read/write file offset relative to the beginning. 2492 pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { 2493 if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { 2494 var result: u64 = undefined; 2495 switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) { 2496 0 => return result, 2497 EBADF => unreachable, // always a race condition 2498 EINVAL => return error.Unseekable, 2499 EOVERFLOW => return error.Unseekable, 2500 ESPIPE => return error.Unseekable, 2501 ENXIO => return error.Unseekable, 2502 else => |err| return unexpectedErrno(err), 2503 } 2504 } 2505 if (builtin.os == .windows) { 2506 return windows.SetFilePointerEx_CURRENT_get(fd); 2507 } 2508 const rc = system.lseek(fd, 0, SEEK_CUR); 2509 switch (errno(rc)) { 2510 0 => return @bitCast(u64, rc), 2511 EBADF => unreachable, // always a race condition 2512 EINVAL => return error.Unseekable, 2513 EOVERFLOW => return error.Unseekable, 2514 ESPIPE => return error.Unseekable, 2515 ENXIO => return error.Unseekable, 2516 else => |err| return unexpectedErrno(err), 2517 } 2518 } 2519 2520 pub const RealPathError = error{ 2521 FileNotFound, 2522 AccessDenied, 2523 NameTooLong, 2524 NotSupported, 2525 NotDir, 2526 SymLinkLoop, 2527 InputOutput, 2528 FileTooBig, 2529 IsDir, 2530 ProcessFdQuotaExceeded, 2531 SystemFdQuotaExceeded, 2532 NoDevice, 2533 SystemResources, 2534 NoSpaceLeft, 2535 FileSystem, 2536 BadPathName, 2537 DeviceBusy, 2538 2539 SharingViolation, 2540 PipeBusy, 2541 2542 /// On Windows, file paths must be valid Unicode. 2543 InvalidUtf8, 2544 2545 PathAlreadyExists, 2546 } || UnexpectedError; 2547 2548 /// Return the canonicalized absolute pathname. 2549 /// Expands all symbolic links and resolves references to `.`, `..`, and 2550 /// extra `/` characters in `pathname`. 2551 /// The return value is a slice of `out_buffer`, but not necessarily from the beginning. 2552 /// See also `realpathC` and `realpathW`. 2553 pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { 2554 if (builtin.os == .windows) { 2555 const pathname_w = try windows.sliceToPrefixedFileW(pathname); 2556 return realpathW(&pathname_w, out_buffer); 2557 } 2558 const pathname_c = try toPosixPath(pathname); 2559 return realpathC(&pathname_c, out_buffer); 2560 } 2561 2562 /// Same as `realpath` except `pathname` is null-terminated. 2563 pub fn realpathC(pathname: [*]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { 2564 if (builtin.os == .windows) { 2565 const pathname_w = try windows.cStrToPrefixedFileW(pathname); 2566 return realpathW(&pathname_w, out_buffer); 2567 } 2568 if (builtin.os == .linux and !builtin.link_libc) { 2569 const fd = try openC(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0); 2570 defer close(fd); 2571 2572 var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; 2573 const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable; 2574 2575 return readlinkC(proc_path.ptr, out_buffer); 2576 } 2577 const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) { 2578 EINVAL => unreachable, 2579 EBADF => unreachable, 2580 EFAULT => unreachable, 2581 EACCES => return error.AccessDenied, 2582 ENOENT => return error.FileNotFound, 2583 ENOTSUP => return error.NotSupported, 2584 ENOTDIR => return error.NotDir, 2585 ENAMETOOLONG => return error.NameTooLong, 2586 ELOOP => return error.SymLinkLoop, 2587 EIO => return error.InputOutput, 2588 else => |err| return unexpectedErrno(@intCast(usize, err)), 2589 }; 2590 return mem.toSlice(u8, result_path); 2591 } 2592 2593 /// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded. 2594 pub fn realpathW(pathname: [*]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { 2595 const h_file = try windows.CreateFileW( 2596 pathname, 2597 windows.GENERIC_READ, 2598 windows.FILE_SHARE_READ, 2599 null, 2600 windows.OPEN_EXISTING, 2601 windows.FILE_ATTRIBUTE_NORMAL, 2602 null, 2603 ); 2604 defer windows.CloseHandle(h_file); 2605 2606 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined; 2607 const wide_len = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS); 2608 assert(wide_len <= wide_buf.len); 2609 const wide_slice = wide_buf[0..wide_len]; 2610 2611 // Windows returns \\?\ prepended to the path. 2612 // We strip it to make this function consistent across platforms. 2613 const prefix = [_]u16{ '\\', '\\', '?', '\\' }; 2614 const start_index = if (mem.startsWith(u16, wide_slice, prefix)) prefix.len else 0; 2615 2616 // Trust that Windows gives us valid UTF-16LE. 2617 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice[start_index..]) catch unreachable; 2618 return out_buffer[0..end_index]; 2619 } 2620 2621 /// Spurious wakeups are possible and no precision of timing is guaranteed. 2622 pub fn nanosleep(seconds: u64, nanoseconds: u64) void { 2623 var req = timespec{ 2624 .tv_sec = math.cast(isize, seconds) catch math.maxInt(isize), 2625 .tv_nsec = math.cast(isize, nanoseconds) catch math.maxInt(isize), 2626 }; 2627 var rem: timespec = undefined; 2628 while (true) { 2629 switch (errno(system.nanosleep(&req, &rem))) { 2630 EFAULT => unreachable, 2631 EINVAL => { 2632 // Sometimes Darwin returns EINVAL for no reason. 2633 // We treat it as a spurious wakeup. 2634 return; 2635 }, 2636 EINTR => { 2637 req = rem; 2638 continue; 2639 }, 2640 // This prong handles success as well as unexpected errors. 2641 else => return, 2642 } 2643 } 2644 } 2645 2646 pub fn dl_iterate_phdr( 2647 comptime T: type, 2648 callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, 2649 data: ?*T, 2650 ) isize { 2651 if (builtin.object_format != .elf) 2652 @compileError("dl_iterate_phdr is not available for this target"); 2653 2654 if (builtin.link_libc) { 2655 return system.dl_iterate_phdr( 2656 @ptrCast(std.c.dl_iterate_phdr_callback, callback), 2657 @ptrCast(?*c_void, data), 2658 ); 2659 } 2660 2661 const elf_base = std.process.getBaseAddress(); 2662 const ehdr = @intToPtr(*elf.Ehdr, elf_base); 2663 // Make sure the base address points to an ELF image 2664 assert(mem.eql(u8, ehdr.e_ident[0..4], "\x7fELF")); 2665 const n_phdr = ehdr.e_phnum; 2666 const phdrs = (@intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff))[0..n_phdr]; 2667 2668 var it = dl.linkmap_iterator(phdrs) catch unreachable; 2669 2670 // The executable has no dynamic link segment, create a single entry for 2671 // the whole ELF image 2672 if (it.end()) { 2673 var info = dl_phdr_info{ 2674 .dlpi_addr = elf_base, 2675 .dlpi_name = c"/proc/self/exe", 2676 .dlpi_phdr = phdrs.ptr, 2677 .dlpi_phnum = ehdr.e_phnum, 2678 }; 2679 2680 return callback(&info, @sizeOf(dl_phdr_info), data); 2681 } 2682 2683 // Last return value from the callback function 2684 var last_r: isize = 0; 2685 while (it.next()) |entry| { 2686 var dlpi_phdr: [*]elf.Phdr = undefined; 2687 var dlpi_phnum: u16 = undefined; 2688 2689 if (entry.l_addr != 0) { 2690 const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr); 2691 dlpi_phdr = @intToPtr([*]elf.Phdr, entry.l_addr + elf_header.e_phoff); 2692 dlpi_phnum = elf_header.e_phnum; 2693 } else { 2694 // This is the running ELF image 2695 dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff); 2696 dlpi_phnum = ehdr.e_phnum; 2697 } 2698 2699 var info = dl_phdr_info{ 2700 .dlpi_addr = entry.l_addr, 2701 .dlpi_name = entry.l_name, 2702 .dlpi_phdr = dlpi_phdr, 2703 .dlpi_phnum = dlpi_phnum, 2704 }; 2705 2706 last_r = callback(&info, @sizeOf(dl_phdr_info), data); 2707 if (last_r != 0) break; 2708 } 2709 2710 return last_r; 2711 } 2712 2713 pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError; 2714 2715 pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { 2716 switch (errno(system.clock_gettime(clk_id, tp))) { 2717 0 => return, 2718 EFAULT => unreachable, 2719 EINVAL => return error.UnsupportedClock, 2720 else => |err| return unexpectedErrno(err), 2721 } 2722 } 2723 2724 pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void { 2725 switch (errno(system.clock_getres(clk_id, res))) { 2726 0 => return, 2727 EFAULT => unreachable, 2728 EINVAL => return error.UnsupportedClock, 2729 else => |err| return unexpectedErrno(err), 2730 } 2731 } 2732 2733 pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError; 2734 2735 pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t { 2736 var set: cpu_set_t = undefined; 2737 switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) { 2738 0 => return set, 2739 EFAULT => unreachable, 2740 EINVAL => unreachable, 2741 ESRCH => unreachable, 2742 EPERM => return error.PermissionDenied, 2743 else => |err| return unexpectedErrno(err), 2744 } 2745 } 2746 2747 /// Used to convert a slice to a null terminated slice on the stack. 2748 /// TODO https://github.com/ziglang/zig/issues/287 2749 pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 { 2750 var path_with_null: [PATH_MAX]u8 = undefined; 2751 // >= rather than > to make room for the null byte 2752 if (file_path.len >= PATH_MAX) return error.NameTooLong; 2753 mem.copy(u8, &path_with_null, file_path); 2754 path_with_null[file_path.len] = 0; 2755 return path_with_null; 2756 } 2757 2758 /// Whether or not error.Unexpected will print its value and a stack trace. 2759 /// if this happens the fix is to add the error code to the corresponding 2760 /// switch expression, possibly introduce a new error in the error set, and 2761 /// send a patch to Zig. 2762 pub const unexpected_error_tracing = builtin.mode == .Debug; 2763 2764 pub const UnexpectedError = error{ 2765 /// The Operating System returned an undocumented error code. 2766 /// This error is in theory not possible, but it would be better 2767 /// to handle this error than to invoke undefined behavior. 2768 Unexpected, 2769 }; 2770 2771 /// Call this when you made a syscall or something that sets errno 2772 /// and you get an unexpected error. 2773 pub fn unexpectedErrno(err: usize) UnexpectedError { 2774 if (unexpected_error_tracing) { 2775 std.debug.warn("unexpected errno: {}\n", err); 2776 std.debug.dumpCurrentStackTrace(null); 2777 } 2778 return error.Unexpected; 2779 } 2780 2781 pub const SigaltstackError = error{ 2782 /// The supplied stack size was less than MINSIGSTKSZ. 2783 SizeTooSmall, 2784 2785 /// Attempted to change the signal stack while it was active. 2786 PermissionDenied, 2787 } || UnexpectedError; 2788 2789 pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void { 2790 if (builtin.os == .windows or builtin.os == .uefi or builtin.os == .wasi) 2791 @compileError("std.os.sigaltstack not available for this target"); 2792 2793 switch (errno(system.sigaltstack(ss, old_ss))) { 2794 0 => return, 2795 EFAULT => unreachable, 2796 EINVAL => unreachable, 2797 ENOMEM => return error.SizeTooSmall, 2798 EPERM => return error.PermissionDenied, 2799 else => |err| return unexpectedErrno(err), 2800 } 2801 } 2802 2803 /// Examine and change a signal action. 2804 pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void { 2805 switch (errno(system.sigaction(sig, act, oact))) { 2806 0 => return, 2807 EFAULT => unreachable, 2808 EINVAL => unreachable, 2809 else => unreachable, 2810 } 2811 } 2812 2813 pub const FutimensError = error{ 2814 /// times is NULL, or both tv_nsec values are UTIME_NOW, and either: 2815 /// * the effective user ID of the caller does not match the owner 2816 /// of the file, the caller does not have write access to the 2817 /// file, and the caller is not privileged (Linux: does not have 2818 /// either the CAP_FOWNER or the CAP_DAC_OVERRIDE capability); 2819 /// or, 2820 /// * the file is marked immutable (see chattr(1)). 2821 AccessDenied, 2822 2823 /// The caller attempted to change one or both timestamps to a value 2824 /// other than the current time, or to change one of the timestamps 2825 /// to the current time while leaving the other timestamp unchanged, 2826 /// (i.e., times is not NULL, neither tv_nsec field is UTIME_NOW, 2827 /// and neither tv_nsec field is UTIME_OMIT) and either: 2828 /// * the caller's effective user ID does not match the owner of 2829 /// file, and the caller is not privileged (Linux: does not have 2830 /// the CAP_FOWNER capability); or, 2831 /// * the file is marked append-only or immutable (see chattr(1)). 2832 PermissionDenied, 2833 2834 ReadOnlyFileSystem, 2835 } || UnexpectedError; 2836 2837 pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void { 2838 switch (errno(system.futimens(fd, times))) { 2839 0 => return, 2840 EACCES => return error.AccessDenied, 2841 EPERM => return error.PermissionDenied, 2842 EBADF => unreachable, // always a race condition 2843 EFAULT => unreachable, 2844 EINVAL => unreachable, 2845 EROFS => return error.ReadOnlyFileSystem, 2846 else => |err| return unexpectedErrno(err), 2847 } 2848 } 2849 2850 pub const GetHostNameError = error{PermissionDenied} || UnexpectedError; 2851 2852 pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { 2853 if (builtin.link_libc) { 2854 switch (errno(system.gethostname(name_buffer, name_buffer.len))) { 2855 0 => return mem.toSlice(u8, name_buffer), 2856 EFAULT => unreachable, 2857 ENAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this 2858 EPERM => return error.PermissionDenied, 2859 else => |err| return unexpectedErrno(err), 2860 } 2861 } 2862 if (builtin.os == .linux) { 2863 var uts: utsname = undefined; 2864 switch (errno(system.uname(&uts))) { 2865 0 => { 2866 const hostname = mem.toSlice(u8, &uts.nodename); 2867 mem.copy(u8, name_buffer, hostname); 2868 return name_buffer[0..hostname.len]; 2869 }, 2870 EFAULT => unreachable, 2871 EPERM => return error.PermissionDenied, 2872 else => |err| return unexpectedErrno(err), 2873 } 2874 } 2875 2876 @compileError("TODO implement gethostname for this OS"); 2877 } 2878 2879 pub fn res_mkquery( 2880 op: u4, 2881 dname: []const u8, 2882 class: u8, 2883 ty: u8, 2884 data: []const u8, 2885 newrr: ?[*]const u8, 2886 buf: []u8, 2887 ) usize { 2888 // This implementation is ported from musl libc. 2889 // A more idiomatic "ziggy" implementation would be welcome. 2890 var name = dname; 2891 if (mem.endsWith(u8, name, ".")) name.len -= 1; 2892 assert(name.len <= 253); 2893 const n = 17 + name.len + @boolToInt(name.len != 0); 2894 2895 // Construct query template - ID will be filled later 2896 var q: [280]u8 = undefined; 2897 @memset(&q, 0, n); 2898 q[2] = u8(op) * 8 + 1; 2899 q[5] = 1; 2900 mem.copy(u8, q[13..], name); 2901 var i: usize = 13; 2902 var j: usize = undefined; 2903 while (q[i] != 0) : (i = j + 1) { 2904 j = i; 2905 while (q[j] != 0 and q[j] != '.') : (j += 1) {} 2906 // TODO determine the circumstances for this and whether or 2907 // not this should be an error. 2908 if (j - i - 1 > 62) unreachable; 2909 q[i - 1] = @intCast(u8, j - i); 2910 } 2911 q[i + 1] = ty; 2912 q[i + 3] = class; 2913 2914 // Make a reasonably unpredictable id 2915 var ts: timespec = undefined; 2916 clock_gettime(CLOCK_REALTIME, &ts) catch {}; 2917 const UInt = @IntType(false, @typeOf(ts.tv_nsec).bit_count); 2918 const unsec = @bitCast(UInt, ts.tv_nsec); 2919 const id = @truncate(u32, unsec + unsec / 65536); 2920 q[0] = @truncate(u8, id / 256); 2921 q[1] = @truncate(u8, id); 2922 2923 mem.copy(u8, buf, q[0..n]); 2924 return n; 2925 } 2926 2927 pub const SendError = error{ 2928 /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied 2929 /// on the destination socket file, or search permission is denied for one of the 2930 /// directories the path prefix. (See path_resolution(7).) 2931 /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though 2932 /// it was a unicast address. 2933 AccessDenied, 2934 2935 /// The socket is marked nonblocking and the requested operation would block, and 2936 /// there is no global event loop configured. 2937 /// It's also possible to get this error under the following condition: 2938 /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously 2939 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was 2940 /// determined that all port numbers in the ephemeral port range are currently in use. See 2941 /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). 2942 WouldBlock, 2943 2944 /// Another Fast Open is already in progress. 2945 FastOpenAlreadyInProgress, 2946 2947 /// Connection reset by peer. 2948 ConnectionResetByPeer, 2949 2950 /// The socket type requires that message be sent atomically, and the size of the message 2951 /// to be sent made this impossible. The message is not transmitted. 2952 /// 2953 MessageTooBig, 2954 2955 /// The output queue for a network interface was full. This generally indicates that the 2956 /// interface has stopped sending, but may be caused by transient congestion. (Normally, 2957 /// this does not occur in Linux. Packets are just silently dropped when a device queue 2958 /// overflows.) 2959 /// This is also caused when there is not enough kernel memory available. 2960 SystemResources, 2961 2962 /// The local end has been shut down on a connection oriented socket. In this case, the 2963 /// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set. 2964 BrokenPipe, 2965 } || UnexpectedError; 2966 2967 /// Transmit a message to another socket. 2968 /// 2969 /// The `sendto` call may be used only when the socket is in a connected state (so that the intended 2970 /// recipient is known). The following call 2971 /// 2972 /// send(sockfd, buf, len, flags); 2973 /// 2974 /// is equivalent to 2975 /// 2976 /// sendto(sockfd, buf, len, flags, NULL, 0); 2977 /// 2978 /// If sendto() is used on a connection-mode (`SOCK_STREAM`, `SOCK_SEQPACKET`) socket, the arguments 2979 /// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted 2980 /// that the socket was actually connected. 2981 /// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size. 2982 /// 2983 /// If the message is too long to pass atomically through the underlying protocol, 2984 /// `SendError.MessageTooBig` is returned, and the message is not transmitted. 2985 /// 2986 /// There is no indication of failure to deliver. 2987 /// 2988 /// When the message does not fit into the send buffer of the socket, `sendto` normally blocks, 2989 /// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail 2990 /// with `SendError.WouldBlock`. The `select` call may be used to determine when it is 2991 /// possible to send more data. 2992 pub fn sendto( 2993 /// The file descriptor of the sending socket. 2994 sockfd: fd_t, 2995 /// Message to send. 2996 buf: []const u8, 2997 flags: u32, 2998 dest_addr: ?*const sockaddr, 2999 addrlen: socklen_t, 3000 ) SendError!usize { 3001 while (true) { 3002 const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen); 3003 switch (errno(rc)) { 3004 0 => return @intCast(usize, rc), 3005 3006 EACCES => return error.AccessDenied, 3007 EAGAIN => if (std.event.Loop.instance) |loop| { 3008 loop.waitUntilFdWritable(sockfd); 3009 continue; 3010 } else { 3011 return error.WouldBlock; 3012 }, 3013 EALREADY => return error.FastOpenAlreadyInProgress, 3014 EBADF => unreachable, // always a race condition 3015 ECONNRESET => return error.ConnectionResetByPeer, 3016 EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set. 3017 EFAULT => unreachable, // An invalid user space address was specified for an argument. 3018 EINTR => continue, 3019 EINVAL => unreachable, // Invalid argument passed. 3020 EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified 3021 EMSGSIZE => return error.MessageTooBig, 3022 ENOBUFS => return error.SystemResources, 3023 ENOMEM => return error.SystemResources, 3024 ENOTCONN => unreachable, // The socket is not connected, and no target has been given. 3025 ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. 3026 EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. 3027 EPIPE => return error.BrokenPipe, 3028 else => |err| return unexpectedErrno(err), 3029 } 3030 } 3031 } 3032 3033 /// Transmit a message to another socket. 3034 /// 3035 /// The `send` call may be used only when the socket is in a connected state (so that the intended 3036 /// recipient is known). The only difference between `send` and `write` is the presence of 3037 /// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following 3038 /// call 3039 /// 3040 /// send(sockfd, buf, len, flags); 3041 /// 3042 /// is equivalent to 3043 /// 3044 /// sendto(sockfd, buf, len, flags, NULL, 0); 3045 /// 3046 /// There is no indication of failure to deliver. 3047 /// 3048 /// When the message does not fit into the send buffer of the socket, `send` normally blocks, 3049 /// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail 3050 /// with `SendError.WouldBlock`. The `select` call may be used to determine when it is 3051 /// possible to send more data. 3052 pub fn send( 3053 /// The file descriptor of the sending socket. 3054 sockfd: fd_t, 3055 buf: []const u8, 3056 flags: u32, 3057 ) SendError!usize { 3058 return sendto(sockfd, buf, flags, null, 0); 3059 } 3060 3061 pub const PollError = error{ 3062 /// The kernel had no space to allocate file descriptor tables. 3063 SystemResources, 3064 } || UnexpectedError; 3065 3066 pub fn poll(fds: []pollfd, timeout: i32) PollError!usize { 3067 while (true) { 3068 const rc = system.poll(fds.ptr, fds.len, timeout); 3069 switch (errno(rc)) { 3070 0 => return @intCast(usize, rc), 3071 EFAULT => unreachable, 3072 EINTR => continue, 3073 EINVAL => unreachable, 3074 ENOMEM => return error.SystemResources, 3075 else => |err| return unexpectedErrno(err), 3076 } 3077 } 3078 } 3079 3080 pub const RecvFromError = error{ 3081 /// The socket is marked nonblocking and the requested operation would block, and 3082 /// there is no global event loop configured. 3083 WouldBlock, 3084 3085 /// A remote host refused to allow the network connection, typically because it is not 3086 /// running the requested service. 3087 ConnectionRefused, 3088 3089 /// Could not allocate kernel memory. 3090 SystemResources, 3091 } || UnexpectedError; 3092 3093 pub fn recvfrom( 3094 sockfd: fd_t, 3095 buf: []u8, 3096 flags: u32, 3097 src_addr: ?*sockaddr, 3098 addrlen: ?*socklen_t, 3099 ) RecvFromError!usize { 3100 while (true) { 3101 const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen); 3102 switch (errno(rc)) { 3103 0 => return @intCast(usize, rc), 3104 EBADF => unreachable, // always a race condition 3105 EFAULT => unreachable, 3106 EINVAL => unreachable, 3107 ENOTCONN => unreachable, 3108 ENOTSOCK => unreachable, 3109 EINTR => continue, 3110 EAGAIN => if (std.event.Loop.instance) |loop| { 3111 loop.waitUntilFdReadable(sockfd); 3112 continue; 3113 } else { 3114 return error.WouldBlock; 3115 }, 3116 ENOMEM => return error.SystemResources, 3117 ECONNREFUSED => return error.ConnectionRefused, 3118 else => |err| return unexpectedErrno(err), 3119 } 3120 } 3121 } 3122 3123 pub const DnExpandError = error{InvalidDnsPacket}; 3124 3125 pub fn dn_expand( 3126 msg: []const u8, 3127 comp_dn: []const u8, 3128 exp_dn: []u8, 3129 ) DnExpandError!usize { 3130 // This implementation is ported from musl libc. 3131 // A more idiomatic "ziggy" implementation would be welcome. 3132 var p = comp_dn.ptr; 3133 var len: usize = std.math.maxInt(usize); 3134 const end = msg.ptr + msg.len; 3135 if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket; 3136 var dest = exp_dn.ptr; 3137 const dend = dest + std.math.min(exp_dn.len, 254); 3138 // detect reference loop using an iteration counter 3139 var i: usize = 0; 3140 while (i < msg.len) : (i += 2) { 3141 // loop invariants: p<end, dest<dend 3142 if ((p[0] & 0xc0) != 0) { 3143 if (p + 1 == end) return error.InvalidDnsPacket; 3144 var j = ((p[0] & usize(0x3f)) << 8) | p[1]; 3145 if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 2 - @ptrToInt(comp_dn.ptr); 3146 if (j >= msg.len) return error.InvalidDnsPacket; 3147 p = msg.ptr + j; 3148 } else if (p[0] != 0) { 3149 if (dest != exp_dn.ptr) { 3150 dest.* = '.'; 3151 dest += 1; 3152 } 3153 var j = p[0]; 3154 p += 1; 3155 if (j >= @ptrToInt(end) - @ptrToInt(p) or j >= @ptrToInt(dend) - @ptrToInt(dest)) { 3156 return error.InvalidDnsPacket; 3157 } 3158 while (j != 0) { 3159 j -= 1; 3160 dest.* = p[0]; 3161 dest += 1; 3162 p += 1; 3163 } 3164 } else { 3165 dest.* = 0; 3166 if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 1 - @ptrToInt(comp_dn.ptr); 3167 return len; 3168 } 3169 } 3170 return error.InvalidDnsPacket; 3171 } 3172 3173 pub fn sched_yield() void { 3174 if (builtin.os == .windows) { 3175 _ = windows.kernel32.SwitchToThread(); 3176 } else if (builtin.os == .linux and !builtin.link_libc) { 3177 assert(linux.sched_yield() == 0); 3178 } else if (builtin.link_libc) { 3179 assert(std.c.sched_yield() == 0); 3180 } 3181 }