Files
bun.sh/src/c.zig
Jarred Sumner c9fe57fa63 wip use wrapper for managing process (#8456)
* WIP sync close (shows ref count bug in stream)

* fix closing on PipeWriter and PipeReader

* remove old todos

* join

* Some shell changes

at least it compiles

* fix some compile errors

* fix ref/unref server on windows

* actually use the ref count in this places

* make windows compile again

* more tests passing

* Make shell compile again

* Slowly remove some `@panic("TODO SHELL")`

* Eliminate `@panic("TODO SHELL")` for BufferedWriter

* Holy cleansing of `@panic("TODO SHELL")`

at least it compiles now

* Okay now the shell compiles, but segfaults

* Fix compiler errors

* more stable stream and now Content-Range pass

* make windows compile again

* revert stuff until the fix is actually ready

* revert onDone thing

* Fix buffered writer for shell

* Fix buffered writer + shell/subproc.zig and windows build

* Fix for #8982 got lost in the merge

* Actually buffer subproc output

* Fix some stuff shell

* oops

* fix context deinit

* fix renderMissing

* shell: Fix array buffer

* more stable streams (#9053)

fix stream ref counting

* wip

* Remove `@panic("TODO")` on shell event loop tasks and Redirect  open flags got lost in merge

* Support redirects

* fixes

cc @cirospaciari

* Update ReadableStreamInternals.ts

* Fix spurious error

* Update stream.js

* leak

* Fix UAF

cc @cirospaciari

* Fix memory leaks

* HOLY FUCK big refactor

* misc cleanup

* shell: Fix a bunch of tests

* clean up

* gitignore: fix ending newline

* get windows compiling again

* tidy

* hide linker warn with icu

* closeIfPossible

* Better leak test

* Fix forgetting to decrement reference count

* Update stdio.zig

* Fix shell windows build

* Stupid unreachable

* Woops

* basic echo hi works on windows

* Fix flaky test on Windows

* Fix windows regression in Bun.main (#9156)

* Fix windows regression in Bun.main

* Handle invalid handles

* Fix flaky test

* Better launch config

* Fixup

* Make this test less flaky on Windows

* Fixup

* Cygwin

* Support signal codes in subprocess.kill(), resolve file path

* Treat null as ignore

* Ignore carriage returns

* Fixup

* shell: Fix IOWriter bug

* shell: Use custom `open()`/`openat()`

* windows shell subproc works

* zack commit

* I think I understand WindowsStreamingWriter

* fix thing

* why were we doing this in tests

* shell: Fix rm

* shell: Add rm -rf node_modules/ test

* shell: use `.runAsTest()` in some places to make it easier to determine which test failed

* [autofix.ci] apply automated fixes

* woopsie

* Various changes

* Fix

* shell: abstract output task logic

* shell: mkdir builtin

* fixup

* stuff

* shell: Make writing length of 0 in IOWriter immediately resolve

* shell: Implement `touch`

* shell: basic `cat` working

* Make it compile on windows

* shell: Fix IOReader bug

* [autofix.ci] apply automated fixes

* fix windows kill on subprocess/process

* fix dns tests to match behavior on windows (same as nodejs)

* fix windows ci

* again

* move `close_handle` to flags in `PipeWriter` and fix shell hanging

* Fix `ls` not giving non-zero exit code on error

* Handle edgecase in is_atty

* Fix writer.flush() when there's no data

* Fix some tests

* Disable uv_unref on uv_process_t on Windows, for now.

* fix writer.end

* fix stdout.write

* fix child-process on win32

* Make this test less flaky on Windows

* Add assertion

* Make these the same

* Make it pass on windows

* Don't commit

* Log the test name

* Make this test less flaky on windows

* Make this test less flaky on windows

* Print which test is taking awhile in the runner

* fixups

* Fixups

* Add some assertions

* Bring back test concurrency

* shell: bring back redirect stdin

* make it compile again cc @zackradisic

* initialize env map with capacity

* some fixes

* cleanup

* oops

* fix leak, fix done

* fix unconsumedPromises on events

* always run expect

* Update child_process.test.ts

* fix reading special files

* Fix a test

* Deflake this test

* Make these comparisons easier

* Won't really fix it but slightly cleaner

* Update serve.test.ts

* Make the checks for if the body is already used more resilient

* Move this to the harness

* Make this test not hang in development

* Fix this test

* Make the logs better

* zero init some things

* Make this test better

* Fix readSocket

* Parallelize this test

* Handle EPipe and avoid big data

* This was a mistake

* Fix a bunch of things

* Fix memory leak

* Avoid sigpipe + optimize + delete dead code

* Make this take less time

* Make it bigger

* Remove some redundant code

* Update process.zig

* Merge and hopefully don't breka things along teh way

* Silence build warning

* Uncomment on posix

* Skip test on windows

* windows

* Cleanup test

* Update

* Deflake

* always

* less flaky test

* [autofix.ci] apply automated fixes

* logs

* fix uaf on shell IOReader

* stuff to make it work with mini event loop

* fix 2 double free scenarios, support redirections on windows

* shell: Make `1>&2` and `2>&1` work with libuv

* yoops

* Partial fix

* Partial fix

* fix build

* fix build

* ok

* Make a couple shell tests pass

* More logging

* fix

* fix

* Fix build issue

* more tests pass

* Deflake

* Deflake

* Use Output.panic instead of garbled text

* Formatting

* Introduce `bun.sys.File`, use it for `Output.Source.StreamType`, fix nested Output.scoped() calls, use Win32 `ReadFile` API for reading when it's not a libuv file descriptor.

This lets us avoid the subtle usages of `unreachable` in std.os when writing to stdout/stderr.

Previously, we were initializing the libuv loop immediately at launch due to checking for the existence of a bun build --compile'd executable. When the file descriptor is not from libuv, it's just overhead to use libuv

cc @paperdave, please tell me if Iany of that is incorrect or if you think this is a bad idea.

* Fix closing undefined memory file descriptors in spawn

cc @zackradisic

* pause instead of close

* Fix poorly-written test

* We don't need big numbers for this test

* sad workaround

* fixup

* Clearer error handling for this test

* Fix incorrect test

@electroid when ReadableStream isn't closed, hanging is the correct behavior when consuming buffered data. We cannot know if the buffered data is finished if the stream never closes.

* Fix build

* Remove known failing on windows

* Deflake

* Mark no longer failing

* show all the failing tests

* Sort the list of tests

* fix argument handling

* dont show "posix_spawn" as an error code on windows

* make bun-upgrade.test.ts pass on windows

* fix bunx and bun create again sorry

* a

* fix invalidexe because we should not be running javascript files as if they were exes

* Concurrency in test runner + better logging

* Revert "fix invalidexe because we should not be running javascript files as if they were exes"

This reverts commit da47cf8247.

* WIP: Unix fixes (#9322)

* wip

* [autofix.ci] apply automated fixes

* wip 2

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>

* Update runner.node.mjs

* Update runner.node.mjs

* Document some environment variables

* shell: Make `Response` work with builtins

* Make it compile

* make pwd test pass

* [autofix.ci] apply automated fixes

* Fix printing garbage for source code previews

* Update javascript.zig

* Fix posix test failures

* Fix signal dispatch

cc @paperdave. Signals can be run from any thread. This causes an assertion failure when the receiving thread happens to not be the main thread. Easiest to reproduce on linux when you spawn 100 short-lived processes at once.

* windows

---------

Co-authored-by: cirospaciari <ciro.spaciari@gmail.com>
Co-authored-by: Zack Radisic <56137411+zackradisic@users.noreply.github.com>
Co-authored-by: Zack Radisic <zackradisic@Zacks-MBP-2.attlocal.net>
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: Meghan Denny <meghan@bun.sh>
Co-authored-by: Zack Radisic <zack@theradisic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: dave caruso <me@paperdave.net>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2024-03-11 08:24:30 -07:00

467 lines
17 KiB
Zig

const std = @import("std");
const bun = @import("root").bun;
const Environment = @import("./env.zig");
const PlatformSpecific = switch (Environment.os) {
.mac => @import("./darwin_c.zig"),
.linux => @import("./linux_c.zig"),
.windows => @import("./windows_c.zig"),
else => struct {},
};
pub usingnamespace PlatformSpecific;
const C = std.c;
const builtin = @import("builtin");
const os = std.os;
const mem = std.mem;
const Stat = std.fs.File.Stat;
const Kind = std.fs.File.Kind;
const StatError = std.fs.File.StatError;
const errno = os.errno;
const mode_t = bun.Mode;
// TODO: this is wrong on Windows
const libc_stat = bun.Stat;
const zeroes = mem.zeroes;
pub const darwin = @import("./darwin_c.zig");
pub const linux = @import("./linux_c.zig");
pub extern "c" fn chmod([*c]const u8, mode_t) c_int;
pub extern "c" fn fchmod(std.c.fd_t, mode_t) c_int;
pub extern "c" fn fchmodat(c_int, [*c]const u8, mode_t, c_int) c_int;
pub extern "c" fn fchown(std.c.fd_t, std.c.uid_t, std.c.gid_t) c_int;
pub extern "c" fn lchown(path: [*:0]const u8, std.c.uid_t, std.c.gid_t) c_int;
pub extern "c" fn chown(path: [*:0]const u8, std.c.uid_t, std.c.gid_t) c_int;
// TODO: this is wrong on Windows
pub extern "c" fn lstat64([*c]const u8, [*c]libc_stat) c_int;
// TODO: this is wrong on Windows
pub extern "c" fn fstat64([*c]const u8, [*c]libc_stat) c_int;
// TODO: this is wrong on Windows
pub extern "c" fn stat64([*c]const u8, [*c]libc_stat) c_int;
pub extern "c" fn lchmod(path: [*:0]const u8, mode: mode_t) c_int;
pub extern "c" fn truncate([*:0]const u8, i64) c_int; // note: truncate64 is not a thing
pub extern "c" fn lutimes(path: [*:0]const u8, times: *const [2]std.os.timeval) c_int;
pub extern "c" fn mkdtemp(template: [*c]u8) ?[*:0]u8;
pub extern "c" fn memcmp(s1: [*c]const u8, s2: [*c]const u8, n: usize) c_int;
pub extern "c" fn memchr(s: [*]const u8, c: u8, n: usize) ?[*]const u8;
pub const lstat = lstat64;
pub const fstat = fstat64;
pub const stat = stat64;
pub extern "c" fn strchr(str: [*]const u8, char: u8) ?[*]const u8;
pub fn lstat_absolute(path: [:0]const u8) !Stat {
if (builtin.os.tag == .windows) {
@compileError("Not implemented yet, conside using bun.sys.lstat()");
}
var st = zeroes(libc_stat);
switch (errno(lstat64(path.ptr, &st))) {
.SUCCESS => {},
.NOENT => return error.FileNotFound,
// .EINVAL => unreachable,
.BADF => unreachable, // Always a race condition.
.NOMEM => return error.SystemResources,
.ACCES => return error.AccessDenied,
else => |err| return os.unexpectedErrno(err),
}
const atime = st.atime();
const mtime = st.mtime();
const ctime = st.ctime();
return Stat{
.inode = st.ino,
.size = @as(u64, @bitCast(st.size)),
.mode = st.mode,
.kind = switch (builtin.os.tag) {
.wasi => switch (st.filetype) {
os.FILETYPE_BLOCK_DEVICE => Kind.block_device,
os.FILETYPE_CHARACTER_DEVICE => Kind.character_device,
os.FILETYPE_DIRECTORY => Kind.directory,
os.FILETYPE_SYMBOLIC_LINK => Kind.sym_link,
os.FILETYPE_REGULAR_FILE => Kind.file,
os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.unix_domain_socket,
else => Kind.unknown,
},
else => switch (st.mode & os.S.IFMT) {
os.S.IFBLK => Kind.block_device,
os.S.IFCHR => Kind.character_device,
os.S.IFDIR => Kind.directory,
os.S.IFIFO => Kind.named_pipe,
os.S.IFLNK => Kind.sym_link,
os.S.IFREG => Kind.file,
os.S.IFSOCK => Kind.unix_domain_socket,
else => Kind.unknown,
},
},
.atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
.mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
.ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
};
}
// renameatZ fails when renaming across mount points
// we assume that this is relatively uncommon
// TODO: change types to use `bun.FileDescriptor`
pub fn moveFileZ(from_dir: bun.FileDescriptor, filename: [:0]const u8, to_dir: bun.FileDescriptor, destination: [:0]const u8) !void {
switch (bun.sys.renameat(from_dir, filename, to_dir, destination)) {
.err => |err| {
// allow over-writing an empty directory
if (err.getErrno() == .ISDIR) {
_ = bun.sys.rmdirat(to_dir, destination.ptr);
try bun.sys.renameat(from_dir, filename, to_dir, destination).unwrap();
return;
}
if (err.getErrno() == .XDEV) {
try moveFileZSlow(from_dir, filename, to_dir, destination);
} else {
return bun.errnoToZigErr(err.errno);
}
},
.result => {},
}
}
pub fn moveFileZWithHandle(from_handle: bun.FileDescriptor, from_dir: bun.FileDescriptor, filename: [:0]const u8, to_dir: bun.FileDescriptor, destination: [:0]const u8) !void {
switch (bun.sys.renameat(from_dir, filename, to_dir, destination)) {
.err => |err| {
// allow over-writing an empty directory
if (err.getErrno() == .ISDIR) {
_ = bun.sys.rmdirat(to_dir, destination.ptr);
try (bun.sys.renameat(from_dir, filename, to_dir, destination).unwrap());
return;
}
if (err.getErrno() == .XDEV) {
try copyFileZSlowWithHandle(from_handle, to_dir, destination);
_ = bun.sys.unlinkat(from_dir, filename);
}
return bun.errnoToZigErr(err.errno);
},
.result => {},
}
}
// On Linux, this will be fast because sendfile() supports copying between two file descriptors on disk
// macOS & BSDs will be slow because
pub fn moveFileZSlow(from_dir: bun.FileDescriptor, filename: [:0]const u8, to_dir: bun.FileDescriptor, destination: [:0]const u8) !void {
const in_handle = try bun.sys.openat(from_dir, filename, std.os.O.RDONLY | std.os.O.CLOEXEC, if (Environment.isWindows) 0 else 0o644).unwrap();
defer _ = bun.sys.close(in_handle);
_ = bun.sys.unlinkat(from_dir, filename);
try copyFileZSlowWithHandle(in_handle, to_dir, destination);
}
pub fn copyFileZSlowWithHandle(in_handle: bun.FileDescriptor, to_dir: bun.FileDescriptor, destination: [:0]const u8) !void {
if (comptime Environment.isWindows) {
@panic("TODO windows");
}
const stat_ = if (comptime Environment.isPosix) try std.os.fstat(in_handle.cast()) else void{};
// Attempt to delete incase it already existed.
// This fixes ETXTBUSY on Linux
_ = bun.sys.unlinkat(to_dir, destination);
const out_handle = try bun.sys.openat(
to_dir,
destination,
std.os.O.WRONLY | std.os.O.CREAT | std.os.O.CLOEXEC | std.os.O.TRUNC,
if (comptime Environment.isPosix) 0o644 else 0,
).unwrap();
defer _ = bun.sys.close(out_handle);
if (comptime Environment.isLinux) {
_ = std.os.linux.fallocate(out_handle.cast(), 0, 0, @intCast(stat_.size));
}
try bun.copyFile(in_handle.cast(), out_handle.cast());
if (comptime Environment.isPosix) {
_ = fchmod(out_handle.cast(), stat_.mode);
_ = fchown(out_handle.cast(), stat_.uid, stat_.gid);
}
}
pub fn kindFromMode(mode: mode_t) std.fs.File.Kind {
return switch (mode & bun.S.IFMT) {
bun.S.IFBLK => std.fs.File.Kind.block_device,
bun.S.IFCHR => std.fs.File.Kind.character_device,
bun.S.IFDIR => std.fs.File.Kind.directory,
bun.S.IFIFO => std.fs.File.Kind.named_pipe,
bun.S.IFLNK => std.fs.File.Kind.sym_link,
bun.S.IFREG => std.fs.File.Kind.file,
bun.S.IFSOCK => std.fs.File.Kind.unix_domain_socket,
else => .unknown,
};
}
pub fn getSelfExeSharedLibPaths(allocator: std.mem.Allocator) error{OutOfMemory}![][:0]u8 {
const List = std.ArrayList([:0]u8);
switch (builtin.os.tag) {
.linux,
.freebsd,
.netbsd,
.dragonfly,
.openbsd,
.solaris,
=> {
var paths = List.init(allocator);
errdefer {
const slice = paths.toOwnedSlice() catch &.{};
for (slice) |item| {
allocator.free(item);
}
allocator.free(slice);
}
try os.dl_iterate_phdr(&paths, error{OutOfMemory}, struct {
fn callback(info: *os.dl_phdr_info, size: usize, list: *List) !void {
_ = size;
const name = info.dlpi_name orelse return;
if (name[0] == '/') {
const item = try list.allocator.dupeZ(u8, mem.sliceTo(name, 0));
errdefer list.allocator.free(item);
try list.append(item);
}
}
}.callback);
return try paths.toOwnedSlice();
},
.macos, .ios, .watchos, .tvos => {
var paths = List.init(allocator);
errdefer {
const slice = paths.toOwnedSlice() catch &.{};
for (slice) |item| {
allocator.free(item);
}
allocator.free(slice);
}
const img_count = std.c._dyld_image_count();
for (0..img_count) |i| {
const name = std.c._dyld_get_image_name(i);
const item = try allocator.dupeZ(u8, mem.sliceTo(name, 0));
errdefer allocator.free(item);
try paths.append(item);
}
return try paths.toOwnedSlice();
},
// revisit if Haiku implements dl_iterat_phdr (https://dev.haiku-os.org/ticket/15743)
.haiku => {
var paths = List.init(allocator);
errdefer {
const slice = paths.toOwnedSlice() catch &.{};
for (slice) |item| {
allocator.free(item);
}
allocator.free(slice);
}
const b = "/boot/system/runtime_loader";
const item = try allocator.dupeZ(u8, mem.sliceTo(b, 0));
errdefer allocator.free(item);
try paths.append(item);
return try paths.toOwnedSlice();
},
else => @compileError("getSelfExeSharedLibPaths unimplemented for this target"),
}
}
/// The madvise() system call allows a process that has knowledge of its mem-ory memory
/// ory behavior to describe it to the system. The advice passed in may be
/// used by the system to alter its virtual memory paging strategy. This
/// advice may improve application and system performance. The behavior
/// specified in advice can only be one of the following values:
///
/// MADV_NORMAL Indicates that the application has no advice to give on
/// its behavior in the specified address range. This is
/// the system default behavior. This is used with
/// madvise() system call.
///
/// POSIX_MADV_NORMAL
/// Same as MADV_NORMAL but used with posix_madvise() system
/// call.
///
/// MADV_SEQUENTIAL Indicates that the application expects to access this
/// address range in a sequential manner. This is used with
/// madvise() system call.
///
/// POSIX_MADV_SEQUENTIAL
/// Same as MADV_SEQUENTIAL but used with posix_madvise()
/// system call.
///
/// MADV_RANDOM Indicates that the application expects to access this
/// address range in a random manner. This is used with
/// madvise() system call.
///
/// POSIX_MADV_RANDOM
/// Same as MADV_RANDOM but used with posix_madvise() system
/// call.
///
/// MADV_WILLNEED Indicates that the application expects to access this
/// address range soon. This is used with madvise() system
/// call.
///
/// POSIX_MADV_WILLNEED
/// Same as MADV_WILLNEED but used with posix_madvise() sys-tem system
/// tem call.
///
/// MADV_DONTNEED Indicates that the application is not expecting to
/// access this address range soon. This is used with
/// madvise() system call.
///
/// POSIX_MADV_DONTNEED
/// Same as MADV_DONTNEED but used with posix_madvise() sys-tem system
/// tem call.
///
/// MADV_FREE Indicates that the application will not need the infor-mation information
/// mation contained in this address range, so the pages may
/// be reused right away. The address range will remain
/// valid. This is used with madvise() system call.
///
/// The posix_madvise() behaves same as madvise() except that it uses values
/// with POSIX_ prefix for the advice system call argument.
pub extern "c" fn posix_madvise(ptr: *anyopaque, len: usize, advice: i32) c_int;
pub fn getProcessPriority(pid_: i32) i32 {
const pid = @as(c_uint, @intCast(pid_));
return get_process_priority(pid);
}
pub fn setProcessPriority(pid_: i32, priority_: i32) std.c.E {
if (pid_ < 0) return .SRCH;
const pid = @as(c_uint, @intCast(pid_));
const priority = @as(c_int, @intCast(priority_));
const code: i32 = set_process_priority(pid, priority);
if (code == -2) return .SRCH;
if (code == 0) return .SUCCESS;
const errcode = std.c.getErrno(code);
return errcode;
}
pub fn getVersion(buf: []u8) []const u8 {
if (comptime Environment.isLinux) {
return linux.get_version(buf.ptr[0..bun.HOST_NAME_MAX]);
} else if (comptime Environment.isMac) {
return darwin.get_version(buf);
} else {
var info: bun.windows.libuv.uv_utsname_s = undefined;
const err = bun.windows.libuv.uv_os_uname(&info);
if (err != 0) {
return "unknown";
}
const slice = bun.sliceTo(&info.version, 0);
@memcpy(buf[0..slice.len], slice);
return buf[0..slice.len];
}
}
pub fn getRelease(buf: []u8) []const u8 {
if (comptime Environment.isLinux) {
return linux.get_release(buf.ptr[0..bun.HOST_NAME_MAX]);
} else if (comptime Environment.isMac) {
return darwin.get_release(buf);
} else {
var info: bun.windows.libuv.uv_utsname_s = undefined;
const err = bun.windows.libuv.uv_os_uname(&info);
if (err != 0) {
return "unknown";
}
const release = bun.sliceTo(&info.release, 0);
@memcpy(buf[0..release.len], release);
return buf[0..release.len];
}
}
pub extern fn memmem(haystack: [*]const u8, haystacklen: usize, needle: [*]const u8, needlelen: usize) ?[*]const u8;
pub extern fn cfmakeraw(*std.os.termios) void;
const LazyStatus = enum {
pending,
loaded,
failed,
};
pub fn _dlsym(handle: ?*anyopaque, name: [:0]const u8) ?*anyopaque {
if (comptime Environment.isWindows) {
return bun.windows.GetProcAddressA(handle, name);
} else if (comptime Environment.isMac or Environment.isLinux) {
return std.c.dlsym(handle, name.ptr);
}
@compileError("dlsym unimplemented for this target");
}
pub fn dlsymWithHandle(comptime Type: type, comptime name: [:0]const u8, comptime handle_getter: fn () ?*anyopaque) ?Type {
if (comptime @typeInfo(Type) != .Pointer) {
@compileError("dlsym must be a pointer type (e.g. ?const *fn()). Received " ++ @typeName(Type) ++ ".");
}
const Wrapper = struct {
pub var function: Type = undefined;
pub var loaded: LazyStatus = LazyStatus.pending;
};
if (Wrapper.loaded == .pending) {
const result = _dlsym(@call(bun.callmod_inline, handle_getter, .{}), name);
if (result) |ptr| {
Wrapper.function = bun.cast(Type, ptr);
Wrapper.loaded = .loaded;
return Wrapper.function;
} else {
Wrapper.loaded = .failed;
return null;
}
}
if (Wrapper.loaded == .loaded) {
return Wrapper.function;
}
return null;
}
pub fn dlsym(comptime Type: type, comptime name: [:0]const u8) ?Type {
const handle_getter = struct {
const RTLD_DEFAULT = if (bun.Environment.isMac)
@as(?*anyopaque, @ptrFromInt(@as(usize, @bitCast(@as(isize, -2)))))
else
@as(?*anyopaque, @ptrFromInt(@as(usize, 0)));
pub fn getter() ?*anyopaque {
return RTLD_DEFAULT;
}
}.getter;
return dlsymWithHandle(Type, name, handle_getter);
}
// set in c-bindings.cpp
pub extern fn get_process_priority(pid: c_uint) i32;
pub extern fn set_process_priority(pid: c_uint, priority: c_int) i32;
pub extern fn strncasecmp(s1: [*]const u8, s2: [*]const u8, n: usize) i32;
pub extern fn memmove(dest: [*]u8, src: [*]const u8, n: usize) void;
// https://man7.org/linux/man-pages/man3/fmod.3.html
pub extern fn fmod(f64, f64) f64;
pub fn dlopen(filename: [:0]const u8, flags: i32) ?*anyopaque {
if (comptime Environment.isWindows) {
return bun.windows.LoadLibraryA(filename);
}
return std.c.dlopen(filename, flags);
}
pub extern "C" fn Bun__ttySetMode(fd: c_int, mode: c_int) c_int;