diff options
| author | tgschultz <tgschultz@gmail.com> | 2018-05-11 21:36:02 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-11 21:36:02 -0500 |
| commit | 8c1872543c8cf76215cc4bf3ced4637bb1065a4e (patch) | |
| tree | 72dfebb643ab61579e3fb8dd58cd4610ffe876fa /std/os | |
| parent | 7186e92c86982950d0aa7c0c2deef9ef96bc1264 (diff) | |
| parent | 6e821078f625a03eb8b7794c983da0f7793366ab (diff) | |
| download | zig-8c1872543c8cf76215cc4bf3ced4637bb1065a4e.tar.gz zig-8c1872543c8cf76215cc4bf3ced4637bb1065a4e.zip | |
Merge pull request #1 from zig-lang/master
Sync with zig-lang/zig master
Diffstat (limited to 'std/os')
| -rw-r--r-- | std/os/darwin.zig | 30 | ||||
| -rw-r--r-- | std/os/epoch.zig | 26 | ||||
| -rw-r--r-- | std/os/file.zig | 45 | ||||
| -rw-r--r-- | std/os/index.zig | 1104 | ||||
| -rw-r--r-- | std/os/linux/i386.zig | 505 | ||||
| -rw-r--r-- | std/os/linux/index.zig | 689 | ||||
| -rw-r--r-- | std/os/linux/test.zig | 1 | ||||
| -rw-r--r-- | std/os/linux/vdso.zig | 89 | ||||
| -rw-r--r-- | std/os/linux/x86_64.zig | 21 | ||||
| -rw-r--r-- | std/os/test.zig | 44 | ||||
| -rw-r--r-- | std/os/time.zig | 288 | ||||
| -rw-r--r-- | std/os/windows/index.zig | 19 |
12 files changed, 2112 insertions, 749 deletions
diff --git a/std/os/darwin.zig b/std/os/darwin.zig index f8b1fbed3b..0a62b03ab2 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -41,6 +41,11 @@ pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64 pub const O_LARGEFILE = 0x0000; pub const O_PATH = 0x0000; +pub const F_OK = 0; +pub const X_OK = 1; +pub const W_OK = 2; +pub const R_OK = 4; + pub const O_RDONLY = 0x0000; /// open for reading only pub const O_WRONLY = 0x0001; /// open for writing only pub const O_RDWR = 0x0002; /// open for reading and writing @@ -179,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize { return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte)); } -pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, +pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { const ptr_result = c.mmap(@ptrCast(&c_void, address), length, @@ -188,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, return errnoWrap(isize_result); } -pub fn munmap(address: &u8, length: usize) usize { - return errnoWrap(c.munmap(@ptrCast(&c_void, address), length)); +pub fn munmap(address: usize, length: usize) usize { + return errnoWrap(c.munmap(@intToPtr(&c_void, address), length)); } pub fn unlink(path: &const u8) usize { @@ -209,6 +214,10 @@ pub fn fork() usize { return errnoWrap(c.fork()); } +pub fn access(path: &const u8, mode: u32) usize { + return errnoWrap(c.access(path, mode)); +} + pub fn pipe(fds: &[2]i32) usize { comptime assert(i32.bit_count == c_int.bit_count); return errnoWrap(c.pipe(@ptrCast(&c_int, fds))); @@ -251,6 +260,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u return errnoWrap(c.readlink(path, buf_ptr, buf_len)); } +pub fn gettimeofday(tv: ?&timeval, tz: ?&timezone) usize { + return errnoWrap(c.gettimeofday(tv, tz)); +} + pub fn nanosleep(req: &const timespec, rem: ?×pec) usize { return errnoWrap(c.nanosleep(req, rem)); } @@ -301,6 +314,9 @@ pub const timespec = c.timespec; pub const Stat = c.Stat; pub const dirent = c.dirent; +pub const sa_family_t = c.sa_family_t; +pub const sockaddr = c.sockaddr; + /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = struct { handler: extern fn(i32)void, @@ -318,3 +334,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void { fn errnoWrap(value: isize) usize { return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value); } + + +pub const timezone = c.timezone; +pub const timeval = c.timeval; +pub const mach_timebase_info_data = c.mach_timebase_info_data; + +pub const mach_absolute_time = c.mach_absolute_time; +pub const mach_timebase_info = c.mach_timebase_info; diff --git a/std/os/epoch.zig b/std/os/epoch.zig new file mode 100644 index 0000000000..e1256c1374 --- /dev/null +++ b/std/os/epoch.zig @@ -0,0 +1,26 @@ +/// Epoch reference times in terms of their difference from +/// posix epoch in seconds. +pub const posix = 0; //Jan 01, 1970 AD +pub const dos = 315532800; //Jan 01, 1980 AD +pub const ios = 978307200; //Jan 01, 2001 AD +pub const openvms = -3506716800; //Nov 17, 1858 AD +pub const zos = -2208988800; //Jan 01, 1900 AD +pub const windows = -11644473600; //Jan 01, 1601 AD +pub const amiga = 252460800; //Jan 01, 1978 AD +pub const pickos = -63244800; //Dec 31, 1967 AD +pub const gps = 315964800; //Jan 06, 1980 AD +pub const clr = -62135769600; //Jan 01, 0001 AD + +pub const unix = posix; +pub const android = posix; +pub const os2 = dos; +pub const bios = dos; +pub const vfat = dos; +pub const ntfs = windows; +pub const ntp = zos; +pub const jbase = pickos; +pub const aros = amiga; +pub const morphos = amiga; +pub const brew = gps; +pub const atsc = gps; +pub const go = clr;
\ No newline at end of file diff --git a/std/os/file.zig b/std/os/file.zig index eed3a443b9..61fc2b1455 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -85,6 +85,47 @@ pub const File = struct { }; } + pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool { + const path_with_null = try std.cstr.addNullByte(allocator, path); + defer allocator.free(path_with_null); + + if (is_posix) { + // mode is ignored and is always F_OK for now + const result = posix.access(path_with_null.ptr, posix.F_OK); + const err = posix.getErrno(result); + if (err > 0) { + return switch (err) { + posix.EACCES => error.PermissionDenied, + posix.EROFS => error.PermissionDenied, + posix.ELOOP => error.PermissionDenied, + posix.ETXTBSY => error.PermissionDenied, + posix.ENOTDIR => error.NotFound, + posix.ENOENT => error.NotFound, + + posix.ENAMETOOLONG => error.NameTooLong, + posix.EINVAL => error.BadMode, + posix.EFAULT => error.BadPathName, + posix.EIO => error.Io, + posix.ENOMEM => error.SystemResources, + else => os.unexpectedErrorPosix(err), + }; + } + return true; + } else if (is_windows) { + if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) { + return true; + } + + const err = windows.GetLastError(); + return switch (err) { + windows.ERROR.FILE_NOT_FOUND => error.NotFound, + windows.ERROR.ACCESS_DENIED => error.PermissionDenied, + else => os.unexpectedErrorWindows(err), + }; + } else { + @compileError("TODO implement access for this OS"); + } + } /// Upon success, the stream is in an uninitialized state. To continue using it, /// you must use the open() function. @@ -245,7 +286,9 @@ pub const File = struct { }; } - return stat.mode; + // TODO: we should be able to cast u16 to ModeError!u32, making this + // explicit cast not necessary + return os.FileMode(stat.mode); } else if (is_windows) { return {}; } else { diff --git a/std/os/index.zig b/std/os/index.zig index 4b74af035e..93c5f70f1e 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2,22 +2,42 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); const Os = builtin.Os; const is_windows = builtin.os == Os.windows; +const is_posix = switch (builtin.os) { + builtin.Os.linux, + builtin.Os.macosx => true, + else => false, +}; const os = this; +test "std.os" { + _ = @import("child_process.zig"); + _ = @import("darwin.zig"); + _ = @import("darwin_errno.zig"); + _ = @import("get_user_id.zig"); + _ = @import("linux/index.zig"); + _ = @import("path.zig"); + _ = @import("test.zig"); + _ = @import("time.zig"); + _ = @import("windows/index.zig"); +} + pub const windows = @import("windows/index.zig"); pub const darwin = @import("darwin.zig"); pub const linux = @import("linux/index.zig"); pub const zen = @import("zen.zig"); -pub const posix = switch(builtin.os) { +pub const posix = switch (builtin.os) { Os.linux => linux, - Os.macosx, Os.ios => darwin, + Os.macosx, + Os.ios => darwin, Os.zen => zen, else => @compileError("Unsupported OS"), }; +pub const net = @import("net.zig"); pub const ChildProcess = @import("child_process.zig").ChildProcess; pub const path = @import("path.zig"); pub const File = @import("file.zig").File; +pub const time = @import("time.zig"); pub const FileMode = switch (builtin.os) { Os.windows => void, @@ -40,7 +60,7 @@ pub const windowsWrite = windows_util.windowsWrite; pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; pub const windowsOpen = windows_util.windowsOpen; pub const windowsLoadDll = windows_util.windowsLoadDll; -pub const windowsUnloadDll = windows_util.windowsUnloadDll; +pub const windowsUnloadDll = windows_util.windowsUnloadDll; pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; pub const WindowsWaitError = windows_util.WaitError; @@ -79,9 +99,9 @@ pub fn getRandomBytes(buf: []u8) !void { switch (err) { posix.EINVAL => unreachable, posix.EFAULT => unreachable, - posix.EINTR => continue, + posix.EINTR => continue, posix.ENOSYS => { - const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0); + const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); defer close(fd); try posixRead(fd, buf); @@ -92,8 +112,9 @@ pub fn getRandomBytes(buf: []u8) !void { } return; }, - Os.macosx, Os.ios => { - const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0); + Os.macosx, + Os.ios => { + const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); defer close(fd); try posixRead(fd, buf); @@ -116,7 +137,20 @@ pub fn getRandomBytes(buf: []u8) !void { } }, Os.zen => { - const randomness = []u8 {42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45}; + const randomness = []u8 { + 42, + 1, + 7, + 12, + 22, + 17, + 99, + 16, + 26, + 87, + 41, + 45, + }; var i: usize = 0; while (i < buf.len) : (i += 1) { if (i > randomness.len) return error.Unknown; @@ -141,7 +175,9 @@ pub fn abort() noreturn { c.abort(); } switch (builtin.os) { - Os.linux, Os.macosx, Os.ios => { + Os.linux, + Os.macosx, + Os.ios => { _ = posix.raise(posix.SIGABRT); _ = posix.raise(posix.SIGKILL); while (true) {} @@ -163,7 +199,9 @@ pub fn exit(status: u8) noreturn { c.exit(status); } switch (builtin.os) { - Os.linux, Os.macosx, Os.ios => { + Os.linux, + Os.macosx, + Os.ios => { posix.exit(status); }, Os.windows => { @@ -173,6 +211,13 @@ pub fn exit(status: u8) noreturn { } } +/// When a file descriptor is closed on linux, it pops the first +/// node from this queue and resumes it. +/// Async functions which get the EMFILE error code can suspend, +/// putting their coroutine handle into this list. +/// TODO make this an atomic linked list +pub var emfile_promise_queue = std.LinkedList(promise).init(); + /// Closes the file handle. Keeps trying if it gets interrupted by a signal. pub fn close(handle: FileHandle) void { if (is_windows) { @@ -180,10 +225,12 @@ pub fn close(handle: FileHandle) void { } else { while (true) { const err = posix.getErrno(posix.close(handle)); - if (err == posix.EINTR) { - continue; - } else { - return; + switch (err) { + posix.EINTR => continue, + else => { + if (emfile_promise_queue.popFirst()) |p| resume p.data; + return; + }, } } } @@ -203,12 +250,14 @@ pub fn posixRead(fd: i32, buf: []u8) !void { if (err > 0) { return switch (err) { posix.EINTR => continue, - posix.EINVAL, posix.EFAULT => unreachable, + posix.EINVAL, + posix.EFAULT => unreachable, posix.EAGAIN => error.WouldBlock, posix.EBADF => error.FileClosed, posix.EIO => error.InputOutput, posix.EISDIR => error.IsDir, - posix.ENOBUFS, posix.ENOMEM => error.SystemResources, + posix.ENOBUFS, + posix.ENOMEM => error.SystemResources, else => unexpectedErrorPosix(err), }; } @@ -242,18 +291,19 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { const write_err = posix.getErrno(rc); if (write_err > 0) { return switch (write_err) { - posix.EINTR => continue, - posix.EINVAL, posix.EFAULT => unreachable, + posix.EINTR => continue, + posix.EINVAL, + posix.EFAULT => unreachable, posix.EAGAIN => PosixWriteError.WouldBlock, posix.EBADF => PosixWriteError.FileClosed, posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired, posix.EDQUOT => PosixWriteError.DiskQuota, - posix.EFBIG => PosixWriteError.FileTooBig, - posix.EIO => PosixWriteError.InputOutput, + posix.EFBIG => PosixWriteError.FileTooBig, + posix.EIO => PosixWriteError.InputOutput, posix.ENOSPC => PosixWriteError.NoSpaceLeft, - posix.EPERM => PosixWriteError.AccessDenied, - posix.EPIPE => PosixWriteError.BrokenPipe, - else => unexpectedErrorPosix(write_err), + posix.EPERM => PosixWriteError.AccessDenied, + posix.EPIPE => PosixWriteError.BrokenPipe, + else => unexpectedErrorPosix(write_err), }; } index += rc; @@ -299,7 +349,8 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 { posix.EFAULT => unreachable, posix.EINVAL => unreachable, posix.EACCES => return PosixOpenError.AccessDenied, - posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, + posix.EFBIG, + posix.EOVERFLOW => return PosixOpenError.FileTooBig, posix.EISDIR => return PosixOpenError.IsDir, posix.ELOOP => return PosixOpenError.SymLinkLoop, posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, @@ -324,7 +375,8 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void { const err = posix.getErrno(posix.dup2(old_fd, new_fd)); if (err > 0) { return switch (err) { - posix.EBUSY, posix.EINTR => continue, + posix.EBUSY, + posix.EINTR => continue, posix.EMFILE => error.ProcessFdQuotaExceeded, posix.EINVAL => unreachable, else => unexpectedErrorPosix(err), @@ -359,7 +411,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { for (envp_buf) |env| { - const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; + const env_buf = if (env) |ptr| ptr[0..cstr.len(ptr) + 1] else break; allocator.free(env_buf); } allocator.free(envp_buf); @@ -370,9 +422,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { /// pointers after the args and after the environment variables. /// `argv[0]` is the executable path. /// This function also uses the PATH environment variable to get the full path to the executable. -pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, - allocator: &Allocator) !void -{ +pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) !void { const argv_buf = try allocator.alloc(?&u8, argv.len + 1); mem.set(?&u8, argv_buf, null); defer { @@ -411,7 +461,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, while (it.next()) |search_path| { mem.copy(u8, path_buf, search_path); path_buf[search_path.len] = '/'; - mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); + mem.copy(u8, path_buf[search_path.len + 1..], exe_path); path_buf[search_path.len + exe_path.len + 1] = 0; err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); assert(err > 0); @@ -443,10 +493,17 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { assert(err > 0); return switch (err) { posix.EFAULT => unreachable, - posix.E2BIG, posix.EMFILE, posix.ENAMETOOLONG, posix.ENFILE, posix.ENOMEM => error.SystemResources, - posix.EACCES, posix.EPERM => error.AccessDenied, - posix.EINVAL, posix.ENOEXEC => error.InvalidExe, - posix.EIO, posix.ELOOP => error.FileSystem, + posix.E2BIG, + posix.EMFILE, + posix.ENAMETOOLONG, + posix.ENFILE, + posix.ENOMEM => error.SystemResources, + posix.EACCES, + posix.EPERM => error.AccessDenied, + posix.EINVAL, + posix.ENOEXEC => error.InvalidExe, + posix.EIO, + posix.ELOOP => error.FileSystem, posix.EISDIR => error.IsDir, posix.ENOENT => error.FileNotFound, posix.ENOTDIR => error.NotDir, @@ -455,6 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { }; } +pub var linux_aux_raw = []usize {0} ** 38; pub var posix_environ_raw: []&u8 = undefined; /// Caller must free result when done. @@ -468,8 +526,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap { var i: usize = 0; while (true) { - if (ptr[i] == 0) - return result; + if (ptr[i] == 0) return result; const key_start = i; @@ -507,8 +564,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { var line_i: usize = 0; while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} const this_key = ptr[0..line_i]; - if (!mem.eql(u8, key, this_key)) - continue; + if (!mem.eql(u8, key, this_key)) continue; var end_i: usize = line_i; while (ptr[end_i] != 0) : (end_i += 1) {} @@ -661,8 +717,10 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr)); if (err > 0) { return switch (err) { - posix.EFAULT, posix.EINVAL => unreachable, - posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EFAULT, + posix.EINVAL => unreachable, + posix.EACCES, + posix.EPERM => error.AccessDenied, posix.EDQUOT => error.DiskQuota, posix.EEXIST => error.PathAlreadyExists, posix.EIO => error.FileSystem, @@ -679,9 +737,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: } // here we replace the standard +/ with -_ so that it can be used in a file name -const b64_fs_encoder = base64.Base64Encoder.init( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", - base64.standard_pad_char); +const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { if (symLink(allocator, existing_path, new_path)) { @@ -700,7 +756,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: tmp_path[dirname.len] = os.path.sep; while (true) { try getRandomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); + b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf); if (symLink(allocator, existing_path, tmp_path)) { return rename(allocator, tmp_path, new_path); @@ -709,7 +765,6 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: else => return err, // TODO zig should know this set does not include PathAlreadyExists } } - } pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void { @@ -732,7 +787,8 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void { return switch (err) { windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, windows.ERROR.ACCESS_DENIED => error.AccessDenied, - windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong, + windows.ERROR.FILENAME_EXCED_RANGE, + windows.ERROR.INVALID_PARAMETER => error.NameTooLong, else => unexpectedErrorWindows(err), }; } @@ -748,9 +804,11 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void { const err = posix.getErrno(posix.unlink(buf.ptr)); if (err > 0) { return switch (err) { - posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EACCES, + posix.EPERM => error.AccessDenied, posix.EBUSY => error.FileBusy, - posix.EFAULT, posix.EINVAL => unreachable, + posix.EFAULT, + posix.EINVAL => unreachable, posix.EIO => error.FileSystem, posix.EISDIR => error.IsDir, posix.ELOOP => error.SymLinkLoop, @@ -828,7 +886,7 @@ pub const AtomicFile = struct { while (true) { try getRandomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); + b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf); const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) { error.PathAlreadyExists => continue, @@ -879,7 +937,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) new_buf[new_path.len] = 0; if (is_windows) { - const flags = windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH; + const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) { const err = windows.GetLastError(); return switch (err) { @@ -890,10 +948,12 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr)); if (err > 0) { return switch (err) { - posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EACCES, + posix.EPERM => error.AccessDenied, posix.EBUSY => error.FileBusy, posix.EDQUOT => error.DiskQuota, - posix.EFAULT, posix.EINVAL => unreachable, + posix.EFAULT, + posix.EINVAL => unreachable, posix.EISDIR => error.IsDir, posix.ELOOP => error.SymLinkLoop, posix.EMLINK => error.LinkQuotaExceeded, @@ -902,7 +962,8 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) posix.ENOTDIR => error.NotDir, posix.ENOMEM => error.SystemResources, posix.ENOSPC => error.NoSpaceLeft, - posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists, + posix.EEXIST, + posix.ENOTEMPTY => error.PathAlreadyExists, posix.EROFS => error.ReadOnlyFileSystem, posix.EXDEV => error.RenameAcrossMountPoints, else => unexpectedErrorPosix(err), @@ -940,7 +1001,8 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void { const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755)); if (err > 0) { return switch (err) { - posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EACCES, + posix.EPERM => error.AccessDenied, posix.EDQUOT => error.DiskQuota, posix.EEXIST => error.PathAlreadyExists, posix.EFAULT => unreachable, @@ -970,27 +1032,23 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void { // TODO stat the file and return an error if it's not a directory // this is important because otherwise a dangling symlink // could cause an infinite loop - if (end_index == resolved_path.len) - return; + if (end_index == resolved_path.len) return; } else if (err == error.FileNotFound) { // march end_index backward until next path component while (true) { end_index -= 1; - if (os.path.isSep(resolved_path[end_index])) - break; + if (os.path.isSep(resolved_path[end_index])) break; } continue; } else { return err; } }; - if (end_index == resolved_path.len) - return; + if (end_index == resolved_path.len) return; // march end_index forward until next path component while (true) { end_index += 1; - if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) - break; + if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break; } } } @@ -1007,15 +1065,18 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { const err = posix.getErrno(posix.rmdir(path_buf.ptr)); if (err > 0) { return switch (err) { - posix.EACCES, posix.EPERM => error.AccessDenied, + posix.EACCES, + posix.EPERM => error.AccessDenied, posix.EBUSY => error.FileBusy, - posix.EFAULT, posix.EINVAL => unreachable, + posix.EFAULT, + posix.EINVAL => unreachable, posix.ELOOP => error.SymLinkLoop, posix.ENAMETOOLONG => error.NameTooLong, posix.ENOENT => error.FileNotFound, posix.ENOMEM => error.SystemResources, posix.ENOTDIR => error.NotDir, - posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty, + posix.EEXIST, + posix.ENOTEMPTY => error.DirNotEmpty, posix.EROFS => error.ReadOnlyFileSystem, else => unexpectedErrorPosix(err), }; @@ -1025,7 +1086,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { /// Whether ::full_path describes a symlink, file, or directory, this function /// removes it. If it cannot be removed because it is a non-empty directory, /// this function recursively removes its entries and then tries again. -// TODO non-recursive implementation +/// TODO non-recursive implementation const DeleteTreeError = error { OutOfMemory, AccessDenied, @@ -1067,8 +1128,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! error.NotDir, error.FileSystem, error.FileBusy, - error.Unexpected - => return err, + error.Unexpected => return err, } { var dir = Dir.open(allocator, full_path) catch |err| switch (err) { @@ -1092,8 +1152,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! error.SystemResources, error.NoSpaceLeft, error.PathAlreadyExists, - error.Unexpected - => return err, + error.Unexpected => return err, }; defer dir.close(); @@ -1123,7 +1182,8 @@ pub const Dir = struct { end_index: usize, const darwin_seek_t = switch (builtin.os) { - Os.macosx, Os.ios => i64, + Os.macosx, + Os.ios => i64, else => void, }; @@ -1147,12 +1207,14 @@ pub const Dir = struct { pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir { const fd = switch (builtin.os) { Os.windows => @compileError("TODO support Dir.open for windows"), - Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0), - Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0), + Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), + Os.macosx, + Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), else => @compileError("Dir.open is not supported for this platform"), }; const darwin_seek_init = switch (builtin.os) { - Os.macosx, Os.ios => 0, + Os.macosx, + Os.ios => 0, else => {}, }; return Dir { @@ -1175,7 +1237,8 @@ pub const Dir = struct { pub fn next(self: &Dir) !?Entry { switch (builtin.os) { Os.linux => return self.nextLinux(), - Os.macosx, Os.ios => return self.nextDarwin(), + Os.macosx, + Os.ios => return self.nextDarwin(), Os.windows => return self.nextWindows(), else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)), } @@ -1189,12 +1252,13 @@ pub const Dir = struct { } while (true) { - const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, - &self.darwin_seek); + const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek); const err = posix.getErrno(result); if (err > 0) { switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EBADF, + posix.EFAULT, + posix.ENOTDIR => unreachable, posix.EINVAL => { self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); continue; @@ -1202,14 +1266,13 @@ pub const Dir = struct { else => return unexpectedErrorPosix(err), } } - if (result == 0) - return null; + if (result == 0) return null; self.index = 0; self.end_index = result; break; } } - const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]); + const darwin_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]); const next_index = self.index + darwin_entry.d_reclen; self.index = next_index; @@ -1254,7 +1317,9 @@ pub const Dir = struct { const err = posix.getErrno(result); if (err > 0) { switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EBADF, + posix.EFAULT, + posix.ENOTDIR => unreachable, posix.EINVAL => { self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); continue; @@ -1262,14 +1327,13 @@ pub const Dir = struct { else => return unexpectedErrorPosix(err), } } - if (result == 0) - return null; + if (result == 0) return null; self.index = 0; self.end_index = result; break; } } - const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]); + const linux_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]); const next_index = self.index + linux_entry.d_reclen; self.index = next_index; @@ -1338,7 +1402,8 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { if (err > 0) { return switch (err) { posix.EACCES => error.AccessDenied, - posix.EFAULT, posix.EINVAL => unreachable, + posix.EFAULT, + posix.EINVAL => unreachable, posix.EIO => error.FileSystem, posix.ELOOP => error.SymLinkLoop, posix.ENAMETOOLONG => error.NameTooLong, @@ -1356,50 +1421,6 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { } } -pub fn sleep(seconds: usize, nanoseconds: usize) void { - switch(builtin.os) { - Os.linux, Os.macosx, Os.ios => { - posixSleep(u63(seconds), u63(nanoseconds)); - }, - Os.windows => { - const milliseconds = seconds * 1000 + nanoseconds / 1000000; - windows.Sleep(windows.DWORD(milliseconds)); - }, - else => @compileError("Unsupported OS"), - } -} - -const u63 = @IntType(false, 63); -pub fn posixSleep(seconds: u63, nanoseconds: u63) void { - var req = posix.timespec { - .tv_sec = seconds, - .tv_nsec = nanoseconds, - }; - var rem: posix.timespec = undefined; - while (true) { - const ret_val = posix.nanosleep(&req, &rem); - const err = posix.getErrno(ret_val); - if (err == 0) return; - switch (err) { - posix.EFAULT => unreachable, - posix.EINVAL => { - // Sometimes Darwin returns EINVAL for no reason. - // We treat it as a spurious wakeup. - return; - }, - posix.EINTR => { - req = rem; - continue; - }, - else => return, - } - } -} - -test "os.sleep" { - sleep(0, 1); -} - pub fn posix_setuid(uid: u32) !void { const err = posix.getErrno(posix.setuid(uid)); if (err == 0) return; @@ -1475,8 +1496,7 @@ pub const ArgIteratorPosix = struct { } pub fn next(self: &ArgIteratorPosix) ?[]const u8 { - if (self.index == self.count) - return null; + if (self.index == self.count) return null; const s = raw[self.index]; self.index += 1; @@ -1484,8 +1504,7 @@ pub const ArgIteratorPosix = struct { } pub fn skip(self: &ArgIteratorPosix) bool { - if (self.index == self.count) - return false; + if (self.index == self.count) return false; self.index += 1; return true; @@ -1503,7 +1522,9 @@ pub const ArgIteratorWindows = struct { quote_count: usize, seen_quote_count: usize, - pub const NextError = error{OutOfMemory}; + pub const NextError = error { + OutOfMemory, + }; pub fn init() ArgIteratorWindows { return initWithCmdLine(windows.GetCommandLineA()); @@ -1526,7 +1547,8 @@ pub const ArgIteratorWindows = struct { const byte = self.cmd_line[self.index]; switch (byte) { 0 => return null, - ' ', '\t' => continue, + ' ', + '\t' => continue, else => break, } } @@ -1540,7 +1562,8 @@ pub const ArgIteratorWindows = struct { const byte = self.cmd_line[self.index]; switch (byte) { 0 => return false, - ' ', '\t' => continue, + ' ', + '\t' => continue, else => break, } } @@ -1559,7 +1582,8 @@ pub const ArgIteratorWindows = struct { '\\' => { backslash_count += 1; }, - ' ', '\t' => { + ' ', + '\t' => { if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) { return true; } @@ -1599,7 +1623,8 @@ pub const ArgIteratorWindows = struct { '\\' => { backslash_count += 1; }, - ' ', '\t' => { + ' ', + '\t' => { try self.emitBackslashes(&buf, backslash_count); backslash_count = 0; if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) { @@ -1643,7 +1668,6 @@ pub const ArgIteratorWindows = struct { } } } - }; pub const ArgIterator = struct { @@ -1658,7 +1682,7 @@ pub const ArgIterator = struct { } pub const NextError = ArgIteratorWindows.NextError; - + /// You must free the returned memory when done. pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) { if (builtin.os == Os.windows) { @@ -1733,15 +1757,47 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void { } test "windows arg parsing" { - testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"}); - testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"}); - testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{"a\\\\\\b", "de fg", "h"}); - testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{"a\\\"b", "c", "d"}); - testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{"a\\\\b c", "d", "e"}); - testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{"a", "b", "c", "\"d", "f"}); - - testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", - [][]const u8{".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", ".\\..\\zig-cache", "--help"}); + testWindowsCmdLine(c"a b\tc d", [][]const u8 { + "a", + "b", + "c", + "d", + }); + testWindowsCmdLine(c"\"abc\" d e", [][]const u8 { + "abc", + "d", + "e", + }); + testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8 { + "a\\\\\\b", + "de fg", + "h", + }); + testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8 { + "a\\\"b", + "c", + "d", + }); + testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8 { + "a\\\\b c", + "d", + "e", + }); + testWindowsCmdLine(c"a b\tc \"d f", [][]const u8 { + "a", + "b", + "c", + "\"d", + "f", + }); + + testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8 { + ".\\..\\zig-cache\\build", + "bin\\zig.exe", + ".\\..", + ".\\..\\zig-cache", + "--help", + }); } fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void { @@ -1753,27 +1809,16 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const assert(it.next(debug.global_allocator) == null); } -test "std.os" { - _ = @import("child_process.zig"); - _ = @import("darwin_errno.zig"); - _ = @import("darwin.zig"); - _ = @import("get_user_id.zig"); - _ = @import("linux/errno.zig"); - //_ = @import("linux_i386.zig"); - _ = @import("linux/x86_64.zig"); - _ = @import("linux/index.zig"); - _ = @import("path.zig"); - _ = @import("windows/index.zig"); - _ = @import("test.zig"); -} - - // TODO make this a build variable that you can set const unexpected_error_tracing = false; +const UnexpectedError = error { + /// The Operating System returned an undocumented error code. + Unexpected, +}; /// Call this when you made a syscall or something that sets errno /// and you get an unexpected error. -pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) { +pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { if (unexpected_error_tracing) { debug.warn("unexpected errno: {}\n", errno); debug.dumpCurrentStackTrace(null); @@ -1783,7 +1828,7 @@ pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) { /// Call this when you made a windows DLL call or something that does SetLastError /// and you get an unexpected error. -pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) { +pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { if (unexpected_error_tracing) { debug.warn("unexpected GetLastError(): {}\n", err); debug.dumpCurrentStackTrace(null); @@ -1799,7 +1844,8 @@ pub fn openSelfExe() !os.File { var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); return os.File.openRead(&fixed_allocator.allocator, proc_file_path); }, - Os.macosx, Os.ios => { + Os.macosx, + Os.ios => { var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined; var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); const self_exe_path = try selfExePath(&fixed_allocator.allocator); @@ -1811,8 +1857,10 @@ pub fn openSelfExe() !os.File { test "openSelfExe" { switch (builtin.os) { - Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(), - else => return, // Unsupported OS. + Os.linux, + Os.macosx, + Os.ios => (try openSelfExe()).close(), + else => return, // Unsupported OS. } } @@ -1849,7 +1897,8 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 { try out_path.resize(new_len); } }, - Os.macosx, Os.ios => { + Os.macosx, + Os.ios => { var u32_len: u32 = 0; const ret1 = c._NSGetExecutablePath(undefined, &u32_len); assert(ret1 != 0); @@ -1877,7 +1926,9 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 { const dir = path.dirname(full_exe_path); return allocator.shrink(u8, full_exe_path, dir.len); }, - Os.windows, Os.macosx, Os.ios => { + Os.windows, + Os.macosx, + Os.ios => { const self_exe_path = try selfExePath(allocator); errdefer allocator.free(self_exe_path); const dirname = os.path.dirname(self_exe_path); @@ -1898,3 +1949,712 @@ pub fn isTty(handle: FileHandle) bool { } } } + +pub const PosixSocketError = error { + /// Permission to create a socket of the specified type and/or + /// pro‐tocol is denied. + PermissionDenied, + + /// The implementation does not support the specified address family. + AddressFamilyNotSupported, + + /// Unknown protocol, or protocol family not available. + ProtocolFamilyNotAvailable, + + /// The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + + /// Insufficient memory is available. The socket cannot be created until sufficient + /// resources are freed. + SystemResources, + + /// The protocol type or the specified protocol is not supported within this domain. + ProtocolNotSupported, +}; + +pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { + const rc = posix.socket(domain, socket_type, protocol); + const err = posix.getErrno(rc); + switch (err) { + 0 => return i32(rc), + posix.EACCES => return PosixSocketError.PermissionDenied, + posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported, + posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, + posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, + posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, + posix.ENOBUFS, + posix.ENOMEM => return PosixSocketError.SystemResources, + posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, + else => return unexpectedErrorPosix(err), + } +} + +pub const PosixBindError = error { + /// The address is protected, and the user is not the superuser. + /// For UNIX domain sockets: Search permission is denied on a component + /// of the path prefix. + AccessDenied, + + /// The given address is already in use, or in the case of Internet domain sockets, + /// The port number was specified as zero in the socket + /// address structure, but, upon attempting to bind to an ephemeral port, it was + /// determined that all port numbers in the ephemeral port range are currently in + /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7). + AddressInUse, + + /// sockfd is not a valid file descriptor. + InvalidFileDescriptor, + + /// The socket is already bound to an address, or addrlen is wrong, or addr is not + /// a valid address for this socket's domain. + InvalidSocketOrAddress, + + /// The file descriptor sockfd does not refer to a socket. + FileDescriptorNotASocket, + + /// A nonexistent interface was requested or the requested address was not local. + AddressNotAvailable, + + /// addr points outside the user's accessible address space. + PageFault, + + /// Too many symbolic links were encountered in resolving addr. + SymLinkLoop, + + /// addr is too long. + NameTooLong, + + /// A component in the directory prefix of the socket pathname does not exist. + FileNotFound, + + /// Insufficient kernel memory was available. + SystemResources, + + /// A component of the path prefix is not a directory. + NotDir, + + /// The socket inode would reside on a read-only filesystem. + ReadOnlyFileSystem, + + Unexpected, +}; + +/// addr is `&const T` where T is one of the sockaddr +pub fn posixBind(fd: i32, addr: &const posix.sockaddr) PosixBindError!void { + const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr)); + const err = posix.getErrno(rc); + switch (err) { + 0 => return, + posix.EACCES => return PosixBindError.AccessDenied, + posix.EADDRINUSE => return PosixBindError.AddressInUse, + posix.EBADF => return PosixBindError.InvalidFileDescriptor, + posix.EINVAL => return PosixBindError.InvalidSocketOrAddress, + posix.ENOTSOCK => return PosixBindError.FileDescriptorNotASocket, + posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable, + posix.EFAULT => return PosixBindError.PageFault, + posix.ELOOP => return PosixBindError.SymLinkLoop, + posix.ENAMETOOLONG => return PosixBindError.NameTooLong, + posix.ENOENT => return PosixBindError.FileNotFound, + posix.ENOMEM => return PosixBindError.SystemResources, + posix.ENOTDIR => return PosixBindError.NotDir, + posix.EROFS => return PosixBindError.ReadOnlyFileSystem, + else => return unexpectedErrorPosix(err), + } +} + +const PosixListenError = error { + /// Another socket is already listening on the same port. + /// For Internet domain sockets, the socket referred to by sockfd had not previously + /// been bound to an address and, upon attempting to bind it to an ephemeral port, it + /// was determined that all port numbers in the ephemeral port range are currently in + /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). + AddressInUse, + + /// The argument sockfd is not a valid file descriptor. + InvalidFileDescriptor, + + /// The file descriptor sockfd does not refer to a socket. + FileDescriptorNotASocket, + + /// The socket is not of a type that supports the listen() operation. + OperationNotSupported, + + Unexpected, +}; + +pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void { + const rc = posix.listen(sockfd, backlog); + const err = posix.getErrno(rc); + switch (err) { + 0 => return, + posix.EADDRINUSE => return PosixListenError.AddressInUse, + posix.EBADF => return PosixListenError.InvalidFileDescriptor, + posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket, + posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported, + else => return unexpectedErrorPosix(err), + } +} + +pub const PosixAcceptError = error { + /// The socket is marked nonblocking and no connections are present to be accepted. + WouldBlock, + + /// sockfd is not an open file descriptor. + FileDescriptorClosed, + + ConnectionAborted, + + /// The addr argument is not in a writable part of the user address space. + PageFault, + + /// Socket is not listening for connections, or addrlen is invalid (e.g., is negative), + /// or invalid value in flags. + InvalidSyscall, + + /// The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + + /// Not enough free memory. This often means that the memory allocation is limited + /// by the socket buffer limits, not by the system memory. + SystemResources, + + /// The file descriptor sockfd does not refer to a socket. + FileDescriptorNotASocket, + + /// The referenced socket is not of type SOCK_STREAM. + OperationNotSupported, + + ProtocolFailure, + + /// Firewall rules forbid connection. + BlockedByFirewall, + + Unexpected, +}; + +pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!i32 { + while (true) { + var sockaddr_size = u32(@sizeOf(posix.sockaddr)); + const rc = posix.accept4(fd, addr, &sockaddr_size, flags); + const err = posix.getErrno(rc); + switch (err) { + 0 => return i32(rc), + posix.EINTR => continue, + else => return unexpectedErrorPosix(err), + + posix.EAGAIN => return PosixAcceptError.WouldBlock, + posix.EBADF => return PosixAcceptError.FileDescriptorClosed, + posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, + posix.EFAULT => return PosixAcceptError.PageFault, + posix.EINVAL => return PosixAcceptError.InvalidSyscall, + posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, + posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, + posix.ENOBUFS, + posix.ENOMEM => return PosixAcceptError.SystemResources, + posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, + posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, + posix.EPROTO => return PosixAcceptError.ProtocolFailure, + posix.EPERM => return PosixAcceptError.BlockedByFirewall, + } + } +} + +pub const LinuxEpollCreateError = error { + /// Invalid value specified in flags. + InvalidSyscall, + + /// The per-user limit on the number of epoll instances imposed by + /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further + /// details. + /// Or, The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + + /// There was insufficient memory to create the kernel object. + SystemResources, + + Unexpected, +}; + +pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { + const rc = posix.epoll_create1(flags); + const err = posix.getErrno(rc); + switch (err) { + 0 => return i32(rc), + else => return unexpectedErrorPosix(err), + + posix.EINVAL => return LinuxEpollCreateError.InvalidSyscall, + posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded, + posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded, + posix.ENOMEM => return LinuxEpollCreateError.SystemResources, + } +} + +pub const LinuxEpollCtlError = error { + /// epfd or fd is not a valid file descriptor. + InvalidFileDescriptor, + + /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered + /// with this epoll instance. + FileDescriptorAlreadyPresentInSet, + + /// epfd is not an epoll file descriptor, or fd is the same as epfd, or the requested + /// operation op is not supported by this interface, or + /// An invalid event type was specified along with EPOLLEXCLUSIVE in events, or + /// op was EPOLL_CTL_MOD and events included EPOLLEXCLUSIVE, or + /// op was EPOLL_CTL_MOD and the EPOLLEXCLUSIVE flag has previously been applied to + /// this epfd, fd pair, or + /// EPOLLEXCLUSIVE was specified in event and fd refers to an epoll instance. + InvalidSyscall, + + /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a + /// circular loop of epoll instances monitoring one another. + OperationCausesCircularLoop, + + /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll + /// instance. + FileDescriptorNotRegistered, + + /// There was insufficient memory to handle the requested op control operation. + SystemResources, + + /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while + /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. + /// See epoll(7) for further details. + UserResourceLimitReached, + + /// The target file fd does not support epoll. This error can occur if fd refers to, + /// for example, a regular file or a directory. + FileDescriptorIncompatibleWithEpoll, + + Unexpected, +}; + +pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: &linux.epoll_event) LinuxEpollCtlError!void { + const rc = posix.epoll_ctl(epfd, op, fd, event); + const err = posix.getErrno(rc); + switch (err) { + 0 => return, + else => return unexpectedErrorPosix(err), + + posix.EBADF => return LinuxEpollCtlError.InvalidFileDescriptor, + posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet, + posix.EINVAL => return LinuxEpollCtlError.InvalidSyscall, + posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop, + posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered, + posix.ENOMEM => return LinuxEpollCtlError.SystemResources, + posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached, + posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll, + } +} + +pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize { + while (true) { + const rc = posix.epoll_wait(epfd, events.ptr, u32(events.len), timeout); + const err = posix.getErrno(rc); + switch (err) { + 0 => return rc, + posix.EINTR => continue, + posix.EBADF => unreachable, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + else => unreachable, + } + } +} + +pub const PosixGetSockNameError = error { + /// Insufficient resources were available in the system to perform the operation. + SystemResources, + + Unexpected, +}; + +pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr { + var addr: posix.sockaddr = undefined; + var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr); + const rc = posix.getsockname(sockfd, &addr, &addrlen); + const err = posix.getErrno(rc); + switch (err) { + 0 => return addr, + else => return unexpectedErrorPosix(err), + + posix.EBADF => unreachable, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.ENOTSOCK => unreachable, + posix.ENOBUFS => return PosixGetSockNameError.SystemResources, + } +} + +pub const PosixConnectError = error { + /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket + /// file, or search permission is denied for one of the directories in the path prefix. + /// or + /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or + /// the connection request failed because of a local firewall rule. + PermissionDenied, + + /// Local address is already in use. + AddressInUse, + + /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an + /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers + /// in the ephemeral port range are currently in use. See the discussion of + /// /proc/sys/net/ipv4/ip_local_port_range in ip(7). + AddressNotAvailable, + + /// The passed address didn't have the correct address family in its sa_family field. + AddressFamilyNotSupported, + + /// Insufficient entries in the routing cache. + SystemResources, + + /// A connect() on a stream socket found no one listening on the remote address. + ConnectionRefused, + + /// Network is unreachable. + NetworkUnreachable, + + /// Timeout while attempting connection. The server may be too busy to accept new connections. Note + /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. + ConnectionTimedOut, + + Unexpected, +}; + +pub fn posixConnect(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void { + while (true) { + const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); + const err = posix.getErrno(rc); + switch (err) { + 0 => return, + else => return unexpectedErrorPosix(err), + + posix.EACCES => return PosixConnectError.PermissionDenied, + posix.EPERM => return PosixConnectError.PermissionDenied, + posix.EADDRINUSE => return PosixConnectError.AddressInUse, + posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, + posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, + posix.EAGAIN => return PosixConnectError.SystemResources, + posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. + posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. + posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, + posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. + posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. + posix.EINTR => continue, + posix.EISCONN => unreachable, // The socket is already connected. + posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, + posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. + posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, + } + } +} + +/// Same as posixConnect except it is for blocking socket file descriptors. +/// It expects to receive EINPROGRESS. +pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void { + while (true) { + const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); + const err = posix.getErrno(rc); + switch (err) { + 0, + posix.EINPROGRESS => return, + else => return unexpectedErrorPosix(err), + + posix.EACCES => return PosixConnectError.PermissionDenied, + posix.EPERM => return PosixConnectError.PermissionDenied, + posix.EADDRINUSE => return PosixConnectError.AddressInUse, + posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, + posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, + posix.EAGAIN => return PosixConnectError.SystemResources, + posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. + posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. + posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, + posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. + posix.EINTR => continue, + posix.EISCONN => unreachable, // The socket is already connected. + posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, + posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. + posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, + } + } +} + +pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { + var err_code: i32 = undefined; + var size: u32 = @sizeOf(i32); + const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(&u8, &err_code), &size); + assert(size == 4); + const err = posix.getErrno(rc); + switch (err) { + 0 => switch (err_code) { + 0 => return, + else => return unexpectedErrorPosix(err), + + posix.EACCES => return PosixConnectError.PermissionDenied, + posix.EPERM => return PosixConnectError.PermissionDenied, + posix.EADDRINUSE => return PosixConnectError.AddressInUse, + posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, + posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, + posix.EAGAIN => return PosixConnectError.SystemResources, + posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. + posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. + posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, + posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. + posix.EISCONN => unreachable, // The socket is already connected. + posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, + posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. + posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, + }, + else => return unexpectedErrorPosix(err), + posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor. + posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. + posix.EINVAL => unreachable, + posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. + posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + } +} + +pub const Thread = struct { + data: Data, + + pub const use_pthreads = is_posix and builtin.link_libc; + const Data = if (use_pthreads) struct { + handle: c.pthread_t, + stack_addr: usize, + stack_len: usize, + } else switch (builtin.os) { + builtin.Os.linux => struct { + pid: i32, + stack_addr: usize, + stack_len: usize, + }, + builtin.Os.windows => struct { + handle: windows.HANDLE, + alloc_start: &c_void, + heap_handle: windows.HANDLE, + }, + else => @compileError("Unsupported OS"), + }; + + pub fn wait(self: &const Thread) void { + if (use_pthreads) { + const err = c.pthread_join(self.data.handle, null); + switch (err) { + 0 => {}, + posix.EINVAL => unreachable, + posix.ESRCH => unreachable, + posix.EDEADLK => unreachable, + else => unreachable, + } + assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); + } else switch (builtin.os) { + builtin.Os.linux => { + while (true) { + const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst); + if (pid_value == 0) break; + const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null); + switch (linux.getErrno(rc)) { + 0 => continue, + posix.EINTR => continue, + posix.EAGAIN => continue, + else => unreachable, + } + } + assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); + }, + builtin.Os.windows => { + assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); + assert(windows.CloseHandle(self.data.handle) != 0); + assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); + }, + else => @compileError("Unsupported OS"), + } + } +}; + +pub const SpawnThreadError = error { + /// A system-imposed limit on the number of threads was encountered. + /// There are a number of limits that may trigger this error: + /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), + /// which limits the number of processes and threads for a real + /// user ID, was reached; + /// * the kernel's system-wide limit on the number of processes and + /// threads, /proc/sys/kernel/threads-max, was reached (see + /// proc(5)); + /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was + /// reached (see proc(5)); or + /// * the PID limit (pids.max) imposed by the cgroup "process num‐ + /// ber" (PIDs) controller was reached. + ThreadQuotaExceeded, + + /// The kernel cannot allocate sufficient memory to allocate a task structure + /// for the child, or to copy those parts of the caller's context that need to + /// be copied. + SystemResources, + + /// Not enough userland memory to spawn the thread. + OutOfMemory, + + Unexpected, +}; + +/// caller must call wait on the returned thread +/// fn startFn(@typeOf(context)) T +/// where T is u8, noreturn, void, or !void +/// caller must call wait on the returned thread +pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread { + // TODO compile-time call graph analysis to determine stack upper bound + // https://github.com/zig-lang/zig/issues/157 + const default_stack_size = 8 * 1024 * 1024; + + const Context = @typeOf(context); + comptime assert(@ArgType(@typeOf(startFn), 0) == Context); + + if (builtin.os == builtin.Os.windows) { + const WinThread = struct { + const OuterContext = struct { + thread: Thread, + inner: Context, + }; + extern fn threadMain(arg: windows.LPVOID) windows.DWORD { + if (@sizeOf(Context) == 0) { + return startFn({}); + } else { + return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg))); + } + } + }; + + const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory; + const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); + const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; + errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); + const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count]; + const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; + outer_context.inner = context; + outer_context.thread.data.heap_handle = heap_handle; + outer_context.thread.data.alloc_start = bytes_ptr; + + const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner); + outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? { + const err = windows.GetLastError(); + return switch (err) { + else => os.unexpectedErrorWindows(err), + }; + }; + return &outer_context.thread; + } + + const MainFuncs = struct { + extern fn linuxThreadMain(ctx_addr: usize) u8 { + if (@sizeOf(Context) == 0) { + return startFn({}); + } else { + return startFn(*@intToPtr(&const Context, ctx_addr)); + } + } + extern fn posixThreadMain(ctx: ?&c_void) ?&c_void { + if (@sizeOf(Context) == 0) { + _ = startFn({}); + return null; + } else { + _ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx))); + return null; + } + } + }; + + const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; + + const mmap_len = default_stack_size; + const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); + if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory; + errdefer assert(posix.munmap(stack_addr, mmap_len) == 0); + + var stack_end: usize = stack_addr + mmap_len; + var arg: usize = undefined; + if (@sizeOf(Context) != 0) { + stack_end -= @sizeOf(Context); + stack_end -= stack_end % @alignOf(Context); + assert(stack_end >= stack_addr); + const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); + *context_ptr = context; + arg = stack_end; + } + + stack_end -= @sizeOf(Thread); + stack_end -= stack_end % @alignOf(Thread); + assert(stack_end >= stack_addr); + const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); + + thread_ptr.data.stack_addr = stack_addr; + thread_ptr.data.stack_len = mmap_len; + + if (builtin.os == builtin.Os.windows) { + // use windows API directly + @compileError("TODO support spawnThread for Windows"); + } else if (Thread.use_pthreads) { + // use pthreads + var attr: c.pthread_attr_t = undefined; + if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; + defer assert(c.pthread_attr_destroy(&attr) == 0); + + // align to page + stack_end -= stack_end % os.page_size; + assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_end - stack_addr) == 0); + + const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); + switch (err) { + 0 => return thread_ptr, + posix.EAGAIN => return SpawnThreadError.SystemResources, + posix.EPERM => unreachable, + posix.EINVAL => unreachable, + else => return unexpectedErrorPosix(usize(err)), + } + } else if (builtin.os == builtin.Os.linux) { + // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly + const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; + const newtls: usize = 0; + const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid); + const err = posix.getErrno(rc); + switch (err) { + 0 => return thread_ptr, + posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, + posix.EINVAL => unreachable, + posix.ENOMEM => return SpawnThreadError.SystemResources, + posix.ENOSPC => unreachable, + posix.EPERM => unreachable, + posix.EUSERS => unreachable, + else => return unexpectedErrorPosix(err), + } + } else { + @compileError("Unsupported OS"); + } +} + +pub fn posixWait(pid: i32) i32 { + var status: i32 = undefined; + while (true) { + const err = posix.getErrno(posix.waitpid(pid, &status, 0)); + switch (err) { + 0 => return status, + posix.EINTR => continue, + posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. + posix.EINVAL => unreachable, // The options argument was invalid + else => unreachable, + } + } +} diff --git a/std/os/linux/i386.zig b/std/os/linux/i386.zig deleted file mode 100644 index 7450ad34fa..0000000000 --- a/std/os/linux/i386.zig +++ /dev/null @@ -1,505 +0,0 @@ -const std = @import("../../index.zig"); -const linux = std.os.linux; -const socklen_t = linux.socklen_t; -const iovec = linux.iovec; - -pub const SYS_restart_syscall = 0; -pub const SYS_exit = 1; -pub const SYS_fork = 2; -pub const SYS_read = 3; -pub const SYS_write = 4; -pub const SYS_open = 5; -pub const SYS_close = 6; -pub const SYS_waitpid = 7; -pub const SYS_creat = 8; -pub const SYS_link = 9; -pub const SYS_unlink = 10; -pub const SYS_execve = 11; -pub const SYS_chdir = 12; -pub const SYS_time = 13; -pub const SYS_mknod = 14; -pub const SYS_chmod = 15; -pub const SYS_lchown = 16; -pub const SYS_break = 17; -pub const SYS_oldstat = 18; -pub const SYS_lseek = 19; -pub const SYS_getpid = 20; -pub const SYS_mount = 21; -pub const SYS_umount = 22; -pub const SYS_setuid = 23; -pub const SYS_getuid = 24; -pub const SYS_stime = 25; -pub const SYS_ptrace = 26; -pub const SYS_alarm = 27; -pub const SYS_oldfstat = 28; -pub const SYS_pause = 29; -pub const SYS_utime = 30; -pub const SYS_stty = 31; -pub const SYS_gtty = 32; -pub const SYS_access = 33; -pub const SYS_nice = 34; -pub const SYS_ftime = 35; -pub const SYS_sync = 36; -pub const SYS_kill = 37; -pub const SYS_rename = 38; -pub const SYS_mkdir = 39; -pub const SYS_rmdir = 40; -pub const SYS_dup = 41; -pub const SYS_pipe = 42; -pub const SYS_times = 43; -pub const SYS_prof = 44; -pub const SYS_brk = 45; -pub const SYS_setgid = 46; -pub const SYS_getgid = 47; -pub const SYS_signal = 48; -pub const SYS_geteuid = 49; -pub const SYS_getegid = 50; -pub const SYS_acct = 51; -pub const SYS_umount2 = 52; -pub const SYS_lock = 53; -pub const SYS_ioctl = 54; -pub const SYS_fcntl = 55; -pub const SYS_mpx = 56; -pub const SYS_setpgid = 57; -pub const SYS_ulimit = 58; -pub const SYS_oldolduname = 59; -pub const SYS_umask = 60; -pub const SYS_chroot = 61; -pub const SYS_ustat = 62; -pub const SYS_dup2 = 63; -pub const SYS_getppid = 64; -pub const SYS_getpgrp = 65; -pub const SYS_setsid = 66; -pub const SYS_sigaction = 67; -pub const SYS_sgetmask = 68; -pub const SYS_ssetmask = 69; -pub const SYS_setreuid = 70; -pub const SYS_setregid = 71; -pub const SYS_sigsuspend = 72; -pub const SYS_sigpending = 73; -pub const SYS_sethostname = 74; -pub const SYS_setrlimit = 75; -pub const SYS_getrlimit = 76; -pub const SYS_getrusage = 77; -pub const SYS_gettimeofday = 78; -pub const SYS_settimeofday = 79; -pub const SYS_getgroups = 80; -pub const SYS_setgroups = 81; -pub const SYS_select = 82; -pub const SYS_symlink = 83; -pub const SYS_oldlstat = 84; -pub const SYS_readlink = 85; -pub const SYS_uselib = 86; -pub const SYS_swapon = 87; -pub const SYS_reboot = 88; -pub const SYS_readdir = 89; -pub const SYS_mmap = 90; -pub const SYS_munmap = 91; -pub const SYS_truncate = 92; -pub const SYS_ftruncate = 93; -pub const SYS_fchmod = 94; -pub const SYS_fchown = 95; -pub const SYS_getpriority = 96; -pub const SYS_setpriority = 97; -pub const SYS_profil = 98; -pub const SYS_statfs = 99; -pub const SYS_fstatfs = 100; -pub const SYS_ioperm = 101; -pub const SYS_socketcall = 102; -pub const SYS_syslog = 103; -pub const SYS_setitimer = 104; -pub const SYS_getitimer = 105; -pub const SYS_stat = 106; -pub const SYS_lstat = 107; -pub const SYS_fstat = 108; -pub const SYS_olduname = 109; -pub const SYS_iopl = 110; -pub const SYS_vhangup = 111; -pub const SYS_idle = 112; -pub const SYS_vm86old = 113; -pub const SYS_wait4 = 114; -pub const SYS_swapoff = 115; -pub const SYS_sysinfo = 116; -pub const SYS_ipc = 117; -pub const SYS_fsync = 118; -pub const SYS_sigreturn = 119; -pub const SYS_clone = 120; -pub const SYS_setdomainname = 121; -pub const SYS_uname = 122; -pub const SYS_modify_ldt = 123; -pub const SYS_adjtimex = 124; -pub const SYS_mprotect = 125; -pub const SYS_sigprocmask = 126; -pub const SYS_create_module = 127; -pub const SYS_init_module = 128; -pub const SYS_delete_module = 129; -pub const SYS_get_kernel_syms = 130; -pub const SYS_quotactl = 131; -pub const SYS_getpgid = 132; -pub const SYS_fchdir = 133; -pub const SYS_bdflush = 134; -pub const SYS_sysfs = 135; -pub const SYS_personality = 136; -pub const SYS_afs_syscall = 137; -pub const SYS_setfsuid = 138; -pub const SYS_setfsgid = 139; -pub const SYS__llseek = 140; -pub const SYS_getdents = 141; -pub const SYS__newselect = 142; -pub const SYS_flock = 143; -pub const SYS_msync = 144; -pub const SYS_readv = 145; -pub const SYS_writev = 146; -pub const SYS_getsid = 147; -pub const SYS_fdatasync = 148; -pub const SYS__sysctl = 149; -pub const SYS_mlock = 150; -pub const SYS_munlock = 151; -pub const SYS_mlockall = 152; -pub const SYS_munlockall = 153; -pub const SYS_sched_setparam = 154; -pub const SYS_sched_getparam = 155; -pub const SYS_sched_setscheduler = 156; -pub const SYS_sched_getscheduler = 157; -pub const SYS_sched_yield = 158; -pub const SYS_sched_get_priority_max = 159; -pub const SYS_sched_get_priority_min = 160; -pub const SYS_sched_rr_get_interval = 161; -pub const SYS_nanosleep = 162; -pub const SYS_mremap = 163; -pub const SYS_setresuid = 164; -pub const SYS_getresuid = 165; -pub const SYS_vm86 = 166; -pub const SYS_query_module = 167; -pub const SYS_poll = 168; -pub const SYS_nfsservctl = 169; -pub const SYS_setresgid = 170; -pub const SYS_getresgid = 171; -pub const SYS_prctl = 172; -pub const SYS_rt_sigreturn = 173; -pub const SYS_rt_sigaction = 174; -pub const SYS_rt_sigprocmask = 175; -pub const SYS_rt_sigpending = 176; -pub const SYS_rt_sigtimedwait = 177; -pub const SYS_rt_sigqueueinfo = 178; -pub const SYS_rt_sigsuspend = 179; -pub const SYS_pread64 = 180; -pub const SYS_pwrite64 = 181; -pub const SYS_chown = 182; -pub const SYS_getcwd = 183; -pub const SYS_capget = 184; -pub const SYS_capset = 185; -pub const SYS_sigaltstack = 186; -pub const SYS_sendfile = 187; -pub const SYS_getpmsg = 188; -pub const SYS_putpmsg = 189; -pub const SYS_vfork = 190; -pub const SYS_ugetrlimit = 191; -pub const SYS_mmap2 = 192; -pub const SYS_truncate64 = 193; -pub const SYS_ftruncate64 = 194; -pub const SYS_stat64 = 195; -pub const SYS_lstat64 = 196; -pub const SYS_fstat64 = 197; -pub const SYS_lchown32 = 198; -pub const SYS_getuid32 = 199; -pub const SYS_getgid32 = 200; -pub const SYS_geteuid32 = 201; -pub const SYS_getegid32 = 202; -pub const SYS_setreuid32 = 203; -pub const SYS_setregid32 = 204; -pub const SYS_getgroups32 = 205; -pub const SYS_setgroups32 = 206; -pub const SYS_fchown32 = 207; -pub const SYS_setresuid32 = 208; -pub const SYS_getresuid32 = 209; -pub const SYS_setresgid32 = 210; -pub const SYS_getresgid32 = 211; -pub const SYS_chown32 = 212; -pub const SYS_setuid32 = 213; -pub const SYS_setgid32 = 214; -pub const SYS_setfsuid32 = 215; -pub const SYS_setfsgid32 = 216; -pub const SYS_pivot_root = 217; -pub const SYS_mincore = 218; -pub const SYS_madvise = 219; -pub const SYS_madvise1 = 219; -pub const SYS_getdents64 = 220; -pub const SYS_fcntl64 = 221; -pub const SYS_gettid = 224; -pub const SYS_readahead = 225; -pub const SYS_setxattr = 226; -pub const SYS_lsetxattr = 227; -pub const SYS_fsetxattr = 228; -pub const SYS_getxattr = 229; -pub const SYS_lgetxattr = 230; -pub const SYS_fgetxattr = 231; -pub const SYS_listxattr = 232; -pub const SYS_llistxattr = 233; -pub const SYS_flistxattr = 234; -pub const SYS_removexattr = 235; -pub const SYS_lremovexattr = 236; -pub const SYS_fremovexattr = 237; -pub const SYS_tkill = 238; -pub const SYS_sendfile64 = 239; -pub const SYS_futex = 240; -pub const SYS_sched_setaffinity = 241; -pub const SYS_sched_getaffinity = 242; -pub const SYS_set_thread_area = 243; -pub const SYS_get_thread_area = 244; -pub const SYS_io_setup = 245; -pub const SYS_io_destroy = 246; -pub const SYS_io_getevents = 247; -pub const SYS_io_submit = 248; -pub const SYS_io_cancel = 249; -pub const SYS_fadvise64 = 250; -pub const SYS_exit_group = 252; -pub const SYS_lookup_dcookie = 253; -pub const SYS_epoll_create = 254; -pub const SYS_epoll_ctl = 255; -pub const SYS_epoll_wait = 256; -pub const SYS_remap_file_pages = 257; -pub const SYS_set_tid_address = 258; -pub const SYS_timer_create = 259; -pub const SYS_timer_settime = SYS_timer_create+1; -pub const SYS_timer_gettime = SYS_timer_create+2; -pub const SYS_timer_getoverrun = SYS_timer_create+3; -pub const SYS_timer_delete = SYS_timer_create+4; -pub const SYS_clock_settime = SYS_timer_create+5; -pub const SYS_clock_gettime = SYS_timer_create+6; -pub const SYS_clock_getres = SYS_timer_create+7; -pub const SYS_clock_nanosleep = SYS_timer_create+8; -pub const SYS_statfs64 = 268; -pub const SYS_fstatfs64 = 269; -pub const SYS_tgkill = 270; -pub const SYS_utimes = 271; -pub const SYS_fadvise64_64 = 272; -pub const SYS_vserver = 273; -pub const SYS_mbind = 274; -pub const SYS_get_mempolicy = 275; -pub const SYS_set_mempolicy = 276; -pub const SYS_mq_open = 277; -pub const SYS_mq_unlink = SYS_mq_open+1; -pub const SYS_mq_timedsend = SYS_mq_open+2; -pub const SYS_mq_timedreceive = SYS_mq_open+3; -pub const SYS_mq_notify = SYS_mq_open+4; -pub const SYS_mq_getsetattr = SYS_mq_open+5; -pub const SYS_kexec_load = 283; -pub const SYS_waitid = 284; -pub const SYS_add_key = 286; -pub const SYS_request_key = 287; -pub const SYS_keyctl = 288; -pub const SYS_ioprio_set = 289; -pub const SYS_ioprio_get = 290; -pub const SYS_inotify_init = 291; -pub const SYS_inotify_add_watch = 292; -pub const SYS_inotify_rm_watch = 293; -pub const SYS_migrate_pages = 294; -pub const SYS_openat = 295; -pub const SYS_mkdirat = 296; -pub const SYS_mknodat = 297; -pub const SYS_fchownat = 298; -pub const SYS_futimesat = 299; -pub const SYS_fstatat64 = 300; -pub const SYS_unlinkat = 301; -pub const SYS_renameat = 302; -pub const SYS_linkat = 303; -pub const SYS_symlinkat = 304; -pub const SYS_readlinkat = 305; -pub const SYS_fchmodat = 306; -pub const SYS_faccessat = 307; -pub const SYS_pselect6 = 308; -pub const SYS_ppoll = 309; -pub const SYS_unshare = 310; -pub const SYS_set_robust_list = 311; -pub const SYS_get_robust_list = 312; -pub const SYS_splice = 313; -pub const SYS_sync_file_range = 314; -pub const SYS_tee = 315; -pub const SYS_vmsplice = 316; -pub const SYS_move_pages = 317; -pub const SYS_getcpu = 318; -pub const SYS_epoll_pwait = 319; -pub const SYS_utimensat = 320; -pub const SYS_signalfd = 321; -pub const SYS_timerfd_create = 322; -pub const SYS_eventfd = 323; -pub const SYS_fallocate = 324; -pub const SYS_timerfd_settime = 325; -pub const SYS_timerfd_gettime = 326; -pub const SYS_signalfd4 = 327; -pub const SYS_eventfd2 = 328; -pub const SYS_epoll_create1 = 329; -pub const SYS_dup3 = 330; -pub const SYS_pipe2 = 331; -pub const SYS_inotify_init1 = 332; -pub const SYS_preadv = 333; -pub const SYS_pwritev = 334; -pub const SYS_rt_tgsigqueueinfo = 335; -pub const SYS_perf_event_open = 336; -pub const SYS_recvmmsg = 337; -pub const SYS_fanotify_init = 338; -pub const SYS_fanotify_mark = 339; -pub const SYS_prlimit64 = 340; -pub const SYS_name_to_handle_at = 341; -pub const SYS_open_by_handle_at = 342; -pub const SYS_clock_adjtime = 343; -pub const SYS_syncfs = 344; -pub const SYS_sendmmsg = 345; -pub const SYS_setns = 346; -pub const SYS_process_vm_readv = 347; -pub const SYS_process_vm_writev = 348; -pub const SYS_kcmp = 349; -pub const SYS_finit_module = 350; -pub const SYS_sched_setattr = 351; -pub const SYS_sched_getattr = 352; -pub const SYS_renameat2 = 353; -pub const SYS_seccomp = 354; -pub const SYS_getrandom = 355; -pub const SYS_memfd_create = 356; -pub const SYS_bpf = 357; -pub const SYS_execveat = 358; -pub const SYS_socket = 359; -pub const SYS_socketpair = 360; -pub const SYS_bind = 361; -pub const SYS_connect = 362; -pub const SYS_listen = 363; -pub const SYS_accept4 = 364; -pub const SYS_getsockopt = 365; -pub const SYS_setsockopt = 366; -pub const SYS_getsockname = 367; -pub const SYS_getpeername = 368; -pub const SYS_sendto = 369; -pub const SYS_sendmsg = 370; -pub const SYS_recvfrom = 371; -pub const SYS_recvmsg = 372; -pub const SYS_shutdown = 373; -pub const SYS_userfaultfd = 374; -pub const SYS_membarrier = 375; -pub const SYS_mlock2 = 376; - - -pub const O_CREAT = 0o100; -pub const O_EXCL = 0o200; -pub const O_NOCTTY = 0o400; -pub const O_TRUNC = 0o1000; -pub const O_APPEND = 0o2000; -pub const O_NONBLOCK = 0o4000; -pub const O_DSYNC = 0o10000; -pub const O_SYNC = 0o4010000; -pub const O_RSYNC = 0o4010000; -pub const O_DIRECTORY = 0o200000; -pub const O_NOFOLLOW = 0o400000; -pub const O_CLOEXEC = 0o2000000; - -pub const O_ASYNC = 0o20000; -pub const O_DIRECT = 0o40000; -pub const O_LARGEFILE = 0o100000; -pub const O_NOATIME = 0o1000000; -pub const O_PATH = 0o10000000; -pub const O_TMPFILE = 0o20200000; -pub const O_NDELAY = O_NONBLOCK; - -pub const F_DUPFD = 0; -pub const F_GETFD = 1; -pub const F_SETFD = 2; -pub const F_GETFL = 3; -pub const F_SETFL = 4; - -pub const F_SETOWN = 8; -pub const F_GETOWN = 9; -pub const F_SETSIG = 10; -pub const F_GETSIG = 11; - -pub const F_GETLK = 12; -pub const F_SETLK = 13; -pub const F_SETLKW = 14; - -pub const F_SETOWN_EX = 15; -pub const F_GETOWN_EX = 16; - -pub const F_GETOWNER_UIDS = 17; - -pub inline fn syscall0(number: usize) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number)); -} - -pub inline fn syscall1(number: usize, arg1: usize) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ebx}" (arg1)); -} - -pub inline fn syscall2(number: usize, arg1: usize, arg2: usize) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ebx}" (arg1), - [arg2] "{ecx}" (arg2)); -} - -pub inline fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ebx}" (arg1), - [arg2] "{ecx}" (arg2), - [arg3] "{edx}" (arg3)); -} - -pub inline fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ebx}" (arg1), - [arg2] "{ecx}" (arg2), - [arg3] "{edx}" (arg3), - [arg4] "{esi}" (arg4)); -} - -pub inline fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, - arg4: usize, arg5: usize) usize -{ - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ebx}" (arg1), - [arg2] "{ecx}" (arg2), - [arg3] "{edx}" (arg3), - [arg4] "{esi}" (arg4), - [arg5] "{edi}" (arg5)); -} - -pub inline fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, - arg4: usize, arg5: usize, arg6: usize) usize -{ - return asm volatile ("int $0x80" - : [ret] "={eax}" (-> usize) - : [number] "{eax}" (number), - [arg1] "{ebx}" (arg1), - [arg2] "{ecx}" (arg2), - [arg3] "{edx}" (arg3), - [arg4] "{esi}" (arg4), - [arg5] "{edi}" (arg5), - [arg6] "{ebp}" (arg6)); -} - -pub nakedcc fn restore() void { - asm volatile ( - \\popl %%eax - \\movl $119, %%eax - \\int $0x80 - : - : - : "rcx", "r11"); -} - -pub nakedcc fn restore_rt() void { - asm volatile ("int $0x80" - : - : [number] "{eax}" (usize(SYS_rt_sigreturn)) - : "rcx", "r11"); -} diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 8fd8bcbe78..368f074b9b 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -1,6 +1,7 @@ const std = @import("../../index.zig"); const assert = std.debug.assert; const builtin = @import("builtin"); +const vdso = @import("vdso.zig"); pub use switch (builtin.arch) { builtin.Arch.x86_64 => @import("x86_64.zig"), builtin.Arch.i386 => @import("i386.zig"), @@ -14,6 +15,22 @@ pub const STDIN_FILENO = 0; pub const STDOUT_FILENO = 1; pub const STDERR_FILENO = 2; +pub const FUTEX_WAIT = 0; +pub const FUTEX_WAKE = 1; +pub const FUTEX_FD = 2; +pub const FUTEX_REQUEUE = 3; +pub const FUTEX_CMP_REQUEUE = 4; +pub const FUTEX_WAKE_OP = 5; +pub const FUTEX_LOCK_PI = 6; +pub const FUTEX_UNLOCK_PI = 7; +pub const FUTEX_TRYLOCK_PI = 8; +pub const FUTEX_WAIT_BITSET = 9; + +pub const FUTEX_PRIVATE_FLAG = 128; + +pub const FUTEX_CLOCK_REALTIME = 256; + + pub const PROT_NONE = 0; pub const PROT_READ = 1; pub const PROT_WRITE = 2; @@ -38,6 +55,11 @@ pub const MAP_STACK = 0x20000; pub const MAP_HUGETLB = 0x40000; pub const MAP_FILE = 0; +pub const F_OK = 0; +pub const X_OK = 1; +pub const W_OK = 2; +pub const R_OK = 4; + pub const WNOHANG = 1; pub const WUNTRACED = 2; pub const WSTOPPED = 2; @@ -101,17 +123,6 @@ pub const SIG_BLOCK = 0; pub const SIG_UNBLOCK = 1; pub const SIG_SETMASK = 2; -pub const SOCK_STREAM = 1; -pub const SOCK_DGRAM = 2; -pub const SOCK_RAW = 3; -pub const SOCK_RDM = 4; -pub const SOCK_SEQPACKET = 5; -pub const SOCK_DCCP = 6; -pub const SOCK_PACKET = 10; -pub const SOCK_CLOEXEC = 0o2000000; -pub const SOCK_NONBLOCK = 0o4000; - - pub const PROTO_ip = 0o000; pub const PROTO_icmp = 0o001; pub const PROTO_igmp = 0o002; @@ -149,6 +160,20 @@ pub const PROTO_encap = 0o142; pub const PROTO_pim = 0o147; pub const PROTO_raw = 0o377; +pub const SHUT_RD = 0; +pub const SHUT_WR = 1; +pub const SHUT_RDWR = 2; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; +pub const SOCK_DCCP = 6; +pub const SOCK_PACKET = 10; +pub const SOCK_CLOEXEC = 0o2000000; +pub const SOCK_NONBLOCK = 0o4000; + pub const PF_UNSPEC = 0; pub const PF_LOCAL = 1; pub const PF_UNIX = PF_LOCAL; @@ -193,7 +218,10 @@ pub const PF_CAIF = 37; pub const PF_ALG = 38; pub const PF_NFC = 39; pub const PF_VSOCK = 40; -pub const PF_MAX = 41; +pub const PF_KCM = 41; +pub const PF_QIPCRTR = 42; +pub const PF_SMC = 43; +pub const PF_MAX = 44; pub const AF_UNSPEC = PF_UNSPEC; pub const AF_LOCAL = PF_LOCAL; @@ -239,8 +267,137 @@ pub const AF_CAIF = PF_CAIF; pub const AF_ALG = PF_ALG; pub const AF_NFC = PF_NFC; pub const AF_VSOCK = PF_VSOCK; +pub const AF_KCM = PF_KCM; +pub const AF_QIPCRTR = PF_QIPCRTR; +pub const AF_SMC = PF_SMC; pub const AF_MAX = PF_MAX; +pub const SO_DEBUG = 1; +pub const SO_REUSEADDR = 2; +pub const SO_TYPE = 3; +pub const SO_ERROR = 4; +pub const SO_DONTROUTE = 5; +pub const SO_BROADCAST = 6; +pub const SO_SNDBUF = 7; +pub const SO_RCVBUF = 8; +pub const SO_KEEPALIVE = 9; +pub const SO_OOBINLINE = 10; +pub const SO_NO_CHECK = 11; +pub const SO_PRIORITY = 12; +pub const SO_LINGER = 13; +pub const SO_BSDCOMPAT = 14; +pub const SO_REUSEPORT = 15; +pub const SO_PASSCRED = 16; +pub const SO_PEERCRED = 17; +pub const SO_RCVLOWAT = 18; +pub const SO_SNDLOWAT = 19; +pub const SO_RCVTIMEO = 20; +pub const SO_SNDTIMEO = 21; +pub const SO_ACCEPTCONN = 30; +pub const SO_SNDBUFFORCE = 32; +pub const SO_RCVBUFFORCE = 33; +pub const SO_PROTOCOL = 38; +pub const SO_DOMAIN = 39; + +pub const SO_SECURITY_AUTHENTICATION = 22; +pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23; +pub const SO_SECURITY_ENCRYPTION_NETWORK = 24; + +pub const SO_BINDTODEVICE = 25; + +pub const SO_ATTACH_FILTER = 26; +pub const SO_DETACH_FILTER = 27; +pub const SO_GET_FILTER = SO_ATTACH_FILTER; + +pub const SO_PEERNAME = 28; +pub const SO_TIMESTAMP = 29; +pub const SCM_TIMESTAMP = SO_TIMESTAMP; + +pub const SO_PEERSEC = 31; +pub const SO_PASSSEC = 34; +pub const SO_TIMESTAMPNS = 35; +pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS; +pub const SO_MARK = 36; +pub const SO_TIMESTAMPING = 37; +pub const SCM_TIMESTAMPING = SO_TIMESTAMPING; +pub const SO_RXQ_OVFL = 40; +pub const SO_WIFI_STATUS = 41; +pub const SCM_WIFI_STATUS = SO_WIFI_STATUS; +pub const SO_PEEK_OFF = 42; +pub const SO_NOFCS = 43; +pub const SO_LOCK_FILTER = 44; +pub const SO_SELECT_ERR_QUEUE = 45; +pub const SO_BUSY_POLL = 46; +pub const SO_MAX_PACING_RATE = 47; +pub const SO_BPF_EXTENSIONS = 48; +pub const SO_INCOMING_CPU = 49; +pub const SO_ATTACH_BPF = 50; +pub const SO_DETACH_BPF = SO_DETACH_FILTER; +pub const SO_ATTACH_REUSEPORT_CBPF = 51; +pub const SO_ATTACH_REUSEPORT_EBPF = 52; +pub const SO_CNX_ADVICE = 53; +pub const SCM_TIMESTAMPING_OPT_STATS = 54; +pub const SO_MEMINFO = 55; +pub const SO_INCOMING_NAPI_ID = 56; +pub const SO_COOKIE = 57; +pub const SCM_TIMESTAMPING_PKTINFO = 58; +pub const SO_PEERGROUPS = 59; +pub const SO_ZEROCOPY = 60; + +pub const SOL_SOCKET = 1; + +pub const SOL_IP = 0; +pub const SOL_IPV6 = 41; +pub const SOL_ICMPV6 = 58; + +pub const SOL_RAW = 255; +pub const SOL_DECNET = 261; +pub const SOL_X25 = 262; +pub const SOL_PACKET = 263; +pub const SOL_ATM = 264; +pub const SOL_AAL = 265; +pub const SOL_IRDA = 266; +pub const SOL_NETBEUI = 267; +pub const SOL_LLC = 268; +pub const SOL_DCCP = 269; +pub const SOL_NETLINK = 270; +pub const SOL_TIPC = 271; +pub const SOL_RXRPC = 272; +pub const SOL_PPPOL2TP = 273; +pub const SOL_BLUETOOTH = 274; +pub const SOL_PNPIPE = 275; +pub const SOL_RDS = 276; +pub const SOL_IUCV = 277; +pub const SOL_CAIF = 278; +pub const SOL_ALG = 279; +pub const SOL_NFC = 280; +pub const SOL_KCM = 281; +pub const SOL_TLS = 282; + +pub const SOMAXCONN = 128; + +pub const MSG_OOB = 0x0001; +pub const MSG_PEEK = 0x0002; +pub const MSG_DONTROUTE = 0x0004; +pub const MSG_CTRUNC = 0x0008; +pub const MSG_PROXY = 0x0010; +pub const MSG_TRUNC = 0x0020; +pub const MSG_DONTWAIT = 0x0040; +pub const MSG_EOR = 0x0080; +pub const MSG_WAITALL = 0x0100; +pub const MSG_FIN = 0x0200; +pub const MSG_SYN = 0x0400; +pub const MSG_CONFIRM = 0x0800; +pub const MSG_RST = 0x1000; +pub const MSG_ERRQUEUE = 0x2000; +pub const MSG_NOSIGNAL = 0x4000; +pub const MSG_MORE = 0x8000; +pub const MSG_WAITFORONE = 0x10000; +pub const MSG_BATCH = 0x40000; +pub const MSG_ZEROCOPY = 0x4000000; +pub const MSG_FASTOPEN = 0x20000000; +pub const MSG_CMSG_CLOEXEC = 0x40000000; + pub const DT_UNKNOWN = 0; pub const DT_FIFO = 1; pub const DT_CHR = 2; @@ -343,6 +500,126 @@ pub const CLOCK_BOOTTIME_ALARM = 9; pub const CLOCK_SGI_CYCLE = 10; pub const CLOCK_TAI = 11; +pub const CSIGNAL = 0x000000ff; +pub const CLONE_VM = 0x00000100; +pub const CLONE_FS = 0x00000200; +pub const CLONE_FILES = 0x00000400; +pub const CLONE_SIGHAND = 0x00000800; +pub const CLONE_PTRACE = 0x00002000; +pub const CLONE_VFORK = 0x00004000; +pub const CLONE_PARENT = 0x00008000; +pub const CLONE_THREAD = 0x00010000; +pub const CLONE_NEWNS = 0x00020000; +pub const CLONE_SYSVSEM = 0x00040000; +pub const CLONE_SETTLS = 0x00080000; +pub const CLONE_PARENT_SETTID = 0x00100000; +pub const CLONE_CHILD_CLEARTID = 0x00200000; +pub const CLONE_DETACHED = 0x00400000; +pub const CLONE_UNTRACED = 0x00800000; +pub const CLONE_CHILD_SETTID = 0x01000000; +pub const CLONE_NEWCGROUP = 0x02000000; +pub const CLONE_NEWUTS = 0x04000000; +pub const CLONE_NEWIPC = 0x08000000; +pub const CLONE_NEWUSER = 0x10000000; +pub const CLONE_NEWPID = 0x20000000; +pub const CLONE_NEWNET = 0x40000000; +pub const CLONE_IO = 0x80000000; + +pub const MS_RDONLY = 1; +pub const MS_NOSUID = 2; +pub const MS_NODEV = 4; +pub const MS_NOEXEC = 8; +pub const MS_SYNCHRONOUS = 16; +pub const MS_REMOUNT = 32; +pub const MS_MANDLOCK = 64; +pub const MS_DIRSYNC = 128; +pub const MS_NOATIME = 1024; +pub const MS_NODIRATIME = 2048; +pub const MS_BIND = 4096; +pub const MS_MOVE = 8192; +pub const MS_REC = 16384; +pub const MS_SILENT = 32768; +pub const MS_POSIXACL = (1<<16); +pub const MS_UNBINDABLE = (1<<17); +pub const MS_PRIVATE = (1<<18); +pub const MS_SLAVE = (1<<19); +pub const MS_SHARED = (1<<20); +pub const MS_RELATIME = (1<<21); +pub const MS_KERNMOUNT = (1<<22); +pub const MS_I_VERSION = (1<<23); +pub const MS_STRICTATIME = (1<<24); +pub const MS_LAZYTIME = (1<<25); +pub const MS_NOREMOTELOCK = (1<<27); +pub const MS_NOSEC = (1<<28); +pub const MS_BORN = (1<<29); +pub const MS_ACTIVE = (1<<30); +pub const MS_NOUSER = (1<<31); + +pub const MS_RMT_MASK = (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME); + +pub const MS_MGC_VAL = 0xc0ed0000; +pub const MS_MGC_MSK = 0xffff0000; + +pub const MNT_FORCE = 1; +pub const MNT_DETACH = 2; +pub const MNT_EXPIRE = 4; +pub const UMOUNT_NOFOLLOW = 8; + + +pub const S_IFMT = 0o170000; + +pub const S_IFDIR = 0o040000; +pub const S_IFCHR = 0o020000; +pub const S_IFBLK = 0o060000; +pub const S_IFREG = 0o100000; +pub const S_IFIFO = 0o010000; +pub const S_IFLNK = 0o120000; +pub const S_IFSOCK = 0o140000; + +pub const S_ISUID = 0o4000; +pub const S_ISGID = 0o2000; +pub const S_ISVTX = 0o1000; +pub const S_IRUSR = 0o400; +pub const S_IWUSR = 0o200; +pub const S_IXUSR = 0o100; +pub const S_IRWXU = 0o700; +pub const S_IRGRP = 0o040; +pub const S_IWGRP = 0o020; +pub const S_IXGRP = 0o010; +pub const S_IRWXG = 0o070; +pub const S_IROTH = 0o004; +pub const S_IWOTH = 0o002; +pub const S_IXOTH = 0o001; +pub const S_IRWXO = 0o007; + +pub fn S_ISREG(m: u32) bool { + return m & S_IFMT == S_IFREG; +} + +pub fn S_ISDIR(m: u32) bool { + return m & S_IFMT == S_IFDIR; +} + +pub fn S_ISCHR(m: u32) bool { + return m & S_IFMT == S_IFCHR; +} + +pub fn S_ISBLK(m: u32) bool { + return m & S_IFMT == S_IFBLK; +} + +pub fn S_ISFIFO(m: u32) bool { + return m & S_IFMT == S_IFIFO; +} + +pub fn S_ISLNK(m: u32) bool { + return m & S_IFMT == S_IFLNK; +} + +pub fn S_ISSOCK(m: u32) bool { + return m & S_IFMT == S_IFSOCK; +} + pub const TFD_NONBLOCK = O_NONBLOCK; pub const TFD_CLOEXEC = O_CLOEXEC; @@ -380,6 +657,10 @@ pub fn chdir(path: &const u8) usize { return syscall1(SYS_chdir, @ptrToInt(path)); } +pub fn chroot(path: &const u8) usize { + return syscall1(SYS_chroot, @ptrToInt(path)); +} + pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize { return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); } @@ -388,6 +669,10 @@ pub fn fork() usize { return syscall0(SYS_fork); } +pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?×pec) usize { + return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout)); +} + pub fn getcwd(buf: &u8, size: usize) usize { return syscall2(SYS_getcwd, @ptrToInt(buf), size); } @@ -409,13 +694,25 @@ pub fn mkdir(path: &const u8, mode: u32) usize { return syscall2(SYS_mkdir, @ptrToInt(path), mode); } -pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize { +pub fn mount(special: &const u8, dir: &const u8, fstype: &const u8, flags: usize, data: usize) usize { + return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); +} + +pub fn umount(special: &const u8) usize { + return syscall2(SYS_umount2, @ptrToInt(special), 0); +} + +pub fn umount2(special: &const u8, flags: u32) usize { + return syscall2(SYS_umount2, @ptrToInt(special), flags); +} + +pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), @bitCast(usize, offset)); } -pub fn munmap(address: &u8, length: usize) usize { - return syscall2(SYS_munmap, @ptrToInt(address), length); +pub fn munmap(address: usize, length: usize) usize { + return syscall2(SYS_munmap, address, length); } pub fn read(fd: i32, buf: &u8, count: usize) usize { @@ -434,6 +731,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize { return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset); } +pub fn access(path: &const u8, mode: u32) usize { + return syscall2(SYS_access, @ptrToInt(path), mode); +} + pub fn pipe(fd: &[2]i32) usize { return pipe2(fd, 0); } @@ -466,6 +767,16 @@ pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize { return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode); } +/// See also `clone` (from the arch-specific include) +pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: &i32, child_tid: &i32, newtls: usize) usize { + return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); +} + +/// See also `clone` (from the arch-specific include) +pub fn clone2(flags: usize, child_stack_ptr: usize) usize { + return syscall2(SYS_clone, flags, child_stack_ptr); +} + pub fn close(fd: i32) usize { return syscall1(SYS_close, usize(fd)); } @@ -495,6 +806,45 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize { return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); } +pub fn clock_gettime(clk_id: i32, tp: ×pec) usize { + if (VDSO_CGT_SYM.len != 0) { + const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered); + if (@ptrToInt(f) != 0) { + const rc = f(clk_id, tp); + switch (rc) { + 0, @bitCast(usize, isize(-EINVAL)) => return rc, + else => {}, + } + } + } + return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} +var vdso_clock_gettime = init_vdso_clock_gettime; +extern fn init_vdso_clock_gettime(clk: i32, ts: ×pec) usize { + const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM); + var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr); + _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, + builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); + if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS)); + return f(clk, ts); +} + +pub fn clock_getres(clk_id: i32, tp: ×pec) usize { + return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} + +pub fn clock_settime(clk_id: i32, tp: &const timespec) usize { + return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} + +pub fn gettimeofday(tv: &timeval, tz: &timezone) usize { + return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); +} + +pub fn settimeofday(tv: &const timeval, tz: &const timezone) usize { + return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); +} + pub fn nanosleep(req: &const timespec, rem: ?×pec) usize { return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); } @@ -515,6 +865,58 @@ pub fn setregid(rgid: u32, egid: u32) usize { return syscall2(SYS_setregid, rgid, egid); } +pub fn getuid() u32 { + return u32(syscall0(SYS_getuid)); +} + +pub fn getgid() u32 { + return u32(syscall0(SYS_getgid)); +} + +pub fn geteuid() u32 { + return u32(syscall0(SYS_geteuid)); +} + +pub fn getegid() u32 { + return u32(syscall0(SYS_getegid)); +} + +pub fn seteuid(euid: u32) usize { + return syscall1(SYS_seteuid, euid); +} + +pub fn setegid(egid: u32) usize { + return syscall1(SYS_setegid, egid); +} + +pub fn getresuid(ruid: &u32, euid: &u32, suid: &u32) usize { + return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid)); +} + +pub fn getresgid(rgid: &u32, egid: &u32, sgid: &u32) usize { + return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid)); +} + +pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize { + return syscall3(SYS_setresuid, ruid, euid, suid); +} + +pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize { + return syscall3(SYS_setresgid, rgid, egid, sgid); +} + +pub fn getgroups(size: usize, list: &u32) usize { + return syscall2(SYS_getgroups, size, @ptrToInt(list)); +} + +pub fn setgroups(size: usize, list: &const u32) usize { + return syscall2(SYS_setgroups, size, @ptrToInt(list)); +} + +pub fn getpid() i32 { + return @bitCast(i32, u32(syscall0(SYS_getpid))); +} + pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize { return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8); } @@ -599,30 +1001,27 @@ pub fn sigismember(set: &const sigset_t, sig: u6) bool { return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0; } - +pub const in_port_t = u16; pub const sa_family_t = u16; pub const socklen_t = u32; -pub const in_addr = u32; -pub const in6_addr = [16]u8; -pub const sockaddr = extern struct { - family: sa_family_t, - port: u16, - data: [12]u8, +pub const sockaddr = extern union { + in: sockaddr_in, + in6: sockaddr_in6, }; pub const sockaddr_in = extern struct { family: sa_family_t, - port: u16, - addr: in_addr, + port: in_port_t, + addr: u32, zero: [8]u8, }; pub const sockaddr_in6 = extern struct { family: sa_family_t, - port: u16, + port: in_port_t, flowinfo: u32, - addr: in6_addr, + addr: [16]u8, scope_id: u32, }; @@ -639,16 +1038,16 @@ pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) us return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len)); } -pub fn socket(domain: i32, socket_type: i32, protocol: i32) usize { - return syscall3(SYS_socket, usize(domain), usize(socket_type), usize(protocol)); +pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { + return syscall3(SYS_socket, domain, socket_type, protocol); } -pub fn setsockopt(fd: i32, level: i32, optname: i32, optval: &const u8, optlen: socklen_t) usize { - return syscall5(SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen)); +pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: &const u8, optlen: socklen_t) usize { + return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen)); } -pub fn getsockopt(fd: i32, level: i32, optname: i32, noalias optval: &u8, noalias optlen: &socklen_t) usize { - return syscall5(SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen)); +pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: &u8, noalias optlen: &socklen_t) usize { + return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize { @@ -677,8 +1076,8 @@ pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize { return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len)); } -pub fn listen(fd: i32, backlog: i32) usize { - return syscall2(SYS_listen, usize(fd), usize(backlog)); +pub fn listen(fd: i32, backlog: u32) usize { + return syscall2(SYS_listen, usize(fd), backlog); } pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) usize { @@ -697,46 +1096,83 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags: return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags); } -// error NameTooLong; -// error SystemResources; -// error Io; -// -// pub fn if_nametoindex(name: []u8) !u32 { -// var ifr: ifreq = undefined; -// -// if (name.len >= ifr.ifr_name.len) { -// return error.NameTooLong; -// } -// -// const socket_ret = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); -// const socket_err = getErrno(socket_ret); -// if (socket_err > 0) { -// return error.SystemResources; -// } -// const socket_fd = i32(socket_ret); -// @memcpy(&ifr.ifr_name[0], &name[0], name.len); -// ifr.ifr_name[name.len] = 0; -// const ioctl_ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr); -// close(socket_fd); -// const ioctl_err = getErrno(ioctl_ret); -// if (ioctl_err > 0) { -// return error.Io; -// } -// return ifr.ifr_ifindex; -// } - pub fn fstat(fd: i32, stat_buf: &Stat) usize { return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf)); } -pub const epoll_data = extern union { +pub fn stat(pathname: &const u8, statbuf: &Stat) usize { + return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf)); +} + +pub fn lstat(pathname: &const u8, statbuf: &Stat) usize { + return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf)); +} + +pub fn listxattr(path: &const u8, list: &u8, size: usize) usize { + return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); +} + +pub fn llistxattr(path: &const u8, list: &u8, size: usize) usize { + return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); +} + +pub fn flistxattr(fd: usize, list: &u8, size: usize) usize { + return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); +} + +pub fn getxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize { + return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); +} + +pub fn lgetxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize { + return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); +} + +pub fn fgetxattr(fd: usize, name: &const u8, value: &void, size: usize) usize { + return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); +} + +pub fn setxattr(path: &const u8, name: &const u8, value: &const void, + size: usize, flags: usize) usize { + + return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), + size, flags); +} + +pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void, + size: usize, flags: usize) usize { + + return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), + size, flags); +} + +pub fn fsetxattr(fd: usize, name: &const u8, value: &const void, + size: usize, flags: usize) usize { + + return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), + size, flags); +} + +pub fn removexattr(path: &const u8, name: &const u8) usize { + return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); +} + +pub fn lremovexattr(path: &const u8, name: &const u8) usize { + return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); +} + +pub fn fremovexattr(fd: usize, name: &const u8) usize { + return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); +} + +pub const epoll_data = packed union { ptr: usize, fd: i32, @"u32": u32, @"u64": u64, }; -pub const epoll_event = extern struct { +pub const epoll_event = packed struct { events: u32, data: epoll_data, }; @@ -749,7 +1185,7 @@ pub fn epoll_create1(flags: usize) usize { return syscall1(SYS_epoll_create1, flags); } -pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize { +pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: &epoll_event) usize { return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev)); } @@ -774,9 +1210,126 @@ pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_va return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value)); } -test "import linux test" { - // TODO lazy analysis should prevent this test from being compiled on windows, but - // it is still compiled on windows +pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; +pub const _LINUX_CAPABILITY_U32S_1 = 1; + +pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026; +pub const _LINUX_CAPABILITY_U32S_2 = 2; + +pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522; +pub const _LINUX_CAPABILITY_U32S_3 = 2; + +pub const VFS_CAP_REVISION_MASK = 0xFF000000; +pub const VFS_CAP_REVISION_SHIFT = 24; +pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; +pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001; + +pub const VFS_CAP_REVISION_1 = 0x01000000; +pub const VFS_CAP_U32_1 = 1; +pub const XATTR_CAPS_SZ_1 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_1); + +pub const VFS_CAP_REVISION_2 = 0x02000000; +pub const VFS_CAP_U32_2 = 2; +pub const XATTR_CAPS_SZ_2 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_2); + +pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2; +pub const VFS_CAP_U32 = VFS_CAP_U32_2; +pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; + +pub const vfs_cap_data = extern struct { + //all of these are mandated as little endian + //when on disk. + const Data = struct { + permitted: u32, + inheritable: u32, + }; + + magic_etc: u32, + data: [VFS_CAP_U32]Data, +}; + + +pub const CAP_CHOWN = 0; +pub const CAP_DAC_OVERRIDE = 1; +pub const CAP_DAC_READ_SEARCH = 2; +pub const CAP_FOWNER = 3; +pub const CAP_FSETID = 4; +pub const CAP_KILL = 5; +pub const CAP_SETGID = 6; +pub const CAP_SETUID = 7; +pub const CAP_SETPCAP = 8; +pub const CAP_LINUX_IMMUTABLE = 9; +pub const CAP_NET_BIND_SERVICE = 10; +pub const CAP_NET_BROADCAST = 11; +pub const CAP_NET_ADMIN = 12; +pub const CAP_NET_RAW = 13; +pub const CAP_IPC_LOCK = 14; +pub const CAP_IPC_OWNER = 15; +pub const CAP_SYS_MODULE = 16; +pub const CAP_SYS_RAWIO = 17; +pub const CAP_SYS_CHROOT = 18; +pub const CAP_SYS_PTRACE = 19; +pub const CAP_SYS_PACCT = 20; +pub const CAP_SYS_ADMIN = 21; +pub const CAP_SYS_BOOT = 22; +pub const CAP_SYS_NICE = 23; +pub const CAP_SYS_RESOURCE = 24; +pub const CAP_SYS_TIME = 25; +pub const CAP_SYS_TTY_CONFIG = 26; +pub const CAP_MKNOD = 27; +pub const CAP_LEASE = 28; +pub const CAP_AUDIT_WRITE = 29; +pub const CAP_AUDIT_CONTROL = 30; +pub const CAP_SETFCAP = 31; +pub const CAP_MAC_OVERRIDE = 32; +pub const CAP_MAC_ADMIN = 33; +pub const CAP_SYSLOG = 34; +pub const CAP_WAKE_ALARM = 35; +pub const CAP_BLOCK_SUSPEND = 36; +pub const CAP_AUDIT_READ = 37; +pub const CAP_LAST_CAP = CAP_AUDIT_READ; + +pub fn cap_valid(u8: x) bool { + return x >= 0 and x <= CAP_LAST_CAP; +} + +pub fn CAP_TO_MASK(cap: u8) u32 { + return u32(1) << u5(cap & 31); +} + +pub fn CAP_TO_INDEX(cap: u8) u8 { + return cap >> 5; +} + +pub const cap_t = extern struct { + hdrp: &cap_user_header_t, + datap: &cap_user_data_t, +}; + +pub const cap_user_header_t = extern struct { + version: u32, + pid: usize, +}; + +pub const cap_user_data_t = extern struct { + effective: u32, + permitted: u32, + inheritable: u32, +}; + +pub fn unshare(flags: usize) usize { + return syscall1(SYS_unshare, usize(flags)); +} + +pub fn capget(hdrp: &cap_user_header_t, datap: &cap_user_data_t) usize { + return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap)); +} + +pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize { + return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); +} + +test "import" { if (builtin.os == builtin.Os.linux) { _ = @import("test.zig"); } diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index e427fd5d59..18a6e5f19f 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -1,4 +1,5 @@ const std = @import("../../index.zig"); +const builtin = @import("builtin"); const linux = std.os.linux; const assert = std.debug.assert; diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig new file mode 100644 index 0000000000..f4fb513af9 --- /dev/null +++ b/std/os/linux/vdso.zig @@ -0,0 +1,89 @@ +const std = @import("../../index.zig"); +const elf = std.elf; +const linux = std.os.linux; +const cstr = std.cstr; +const mem = std.mem; + +pub fn lookup(vername: []const u8, name: []const u8) usize { + const vdso_addr = std.os.linux_aux_raw[std.elf.AT_SYSINFO_EHDR]; + if (vdso_addr == 0) return 0; + + const eh = @intToPtr(&elf.Ehdr, vdso_addr); + var ph_addr: usize = vdso_addr + eh.e_phoff; + const ph = @intToPtr(&elf.Phdr, ph_addr); + + var maybe_dynv: ?&usize = null; + var base: usize = @maxValue(usize); + { + var i: usize = 0; + while (i < eh.e_phnum) : ({i += 1; ph_addr += eh.e_phentsize;}) { + const this_ph = @intToPtr(&elf.Phdr, ph_addr); + switch (this_ph.p_type) { + elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr, + elf.PT_DYNAMIC => maybe_dynv = @intToPtr(&usize, vdso_addr + this_ph.p_offset), + else => {}, + } + } + } + const dynv = maybe_dynv ?? return 0; + if (base == @maxValue(usize)) return 0; + + var maybe_strings: ?&u8 = null; + var maybe_syms: ?&elf.Sym = null; + var maybe_hashtab: ?&linux.Elf_Symndx = null; + var maybe_versym: ?&u16 = null; + var maybe_verdef: ?&elf.Verdef = null; + + { + var i: usize = 0; + while (dynv[i] != 0) : (i += 2) { + const p = base + dynv[i + 1]; + switch (dynv[i]) { + elf.DT_STRTAB => maybe_strings = @intToPtr(&u8, p), + elf.DT_SYMTAB => maybe_syms = @intToPtr(&elf.Sym, p), + elf.DT_HASH => maybe_hashtab = @intToPtr(&linux.Elf_Symndx, p), + elf.DT_VERSYM => maybe_versym = @intToPtr(&u16, p), + elf.DT_VERDEF => maybe_verdef = @intToPtr(&elf.Verdef, p), + else => {}, + } + } + } + + const strings = maybe_strings ?? return 0; + const syms = maybe_syms ?? return 0; + const hashtab = maybe_hashtab ?? return 0; + if (maybe_verdef == null) maybe_versym = null; + + + const OK_TYPES = (1<<elf.STT_NOTYPE | 1<<elf.STT_OBJECT | 1<<elf.STT_FUNC | 1<<elf.STT_COMMON); + const OK_BINDS = (1<<elf.STB_GLOBAL | 1<<elf.STB_WEAK | 1<<elf.STB_GNU_UNIQUE); + + var i: usize = 0; + while (i < hashtab[1]) : (i += 1) { + if (0==(u32(1)<<u5(syms[i].st_info&0xf) & OK_TYPES)) continue; + if (0==(u32(1)<<u5(syms[i].st_info>>4) & OK_BINDS)) continue; + if (0==syms[i].st_shndx) continue; + if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue; + if (maybe_versym) |versym| { + if (!checkver(??maybe_verdef, versym[i], vername, strings)) + continue; + } + return base + syms[i].st_value; + } + + return 0; +} + +fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: &u8) bool { + var def = def_arg; + const vsym = @bitCast(u32, vsym_arg) & 0x7fff; + while (true) { + if (0==(def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym) + break; + if (def.vd_next == 0) + return false; + def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next); + } + const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def ) + def.vd_aux); + return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name])); +} diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index cfb2231df9..544b2365ce 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -371,6 +371,13 @@ pub const F_GETOWN_EX = 16; pub const F_GETOWNER_UIDS = 17; + +pub const VDSO_USEFUL = true; +pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; +pub const VDSO_CGT_VER = "LINUX_2.6"; +pub const VDSO_GETCPU_SYM = "__vdso_getcpu"; +pub const VDSO_GETCPU_VER = "LINUX_2.6"; + pub fn syscall0(number: usize) usize { return asm volatile ("syscall" : [ret] "={rax}" (-> usize) @@ -443,6 +450,9 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz : "rcx", "r11"); } +/// This matches the libc clone function. +pub extern fn clone(func: extern fn(arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize; + pub nakedcc fn restore_rt() void { return asm volatile ("syscall" : @@ -489,6 +499,16 @@ pub const timespec = extern struct { tv_nsec: isize, }; +pub const timeval = extern struct { + tv_sec: isize, + tv_usec: isize, +}; + +pub const timezone = extern struct { + tz_minuteswest: i32, + tz_dsttime: i32, +}; + pub const dirent = extern struct { d_ino: usize, d_off: usize, @@ -496,3 +516,4 @@ pub const dirent = extern struct { d_name: u8, // field address is the address of first byte of name }; +pub const Elf_Symndx = u32; diff --git a/std/os/test.zig b/std/os/test.zig index 9c718d5b6b..56d6e8b309 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -6,6 +6,8 @@ const io = std.io; const a = std.debug.global_allocator; const builtin = @import("builtin"); +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; test "makePath, put some files in it, deleteTree" { if (builtin.os == builtin.Os.windows) { @@ -23,3 +25,45 @@ test "makePath, put some files in it, deleteTree" { assert(err == error.PathNotFound); } } + +test "access file" { + if (builtin.os == builtin.Os.windows) { + return; + } + + try os.makePath(a, "os_test_tmp"); + if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| { + unreachable; + } else |err| { + assert(err == error.NotFound); + } + + try io.writeFile(a, "os_test_tmp/file.txt", ""); + assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true); + try os.deleteTree(a, "os_test_tmp"); +} + +test "spawn threads" { + var shared_ctx: i32 = 1; + + const thread1 = try std.os.spawnThread({}, start1); + const thread2 = try std.os.spawnThread(&shared_ctx, start2); + const thread3 = try std.os.spawnThread(&shared_ctx, start2); + const thread4 = try std.os.spawnThread(&shared_ctx, start2); + + thread1.wait(); + thread2.wait(); + thread3.wait(); + thread4.wait(); + + assert(shared_ctx == 4); +} + +fn start1(ctx: void) u8 { + return 0; +} + +fn start2(ctx: &i32) u8 { + _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + return 0; +} diff --git a/std/os/time.zig b/std/os/time.zig new file mode 100644 index 0000000000..4fd2c4e924 --- /dev/null +++ b/std/os/time.zig @@ -0,0 +1,288 @@ +const std = @import("../index.zig"); +const builtin = @import("builtin"); +const Os = builtin.Os; +const debug = std.debug; + +const windows = std.os.windows; +const linux = std.os.linux; +const darwin = std.os.darwin; +const posix = std.os.posix; + +pub const epoch = @import("epoch.zig"); + +/// Sleep for the specified duration +pub fn sleep(seconds: usize, nanoseconds: usize) void { + switch (builtin.os) { + Os.linux, Os.macosx, Os.ios => { + posixSleep(u63(seconds), u63(nanoseconds)); + }, + Os.windows => { + const ns_per_ms = ns_per_s / ms_per_s; + const milliseconds = seconds * ms_per_s + nanoseconds / ns_per_ms; + windows.Sleep(windows.DWORD(milliseconds)); + }, + else => @compileError("Unsupported OS"), + } +} + +const u63 = @IntType(false, 63); +pub fn posixSleep(seconds: u63, nanoseconds: u63) void { + var req = posix.timespec { + .tv_sec = seconds, + .tv_nsec = nanoseconds, + }; + var rem: posix.timespec = undefined; + while (true) { + const ret_val = posix.nanosleep(&req, &rem); + const err = posix.getErrno(ret_val); + if (err == 0) return; + switch (err) { + posix.EFAULT => unreachable, + posix.EINVAL => { + // Sometimes Darwin returns EINVAL for no reason. + // We treat it as a spurious wakeup. + return; + }, + posix.EINTR => { + req = rem; + continue; + }, + else => return, + } + } +} + +/// Get the posix timestamp, UTC, in seconds +pub fn timestamp() u64 { + return @divFloor(milliTimestamp(), ms_per_s); +} + +/// Get the posix timestamp, UTC, in milliseconds +pub const milliTimestamp = switch (builtin.os) { + Os.windows => milliTimestampWindows, + Os.linux => milliTimestampPosix, + Os.macosx, Os.ios => milliTimestampDarwin, + else => @compileError("Unsupported OS"), +}; + +fn milliTimestampWindows() u64 { + //FileTime has a granularity of 100 nanoseconds + // and uses the NTFS/Windows epoch + var ft: i64 = undefined; + windows.GetSystemTimeAsFileTime(&ft); + const hns_per_ms = (ns_per_s / 100) / ms_per_s; + const epoch_adj = epoch.windows * ms_per_s; + return u64(@divFloor(ft, hns_per_ms) + epoch_adj); +} + +fn milliTimestampDarwin() u64 { + //Sources suggest MacOS 10.12 has support for + // posix clock_gettime. + var tv: darwin.timeval = undefined; + var err = darwin.gettimeofday(&tv, null); + debug.assert(err == 0); + const sec_ms = u64(tv.tv_sec) * ms_per_s; + const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s); + return u64(sec_ms) + u64(usec_ms); +} + +fn milliTimestampPosix() u64 { + //From what I can tell there's no reason clock_gettime + // should ever fail for us with CLOCK_REALTIME, + // seccomp aside. + var ts: posix.timespec = undefined; + const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts); + debug.assert(err == 0); + const sec_ms = u64(ts.tv_sec) * ms_per_s; + const nsec_ms = @divFloor(u64(ts.tv_nsec), ns_per_s / ms_per_s); + return sec_ms + nsec_ms; +} + +/// Divisions of a second +pub const ns_per_s = 1000000000; +pub const us_per_s = 1000000; +pub const ms_per_s = 1000; +pub const cs_per_s = 100; + +/// Common time divisions +pub const s_per_min = 60; +pub const s_per_hour = s_per_min * 60; +pub const s_per_day = s_per_hour * 24; +pub const s_per_week = s_per_day * 7; + + +/// A monotonic high-performance timer. +/// Timer.start() must be called to initialize the struct, which captures +/// the counter frequency on windows and darwin, records the resolution, +/// and gives the user an oportunity to check for the existnece of +/// monotonic clocks without forcing them to check for error on each read. +/// .resolution is in nanoseconds on all platforms but .start_time's meaning +/// depends on the OS. On Windows and Darwin it is a hardware counter +/// value that requires calculation to convert to a meaninful unit. +pub const Timer = struct { + + //if we used resolution's value when performing the + // performance counter calc on windows/darwin, it would + // be less precise + frequency: switch (builtin.os) { + Os.windows => u64, + Os.macosx, Os.ios => darwin.mach_timebase_info_data, + else => void, + }, + resolution: u64, + start_time: u64, + + + //At some point we may change our minds on RAW, but for now we're + // sticking with posix standard MONOTONIC. For more information, see: + // https://github.com/zig-lang/zig/pull/933 + // + //const monotonic_clock_id = switch(builtin.os) { + // Os.linux => linux.CLOCK_MONOTONIC_RAW, + // else => posix.CLOCK_MONOTONIC, + //}; + const monotonic_clock_id = posix.CLOCK_MONOTONIC; + + + /// Initialize the timer structure. + //This gives us an oportunity to grab the counter frequency in windows. + //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000. + //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not + // supported, or if the timespec pointer is out of bounds, which should be + // impossible here barring cosmic rays or other such occurances of + // incredibly bad luck. + //On Darwin: This cannot fail, as far as I am able to tell. + const TimerError = error{TimerUnsupported, Unexpected}; + pub fn start() TimerError!Timer { + var self: Timer = undefined; + + switch (builtin.os) { + Os.windows => { + var freq: i64 = undefined; + var err = windows.QueryPerformanceFrequency(&freq); + if (err == windows.FALSE) return error.TimerUnsupported; + self.frequency = u64(freq); + self.resolution = @divFloor(ns_per_s, self.frequency); + + var start_time: i64 = undefined; + err = windows.QueryPerformanceCounter(&start_time); + debug.assert(err != windows.FALSE); + self.start_time = u64(start_time); + }, + Os.linux => { + //On Linux, seccomp can do arbitrary things to our ability to call + // syscalls, including return any errno value it wants and + // inconsistently throwing errors. Since we can't account for + // abuses of seccomp in a reasonable way, we'll assume that if + // seccomp is going to block us it will at least do so consistently + var ts: posix.timespec = undefined; + var result = posix.clock_getres(monotonic_clock_id, &ts); + var errno = posix.getErrno(result); + switch (errno) { + 0 => {}, + posix.EINVAL => return error.TimerUnsupported, + else => return std.os.unexpectedErrorPosix(errno), + } + self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + + result = posix.clock_gettime(monotonic_clock_id, &ts); + errno = posix.getErrno(result); + if (errno != 0) return std.os.unexpectedErrorPosix(errno); + self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + }, + Os.macosx, Os.ios => { + darwin.mach_timebase_info(&self.frequency); + self.resolution = @divFloor(self.frequency.numer, self.frequency.denom); + self.start_time = darwin.mach_absolute_time(); + }, + else => @compileError("Unsupported OS"), + } + return self; + } + + /// Reads the timer value since start or the last reset in nanoseconds + pub fn read(self: &Timer) u64 { + var clock = clockNative() - self.start_time; + return switch (builtin.os) { + Os.windows => @divFloor(clock * ns_per_s, self.frequency), + Os.linux => clock, + Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom), + else => @compileError("Unsupported OS"), + }; + } + + /// Resets the timer value to 0/now. + pub fn reset(self: &Timer) void + { + self.start_time = clockNative(); + } + + /// Returns the current value of the timer in nanoseconds, then resets it + pub fn lap(self: &Timer) u64 { + var now = clockNative(); + var lap_time = self.read(); + self.start_time = now; + return lap_time; + } + + + const clockNative = switch (builtin.os) { + Os.windows => clockWindows, + Os.linux => clockLinux, + Os.macosx, Os.ios => clockDarwin, + else => @compileError("Unsupported OS"), + }; + + fn clockWindows() u64 { + var result: i64 = undefined; + var err = windows.QueryPerformanceCounter(&result); + debug.assert(err != windows.FALSE); + return u64(result); + } + + fn clockDarwin() u64 { + return darwin.mach_absolute_time(); + } + + fn clockLinux() u64 { + var ts: posix.timespec = undefined; + var result = posix.clock_gettime(monotonic_clock_id, &ts); + debug.assert(posix.getErrno(result) == 0); + return u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + } +}; + + + + + +test "os.time.sleep" { + sleep(0, 1); +} + +test "os.time.timestamp" { + const ns_per_ms = (ns_per_s / ms_per_s); + const margin = 50; + + const time_0 = milliTimestamp(); + sleep(0, ns_per_ms); + const time_1 = milliTimestamp(); + const interval = time_1 - time_0; + debug.assert(interval > 0 and interval < margin); +} + +test "os.time.Timer" { + const ns_per_ms = (ns_per_s / ms_per_s); + const margin = ns_per_ms * 50; + + var timer = try Timer.start(); + sleep(0, 10 * ns_per_ms); + const time_0 = timer.read(); + debug.assert(time_0 > 0 and time_0 < margin); + + const time_1 = timer.lap(); + debug.assert(time_1 >= time_0); + + timer.reset(); + debug.assert(timer.read() < time_1); +} diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 2709cf2a78..e13ed0f131 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR, dwFlags: DWORD) BOOLEAN; + +pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; + pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; @@ -61,6 +64,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; +pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?&FILETIME) void; + pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void; @@ -77,6 +82,12 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR, dwFlags: DWORD) BOOL; + +pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL; + +pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL; + +pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void, in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD, @@ -137,6 +148,7 @@ pub const UNICODE = false; pub const WCHAR = u16; pub const WORD = u16; pub const LARGE_INTEGER = i64; +pub const FILETIME = i64; pub const TRUE = 1; pub const FALSE = 0; @@ -308,3 +320,10 @@ pub const FILE_END = 2; pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; pub const HEAP_NO_SERIALIZE = 0x00000001; + +pub const PTHREAD_START_ROUTINE = extern fn(LPVOID) DWORD; +pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; + +test "import" { + _ = @import("util.zig"); +} |
