mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
738 lines
19 KiB
C++
738 lines
19 KiB
C++
// when we don't want to use @cInclude, we can just stick wrapper functions here
|
|
#include "root.h"
|
|
|
|
#if !OS(WINDOWS)
|
|
#include <sys/resource.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/signal.h>
|
|
#include <unistd.h>
|
|
#include <cstring>
|
|
#include <csignal>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <sys/termios.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#else
|
|
#include <uv.h>
|
|
#include <windows.h>
|
|
#include <corecrt_io.h>
|
|
#endif // !OS(WINDOWS)
|
|
#include <lshpack.h>
|
|
|
|
#if CPU(X86_64) && !OS(WINDOWS)
|
|
extern "C" void bun_warn_avx_missing(const char* url)
|
|
{
|
|
__builtin_cpu_init();
|
|
if (__builtin_cpu_supports("avx")) {
|
|
return;
|
|
}
|
|
|
|
static constexpr const char* str = "warn: CPU lacks AVX support, strange crashes may occur. Reinstall Bun or use *-baseline build:\n ";
|
|
const size_t len = strlen(str);
|
|
|
|
char buf[512];
|
|
strcpy(buf, str);
|
|
strcpy(buf + len, url);
|
|
strcpy(buf + len + strlen(url), "\n\0");
|
|
write(STDERR_FILENO, buf, strlen(buf));
|
|
}
|
|
#endif
|
|
|
|
extern "C" int32_t get_process_priority(uint32_t pid)
|
|
{
|
|
#if OS(WINDOWS)
|
|
int priority = 0;
|
|
if (uv_os_getpriority(pid, &priority))
|
|
return 0;
|
|
return priority;
|
|
#else
|
|
return getpriority(PRIO_PROCESS, pid);
|
|
#endif // OS(WINDOWS)
|
|
}
|
|
|
|
extern "C" int32_t set_process_priority(uint32_t pid, int32_t priority)
|
|
{
|
|
#if OS(WINDOWS)
|
|
return uv_os_setpriority(pid, priority);
|
|
#else
|
|
return setpriority(PRIO_PROCESS, pid, priority);
|
|
#endif // OS(WINDOWS)
|
|
}
|
|
|
|
#if !OS(WINDOWS)
|
|
extern "C" bool is_executable_file(const char* path)
|
|
{
|
|
#if defined(O_EXEC)
|
|
// O_EXEC is macOS specific
|
|
int fd = open(path, O_EXEC | O_CLOEXEC, 0);
|
|
if (fd < 0)
|
|
return false;
|
|
close(fd);
|
|
return true;
|
|
#endif // defined(O_EXEC)
|
|
|
|
struct stat st;
|
|
if (stat(path, &st) != 0)
|
|
return false;
|
|
|
|
// regular file and user can execute
|
|
return S_ISREG(st.st_mode) && (st.st_mode & S_IXUSR);
|
|
}
|
|
#endif
|
|
|
|
extern "C" void bun_ignore_sigpipe()
|
|
{
|
|
#if !OS(WINDOWS)
|
|
// ignore SIGPIPE
|
|
signal(SIGPIPE, SIG_IGN);
|
|
#endif
|
|
}
|
|
extern "C" ssize_t bun_sysconf__SC_CLK_TCK()
|
|
{
|
|
#ifdef __APPLE__
|
|
return sysconf(_SC_CLK_TCK);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
#if OS(DARWIN) && BUN_DEBUG
|
|
#include <malloc/malloc.h>
|
|
|
|
extern "C" void dump_zone_malloc_stats()
|
|
{
|
|
vm_address_t* zones;
|
|
unsigned count;
|
|
|
|
// Zero out the structures in case a zone is missing
|
|
malloc_statistics_t stats;
|
|
stats.blocks_in_use = 0;
|
|
stats.size_in_use = 0;
|
|
stats.max_size_in_use = 0;
|
|
stats.size_allocated = 0;
|
|
|
|
malloc_get_all_zones(mach_task_self(), 0, &zones, &count);
|
|
for (unsigned i = 0; i < count; i++) {
|
|
if (const char* name = malloc_get_zone_name(reinterpret_cast<malloc_zone_t*>(zones[i]))) {
|
|
printf("%s:\n", name);
|
|
malloc_zone_statistics(reinterpret_cast<malloc_zone_t*>(zones[i]), &stats);
|
|
printf(" blocks_in_use: %u\n", stats.blocks_in_use);
|
|
printf(" size_in_use: %zu\n", stats.size_in_use);
|
|
printf(" max_size_in_use: %zu\n", stats.max_size_in_use);
|
|
printf(" size_allocated: %zu\n", stats.size_allocated);
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
#elif OS(DARWIN)
|
|
|
|
extern "C" void dump_zone_malloc_stats()
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
#if OS(WINDOWS)
|
|
#define MS_PER_SEC 1000ULL // MS = milliseconds
|
|
#define US_PER_MS 1000ULL // US = microseconds
|
|
#define HNS_PER_US 10ULL // HNS = hundred-nanoseconds (e.g., 1 hns = 100 ns)
|
|
#define NS_PER_US 1000ULL
|
|
|
|
#define HNS_PER_SEC (MS_PER_SEC * US_PER_MS * HNS_PER_US)
|
|
#define NS_PER_HNS (100ULL) // NS = nanoseconds
|
|
#define NS_PER_SEC (MS_PER_SEC * US_PER_MS * NS_PER_US)
|
|
|
|
extern "C" int clock_gettime_monotonic(int64_t* tv_sec, int64_t* tv_nsec)
|
|
{
|
|
static LARGE_INTEGER ticksPerSec;
|
|
LARGE_INTEGER ticks;
|
|
|
|
if (!ticksPerSec.QuadPart) {
|
|
QueryPerformanceFrequency(&ticksPerSec);
|
|
if (!ticksPerSec.QuadPart) {
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
QueryPerformanceCounter(&ticks);
|
|
|
|
*tv_sec = (int64_t)(ticks.QuadPart / ticksPerSec.QuadPart);
|
|
*tv_nsec = (int64_t)(((ticks.QuadPart % ticksPerSec.QuadPart) * NS_PER_SEC) / ticksPerSec.QuadPart);
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern "C" void windows_enable_stdio_inheritance()
|
|
{
|
|
HANDLE handle;
|
|
|
|
handle = GetStdHandle(STD_INPUT_HANDLE);
|
|
if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
|
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 1);
|
|
|
|
handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
|
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 1);
|
|
|
|
handle = GetStdHandle(STD_ERROR_HANDLE);
|
|
if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
|
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 1);
|
|
}
|
|
|
|
#endif
|
|
|
|
#if OS(LINUX)
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#ifndef CLOSE_RANGE_CLOEXEC
|
|
#define CLOSE_RANGE_CLOEXEC (1U << 2)
|
|
#endif
|
|
|
|
// close_range is glibc > 2.33, which is very new
|
|
extern "C" ssize_t bun_close_range(unsigned int start, unsigned int end, unsigned int flags)
|
|
{
|
|
// https://github.com/oven-sh/bun/issues/9669
|
|
#ifdef __NR_close_range
|
|
return syscall(__NR_close_range, start, end, flags);
|
|
#else
|
|
return ENOSYS;
|
|
#endif
|
|
}
|
|
|
|
static void unset_cloexec(int fd)
|
|
{
|
|
int flags = fcntl(fd, F_GETFD, 0);
|
|
if (flags == -1) {
|
|
return;
|
|
}
|
|
flags &= ~FD_CLOEXEC;
|
|
fcntl(fd, F_SETFD, flags);
|
|
}
|
|
|
|
extern "C" void on_before_reload_process_linux()
|
|
{
|
|
unset_cloexec(STDIN_FILENO);
|
|
unset_cloexec(STDOUT_FILENO);
|
|
unset_cloexec(STDERR_FILENO);
|
|
|
|
// close all file descriptors except stdin, stdout, stderr and possibly IPC.
|
|
// if you're passing additional file descriptors to Bun, you're probably not passing more than 8.
|
|
// If this fails, it's ultimately okay, we're just trying our best to avoid leaking file descriptors.
|
|
bun_close_range(3, ~0U, CLOSE_RANGE_CLOEXEC);
|
|
|
|
// reset all signals to default
|
|
sigset_t signal_set;
|
|
sigemptyset(&signal_set);
|
|
sigprocmask(SIG_SETMASK, &signal_set, nullptr);
|
|
}
|
|
|
|
#endif
|
|
|
|
#define LSHPACK_MAX_HEADER_SIZE 65536
|
|
|
|
static thread_local char shared_header_buffer[LSHPACK_MAX_HEADER_SIZE];
|
|
|
|
extern "C" {
|
|
typedef void* (*lshpack_wrapper_alloc)(size_t size);
|
|
typedef void (*lshpack_wrapper_free)(void*);
|
|
typedef struct {
|
|
struct lshpack_enc enc;
|
|
struct lshpack_dec dec;
|
|
lshpack_wrapper_free free;
|
|
} lshpack_wrapper;
|
|
|
|
typedef struct {
|
|
const char* name;
|
|
size_t name_len;
|
|
const char* value;
|
|
size_t value_len;
|
|
} lshpack_header;
|
|
|
|
lshpack_wrapper* lshpack_wrapper_init(lshpack_wrapper_alloc alloc, lshpack_wrapper_free free, unsigned max_capacity)
|
|
{
|
|
lshpack_wrapper* coders = (lshpack_wrapper*)alloc(sizeof(lshpack_wrapper));
|
|
if (!coders)
|
|
return nullptr;
|
|
coders->free = free;
|
|
if (lshpack_enc_init(&coders->enc) != 0)
|
|
return nullptr;
|
|
lshpack_dec_init(&coders->dec);
|
|
lshpack_enc_set_max_capacity(&coders->enc, max_capacity);
|
|
lshpack_dec_set_max_capacity(&coders->dec, max_capacity);
|
|
return coders;
|
|
}
|
|
|
|
size_t lshpack_wrapper_encode(lshpack_wrapper* self,
|
|
const unsigned char* name, size_t name_len,
|
|
const unsigned char* val, size_t val_len,
|
|
int never_index,
|
|
unsigned char* buffer, size_t buffer_len, size_t buffer_offset)
|
|
{
|
|
if (name_len + val_len > LSHPACK_MAX_HEADER_SIZE)
|
|
return 0;
|
|
|
|
lsxpack_header_t hdr;
|
|
memset(&hdr, 0, sizeof(lsxpack_header_t));
|
|
memcpy(&shared_header_buffer[0], name, name_len);
|
|
memcpy(&shared_header_buffer[name_len], val, val_len);
|
|
lsxpack_header_set_offset2(&hdr, &shared_header_buffer[0], 0, name_len, name_len, val_len);
|
|
if (never_index) {
|
|
hdr.indexed_type = 2;
|
|
}
|
|
auto* start = buffer + buffer_offset;
|
|
auto* ptr = lshpack_enc_encode(&self->enc, start, buffer + buffer_len, &hdr);
|
|
if (!ptr)
|
|
return 0;
|
|
return ptr - start;
|
|
}
|
|
|
|
size_t lshpack_wrapper_decode(lshpack_wrapper* self,
|
|
const unsigned char* src, size_t src_len,
|
|
lshpack_header* output)
|
|
{
|
|
lsxpack_header_t hdr;
|
|
memset(&hdr, 0, sizeof(lsxpack_header_t));
|
|
lsxpack_header_prepare_decode(&hdr, &shared_header_buffer[0], 0, LSHPACK_MAX_HEADER_SIZE);
|
|
|
|
const unsigned char* s = src;
|
|
|
|
auto rc = lshpack_dec_decode(&self->dec, &s, s + src_len, &hdr);
|
|
if (rc != 0)
|
|
return 0;
|
|
|
|
output->name = lsxpack_header_get_name(&hdr);
|
|
output->name_len = hdr.name_len;
|
|
output->value = lsxpack_header_get_value(&hdr);
|
|
output->value_len = hdr.val_len;
|
|
return s - src;
|
|
}
|
|
|
|
void lshpack_wrapper_deinit(lshpack_wrapper* self)
|
|
{
|
|
lshpack_dec_cleanup(&self->dec);
|
|
lshpack_enc_cleanup(&self->enc);
|
|
self->free(self);
|
|
}
|
|
}
|
|
|
|
#if OS(LINUX)
|
|
|
|
#include <linux/fs.h>
|
|
|
|
static inline void make_pos_h_l(unsigned long* pos_h, unsigned long* pos_l,
|
|
off_t offset)
|
|
{
|
|
#if __BITS_PER_LONG == 64
|
|
*pos_l = offset;
|
|
*pos_h = 0;
|
|
#else
|
|
*pos_l = offset & 0xffffffff;
|
|
*pos_h = ((uint64_t)offset) >> 32;
|
|
#endif
|
|
}
|
|
extern "C" ssize_t sys_preadv2(int fd, const struct iovec* iov, int iovcnt,
|
|
off_t offset, unsigned int flags)
|
|
{
|
|
return syscall(SYS_preadv2, fd, iov, iovcnt, offset, offset >> 32, RWF_NOWAIT);
|
|
}
|
|
extern "C" ssize_t sys_pwritev2(int fd, const struct iovec* iov, int iovcnt,
|
|
off_t offset, unsigned int flags)
|
|
{
|
|
unsigned long pos_l, pos_h;
|
|
|
|
make_pos_h_l(&pos_h, &pos_l, offset);
|
|
return syscall(__NR_pwritev2, fd, iov, iovcnt, pos_l, pos_h, flags);
|
|
}
|
|
#else
|
|
extern "C" ssize_t preadv2(int fd, const struct iovec* iov, int iovcnt,
|
|
off_t offset, unsigned int flags)
|
|
{
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
extern "C" ssize_t pwritev2(int fd, const struct iovec* iov, int iovcnt,
|
|
off_t offset, unsigned int flags)
|
|
{
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
|
|
extern "C" void Bun__onExit();
|
|
extern "C" int32_t bun_stdio_tty[3];
|
|
#if !OS(WINDOWS)
|
|
static termios termios_to_restore_later[3];
|
|
#endif
|
|
|
|
extern "C" void bun_restore_stdio()
|
|
{
|
|
|
|
#if !OS(WINDOWS)
|
|
|
|
// restore stdio
|
|
for (int32_t fd = 0; fd < 3; fd++) {
|
|
if (!bun_stdio_tty[fd])
|
|
continue;
|
|
|
|
sigset_t sa;
|
|
int err;
|
|
|
|
// We might be a background job that doesn't own the TTY so block SIGTTOU
|
|
// before making the tcsetattr() call, otherwise that signal suspends us.
|
|
sigemptyset(&sa);
|
|
sigaddset(&sa, SIGTTOU);
|
|
|
|
pthread_sigmask(SIG_BLOCK, &sa, nullptr);
|
|
do
|
|
err = tcsetattr(fd, TCSANOW, &termios_to_restore_later[fd]);
|
|
while (err == -1 && errno == EINTR);
|
|
pthread_sigmask(SIG_UNBLOCK, &sa, nullptr);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !OS(WINDOWS)
|
|
extern "C" void onExitSignal(int sig)
|
|
{
|
|
bun_restore_stdio();
|
|
raise(sig);
|
|
}
|
|
#endif
|
|
|
|
#if OS(WINDOWS)
|
|
extern "C" void Bun__restoreWindowsStdio();
|
|
BOOL WINAPI Ctrlhandler(DWORD signal)
|
|
{
|
|
|
|
if (signal == CTRL_C_EVENT) {
|
|
Bun__restoreWindowsStdio();
|
|
SetConsoleCtrlHandler(Ctrlhandler, FALSE);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
extern "C" void Bun__setCTRLHandler(BOOL add)
|
|
{
|
|
SetConsoleCtrlHandler(Ctrlhandler, add);
|
|
}
|
|
#endif
|
|
|
|
extern "C" int32_t bun_is_stdio_null[3] = { 0, 0, 0 };
|
|
|
|
extern "C" void bun_initialize_process()
|
|
{
|
|
// Disable printf() buffering. We buffer it ourselves.
|
|
setvbuf(stdout, nullptr, _IONBF, 0);
|
|
setvbuf(stderr, nullptr, _IONBF, 0);
|
|
|
|
#if OS(LINUX)
|
|
// Prevent leaking inherited file descriptors on Linux
|
|
// This is less of an issue for macOS due to posix_spawn
|
|
// This is best effort, not all linux kernels support close_range or CLOSE_RANGE_CLOEXEC
|
|
// To avoid breaking --watch, we skip stdin, stdout, stderr and IPC.
|
|
bun_close_range(4, ~0U, CLOSE_RANGE_CLOEXEC);
|
|
#endif
|
|
|
|
#if OS(LINUX) || OS(DARWIN)
|
|
|
|
int devNullFd_ = -1;
|
|
bool anyTTYs = false;
|
|
|
|
const auto setDevNullFd = [&](int target_fd) -> void {
|
|
bun_is_stdio_null[target_fd] = 1;
|
|
if (devNullFd_ == -1) {
|
|
do {
|
|
devNullFd_ = open("/dev/null", O_RDWR | O_CLOEXEC, 0);
|
|
} while (devNullFd_ < 0 and errno == EINTR);
|
|
};
|
|
|
|
if (devNullFd_ == target_fd) {
|
|
devNullFd_ = -1;
|
|
return;
|
|
}
|
|
|
|
ASSERT(devNullFd_ != -1);
|
|
int err;
|
|
do {
|
|
err = dup2(devNullFd_, target_fd);
|
|
} while (err < 0 && errno == EINTR);
|
|
|
|
if (UNLIKELY(err != 0)) {
|
|
abort();
|
|
}
|
|
};
|
|
|
|
for (int fd = 0; fd < 3; fd++) {
|
|
int result = isatty(fd);
|
|
if (result == 0) {
|
|
if (UNLIKELY(errno == EBADF)) {
|
|
// the fd is invalid, let's make sure it's always valid
|
|
setDevNullFd(fd);
|
|
}
|
|
} else {
|
|
bun_stdio_tty[fd] = 1;
|
|
int err = 0;
|
|
|
|
do {
|
|
err = tcgetattr(fd, &termios_to_restore_later[fd]);
|
|
} while (err == -1 && errno == EINTR);
|
|
|
|
if (LIKELY(err == 0)) {
|
|
anyTTYs = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(devNullFd_ == -1 || devNullFd_ > 2);
|
|
if (devNullFd_ > 2) {
|
|
close(devNullFd_);
|
|
}
|
|
|
|
// Restore TTY state on exit
|
|
if (anyTTYs) {
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
sa.sa_flags = SA_RESETHAND;
|
|
sa.sa_handler = onExitSignal;
|
|
|
|
sigaction(SIGTERM, &sa, nullptr);
|
|
sigaction(SIGINT, &sa, nullptr);
|
|
}
|
|
#elif OS(WINDOWS)
|
|
for (int fd = 0; fd <= 2; ++fd) {
|
|
auto handle = reinterpret_cast<HANDLE>(uv_get_osfhandle(fd));
|
|
if (handle == INVALID_HANDLE_VALUE || GetFileType(handle) == FILE_TYPE_UNKNOWN) {
|
|
// Ignore _close result. If it fails or not depends on used Windows
|
|
// version. We will just check _open result.
|
|
_close(fd);
|
|
bun_is_stdio_null[fd] = 1;
|
|
if (fd != _open("nul", O_RDWR)) {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
} else {
|
|
switch (fd) {
|
|
case 0: {
|
|
SetStdHandle(STD_INPUT_HANDLE, uv_get_osfhandle(fd));
|
|
ASSERT(GetStdHandle(STD_INPUT_HANDLE) == uv_get_osfhandle(fd));
|
|
break;
|
|
}
|
|
case 1: {
|
|
SetStdHandle(STD_OUTPUT_HANDLE, uv_get_osfhandle(fd));
|
|
ASSERT(GetStdHandle(STD_OUTPUT_HANDLE) == uv_get_osfhandle(fd));
|
|
break;
|
|
}
|
|
case 2: {
|
|
SetStdHandle(STD_ERROR_HANDLE, uv_get_osfhandle(fd));
|
|
ASSERT(GetStdHandle(STD_ERROR_HANDLE) == uv_get_osfhandle(fd));
|
|
break;
|
|
}
|
|
default: {
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add ctrl+c handler on windows
|
|
Bun__setCTRLHandler(1);
|
|
#endif
|
|
|
|
#if OS(DARWIN)
|
|
atexit(Bun__onExit);
|
|
#else
|
|
at_quick_exit(Bun__onExit);
|
|
#endif
|
|
}
|
|
|
|
#if OS(WINDOWS)
|
|
extern "C" int32_t open_as_nonblocking_tty(int32_t fd, int32_t mode)
|
|
{
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
#else
|
|
|
|
static bool can_open_as_nonblocking_tty(int32_t fd)
|
|
{
|
|
int result;
|
|
#if OS(LINUX) || OS(FreeBSD)
|
|
int dummy = 0;
|
|
|
|
result = ioctl(fd, TIOCGPTN, &dummy) != 0;
|
|
#elif OS(DARWIN)
|
|
|
|
char dummy[256];
|
|
|
|
result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0;
|
|
|
|
#else
|
|
|
|
#error "TODO"
|
|
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
extern "C" int32_t open_as_nonblocking_tty(int32_t fd, int32_t mode)
|
|
{
|
|
if (!can_open_as_nonblocking_tty(fd)) {
|
|
return -1;
|
|
}
|
|
|
|
char pathbuf[PATH_MAX + 1];
|
|
if (ttyname_r(fd, pathbuf, sizeof(pathbuf)) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return open(pathbuf, mode | O_NONBLOCK | O_NOCTTY | O_CLOEXEC);
|
|
}
|
|
|
|
#endif
|
|
|
|
extern "C" size_t Bun__ramSize()
|
|
{
|
|
// This value is cached internally.
|
|
return WTF::ramSize();
|
|
}
|
|
|
|
#if !OS(WINDOWS)
|
|
|
|
extern "C" void Bun__disableSOLinger(int fd)
|
|
{
|
|
struct linger l = { 1, 0 };
|
|
setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
|
|
}
|
|
|
|
#else
|
|
|
|
#include <winsock2.h>
|
|
|
|
extern "C" void Bun__disableSOLinger(SOCKET fd)
|
|
{
|
|
struct linger l = { 1, 0 };
|
|
setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
|
|
}
|
|
|
|
#endif
|
|
|
|
// Handle signals in bun.spawnSync.
|
|
// If we receive a signal, we want to forward the signal to the child process.
|
|
#if OS(LINUX) || OS(DARWIN)
|
|
#include <signal.h>
|
|
#include <pthread.h>
|
|
|
|
// Note: We only ever use bun.spawnSync on the main thread.
|
|
extern "C" int64_t Bun__currentSyncPID = 0;
|
|
static int Bun__pendingSignalToSend = 0;
|
|
static struct sigaction previous_actions[NSIG];
|
|
|
|
// This list of signals is copied from npm.
|
|
// https://github.com/npm/cli/blob/fefd509992a05c2dfddbe7bc46931c42f1da69d7/workspaces/arborist/lib/signals.js#L26-L57
|
|
#define FOR_EACH_POSIX_SIGNAL(M) \
|
|
M(SIGABRT); \
|
|
M(SIGALRM); \
|
|
M(SIGHUP); \
|
|
M(SIGINT); \
|
|
M(SIGTERM); \
|
|
M(SIGVTALRM); \
|
|
M(SIGXCPU); \
|
|
M(SIGXFSZ); \
|
|
M(SIGUSR2); \
|
|
M(SIGTRAP); \
|
|
M(SIGSYS); \
|
|
M(SIGQUIT); \
|
|
M(SIGIOT); \
|
|
M(SIGIO);
|
|
|
|
#if OS(LINUX)
|
|
#define FOR_EACH_LINUX_ONLY_SIGNAL(M) \
|
|
M(SIGPOLL); \
|
|
M(SIGPWR); \
|
|
M(SIGSTKFLT);
|
|
|
|
#endif
|
|
|
|
#if OS(DARWIN)
|
|
#define FOR_EACH_SIGNAL(M) FOR_EACH_POSIX_SIGNAL(M)
|
|
#endif
|
|
|
|
#if OS(LINUX)
|
|
#define FOR_EACH_SIGNAL(M) \
|
|
FOR_EACH_POSIX_SIGNAL(M) \
|
|
FOR_EACH_LINUX_ONLY_SIGNAL(M)
|
|
#endif
|
|
|
|
static void Bun__forwardSignalFromParentToChildAndRestorePreviousAction(pid_t pid, int sig)
|
|
{
|
|
sigset_t restore_mask;
|
|
sigset_t mask;
|
|
sigemptyset(&mask);
|
|
sigaddset(&mask, sig);
|
|
sigemptyset(&restore_mask);
|
|
sigaddset(&restore_mask, sig);
|
|
pthread_sigmask(SIG_BLOCK, &mask, &restore_mask);
|
|
kill(pid, sig);
|
|
pthread_sigmask(SIG_UNBLOCK, &restore_mask, nullptr);
|
|
}
|
|
|
|
extern "C" void Bun__sendPendingSignalIfNecessary()
|
|
{
|
|
int sig = Bun__pendingSignalToSend;
|
|
Bun__pendingSignalToSend = 0;
|
|
int pid = Bun__currentSyncPID;
|
|
if (sig == 0 || pid == 0)
|
|
return;
|
|
|
|
Bun__forwardSignalFromParentToChildAndRestorePreviousAction(pid, sig);
|
|
}
|
|
|
|
extern "C" void Bun__registerSignalsForForwarding()
|
|
{
|
|
Bun__pendingSignalToSend = 0;
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = SA_RESETHAND;
|
|
sa.sa_handler = [](int sig) {
|
|
if (Bun__currentSyncPID == 0) {
|
|
Bun__pendingSignalToSend = sig;
|
|
return;
|
|
}
|
|
|
|
Bun__forwardSignalFromParentToChildAndRestorePreviousAction(Bun__currentSyncPID, sig);
|
|
};
|
|
|
|
#define REGISTER_SIGNAL(SIG) \
|
|
if (sigaction(SIG, &sa, &previous_actions[SIG]) == -1) { \
|
|
}
|
|
|
|
FOR_EACH_SIGNAL(REGISTER_SIGNAL)
|
|
|
|
#undef REGISTER_SIGNAL
|
|
}
|
|
|
|
extern "C" void Bun__unregisterSignalsForForwarding()
|
|
{
|
|
Bun__currentSyncPID = 0;
|
|
|
|
#define UNREGISTER_SIGNAL(SIG) \
|
|
if (sigaction(SIG, &previous_actions[SIG], NULL) == -1) { \
|
|
}
|
|
|
|
FOR_EACH_SIGNAL(UNREGISTER_SIGNAL)
|
|
memset(previous_actions, 0, sizeof(previous_actions));
|
|
#undef UNREGISTER_SIGNAL
|
|
}
|
|
|
|
#endif
|