mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-28 13:30:29 +00:00
Make improvements
- We now serialize the file descriptor table when spawning / executing processes on Windows. This means you can now inherit more stuff than just standard i/o. It's needed by bash, which duplicates the console to file descriptor #255. We also now do a better job serializing the environment variables, so you're less likely to encounter E2BIG when using your bash shell. We also no longer coerce environ to uppercase - execve() on Windows now remotely controls its parent process to make them spawn a replacement for itself. Then it'll be able to terminate immediately once the spawn succeeds, without having to linger around for the lifetime as a shell process for proxying the exit code. When process worker thread running in the parent sees the child die, it's given a handle to the new child, to replace it in the process table. - execve() and posix_spawn() on Windows will now provide CreateProcess an explicit handle list. This allows us to remove handle locks which enables better fork/spawn concurrency, with seriously correct thread safety. Other codebases like Go use the same technique. On the other hand fork() still favors the conventional WIN32 inheritence approach which can be a little bit messy, but is *controlled* by guaranteeing perfectly clean slates at both the spawning and execution boundaries - sigset_t is now 64 bits. Having it be 128 bits was a mistake because there's no reason to use that and it's only supported by FreeBSD. By using the system word size, signal mask manipulation on Windows goes very fast. Furthermore @asyncsignalsafe funcs have been rewritten on Windows to take advantage of signal masking, now that it's much more pleasant to use. - All the overlapped i/o code on Windows has been rewritten for pretty good signal and cancelation safety. We're now able to ensure overlap data structures are cleaned up so long as you don't longjmp() out of out of a signal handler that interrupted an i/o operation. Latencies are also improved thanks to the removal of lots of "busy wait" code. Waits should be optimal for everything except poll(), which shall be the last and final demon we slay in the win32 i/o horror show. - getrusage() on Windows is now able to report RUSAGE_CHILDREN as well as RUSAGE_SELF, thanks to aggregation in the process manager thread.
This commit is contained in:
parent
af7cb3c82f
commit
791f79fcb3
382 changed files with 4008 additions and 4511 deletions
|
@ -4,18 +4,18 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define BLOCK_CANCELLATIONS \
|
||||
do { \
|
||||
int _CancelState; \
|
||||
_CancelState = _pthread_block_cancellations()
|
||||
#define BLOCK_CANCELATION \
|
||||
do { \
|
||||
int _CancelState; \
|
||||
_CancelState = _pthread_block_cancelation()
|
||||
|
||||
#define ALLOW_CANCELLATIONS \
|
||||
_pthread_allow_cancellations(_CancelState); \
|
||||
} \
|
||||
#define ALLOW_CANCELATION \
|
||||
_pthread_allow_cancelation(_CancelState); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
int _pthread_block_cancellations(void);
|
||||
void _pthread_allow_cancellations(int);
|
||||
int _pthread_block_cancelation(void);
|
||||
void _pthread_allow_cancelation(int);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_H_
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define BLOCK_SIGNALS \
|
||||
do { \
|
||||
sigset_t _SigMask; \
|
||||
_SigMask = _sigblockall()
|
||||
|
||||
#define ALLOW_SIGNALS \
|
||||
_sigsetmask(_SigMask); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_H_ */
|
|
@ -1,11 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_BO_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_BO_INTERNAL_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define BEGIN_BLOCKING_OPERATION (void)0
|
||||
#define END_BLOCKING_OPERATION (void)0
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_BO_INTERNAL_H_ */
|
|
@ -26,6 +26,8 @@
|
|||
#define _POSIX_MEMLOCK_RANGE _POSIX_VERSION
|
||||
#define _POSIX_SPAWN _POSIX_VERSION
|
||||
|
||||
#define NSIG 64
|
||||
|
||||
#define SEEK_SET 0 /* relative to beginning */
|
||||
#define SEEK_CUR 1 /* relative to current position */
|
||||
#define SEEK_END 2 /* relative to end */
|
||||
|
|
|
@ -47,6 +47,7 @@ LIBC_CALLS_A_DIRECTDEPS = \
|
|||
LIBC_NT_PDH \
|
||||
LIBC_NT_POWRPROF \
|
||||
LIBC_NT_PSAPI \
|
||||
LIBC_NT_SYNCHRONIZATION \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/sysv/consts/rusage.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
/**
|
||||
* Returns sum of CPU time consumed by current process since birth.
|
||||
*
|
||||
* This function provides a basic idea of how computationally expensive
|
||||
* your program is, in terms of both the userspace and kernel processor
|
||||
* resources it's hitherto consumed. Here's an example of how you might
|
||||
* display this information:
|
||||
*
|
||||
* printf("consumed %g seconds of cpu time\n",
|
||||
* (double)clock() / CLOCKS_PER_SEC);
|
||||
*
|
||||
* This function offers at best microsecond accuracy on all supported
|
||||
* platforms. Please note the reported values might be a bit chunkier
|
||||
* depending on the kernel scheduler sampling interval see `CLK_TCK`.
|
||||
*
|
||||
* @return units of CPU time consumed, where each unit's time length
|
||||
* should be `1./CLOCKS_PER_SEC` seconds; Cosmopolitan currently
|
||||
* returns the unit count in microseconds, i.e. `CLOCKS_PER_SEC`
|
||||
* is hard-coded as 1000000. On failure this returns -1 / errno.
|
||||
* @raise ENOSYS should be returned currently if run on Bare Metal
|
||||
* @see clock_gettime() which polyfills this on Linux and BSDs
|
||||
* @see getrusage() which polyfills this on XNU and NT
|
||||
*/
|
||||
int64_t clock(void) {
|
||||
int e;
|
||||
struct rusage ru;
|
||||
struct timespec ts;
|
||||
e = errno;
|
||||
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) {
|
||||
errno = e;
|
||||
if (getrusage(RUSAGE_SELF, &ru) != -1) {
|
||||
ts = timeval_totimespec(timeval_add(ru.ru_utime, ru.ru_stime));
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// convert nanoseconds to microseconds w/ ceil rounding
|
||||
// this would need roughly ~7,019,309 years to overflow
|
||||
return ts.tv_sec * 1000000 + (ts.tv_nsec + 999) / 1000;
|
||||
}
|
|
@ -17,32 +17,25 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "third_party/finger/finger.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
static textwindows int sys_clock_nanosleep_nt_impl(int clock,
|
||||
struct timespec abs) {
|
||||
struct timespec abs,
|
||||
sigset_t waitmask) {
|
||||
uint32_t msdelay;
|
||||
struct timespec now;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
for (;;) {
|
||||
if (sys_clock_gettime_nt(clock, &now)) return -1;
|
||||
if (timespec_cmp(now, abs) >= 0) return 0;
|
||||
if (_check_interrupts(0)) return -1;
|
||||
pt->abort_errno = 0;
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore,
|
||||
timespec_tomillis(timespec_sub(abs, now)));
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno) {
|
||||
errno = pt->abort_errno;
|
||||
return -1;
|
||||
}
|
||||
msdelay = timespec_tomillis(timespec_sub(abs, now));
|
||||
if (_park_norestart(msdelay, waitmask)) return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,16 +44,21 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
|
|||
struct timespec *rem) {
|
||||
int rc;
|
||||
struct timespec abs, now;
|
||||
sigset_t m = __sig_block();
|
||||
if (flags & TIMER_ABSTIME) {
|
||||
abs = *req;
|
||||
} else {
|
||||
if (sys_clock_gettime_nt(clock, &now)) return -1;
|
||||
if ((rc = sys_clock_gettime_nt(clock, &now))) goto BailOut;
|
||||
abs = timespec_add(now, *req);
|
||||
}
|
||||
rc = sys_clock_nanosleep_nt_impl(clock, abs);
|
||||
rc = sys_clock_nanosleep_nt_impl(clock, abs, m);
|
||||
if (rc == -1 && rem && errno == EINTR) {
|
||||
sys_clock_gettime_nt(clock, &now);
|
||||
*rem = timespec_subz(abs, now);
|
||||
}
|
||||
BailOut:
|
||||
__sig_unblock(m);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -36,7 +36,7 @@ static errno_t sys_clock_nanosleep(int clock, int flags,
|
|||
const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
int e, rc;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
e = errno;
|
||||
if (IsLinux() || IsFreebsd() || IsNetbsd()) {
|
||||
rc = __sys_clock_nanosleep(clock, flags, req, rem);
|
||||
|
@ -53,7 +53,7 @@ static errno_t sys_clock_nanosleep(int clock, int flags,
|
|||
rc = errno;
|
||||
errno = e;
|
||||
}
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
#if 0
|
||||
STRACE("sys_clock_nanosleep(%s, %s, %s, [%s]) → %d% m",
|
||||
DescribeClockName(clock), DescribeSleepFlags(flags),
|
||||
|
@ -197,7 +197,7 @@ static bool ShouldUseSpinNanosleep(int clock, int flags,
|
|||
* @raise EINVAL if `flags` has an unrecognized value
|
||||
* @raise EINVAL if `req->tv_nsec ∉ [0,1000000000)`
|
||||
* @raise ENOSYS on bare metal
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @returnserrno
|
||||
* @norestart
|
||||
*/
|
||||
|
|
|
@ -16,43 +16,47 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
void sys_fcntl_nt_lock_cleanup(int);
|
||||
|
||||
textwindows int sys_close_nt(struct Fd *fd, int fildes) {
|
||||
int e;
|
||||
bool ok = true;
|
||||
|
||||
if (_weaken(sys_fcntl_nt_lock_cleanup)) {
|
||||
_weaken(sys_fcntl_nt_lock_cleanup)(fildes);
|
||||
textwindows int sys_close_nt(int fd, int fildes) {
|
||||
if (fd + 0u >= g_fds.n) return ebadf();
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
switch (f->kind) {
|
||||
case kFdFile:
|
||||
void sys_fcntl_nt_lock_cleanup(int);
|
||||
if (_weaken(sys_fcntl_nt_lock_cleanup)) {
|
||||
_weaken(sys_fcntl_nt_lock_cleanup)(fildes);
|
||||
}
|
||||
if ((f->flags & O_ACCMODE) != O_RDONLY &&
|
||||
GetFileType(f->handle) == kNtFileTypeDisk) {
|
||||
// Like Linux, closing a file on Windows doesn't guarantee it is
|
||||
// immediately synced to disk. But unlike Linux this could cause
|
||||
// subsequent operations, e.g. unlink() to break w/ access error
|
||||
FlushFileBuffers(f->handle);
|
||||
}
|
||||
break;
|
||||
case kFdEpoll:
|
||||
if (_weaken(sys_close_epoll_nt)) {
|
||||
return _weaken(sys_close_epoll_nt)(fd);
|
||||
}
|
||||
break;
|
||||
case kFdSocket:
|
||||
if (_weaken(sys_closesocket_nt)) {
|
||||
return _weaken(sys_closesocket_nt)(g_fds.p + fd);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (fd->kind == kFdFile && ((fd->flags & O_ACCMODE) != O_RDONLY &&
|
||||
GetFileType(fd->handle) == kNtFileTypeDisk)) {
|
||||
// Like Linux, closing a file on Windows doesn't guarantee it's
|
||||
// immediately synced to disk. But unlike Linux, this could cause
|
||||
// subsequent operations, e.g. unlink() to break w/ access error.
|
||||
e = errno;
|
||||
FlushFileBuffers(fd->handle);
|
||||
errno = e;
|
||||
}
|
||||
|
||||
// if this file descriptor is wrapped in a named pipe worker thread
|
||||
// then we need to close our copy of the worker thread handle. it's
|
||||
// also required that whatever install a worker use malloc, so free
|
||||
if (!fd->dontclose) {
|
||||
if (!CloseHandle(fd->handle)) ok = false;
|
||||
if (fd->kind == kFdConsole && fd->extra && fd->extra != -1) {
|
||||
if (!CloseHandle(fd->extra)) ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
return ok ? 0 : -1;
|
||||
return CloseHandle(f->handle) ? 0 : __winerr();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
@ -30,6 +31,44 @@
|
|||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
// for performance reasons we want to avoid holding __fds_lock()
|
||||
// while sys_close() is happening. this leaves the kernel / libc
|
||||
// having a temporarily inconsistent state. routines that obtain
|
||||
// file descriptors the way __zipos_open() does need to retry if
|
||||
// there's indication this race condition happened.
|
||||
|
||||
static int close_impl(int fd) {
|
||||
|
||||
if (fd < 0) {
|
||||
return ebadf();
|
||||
}
|
||||
|
||||
// give kprintf() the opportunity to dup() stderr
|
||||
if (fd == 2 && _weaken(kloghandle)) {
|
||||
_weaken(kloghandle)();
|
||||
}
|
||||
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
if (_weaken(__zipos_close)) {
|
||||
return _weaken(__zipos_close)(fd);
|
||||
}
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
sys_close(fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
return sys_close(fd);
|
||||
}
|
||||
|
||||
if (IsWindows()) {
|
||||
return sys_close_nt(fd, fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes file descriptor.
|
||||
*
|
||||
|
@ -56,42 +95,8 @@
|
|||
* @vforksafe
|
||||
*/
|
||||
int close(int fd) {
|
||||
int rc;
|
||||
if (fd < 0) {
|
||||
rc = ebadf();
|
||||
} else {
|
||||
// helps guarantee stderr log gets duplicated before user closes
|
||||
if (_weaken(kloghandle)) _weaken(kloghandle)();
|
||||
// for performance reasons we want to avoid holding __fds_lock()
|
||||
// while sys_close() is happening. this leaves the kernel / libc
|
||||
// having a temporarily inconsistent state. routines that obtain
|
||||
// file descriptors the way __zipos_open() does need to retry if
|
||||
// there's indication this race condition happened.
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
rc = _weaken(__zipos_close)(fd);
|
||||
} else {
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
rc = sys_close(fd);
|
||||
} else if (IsMetal()) {
|
||||
rc = 0;
|
||||
} else {
|
||||
if (__isfdkind(fd, kFdEpoll)) {
|
||||
rc = _weaken(sys_close_epoll_nt)(fd);
|
||||
} else if (__isfdkind(fd, kFdSocket)) {
|
||||
rc = _weaken(sys_closesocket_nt)(g_fds.p + fd);
|
||||
} else if (__isfdkind(fd, kFdFile) || //
|
||||
__isfdkind(fd, kFdConsole) || //
|
||||
__isfdkind(fd, kFdProcess)) { //
|
||||
rc = sys_close_nt(g_fds.p + fd, fd);
|
||||
} else {
|
||||
rc = eio();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!__vforked) {
|
||||
__releasefd(fd);
|
||||
}
|
||||
}
|
||||
int rc = close_impl(fd);
|
||||
if (!__vforked) __releasefd(fd);
|
||||
STRACE("close(%d) → %d% m", fd, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ static bool HasCopyFileRange(void) {
|
|||
int e;
|
||||
bool ok;
|
||||
e = errno;
|
||||
BLOCK_CANCELLATIONS;
|
||||
BLOCK_CANCELATION;
|
||||
if (IsLinux()) {
|
||||
// We modernize our detection by a few years for simplicity.
|
||||
// This system call is chosen since it's listed by pledge().
|
||||
|
@ -53,7 +53,7 @@ static bool HasCopyFileRange(void) {
|
|||
} else {
|
||||
ok = false;
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_CANCELATION;
|
||||
errno = e;
|
||||
return ok;
|
||||
}
|
||||
|
@ -98,14 +98,14 @@ static void copy_file_range_init(void) {
|
|||
* @raise EIO if a low-level i/o error happens
|
||||
* @see sendfile() for seekable → socket
|
||||
* @see splice() for fd ↔ pipe
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
*/
|
||||
ssize_t copy_file_range(int infd, int64_t *opt_in_out_inoffset, int outfd,
|
||||
int64_t *opt_in_out_outoffset, size_t uptobytes,
|
||||
uint32_t flags) {
|
||||
ssize_t rc;
|
||||
cosmo_once(&g_copy_file_range.once, copy_file_range_init);
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (!g_copy_file_range.ok) {
|
||||
rc = enosys();
|
||||
|
@ -123,7 +123,7 @@ ssize_t copy_file_range(int infd, int64_t *opt_in_out_inoffset, int outfd,
|
|||
opt_in_out_outoffset, uptobytes, flags);
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("copy_file_range(%d, %s, %d, %s, %'zu, %#x) → %'ld% m", infd,
|
||||
DescribeInOutInt64(rc, opt_in_out_inoffset), outfd,
|
||||
DescribeInOutInt64(rc, opt_in_out_outoffset), uptobytes, flags, rc);
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/copyfile.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/filesharemode.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/filetime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/madv.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
static textwindows int sys_copyfile_nt(const char *src, const char *dst,
|
||||
int flags) {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
struct {
|
||||
char16_t src16[PATH_MAX];
|
||||
char16_t dst16[PATH_MAX];
|
||||
} M;
|
||||
CheckLargeStackAllocation(&M, sizeof(M));
|
||||
#pragma GCC pop_options
|
||||
int64_t fhsrc, fhdst;
|
||||
struct NtFileTime accessed, modified;
|
||||
if (__mkntpath(src, M.src16) == -1) return -1;
|
||||
if (__mkntpath(dst, M.dst16) == -1) return -1;
|
||||
if (CopyFile(M.src16, M.dst16, !!(flags & COPYFILE_NOCLOBBER))) {
|
||||
if (flags & COPYFILE_PRESERVE_TIMESTAMPS) {
|
||||
fhsrc = CreateFile(M.src16, kNtFileReadAttributes, kNtFileShareRead, NULL,
|
||||
kNtOpenExisting, kNtFileAttributeNormal, 0);
|
||||
fhdst = CreateFile(M.dst16, kNtFileWriteAttributes, kNtFileShareRead,
|
||||
NULL, kNtOpenExisting, kNtFileAttributeNormal, 0);
|
||||
if (fhsrc != -1 && fhdst != -1) {
|
||||
GetFileTime(fhsrc, NULL, &accessed, &modified);
|
||||
SetFileTime(fhdst, NULL, &accessed, &modified);
|
||||
}
|
||||
CloseHandle(fhsrc);
|
||||
CloseHandle(fhdst);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
}
|
||||
|
||||
static int sys_copyfile(const char *src, const char *dst, int flags) {
|
||||
struct stat st;
|
||||
size_t remaining;
|
||||
ssize_t transferred;
|
||||
struct timespec amtime[2];
|
||||
int64_t inoffset, outoffset;
|
||||
int rc, srcfd, dstfd, oflags, omode;
|
||||
rc = -1;
|
||||
if ((srcfd = openat(AT_FDCWD, src, O_RDONLY, 0)) != -1) {
|
||||
if (fstat(srcfd, &st) != -1) {
|
||||
omode = st.st_mode & 0777;
|
||||
oflags = O_WRONLY | O_CREAT;
|
||||
if (flags & COPYFILE_NOCLOBBER) oflags |= O_EXCL;
|
||||
if ((dstfd = openat(AT_FDCWD, dst, oflags, omode)) != -1) {
|
||||
remaining = st.st_size;
|
||||
ftruncate(dstfd, remaining);
|
||||
inoffset = 0;
|
||||
outoffset = 0;
|
||||
while (remaining &&
|
||||
(transferred = copy_file_range(
|
||||
srcfd, &inoffset, dstfd, &outoffset, remaining, 0)) != -1) {
|
||||
remaining -= transferred;
|
||||
}
|
||||
if (!remaining) {
|
||||
rc = 0;
|
||||
if (flags & COPYFILE_PRESERVE_TIMESTAMPS) {
|
||||
amtime[0] = st.st_atim;
|
||||
amtime[1] = st.st_mtim;
|
||||
utimensat(dstfd, NULL, amtime, 0);
|
||||
}
|
||||
}
|
||||
rc |= close(dstfd);
|
||||
}
|
||||
}
|
||||
rc |= close(srcfd);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies file.
|
||||
*
|
||||
* This implementation goes 2x faster than the `cp` command that comes
|
||||
* included with most systems since we use the newer copy_file_range()
|
||||
* system call rather than sendfile().
|
||||
*
|
||||
* @param flags may have COPYFILE_PRESERVE_TIMESTAMPS, COPYFILE_NOCLOBBER
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
*/
|
||||
int _copyfile(const char *src, const char *dst, int flags) {
|
||||
if (!IsWindows() || startswith(src, "/zip/") || startswith(dst, "/zip/")) {
|
||||
return sys_copyfile(src, dst, flags);
|
||||
} else {
|
||||
return sys_copyfile_nt(src, dst, flags);
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_COPYFILE_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_COPYFILE_H_
|
||||
|
||||
#define COPYFILE_NOCLOBBER 1
|
||||
#define COPYFILE_PRESERVE_OWNER 2
|
||||
#define COPYFILE_PRESERVE_TIMESTAMPS 4
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int _copyfile(const char *, const char *, int) paramsnonnull();
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_COPYFILE_H_ */
|
|
@ -3,19 +3,19 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int begin_cancellation_point(void);
|
||||
void end_cancellation_point(int);
|
||||
int begin_cancelation_point(void);
|
||||
void end_cancelation_point(int);
|
||||
|
||||
#ifndef MODE_DBG
|
||||
#define BEGIN_CANCELLATION_POINT (void)0
|
||||
#define END_CANCELLATION_POINT (void)0
|
||||
#define BEGIN_CANCELATION_POINT (void)0
|
||||
#define END_CANCELATION_POINT (void)0
|
||||
#else
|
||||
#define BEGIN_CANCELLATION_POINT \
|
||||
#define BEGIN_CANCELATION_POINT \
|
||||
do { \
|
||||
int _Cp; \
|
||||
_Cp = begin_cancellation_point()
|
||||
#define END_CANCELLATION_POINT \
|
||||
end_cancellation_point(_Cp); \
|
||||
_Cp = begin_cancelation_point()
|
||||
#define END_CANCELATION_POINT \
|
||||
end_cancelation_point(_Cp); \
|
||||
} \
|
||||
while (0)
|
||||
#endif
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
* @param mode is octal bits, e.g. 0644 usually
|
||||
* @return file descriptor, or -1 w/ errno
|
||||
* @see openat() for further documentation
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
|
|
|
@ -20,24 +20,26 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
// Implements dup(), dup2(), dup3(), and F_DUPFD for Windows.
|
||||
textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
|
||||
int64_t rc, proc, handle;
|
||||
static textwindows int sys_dup_nt_impl(int oldfd, int newfd, int flags,
|
||||
int start) {
|
||||
int64_t rc, handle;
|
||||
unassert(!(flags & ~O_CLOEXEC));
|
||||
|
||||
__fds_lock();
|
||||
|
||||
if (!__isfdopen(oldfd) || newfd < -1 ||
|
||||
(g_fds.p[oldfd].kind != kFdFile && g_fds.p[oldfd].kind != kFdSocket &&
|
||||
g_fds.p[oldfd].kind != kFdConsole)) {
|
||||
if (!__isfdopen(oldfd) || newfd < -1) {
|
||||
__fds_unlock();
|
||||
return ebadf();
|
||||
}
|
||||
|
@ -54,29 +56,19 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
|
|||
return -1;
|
||||
}
|
||||
if (g_fds.p[newfd].kind) {
|
||||
sys_close_nt(g_fds.p + newfd, newfd);
|
||||
bzero(g_fds.p + newfd, sizeof(*g_fds.p));
|
||||
sys_close_nt(newfd, newfd);
|
||||
}
|
||||
}
|
||||
|
||||
handle = g_fds.p[oldfd].handle;
|
||||
proc = GetCurrentProcess();
|
||||
|
||||
if (DuplicateHandle(proc, handle, proc, &g_fds.p[newfd].handle, 0, false,
|
||||
if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle,
|
||||
GetCurrentProcess(), &handle, 0, true,
|
||||
kNtDuplicateSameAccess)) {
|
||||
g_fds.p[newfd].kind = g_fds.p[oldfd].kind;
|
||||
g_fds.p[newfd].mode = g_fds.p[oldfd].mode;
|
||||
g_fds.p[newfd].flags = g_fds.p[oldfd].flags & ~O_CLOEXEC;
|
||||
if (flags & O_CLOEXEC) g_fds.p[newfd].flags |= O_CLOEXEC;
|
||||
if (g_fds.p[oldfd].kind == kFdSocket && _weaken(_dupsockfd)) {
|
||||
g_fds.p[newfd].extra =
|
||||
(intptr_t)_weaken(_dupsockfd)((struct SockFd *)g_fds.p[oldfd].extra);
|
||||
} else if (g_fds.p[oldfd].kind == kFdConsole) {
|
||||
unassert(DuplicateHandle(proc, g_fds.p[oldfd].extra, proc,
|
||||
&g_fds.p[newfd].extra, 0, false,
|
||||
kNtDuplicateSameAccess));
|
||||
g_fds.p[newfd] = g_fds.p[oldfd];
|
||||
g_fds.p[newfd].handle = handle;
|
||||
if (flags & O_CLOEXEC) {
|
||||
g_fds.p[newfd].flags |= O_CLOEXEC;
|
||||
} else {
|
||||
g_fds.p[newfd].extra = g_fds.p[oldfd].extra;
|
||||
g_fds.p[newfd].flags &= ~O_CLOEXEC;
|
||||
}
|
||||
rc = newfd;
|
||||
} else {
|
||||
|
@ -87,3 +79,11 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
|
|||
__fds_unlock();
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
|
||||
int rc;
|
||||
BLOCK_SIGNALS;
|
||||
rc = sys_dup_nt_impl(oldfd, newfd, flags, start);
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
|
@ -28,8 +30,8 @@
|
|||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_fadvise_nt(int fd, uint64_t offset, uint64_t len,
|
||||
int advice) {
|
||||
static textwindows int sys_fadvise_nt_impl(int fd, uint64_t offset,
|
||||
uint64_t len, int advice) {
|
||||
int64_t h1, h2;
|
||||
int rc, flags, mode;
|
||||
uint32_t perm, share, attr;
|
||||
|
@ -85,3 +87,12 @@ textwindows int sys_fadvise_nt(int fd, uint64_t offset, uint64_t len,
|
|||
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows int sys_fadvise_nt(int fd, uint64_t offset, uint64_t len,
|
||||
int advice) {
|
||||
int rc;
|
||||
BLOCK_SIGNALS;
|
||||
rc = sys_fadvise_nt_impl(fd, offset, len, advice);
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/wincrash.internal.h"
|
||||
|
@ -125,11 +126,17 @@ textwindows void sys_fcntl_nt_lock_cleanup(int fd) {
|
|||
pthread_mutex_unlock(&g_locks.mu);
|
||||
}
|
||||
|
||||
static textwindows int64_t GetfileSize(int64_t handle) {
|
||||
struct NtByHandleFileInformation wst;
|
||||
if (!GetFileInformationByHandle(handle, &wst)) return __winerr();
|
||||
return (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
|
||||
}
|
||||
|
||||
static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd,
|
||||
uintptr_t arg) {
|
||||
uint32_t flags;
|
||||
struct flock *l;
|
||||
int64_t pos, off, len, end;
|
||||
int64_t off, len, end;
|
||||
struct FileLock *fl, *ft, **flp;
|
||||
|
||||
if (!_weaken(malloc)) {
|
||||
|
@ -144,16 +151,14 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd,
|
|||
case SEEK_SET:
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
pos = 0;
|
||||
if (SetFilePointerEx(f->handle, 0, &pos, SEEK_CUR)) {
|
||||
off = pos + off;
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
off = f->pointer + off;
|
||||
break;
|
||||
case SEEK_END:
|
||||
off = INT64_MAX - off;
|
||||
case SEEK_END: {
|
||||
int64_t size;
|
||||
if ((size = GetfileSize(f->handle)) == -1) return -1;
|
||||
off = size - off;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return einval();
|
||||
}
|
||||
|
@ -363,6 +368,7 @@ static textwindows int sys_fcntl_nt_setfl(int fd, unsigned *flags,
|
|||
|
||||
textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
|
||||
int rc;
|
||||
BLOCK_SIGNALS;
|
||||
if (__isfdkind(fd, kFdFile) || //
|
||||
__isfdkind(fd, kFdSocket) || //
|
||||
__isfdkind(fd, kFdConsole)) {
|
||||
|
@ -397,5 +403,6 @@ textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
|
|||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
* @raise EDEADLK if `cmd` was `F_SETLKW` and waiting would deadlock
|
||||
* @raise EMFILE if `cmd` is `F_DUPFD` or `F_DUPFD_CLOEXEC` and
|
||||
* `RLIMIT_NOFILE` would be exceeded
|
||||
* @cancellationpoint when `cmd` is `F_SETLKW` or `F_OFD_SETLKW`
|
||||
* @cancelationpoint when `cmd` is `F_SETLKW` or `F_OFD_SETLKW`
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
|
@ -120,9 +120,9 @@ int fcntl(int fd, int cmd, ...) {
|
|||
rc = _weaken(__zipos_fcntl)(fd, cmd, arg);
|
||||
} else if (!IsWindows()) {
|
||||
if (cmd == F_SETLKW || cmd == F_OFD_SETLKW) {
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl_cp);
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
} else {
|
||||
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl);
|
||||
}
|
||||
|
|
|
@ -17,15 +17,20 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
textwindows int sys_fdatasync_nt(int fd, bool fake) {
|
||||
if (!__isfdopen(fd)) return ebadf();
|
||||
if (!__isfdkind(fd, kFdFile)) return einval();
|
||||
if (GetFileType(g_fds.p[fd].handle) != kNtFileTypeDisk) return einval();
|
||||
if (_check_interrupts(0)) return -1;
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (_check_signal(false) == -1) return -1;
|
||||
if (fake) return 0;
|
||||
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : -1;
|
||||
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : __winerr();
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -39,13 +39,13 @@
|
|||
* @raise EIO if an i/o error happened
|
||||
* @see sync(), fsync(), sync_file_range()
|
||||
* @see __nosync to secretly disable
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int fdatasync(int fd) {
|
||||
int rc;
|
||||
bool fake = __nosync == 0x5453455454534146;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
rc = erofs();
|
||||
} else if (!IsWindows()) {
|
||||
|
@ -57,7 +57,7 @@ int fdatasync(int fd) {
|
|||
} else {
|
||||
rc = sys_fdatasync_nt(fd, fake);
|
||||
}
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("fdatasync%s(%d) → %d% m", fake ? "_fake" : "", fd, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -31,12 +31,12 @@
|
|||
* @param op can have LOCK_{SH,EX,NB,UN} for shared, exclusive,
|
||||
* non-blocking, and unlocking
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @restartable
|
||||
*/
|
||||
int flock(int fd, int op) {
|
||||
int rc;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (!IsWindows()) {
|
||||
rc = sys_flock(fd, op);
|
||||
|
@ -44,7 +44,7 @@ int flock(int fd, int op) {
|
|||
rc = sys_flock_nt(fd, op);
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("flock(%d, %d) → %d% m", fd, op, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/struct/stat.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
|
@ -33,6 +35,7 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
|
|||
int64_t fh;
|
||||
uint16_t path16[PATH_MAX];
|
||||
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
|
||||
BLOCK_SIGNALS;
|
||||
if ((fh = CreateFile(
|
||||
path16, kNtFileGenericRead,
|
||||
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, 0,
|
||||
|
@ -46,5 +49,6 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
|
|||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
ALLOW_SIGNALS;
|
||||
return __fix_enotdir(rc, path16);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ENOTSUP if /zip path
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
*/
|
||||
int fstatfs(int fd, struct statfs *sf) {
|
||||
#pragma GCC push_options
|
||||
|
@ -41,7 +41,7 @@ int fstatfs(int fd, struct statfs *sf) {
|
|||
CheckLargeStackAllocation(&m, sizeof(m));
|
||||
#pragma GCC pop_options
|
||||
int rc;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = enotsup();
|
||||
|
@ -55,7 +55,7 @@ int fstatfs(int fd, struct statfs *sf) {
|
|||
rc = ebadf();
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("fstatfs(%d, [%s]) → %d% m", fd, DescribeStatfs(rc, sf));
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -39,13 +39,13 @@
|
|||
* @raise EIO if an i/o error happened
|
||||
* @see fdatasync(), sync_file_range()
|
||||
* @see __nosync to secretly disable
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int fsync(int fd) {
|
||||
int rc;
|
||||
bool fake = __nosync == 0x5453455454534146;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
rc = erofs();
|
||||
} else if (!IsWindows()) {
|
||||
|
@ -57,7 +57,7 @@ int fsync(int fd) {
|
|||
} else {
|
||||
rc = sys_fdatasync_nt(fd, fake);
|
||||
}
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("fsync%s(%d) → %d% m", fake ? "_fake" : "", fd, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -17,23 +17,17 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/enum/filemovemethod.h"
|
||||
#include "libc/nt/enum/fileinfobyhandleclass.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_ftruncate_nt(int64_t handle, uint64_t length) {
|
||||
bool32 ok;
|
||||
int64_t tell;
|
||||
tell = -1;
|
||||
if ((ok = SetFilePointerEx(handle, 0, &tell, kNtFileCurrent))) {
|
||||
ok = SetFilePointerEx(handle, length, NULL, kNtFileBegin) &&
|
||||
SetEndOfFile(handle);
|
||||
npassert(SetFilePointerEx(handle, tell, NULL, kNtFileBegin));
|
||||
}
|
||||
if (ok) {
|
||||
if (SetFileInformationByHandle(handle, kNtFileAllocationInfo, &length,
|
||||
sizeof(length))) {
|
||||
return 0;
|
||||
} else if (GetLastError() == kNtErrorAccessDenied) {
|
||||
return einval(); // ftruncate() doesn't raise EACCES
|
||||
|
|
|
@ -58,12 +58,12 @@
|
|||
* @raise EINVAL if `fd` wasn't opened in a writeable mode
|
||||
* @raise EROFS if `fd` is on a read-only filesystem (e.g. zipos)
|
||||
* @raise ENOSYS on bare metal
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int ftruncate(int fd, int64_t length) {
|
||||
int rc;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (fd < 0) {
|
||||
rc = ebadf();
|
||||
|
@ -82,7 +82,7 @@ int ftruncate(int fd, int64_t length) {
|
|||
rc = ebadf();
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("ftruncate(%d, %'ld) → %d% m", fd, length, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/nt/process.h"
|
||||
#include "libc/nt/ntdll.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/processbasicinformation.h"
|
||||
|
@ -31,7 +31,6 @@ textwindows int sys_getppid_nt(void) {
|
|||
sizeof(ProcessInformation), &gotsize)) &&
|
||||
gotsize >= sizeof(ProcessInformation) &&
|
||||
ProcessInformation.InheritedFromUniqueProcessId) {
|
||||
/* TODO(jart): Fix type mismatch and do we need to close this? */
|
||||
return ProcessInformation.InheritedFromUniqueProcessId;
|
||||
}
|
||||
return GetCurrentProcessId();
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/enum/processaccess.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sysv/consts/prio.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_getpriority_nt(int which, unsigned pid) {
|
||||
int rc;
|
||||
uint32_t tier;
|
||||
int64_t h, closeme = -1;
|
||||
|
||||
if (which != PRIO_PROCESS) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
if (!pid || pid == getpid()) {
|
||||
h = GetCurrentProcess();
|
||||
} else if (__isfdkind(pid, kFdProcess)) {
|
||||
h = g_fds.p[pid].handle;
|
||||
} else {
|
||||
h = OpenProcess(kNtProcessQueryInformation, false, pid);
|
||||
if (!h) return __winerr();
|
||||
closeme = h;
|
||||
}
|
||||
|
||||
if ((tier = GetPriorityClass(h))) {
|
||||
switch (tier) {
|
||||
case kNtRealtimePriorityClass:
|
||||
rc = -16;
|
||||
break;
|
||||
case kNtHighPriorityClass:
|
||||
rc = -10;
|
||||
break;
|
||||
case kNtAboveNormalPriorityClass:
|
||||
rc = -5;
|
||||
break;
|
||||
case kNtNormalPriorityClass:
|
||||
rc = 0;
|
||||
break;
|
||||
case kNtBelowNormalPriorityClass:
|
||||
rc = 5;
|
||||
break;
|
||||
case kNtIdlePriorityClass:
|
||||
rc = 15;
|
||||
break;
|
||||
default:
|
||||
notpossible;
|
||||
}
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
|
||||
if (closeme != -1) {
|
||||
CloseHandle(closeme);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asmflag.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Returns nice value of thing.
|
||||
*
|
||||
* Since -1 might be a valid return value for this API, it's necessary
|
||||
* to clear `errno` beforehand and see if it changed, in order to truly
|
||||
* determine if an error happened.
|
||||
*
|
||||
* On Windows, there's only six priority classes. We define them as -16
|
||||
* (realtime), -10 (high), -5 (above), 0 (normal), 5 (below), 15 (idle)
|
||||
* which are the only values that'll roundtrip getpriority/setpriority.
|
||||
*
|
||||
* @param which can be one of:
|
||||
* - `PRIO_PROCESS` is supported universally
|
||||
* - `PRIO_PGRP` is supported on unix
|
||||
* - `PRIO_USER` is supported on unix
|
||||
* @param who is the pid, pgid, or uid (0 means current)
|
||||
* @return value ∈ [-NZERO,NZERO) or -1 w/ errno
|
||||
* @raise EINVAL if `which` was invalid or unsupported
|
||||
* @raise EPERM if access to process was denied
|
||||
* @raise ESRCH if no such process existed
|
||||
* @see setpriority()
|
||||
*/
|
||||
int getpriority(int which, unsigned who) {
|
||||
int rc;
|
||||
#ifdef __x86_64__
|
||||
char cf;
|
||||
if (IsLinux()) {
|
||||
asm volatile("syscall"
|
||||
: "=a"(rc)
|
||||
: "0"(140), "D"(which), "S"(who)
|
||||
: "rcx", "r11", "memory");
|
||||
if (rc >= 0) {
|
||||
rc = NZERO - rc;
|
||||
} else {
|
||||
errno = -rc;
|
||||
rc = -1;
|
||||
}
|
||||
} else if (IsBsd()) {
|
||||
asm volatile(CFLAG_ASM("syscall")
|
||||
: CFLAG_CONSTRAINT(cf), "=a"(rc)
|
||||
: "1"((IsXnu() ? 0x2000000 : 0) | 100), "D"(which), "S"(who)
|
||||
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory");
|
||||
if (cf) {
|
||||
errno = rc;
|
||||
rc = -1;
|
||||
}
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_getpriority_nt(which, who);
|
||||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
#else
|
||||
rc = sys_getpriority(which, who);
|
||||
if (rc != -1) {
|
||||
rc = NZERO - rc;
|
||||
}
|
||||
#endif
|
||||
STRACE("getpriority(%s, %u) → %d% m", DescribeWhichPrio(which), who, rc);
|
||||
return rc;
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
|
@ -42,7 +43,6 @@
|
|||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/xorshift.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
|
@ -181,18 +181,18 @@ ssize_t __getrandom(void *p, size_t n, unsigned f) {
|
|||
if (IsXnu() || IsOpenbsd()) {
|
||||
rc = GetRandomBsd(p, n, GetRandomEntropy);
|
||||
} else {
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
rc = sys_getrandom(p, n, f);
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
}
|
||||
} else if (IsFreebsd() || IsNetbsd()) {
|
||||
rc = GetRandomBsd(p, n, GetRandomArnd);
|
||||
} else if (IsMetal()) {
|
||||
rc = GetRandomMetal(p, n, f);
|
||||
} else {
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
rc = GetDevUrandom(p, n);
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ ssize_t __getrandom(void *p, size_t n, unsigned f) {
|
|||
* On BSD OSes, this entire process is uninterruptible so be careful
|
||||
* when using large sizes if interruptibility is needed.
|
||||
*
|
||||
* Unlike getentropy() this function is a cancellation point. But it
|
||||
* Unlike getentropy() this function is a cancelation point. But it
|
||||
* shouldn't be a problem, unless you're using masked mode, in which
|
||||
* case extra care must be taken to consider the result.
|
||||
*
|
||||
|
@ -243,7 +243,7 @@ ssize_t __getrandom(void *p, size_t n, unsigned f) {
|
|||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @raise EFAULT if the `n` bytes at `p` aren't valid memory
|
||||
* @raise EINTR if we needed to block and a signal was delivered instead
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
|
@ -264,13 +264,13 @@ ssize_t getrandom(void *p, size_t n, unsigned f) {
|
|||
__attribute__((__constructor__)) static textstartup void getrandom_init(void) {
|
||||
int e, rc;
|
||||
if (IsWindows() || IsMetal()) return;
|
||||
BLOCK_CANCELLATIONS;
|
||||
BLOCK_CANCELATION;
|
||||
e = errno;
|
||||
if (!(rc = sys_getrandom(0, 0, 0))) {
|
||||
have_getrandom = true;
|
||||
} else {
|
||||
errno = e;
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_CANCELATION;
|
||||
STRACE("sys_getrandom(0,0,0) → %d% m", rc);
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/rusage.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/filetime.h"
|
||||
#include "libc/nt/struct/iocounters.h"
|
||||
#include "libc/nt/struct/processmemorycounters.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/sysv/consts/rusage.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_getrusage_nt(int who, struct rusage *usage) {
|
||||
int64_t me;
|
||||
struct NtIoCounters iocount;
|
||||
struct NtProcessMemoryCountersEx memcount;
|
||||
struct NtFileTime ftExit, ftUser, ftKernel, ftCreation;
|
||||
if (!usage) return efault();
|
||||
if (who == 99) return enosys(); // @see libc/sysv/consts.sh
|
||||
if (!usage) return 0;
|
||||
me = GetCurrentProcess();
|
||||
if (!(who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)(
|
||||
who == RUSAGE_SELF ? GetCurrentProcess() : GetCurrentThread(),
|
||||
&ftCreation, &ftExit, &ftKernel, &ftUser) ||
|
||||
!GetProcessMemoryInfo(me, &memcount, sizeof(memcount)) ||
|
||||
!GetProcessIoCounters(me, &iocount)) {
|
||||
return __winerr();
|
||||
}
|
||||
*usage = (struct rusage){
|
||||
.ru_utime = WindowsDurationToTimeVal(ReadFileTime(ftUser)),
|
||||
.ru_stime = WindowsDurationToTimeVal(ReadFileTime(ftKernel)),
|
||||
.ru_maxrss = memcount.PeakWorkingSetSize / 1024,
|
||||
.ru_majflt = memcount.PageFaultCount,
|
||||
.ru_inblock = iocount.ReadOperationCount,
|
||||
.ru_oublock = iocount.WriteOperationCount,
|
||||
.ru_nsignals = __sig.count,
|
||||
};
|
||||
return 0;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/rusage.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Returns resource usage statistics.
|
||||
*
|
||||
* @param who can be RUSAGE_{SELF,CHILDREN,THREAD}
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
*/
|
||||
int sys_getrusage(int who, struct rusage *usage) {
|
||||
int rc;
|
||||
if ((rc = __sys_getrusage(who, usage)) != -1) {
|
||||
__rusage2linux(usage);
|
||||
}
|
||||
return rc;
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/rusage.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Returns resource usage statistics.
|
||||
*
|
||||
* @param who can be RUSAGE_{SELF,CHILDREN,THREAD}
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
*/
|
||||
int getrusage(int who, struct rusage *usage) {
|
||||
int rc;
|
||||
if (who == 99) {
|
||||
rc = einval();
|
||||
} else if (IsAsan() && !__asan_is_valid(usage, sizeof(*usage))) {
|
||||
rc = efault();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_getrusage(who, usage);
|
||||
} else {
|
||||
rc = sys_getrusage_nt(who, usage);
|
||||
}
|
||||
STRACE("getrusage(%d, %p) → %d% m", who, usage, rc);
|
||||
return rc;
|
||||
}
|
|
@ -6,8 +6,7 @@
|
|||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */
|
||||
#define kSigOpRestartable 1
|
||||
#define kSigactionMinRva 8 /* >SIG_{ERR,DFL,IGN,...} */
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
@ -22,13 +21,15 @@ int __reservefd(int);
|
|||
int __reservefd_unlocked(int);
|
||||
void __releasefd(int);
|
||||
int __ensurefds(int);
|
||||
int __ensurefds_unlocked(int);
|
||||
void __printfds(void);
|
||||
uint32_t sys_getuid_nt(void);
|
||||
int __pause_thread(uint32_t);
|
||||
int __ensurefds_unlocked(int);
|
||||
void __printfds(struct Fd *, size_t);
|
||||
int IsWindowsExecutable(int64_t);
|
||||
int CountConsoleInputBytes(struct Fd *);
|
||||
int FlushConsoleInputBytes(int64_t);
|
||||
int CountConsoleInputBytes(void);
|
||||
int FlushConsoleInputBytes(void);
|
||||
int64_t GetConsoleInputHandle(void);
|
||||
int64_t GetConsoleOutputHandle(void);
|
||||
void InterceptTerminalCommands(const char *, size_t);
|
||||
|
||||
forceinline int64_t __getfdhandleactual(int fd) {
|
||||
return g_fds.p[fd].handle;
|
||||
|
@ -42,8 +43,11 @@ forceinline bool __isfdkind(int fd, int kind) {
|
|||
return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind == kind;
|
||||
}
|
||||
|
||||
int _check_interrupts(int);
|
||||
int sys_close_nt(struct Fd *, int);
|
||||
int _check_signal(bool);
|
||||
int _check_cancel(void);
|
||||
int sys_close_nt(int, int);
|
||||
int _park_norestart(uint32_t, uint64_t);
|
||||
int _park_restartable(uint32_t, uint64_t);
|
||||
int sys_openat_metal(int, const char *, int, unsigned);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
|
@ -18,31 +18,28 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
textwindows int _check_interrupts(int sigops) {
|
||||
int status;
|
||||
errno_t err;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
if (_weaken(pthread_testcancel_np) &&
|
||||
(err = _weaken(pthread_testcancel_np)())) {
|
||||
goto Interrupted;
|
||||
}
|
||||
if (_weaken(__sig_check) && (status = _weaken(__sig_check)())) {
|
||||
STRACE("syscall interrupted (status=%d, sigops=%d)", status, sigops);
|
||||
if (status == 2 && (sigops & kSigOpRestartable)) {
|
||||
STRACE("restarting system call");
|
||||
return 0;
|
||||
}
|
||||
err = EINTR;
|
||||
Interrupted:
|
||||
pt->abort_errno = errno = err;
|
||||
return -1;
|
||||
textwindows int _check_cancel(void) {
|
||||
if (_weaken(_pthread_cancel_ack) && //
|
||||
_pthread_self() && !(_pthread_self()->pt_flags & PT_NOCANCEL) &&
|
||||
atomic_load_explicit(&_pthread_self()->pt_canceled,
|
||||
memory_order_acquire)) {
|
||||
return _weaken(_pthread_cancel_ack)();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
textwindows int _check_signal(bool restartable) {
|
||||
int status;
|
||||
if (!_weaken(__sig_check)) return 0;
|
||||
if (!(status = _weaken(__sig_check)())) return 0;
|
||||
if (status == 2 && restartable) return 0;
|
||||
return eintr();
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -91,7 +91,6 @@ static int ioctl_default(int fd, unsigned long request, void *arg) {
|
|||
|
||||
static int ioctl_fionread(int fd, uint32_t *arg) {
|
||||
int rc;
|
||||
uint32_t cm;
|
||||
int64_t handle;
|
||||
if (!IsWindows()) {
|
||||
return sys_ioctl(fd, FIONREAD, arg);
|
||||
|
@ -103,6 +102,10 @@ static int ioctl_fionread(int fd, uint32_t *arg) {
|
|||
} else {
|
||||
return _weaken(__winsockerr)();
|
||||
}
|
||||
} else if (g_fds.p[fd].kind == kFdConsole) {
|
||||
int bytes = CountConsoleInputBytes();
|
||||
*arg = MAX(0, bytes);
|
||||
return 0;
|
||||
} else if (GetFileType(handle) == kNtFileTypePipe) {
|
||||
uint32_t avail;
|
||||
if (PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) {
|
||||
|
@ -113,10 +116,6 @@ static int ioctl_fionread(int fd, uint32_t *arg) {
|
|||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
} else if (GetConsoleMode(handle, &cm)) {
|
||||
int bytes = CountConsoleInputBytes(g_fds.p + fd);
|
||||
*arg = MAX(0, bytes);
|
||||
return 0;
|
||||
} else {
|
||||
return eopnotsupp();
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
bool32 sys_isatty_metal(int fd) {
|
||||
if (__isfdopen(fd)) {
|
||||
if (__isfdkind(fd, kFdSerial)) {
|
||||
return true;
|
||||
} else {
|
||||
enotty();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ebadf();
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -17,14 +17,13 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows bool32 sys_isatty_nt(int fd) {
|
||||
bool32 sys_isatty(int fd) {
|
||||
if (__isfdopen(fd)) {
|
||||
uint32_t mode;
|
||||
if (GetConsoleMode(g_fds.p[fd].handle, &mode)) {
|
||||
if (__isfdkind(fd, kFdConsole) || __isfdkind(fd, kFdSerial)) {
|
||||
return true;
|
||||
} else {
|
||||
enotty();
|
||||
|
|
|
@ -42,10 +42,8 @@ bool32 isatty(int fd) {
|
|||
if (__isfdkind(fd, kFdZip)) {
|
||||
enotty();
|
||||
res = false;
|
||||
} else if (IsWindows()) {
|
||||
res = sys_isatty_nt(fd);
|
||||
} else if (IsMetal()) {
|
||||
res = sys_isatty_metal(fd);
|
||||
} else if (IsWindows() || IsMetal()) {
|
||||
res = sys_isatty(fd);
|
||||
} else if (!sys_ioctl(fd, TIOCGWINSZ, &ws)) {
|
||||
res = true;
|
||||
} else {
|
||||
|
|
|
@ -67,11 +67,9 @@ textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) {
|
|||
int filetype = GetFileType(f->handle);
|
||||
if (filetype != kNtFileTypePipe && filetype != kNtFileTypeChar) {
|
||||
int64_t res;
|
||||
pthread_mutex_lock(&f->lock);
|
||||
if ((res = Seek(f, offset, whence)) != -1) {
|
||||
f->pointer = res;
|
||||
}
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
return res;
|
||||
} else {
|
||||
return espipe();
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
* with random text on success (and not modified on error)
|
||||
* @return pointer to template on success, or NULL w/ errno
|
||||
* @raise EINVAL if template didn't end with XXXXXX
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
*/
|
||||
char *mkdtemp(char *template) {
|
||||
int n;
|
||||
|
|
140
libc/calls/mkntcmdline.c
Normal file
140
libc/calls/mkntcmdline.c
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/thompike.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define APPEND(c) \
|
||||
do { \
|
||||
if (k == 32766) { \
|
||||
return e2big(); \
|
||||
} \
|
||||
cmdline[k++] = c; \
|
||||
} while (0)
|
||||
|
||||
static bool NeedsQuotes(const char *s) {
|
||||
if (!*s) {
|
||||
return true;
|
||||
}
|
||||
do {
|
||||
switch (*s) {
|
||||
case '"':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\n':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (*s++);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
// Converts System V argv to Windows-style command line.
|
||||
//
|
||||
// Escaping is performed and it's designed to round-trip with
|
||||
// GetDosArgv() or GetDosArgv(). This function does NOT escape
|
||||
// command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd).
|
||||
//
|
||||
// @param cmdline is output buffer
|
||||
// @param argv is an a NULL-terminated array of UTF-8 strings
|
||||
// @return 0 on success, or -1 w/ errno
|
||||
// @raise E2BIG if everything is too huge
|
||||
// @see "Everyone quotes command line arguments the wrong way" MSDN
|
||||
// @see libc/runtime/getdosargv.c
|
||||
// @asyncsignalsafe
|
||||
textwindows int mkntcmdline(char16_t cmdline[32767], char *const argv[]) {
|
||||
int slashes, n;
|
||||
bool needsquote;
|
||||
size_t i, j, k, s;
|
||||
for (k = i = 0; argv[i]; ++i) {
|
||||
if (i) APPEND(u' ');
|
||||
if ((needsquote = NeedsQuotes(argv[i]))) APPEND(u'"');
|
||||
for (slashes = j = 0;;) {
|
||||
wint_t x = argv[i][j++] & 255;
|
||||
if (x >= 0300) {
|
||||
n = ThomPikeLen(x);
|
||||
x = ThomPikeByte(x);
|
||||
while (--n) {
|
||||
wint_t y;
|
||||
if ((y = argv[i][j++] & 255)) {
|
||||
x = ThomPikeMerge(x, y);
|
||||
} else {
|
||||
x = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!x) break;
|
||||
if (x == '/' || x == '\\') {
|
||||
if (!i) {
|
||||
// turn / into \ for first argv[i]
|
||||
x = '\\';
|
||||
// turn \c\... into c:\ for first argv[i]
|
||||
if (k == 2 && IsAlpha(cmdline[1]) && cmdline[0] == '\\') {
|
||||
cmdline[0] = cmdline[1];
|
||||
cmdline[1] = ':';
|
||||
}
|
||||
} else {
|
||||
// turn stuff like `less /c/...`
|
||||
// into `less c:/...`
|
||||
// turn stuff like `more <"/c/..."`
|
||||
// into `more <"c:/..."`
|
||||
if (k > 3 && IsAlpha(cmdline[k - 1]) &&
|
||||
(cmdline[k - 2] == '/' || cmdline[k - 2] == '\\') &&
|
||||
(cmdline[k - 3] == '"' || cmdline[k - 3] == ' ')) {
|
||||
cmdline[k - 2] = cmdline[k - 1];
|
||||
cmdline[k - 1] = ':';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x == '\\') {
|
||||
++slashes;
|
||||
} else if (x == '"') {
|
||||
APPEND(u'"');
|
||||
APPEND(u'"');
|
||||
APPEND(u'"');
|
||||
} else {
|
||||
for (s = 0; s < slashes; ++s) {
|
||||
APPEND(u'\\');
|
||||
}
|
||||
slashes = 0;
|
||||
uint32_t w = EncodeUtf16(x);
|
||||
do APPEND(w);
|
||||
while ((w >>= 16));
|
||||
}
|
||||
}
|
||||
for (s = 0; s < (slashes << needsquote); ++s) {
|
||||
APPEND(u'\\');
|
||||
}
|
||||
if (needsquote) {
|
||||
APPEND(u'"');
|
||||
}
|
||||
}
|
||||
cmdline[k] = 0;
|
||||
return 0;
|
||||
}
|
196
libc/calls/mkntenvblock.c
Normal file
196
libc/calls/mkntenvblock.c
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/intrin/getenv.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
|
||||
|
||||
struct EnvBuilder {
|
||||
char *buf;
|
||||
char **var;
|
||||
int bufi;
|
||||
int vari;
|
||||
};
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
static textwindows int Compare(const char *l, const char *r) {
|
||||
int a, b;
|
||||
size_t i = 0;
|
||||
for (;;) {
|
||||
a = l[i] & 255;
|
||||
b = r[i] & 255;
|
||||
if (a == '=') a = 0;
|
||||
if (b == '=') b = 0;
|
||||
if (a != b || !b) break;
|
||||
++i;
|
||||
}
|
||||
return a - b;
|
||||
}
|
||||
|
||||
static textwindows void FixPath(char *path) {
|
||||
char *p;
|
||||
|
||||
// turn colon into semicolon
|
||||
// unless it already looks like a dos path
|
||||
for (p = path; *p; ++p) {
|
||||
if (p[0] == ':' && p[1] != '\\') {
|
||||
p[0] = ';';
|
||||
}
|
||||
}
|
||||
|
||||
// turn /c/... into c:\...
|
||||
p = path;
|
||||
if (p[0] == '/' && IsAlpha(p[1]) && p[2] == '/') {
|
||||
p[0] = p[1];
|
||||
p[1] = ':';
|
||||
}
|
||||
for (; *p; ++p) {
|
||||
if (p[0] == ';' && p[1] == '/' && IsAlpha(p[2]) && p[3] == '/') {
|
||||
p[1] = p[2];
|
||||
p[2] = ':';
|
||||
}
|
||||
}
|
||||
|
||||
// turn slash into backslash
|
||||
for (p = path; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\\';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows int InsertString(struct EnvBuilder *env, const char *str) {
|
||||
int c, i, cmp;
|
||||
char *var, *path = 0;
|
||||
|
||||
if (!str) return 0;
|
||||
|
||||
// copy key=val to buf
|
||||
var = env->buf + env->bufi;
|
||||
do {
|
||||
c = *str++;
|
||||
if (env->bufi + 2 > 32767) return e2big();
|
||||
env->buf[env->bufi++] = c;
|
||||
if (c == '=' && str[0] == '/' && IsAlpha(str[1]) && str[2] == '/') {
|
||||
path = env->buf + env->bufi;
|
||||
}
|
||||
} while (c);
|
||||
|
||||
// fixup key=/c/... → key=c:\...
|
||||
if (path) FixPath(path);
|
||||
|
||||
// append key=val to sorted list using insertion sort technique
|
||||
for (i = env->vari;; --i) {
|
||||
if (!i || (cmp = Compare(var, env->var[i - 1])) > 0) {
|
||||
// insert entry for new key
|
||||
env->var[i] = var;
|
||||
env->vari++;
|
||||
break;
|
||||
}
|
||||
if (!cmp) {
|
||||
// deduplicate preferring latter
|
||||
env->var[i - 1] = var;
|
||||
for (; i < env->vari; ++i) {
|
||||
env->var[i] = env->var[i + 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
// sift items right to create empty slot at insertion point
|
||||
env->var[i] = env->var[i - 1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows int InsertStrings(struct EnvBuilder *env,
|
||||
char *const strs[]) {
|
||||
if (strs) {
|
||||
for (int i = 0; strs[i]; ++i) {
|
||||
if (InsertString(env, strs[i]) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows int CountStrings(char *const strs[]) {
|
||||
int n = 0;
|
||||
if (strs) {
|
||||
while (*strs++) {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies sorted environment variable block for Windows.
|
||||
*
|
||||
* This is designed to meet the requirements of CreateProcess().
|
||||
*
|
||||
* @param envblock receives sorted double-NUL terminated string list
|
||||
* @param envp is an a NULL-terminated array of UTF-8 strings
|
||||
* @param extravar is a VAR=val string we consider part of envp or NULL
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @error E2BIG if total number of shorts (including nul) exceeded 32767
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
textwindows int mkntenvblock(char16_t envblock[32767], char *const envp[],
|
||||
char *const extravars[], char buf[32767]) {
|
||||
int i, k, n;
|
||||
struct Env e;
|
||||
struct EnvBuilder env = {buf};
|
||||
|
||||
// allocate string pointer array for sorting purposes
|
||||
n = (CountStrings(envp) + CountStrings(extravars) + 1) * sizeof(char *);
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
env.var = alloca(n);
|
||||
CheckLargeStackAllocation(env.var, n);
|
||||
#pragma GCC pop_options
|
||||
|
||||
// load new environment into string pointer array and fix file paths
|
||||
if (InsertStrings(&env, envp) == -1) return -1;
|
||||
if (InsertStrings(&env, extravars) == -1) return -1;
|
||||
if (environ) {
|
||||
// https://jpassing.com/2009/12/28/the-hidden-danger-of-forgetting-to-specify-systemroot-in-a-custom-environment-block/
|
||||
e = __getenv(environ, "SYSTEMROOT");
|
||||
if (e.s && InsertString(&env, environ[e.i]) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// copy utf-8 sorted string pointer array into contiguous utf-16 block
|
||||
// in other words, we're creating a double-nul terminated string list!
|
||||
for (k = i = 0; i < env.vari; ++i) {
|
||||
k += tprecode8to16(envblock + k, -1, env.var[i]).ax + 1;
|
||||
}
|
||||
unassert(k <= env.bufi);
|
||||
envblock[k] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -34,7 +34,7 @@
|
|||
* @see mkstemp() if you don't need flags
|
||||
* @see mktemp() if you don't need an fd
|
||||
* @see tmpfd() if you don't need a path
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
*/
|
||||
int mkostemp(char *template, unsigned flags) {
|
||||
return openatemp(AT_FDCWD, template, 0, flags, 0);
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
* @see mkostemp() if you don't need suffix
|
||||
* @see mktemp() if you don't need an fd
|
||||
* @see tmpfd() if you don't need a path
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
*/
|
||||
int mkostemps(char *template, int suffixlen, unsigned flags) {
|
||||
return openatemp(AT_FDCWD, template, suffixlen, flags, 0);
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
* @see mkstemps() if you you need a suffix
|
||||
* @see mktemp() if you don't need an fd
|
||||
* @see tmpfd() if you don't need a path
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
*/
|
||||
int mkstemp(char *template) {
|
||||
return openatemp(AT_FDCWD, template, 0, 0, 0);
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
* @see mkstemp() if you don't need `suffixlen`
|
||||
* @see mktemp() if you don't need an fd
|
||||
* @see tmpfd() if you don't need a path
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
*/
|
||||
int mkstemps(char *template, int suffixlen) {
|
||||
return openatemp(AT_FDCWD, template, suffixlen, 0, 0);
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
* @see mkstemps() if you you need a file extension
|
||||
* @see openatemp() for one temp roller to rule them all
|
||||
* @see mkostemp() if you you need a `O_CLOEXEC`, `O_APPEND`, etc.
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
*/
|
||||
char *mktemp(char *template) {
|
||||
int fd;
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
* @raise EFAULT if `req` is NULL or `req` / `rem` is a bad pointer
|
||||
* @raise ENOSYS on bare metal
|
||||
* @see clock_nanosleep()
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @norestart
|
||||
*/
|
||||
int nanosleep(const struct timespec *req, struct timespec *rem) {
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/sysv/consts/prio.h"
|
||||
|
||||
static int clamp(int p) {
|
||||
return MAX(-NZERO, MIN(NZERO - 1, p));
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes process priority.
|
||||
*
|
||||
* @param delta is added to current priority w/ clamping
|
||||
* @return new priority, or -1 w/ errno
|
||||
* @see Linux claims ioprio_set() is tuned automatically by this
|
||||
*/
|
||||
int nice(int delta) {
|
||||
int p;
|
||||
if (ABS(delta) >= NZERO * 2) {
|
||||
p = delta;
|
||||
} else {
|
||||
delta = clamp(delta);
|
||||
if ((p = getpriority(PRIO_PROCESS, 0)) == -1) return -1;
|
||||
p += delta;
|
||||
}
|
||||
p = clamp(p);
|
||||
if (setpriority(PRIO_PROCESS, 0, p) == -1) return -1;
|
||||
return p;
|
||||
}
|
|
@ -19,6 +19,8 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
|
@ -67,6 +69,7 @@ textwindows int ntaccesscheck(const char16_t *pathname, uint32_t flags) {
|
|||
struct NtByHandleFileInformation wst;
|
||||
int64_t hToken, hImpersonatedToken, hFile;
|
||||
intptr_t buffer[1024 / sizeof(intptr_t)];
|
||||
BLOCK_SIGNALS;
|
||||
if (flags & X_OK) flags |= R_OK;
|
||||
granted = 0;
|
||||
result = false;
|
||||
|
@ -148,5 +151,6 @@ textwindows int ntaccesscheck(const char16_t *pathname, uint32_t flags) {
|
|||
if (hToken != -1) {
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
|
161
libc/calls/ntspawn.c
Normal file
161
libc/calls/ntspawn.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/enum/processaccess.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/memory.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/startupinfo.h"
|
||||
#include "libc/nt/struct/processinformation.h"
|
||||
#include "libc/nt/struct/procthreadattributelist.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#include "libc/nt/struct/startupinfoex.h"
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
struct SpawnBlock {
|
||||
char16_t path[PATH_MAX];
|
||||
char16_t cmdline[32767];
|
||||
char16_t envblock[32767];
|
||||
char envbuf[32767];
|
||||
};
|
||||
|
||||
static void *ntspawn_malloc(size_t size) {
|
||||
return HeapAlloc(GetProcessHeap(), 0, size);
|
||||
}
|
||||
|
||||
static void ntspawn_free(void *ptr) {
|
||||
HeapFree(GetProcessHeap(), 0, ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns process on Windows NT.
|
||||
*
|
||||
* This function delegates to CreateProcess() with UTF-8 → UTF-16
|
||||
* translation and argv escaping. Please note this will NOT escape
|
||||
* command interpreter syntax.
|
||||
*
|
||||
* @param prog won't be PATH searched
|
||||
* @param argv specifies prog arguments
|
||||
* @param envp[𝟶,m-2] specifies "foo=bar" environment variables, which
|
||||
* don't need to be passed in sorted order; however, this function
|
||||
* goes faster the closer they are to sorted
|
||||
* @param envp[m-1] is NULL
|
||||
* @param extravars is added to envp to avoid setenv() in caller
|
||||
* @param opt_out_lpProcessInformation can be used to return process and
|
||||
* thread IDs to parent, as well as open handles that need close()
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @see spawnve() which abstracts this function
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
textwindows int ntspawn(
|
||||
const char *prog, char *const argv[], char *const envp[],
|
||||
char *const extravars[], uint32_t dwCreationFlags,
|
||||
const char16_t *opt_lpCurrentDirectory, int64_t opt_hParentProcess,
|
||||
int64_t *opt_lpExplicitHandleList, uint32_t dwExplicitHandleCount,
|
||||
const struct NtStartupInfo *lpStartupInfo,
|
||||
struct NtProcessInformation *opt_out_lpProcessInformation) {
|
||||
int rc = -1;
|
||||
struct SpawnBlock *sb;
|
||||
BLOCK_SIGNALS;
|
||||
if ((sb = ntspawn_malloc(sizeof(*sb))) && __mkntpath(prog, sb->path) != -1) {
|
||||
if (!mkntcmdline(sb->cmdline, argv) &&
|
||||
!mkntenvblock(sb->envblock, envp, extravars, sb->envbuf)) {
|
||||
bool32 ok;
|
||||
int64_t dp = GetCurrentProcess();
|
||||
|
||||
// create attribute list
|
||||
// this code won't call malloc in practice
|
||||
void *freeme = 0;
|
||||
_Alignas(16) char memory[128];
|
||||
size_t size = sizeof(memory);
|
||||
struct NtProcThreadAttributeList *alist = (void *)memory;
|
||||
uint32_t items = !!opt_hParentProcess + !!opt_lpExplicitHandleList;
|
||||
ok = InitializeProcThreadAttributeList(alist, items, 0, &size);
|
||||
if (!ok && GetLastError() == kNtErrorInsufficientBuffer) {
|
||||
ok = !!(alist = freeme = ntspawn_malloc(size));
|
||||
if (ok) {
|
||||
ok = InitializeProcThreadAttributeList(alist, items, 0, &size);
|
||||
}
|
||||
}
|
||||
if (ok && opt_hParentProcess) {
|
||||
ok = UpdateProcThreadAttribute(
|
||||
alist, 0, kNtProcThreadAttributeParentProcess, &opt_hParentProcess,
|
||||
sizeof(opt_hParentProcess), 0, 0);
|
||||
}
|
||||
if (ok && opt_lpExplicitHandleList) {
|
||||
ok = UpdateProcThreadAttribute(
|
||||
alist, 0, kNtProcThreadAttributeHandleList,
|
||||
opt_lpExplicitHandleList,
|
||||
dwExplicitHandleCount * sizeof(*opt_lpExplicitHandleList), 0, 0);
|
||||
}
|
||||
|
||||
// create the process
|
||||
if (ok) {
|
||||
struct NtStartupInfoEx info;
|
||||
bzero(&info, sizeof(info));
|
||||
info.StartupInfo = *lpStartupInfo;
|
||||
info.StartupInfo.cb = sizeof(info);
|
||||
info.lpAttributeList = alist;
|
||||
if (ok) {
|
||||
if (CreateProcess(sb->path, sb->cmdline, 0, 0, true,
|
||||
dwCreationFlags | kNtCreateUnicodeEnvironment |
|
||||
kNtExtendedStartupinfoPresent |
|
||||
kNtInheritParentAffinity,
|
||||
sb->envblock, opt_lpCurrentDirectory,
|
||||
&info.StartupInfo, opt_out_lpProcessInformation)) {
|
||||
rc = 0;
|
||||
} else {
|
||||
STRACE("CreateProcess() failed w/ %d", GetLastError());
|
||||
if (GetLastError() == kNtErrorSharingViolation) {
|
||||
etxtbsy();
|
||||
}
|
||||
}
|
||||
rc = __fix_enotdir(rc, sb->path);
|
||||
}
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
|
||||
// clean up resources
|
||||
if (alist) {
|
||||
DeleteProcThreadAttributeList(alist);
|
||||
}
|
||||
if (freeme) {
|
||||
ntspawn_free(freeme);
|
||||
}
|
||||
if (dp && dp != GetCurrentProcess()) {
|
||||
CloseHandle(dp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sb) ntspawn_free(sb);
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -120,11 +120,12 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
|
|||
|
||||
// open the file, following symlinks
|
||||
int e = errno;
|
||||
int64_t hand = CreateFile(path16, perm | extra_perm, share, 0, disp,
|
||||
attr | extra_attr, 0);
|
||||
int64_t hand = CreateFile(path16, perm | extra_perm, share, &kNtIsInheritable,
|
||||
disp, attr | extra_attr, 0);
|
||||
if (hand == -1 && errno == EACCES && (flags & O_ACCMODE) == O_RDONLY) {
|
||||
errno = e;
|
||||
hand = CreateFile(path16, perm, share, 0, disp, attr | extra_attr, 0);
|
||||
hand = CreateFile(path16, perm, share, &kNtIsInheritable, disp,
|
||||
attr | extra_attr, 0);
|
||||
}
|
||||
|
||||
return __fix_enotdir(hand, path16);
|
||||
|
@ -137,10 +138,9 @@ static textwindows int sys_open_nt_console(int dirfd,
|
|||
g_fds.p[fd].kind = kFdConsole;
|
||||
g_fds.p[fd].flags = flags;
|
||||
g_fds.p[fd].mode = mode;
|
||||
g_fds.p[fd].handle = CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite,
|
||||
kNtFileShareRead, 0, kNtOpenExisting, 0, 0);
|
||||
g_fds.p[fd].extra = CreateFile(u"CONOUT$", kNtGenericRead | kNtGenericWrite,
|
||||
kNtFileShareWrite, 0, kNtOpenExisting, 0, 0);
|
||||
g_fds.p[fd].handle =
|
||||
CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite, kNtFileShareRead,
|
||||
&kNtIsInheritable, kNtOpenExisting, 0, 0);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
@ -163,6 +163,7 @@ textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags,
|
|||
int fd;
|
||||
ssize_t rc;
|
||||
__fds_lock();
|
||||
if (!(flags & O_CREAT)) mode = 0;
|
||||
if ((rc = fd = __reservefd_unlocked(-1)) != -1) {
|
||||
if (!strcmp(file, kNtMagicPaths.devtty)) {
|
||||
rc = sys_open_nt_console(dirfd, &kNtMagicPaths, flags, mode, fd);
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
* @param file specifies filesystem path to open
|
||||
* @return file descriptor, or -1 w/ errno
|
||||
* @see openat() for further documentation
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
|
|
|
@ -165,7 +165,7 @@
|
|||
* @raise ELOOP if `flags` had `O_NOFOLLOW` and `path` is a symbolic link
|
||||
* @raise ELOOP if a loop was detected resolving components of `path`
|
||||
* @raise EISDIR if writing is requested and `path` names a directory
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
|
@ -178,7 +178,7 @@ int openat(int dirfd, const char *path, int flags, ...) {
|
|||
va_start(va, flags);
|
||||
mode = va_arg(va, unsigned);
|
||||
va_end(va);
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
|
||||
rc = efault();
|
||||
|
@ -235,7 +235,7 @@ int openat(int dirfd, const char *path, int flags, ...) {
|
|||
rc = enosys();
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("openat(%s, %#s, %s%s) → %d% m", DescribeDirfd(dirfd), path,
|
||||
DescribeOpenFlags(flags), DescribeOpenMode(flags, mode), rc);
|
||||
return rc;
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
* @raise EINVAL if `template` (less the `suffixlen` region) didn't
|
||||
* end with the string "XXXXXXX"
|
||||
* @raise EINVAL if `suffixlen` was negative or too large
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
*/
|
||||
int openatemp(int dirfd, char *template, int suffixlen, int flags, int mode) {
|
||||
flags &= ~O_ACCMODE;
|
||||
|
|
|
@ -97,8 +97,8 @@ int openpty(int *mfd, int *sfd, char *name, //
|
|||
(wsz && !__asan_is_valid(wsz, sizeof(*wsz))))) {
|
||||
return efault();
|
||||
}
|
||||
BLOCK_CANCELLATIONS;
|
||||
BLOCK_CANCELATION;
|
||||
rc = openpty_impl(mfd, sfd, name, tio, wsz);
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_CANCELATION;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/overlap.h"
|
||||
#include "libc/calls/overlapped.internal.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/thread.h"
|
||||
|
||||
void overlapped_cleanup_callback(void *arg) {
|
||||
uint32_t got;
|
||||
struct OverlappedCleanup *cleanup = arg;
|
||||
CancelIoEx(cleanup->handle, cleanup->overlap);
|
||||
GetOverlappedResult(cleanup->handle, cleanup->overlap, &got, true);
|
||||
CloseHandle(cleanup->overlap->hEvent);
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_OVERLAPPED_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_OVERLAPPED_INTERNAL_H_
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define overlapped_cleanup_push(handle, overlap) \
|
||||
{ \
|
||||
struct OverlappedCleanup overlapped_cleanup = {handle, overlap}; \
|
||||
pthread_cleanup_push(overlapped_cleanup_callback, &overlapped_cleanup);
|
||||
|
||||
#define overlapped_cleanup_pop() \
|
||||
pthread_cleanup_pop(false); \
|
||||
}
|
||||
|
||||
struct OverlappedCleanup {
|
||||
int64_t handle;
|
||||
struct NtOverlapped *overlap;
|
||||
};
|
||||
|
||||
void overlapped_cleanup_callback(void *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_OVERLAPPED_INTERNAL_H_ */
|
76
libc/calls/park.c
Normal file
76
libc/calls/park.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
// each thread has its own pt_futex which is used by both posix signals
|
||||
// and posix thread cancelation to "park" blocking operations that dont
|
||||
// need win32 overlapped i/o. the delay is advisory and may be -1 which
|
||||
// means wait forever. these functions don't guarantee to wait the full
|
||||
// duration. other threads wanting to deliver a signal, can wake parked
|
||||
// futexes without releasing them, just to stir up activity. if a futex
|
||||
// is both woken and released then the cancelation point shall generate
|
||||
// an eintr. we also abstract checking for signals & thread cancelation
|
||||
|
||||
static textwindows int _park_wait(uint32_t msdelay, bool restartable,
|
||||
struct PosixThread *pt) {
|
||||
int got, expect = 0;
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (_check_signal(restartable) == -1) return -1;
|
||||
WaitOnAddress(&pt->pt_futex, &expect, sizeof(expect), msdelay);
|
||||
got = atomic_load_explicit(&pt->pt_futex, memory_order_acquire);
|
||||
return got != expect ? eintr() : 0;
|
||||
}
|
||||
|
||||
static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
|
||||
bool restartable) {
|
||||
int rc;
|
||||
sigset_t om;
|
||||
struct PosixThread *pt;
|
||||
pt = _pthread_self();
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
if (restartable) pt->pt_flags |= PT_RESTARTABLE;
|
||||
atomic_store_explicit(&pt->pt_futex, 0, memory_order_release);
|
||||
atomic_store_explicit(&pt->pt_blocker, &pt->pt_futex, memory_order_release);
|
||||
om = __sig_beginwait(waitmask);
|
||||
rc = _park_wait(msdelay, restartable, pt);
|
||||
if (rc == -1 && errno == EINTR) _check_cancel();
|
||||
__sig_finishwait(om);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU, memory_order_release);
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows int _park_norestart(uint32_t msdelay, sigset_t waitmask) {
|
||||
return _park_thread(msdelay, waitmask, false);
|
||||
}
|
||||
|
||||
textwindows int _park_restartable(uint32_t msdelay, sigset_t waitmask) {
|
||||
return _park_thread(msdelay, waitmask, true);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -19,13 +19,12 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
textwindows int sys_pause_nt(void) {
|
||||
int rc;
|
||||
while (!(rc = _check_interrupts(0))) {
|
||||
if ((rc = __pause_thread(__SIG_SIG_INTERVAL_MS))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (!(rc = _park_norestart(-1u, 0))) donothing;
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -40,14 +40,14 @@
|
|||
* @return -1 w/ errno
|
||||
* @raise ECANCELED if this thread was canceled in masked mode
|
||||
* @raise EINTR if interrupted by a signal
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @see sigsuspend()
|
||||
* @norestart
|
||||
*/
|
||||
int pause(void) {
|
||||
int rc;
|
||||
STRACE("pause() → [...]");
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (!IsWindows()) {
|
||||
// We'll polyfill pause() using select() with a null timeout, which
|
||||
|
@ -72,7 +72,7 @@ int pause(void) {
|
|||
rc = sys_pause_nt();
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("[...] pause → %d% m", rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
textwindows int __pause_thread(uint32_t ms) {
|
||||
uint32_t status;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
status = WaitForSingleObject(pt->semaphore, ms);
|
||||
if (status == -1u) notpossible;
|
||||
if (!(pt->pt_flags & PT_INSEMAPHORE)) {
|
||||
errno = pt->abort_errno;
|
||||
return -1;
|
||||
}
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
return 0;
|
||||
}
|
|
@ -17,9 +17,10 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/handlock.internal.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
|
@ -30,7 +31,7 @@
|
|||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) {
|
||||
static textwindows int sys_pipe_nt_impl(int pipefd[2], unsigned flags) {
|
||||
uint32_t mode;
|
||||
int64_t hin, hout;
|
||||
int reader, writer;
|
||||
|
@ -53,11 +54,11 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) {
|
|||
}
|
||||
__fds_unlock();
|
||||
hin = CreateNamedPipe(pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped,
|
||||
mode, 1, PIPE_BUF, PIPE_BUF, 0, 0);
|
||||
mode, 1, PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable);
|
||||
__fds_lock();
|
||||
if (hin != -1) {
|
||||
if ((hout = CreateFile(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting,
|
||||
kNtFileFlagOverlapped, 0)) != -1) {
|
||||
if ((hout = CreateFile(pipename, kNtGenericWrite, 0, &kNtIsInheritable,
|
||||
kNtOpenExisting, kNtFileFlagOverlapped, 0)) != -1) {
|
||||
g_fds.p[reader].kind = kFdFile;
|
||||
g_fds.p[reader].flags = O_RDONLY | flags;
|
||||
g_fds.p[reader].mode = 0010444;
|
||||
|
@ -79,3 +80,11 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) {
|
|||
__fds_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) {
|
||||
int rc;
|
||||
BLOCK_SIGNALS;
|
||||
rc = sys_pipe_nt_impl(pipefd, flags);
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
|
@ -41,16 +42,17 @@
|
|||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/struct/pollfd.h"
|
||||
#include "libc/sock/struct/pollfd.internal.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
// Polls on the New Technology.
|
||||
|
@ -61,25 +63,22 @@
|
|||
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
|
||||
const sigset_t *sigmask) {
|
||||
bool ok;
|
||||
uint64_t m;
|
||||
bool interrupted;
|
||||
sigset_t oldmask;
|
||||
uint64_t millis;
|
||||
uint32_t cm, avail, waitfor;
|
||||
struct sys_pollfd_nt pipefds[8];
|
||||
struct sys_pollfd_nt sockfds[64];
|
||||
int pipeindices[ARRAYLEN(pipefds)];
|
||||
int sockindices[ARRAYLEN(sockfds)];
|
||||
struct timespec started, deadline, remain, now;
|
||||
int i, rc, sn, pn, gotinvals, gotpipes, gotsocks;
|
||||
|
||||
#if IsModeDbg()
|
||||
struct timespec noearlier =
|
||||
timespec_add(timespec_real(), timespec_frommillis(ms ? *ms : -1u));
|
||||
#endif
|
||||
BLOCK_SIGNALS;
|
||||
started = timespec_real();
|
||||
deadline = timespec_add(started, timespec_frommillis(ms ? *ms : -1u));
|
||||
|
||||
// do the planning
|
||||
// we need to read static variables
|
||||
// we might need to spawn threads and open pipes
|
||||
m = atomic_exchange(&__get_tls()->tib_sigmask, -1);
|
||||
__fds_lock();
|
||||
for (gotinvals = rc = sn = pn = i = 0; i < nfds; ++i) {
|
||||
if (fds[i].fd < 0) continue;
|
||||
|
@ -114,7 +113,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
|
|||
pipefds[pn].events = fds[i].events & (POLLIN | POLLOUT);
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
}
|
||||
++pn;
|
||||
} else {
|
||||
|
@ -127,7 +126,6 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
|
|||
}
|
||||
}
|
||||
__fds_unlock();
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
|
||||
if (rc) {
|
||||
// failed to create a polling solution
|
||||
goto Finished;
|
||||
|
@ -155,8 +153,8 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
|
|||
pipefds[i].revents |= POLLERR;
|
||||
}
|
||||
} else if (GetConsoleMode(pipefds[i].handle, &cm)) {
|
||||
if (CountConsoleInputBytes(g_fds.p + fds[pipeindices[i]].fd)) {
|
||||
pipefds[i].revents |= POLLIN;
|
||||
if (CountConsoleInputBytes()) {
|
||||
pipefds[i].revents |= POLLIN; // both >0 and -1 (eof) are pollin
|
||||
}
|
||||
} else {
|
||||
// we have no way of polling if a non-socket is readable yet
|
||||
|
@ -172,31 +170,36 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
|
|||
// compute a small time slice we don't mind sleeping for
|
||||
if (sn) {
|
||||
if ((gotsocks = WSAPoll(sockfds, sn, 0)) == -1) {
|
||||
return __winsockerr();
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
}
|
||||
} else {
|
||||
gotsocks = 0;
|
||||
}
|
||||
waitfor = MIN(__SIG_POLL_INTERVAL_MS, *ms);
|
||||
if (!gotinvals && !gotsocks && !gotpipes && waitfor) {
|
||||
POLLTRACE("poll() sleeping for %'d out of %'u ms", waitfor, *ms);
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->abort_errno = 0;
|
||||
if (sigmask) __sig_mask(SIG_SETMASK, sigmask, &oldmask);
|
||||
interrupted = _check_interrupts(0) || __pause_thread(waitfor);
|
||||
if (sigmask) __sig_mask(SIG_SETMASK, &oldmask, 0);
|
||||
if (interrupted) return -1;
|
||||
if (*ms != -1u) {
|
||||
if (waitfor < *ms) {
|
||||
*ms -= waitfor;
|
||||
} else {
|
||||
*ms = 0;
|
||||
|
||||
// add some artificial delay, which we use as an opportunity to also
|
||||
// check for pending signals, thread cancelation, etc.
|
||||
waitfor = 0;
|
||||
if (!gotinvals && !gotsocks && !gotpipes) {
|
||||
now = timespec_real();
|
||||
if (timespec_cmp(now, deadline) < 0) {
|
||||
remain = timespec_sub(deadline, now);
|
||||
millis = timespec_tomillis(remain);
|
||||
waitfor = MIN(millis, 0xffffffffu);
|
||||
waitfor = MIN(waitfor, __SIG_POLL_INTERVAL_MS);
|
||||
if (waitfor) {
|
||||
POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor,
|
||||
timespec_tomillis(remain));
|
||||
if ((rc = _park_norestart(waitfor, sigmask ? *sigmask : 0)) == -1) {
|
||||
goto Finished; // eintr, ecanceled, etc.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we gave all the sockets and all the named pipes a shot
|
||||
// if we found anything at all then it's time to end work
|
||||
if (gotinvals || gotpipes || gotsocks || !*ms) {
|
||||
if (gotinvals || gotpipes || gotsocks || !waitfor) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -221,15 +224,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
|
|||
rc = gotinvals + gotpipes + gotsocks;
|
||||
|
||||
Finished:
|
||||
|
||||
#if IsModeDbg()
|
||||
struct timespec ended = timespec_real();
|
||||
if (!rc && timespec_cmp(ended, noearlier) < 0) {
|
||||
STRACE("poll() ended %'ld ns too soon!",
|
||||
timespec_tonanos(timespec_sub(noearlier, ended)));
|
||||
}
|
||||
#endif
|
||||
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/bo.internal.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
|
@ -60,14 +59,14 @@
|
|||
* was determined about the file descriptor
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @raise EINTR if signal was delivered
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @norestart
|
||||
*/
|
||||
int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
|
||||
int rc;
|
||||
size_t n;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (IsAsan() &&
|
||||
(ckd_mul(&n, nfds, sizeof(struct pollfd)) || !__asan_is_valid(fds, n))) {
|
||||
|
@ -79,13 +78,11 @@ int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
|
|||
rc = sys_poll_metal(fds, nfds, timeout_ms);
|
||||
}
|
||||
} else {
|
||||
BEGIN_BLOCKING_OPERATION;
|
||||
uint32_t ms = timeout_ms >= 0 ? timeout_ms : -1u;
|
||||
rc = sys_poll_nt(fds, nfds, &ms, 0);
|
||||
END_BLOCKING_OPERATION;
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("poll(%s, %'zu, %'d) → %d% lm", DescribePollFds(rc, fds, nfds), nfds,
|
||||
timeout_ms, rc);
|
||||
return rc;
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/bo.internal.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
|
@ -55,7 +54,7 @@
|
|||
* @param sigmask may be null in which case no mask change happens
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @raise EINTR if signal was delivered
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @norestart
|
||||
*/
|
||||
|
@ -65,7 +64,7 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
|
|||
int e, rc;
|
||||
sigset_t oldmask;
|
||||
struct timespec ts, *tsp;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (IsAsan() &&
|
||||
(ckd_mul(&n, nfds, sizeof(struct pollfd)) || !__asan_is_valid(fds, n) ||
|
||||
|
@ -98,12 +97,10 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
|
|||
ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) {
|
||||
ms = -1u;
|
||||
}
|
||||
BEGIN_BLOCKING_OPERATION;
|
||||
rc = sys_poll_nt(fds, nfds, &ms, sigmask);
|
||||
END_BLOCKING_OPERATION;
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("ppoll(%s, %'zu, %s, %s) → %d% lm", DescribePollFds(rc, fds, nfds),
|
||||
nfds, DescribeTimespec(0, timeout), DescribeSigset(0, sigmask), rc);
|
||||
return rc;
|
||||
|
|
|
@ -50,13 +50,13 @@
|
|||
* @raise EINTR if signal was delivered instead
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @see pwrite(), write()
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
ssize_t pread(int fd, void *buf, size_t size, int64_t offset) {
|
||||
ssize_t rc;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (offset < 0) {
|
||||
rc = einval();
|
||||
|
@ -79,7 +79,7 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) {
|
|||
}
|
||||
npassert(rc == -1 || (size_t)rc <= size);
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
DATATRACE("pread(%d, [%#.*hhs%s], %'zu, %'zd) → %'zd% m", fd,
|
||||
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, offset, rc);
|
||||
return rc;
|
||||
|
|
|
@ -112,15 +112,15 @@ static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
|
|||
* Reads with maximum generality.
|
||||
*
|
||||
* @return number of bytes actually read, or -1 w/ errno
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
ssize_t preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
|
||||
ssize_t rc;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
rc = Preadv(fd, iov, iovlen, off);
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("preadv(%d, [%s], %d, %'ld) → %'ld% m", fd,
|
||||
DescribeIovec(rc, iov, iovlen), iovlen, off, rc);
|
||||
return rc;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
|
||||
static const char *__fdkind2str(int x) {
|
||||
|
@ -29,8 +30,6 @@ static const char *__fdkind2str(int x) {
|
|||
return "kFdFile";
|
||||
case kFdSocket:
|
||||
return "kFdSocket";
|
||||
case kFdProcess:
|
||||
return "kFdProcess";
|
||||
case kFdConsole:
|
||||
return "kFdConsole";
|
||||
case kFdSerial:
|
||||
|
@ -44,17 +43,17 @@ static const char *__fdkind2str(int x) {
|
|||
}
|
||||
}
|
||||
|
||||
void __printfds(void) {
|
||||
void __printfds(struct Fd *fds, size_t fdslen) {
|
||||
int i;
|
||||
__fds_lock();
|
||||
for (i = 0; i < g_fds.n; ++i) {
|
||||
if (!g_fds.p[i].kind) continue;
|
||||
kprintf("%3d %s", i, __fdkind2str(g_fds.p[i].kind));
|
||||
if (g_fds.p[i].flags) kprintf(" flags=%#x", g_fds.p[i].flags);
|
||||
if (g_fds.p[i].mode) kprintf(" mode=%#o", g_fds.p[i].mode);
|
||||
if (g_fds.p[i].handle) kprintf(" handle=%ld", g_fds.p[i].handle);
|
||||
if (g_fds.p[i].extra) kprintf(" extra=%ld", g_fds.p[i].extra);
|
||||
char buf[128];
|
||||
for (i = 0; i < fdslen; ++i) {
|
||||
if (!fds[i].kind) continue;
|
||||
kprintf("%3d %s", i, __fdkind2str(fds[i].kind));
|
||||
if (fds[i].flags) {
|
||||
kprintf(" flags=%s", (DescribeOpenFlags)(buf, fds[i].flags));
|
||||
}
|
||||
if (fds[i].mode) kprintf(" mode=%#o", fds[i].mode);
|
||||
if (fds[i].handle) kprintf(" handle=%ld", fds[i].handle);
|
||||
kprintf("\n");
|
||||
}
|
||||
__fds_unlock();
|
||||
}
|
||||
|
|
|
@ -44,14 +44,14 @@
|
|||
* @return [1..size] bytes on success, or -1 w/ errno; noting zero is
|
||||
* impossible unless size was passed as zero to do an error check
|
||||
* @see pread(), write()
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) {
|
||||
ssize_t rc;
|
||||
size_t wrote;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (offset < 0) {
|
||||
rc = einval();
|
||||
|
@ -79,7 +79,7 @@ ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) {
|
|||
}
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
DATATRACE("pwrite(%d, %#.*hhs%s, %'zu, %'zd) → %'zd% m", fd,
|
||||
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, offset, rc);
|
||||
return rc;
|
||||
|
|
|
@ -116,15 +116,15 @@ static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen,
|
|||
* call using pwrite().
|
||||
*
|
||||
* @return number of bytes actually sent, or -1 w/ errno
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
ssize_t pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) {
|
||||
ssize_t rc;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
rc = Pwritev(fd, iov, iovlen, off);
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("pwritev(%d, %s, %d, %'ld) → %'ld% m", fd,
|
||||
DescribeIovec(rc != -1 ? rc : -2, iov, iovlen), iovlen, off, rc);
|
||||
return rc;
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
/**
|
||||
* Sends signal to self.
|
||||
|
|
|
@ -16,12 +16,15 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/ttydefaults.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
|
@ -34,41 +37,55 @@
|
|||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/consolemodeflags.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/filesharemode.h"
|
||||
#include "libc/nt/enum/vk.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/events.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/inputrecord.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
static const struct {
|
||||
/**
|
||||
* @fileoverview Cosmopolitan Standard Input
|
||||
*
|
||||
* This file implements pollable terminal i/o for Windows consoles. On
|
||||
* Windows 10 the "virtual terminal processing" feature works great on
|
||||
* output but their solution for input processing isn't good enough to
|
||||
* support running Linux programs like Emacs. This polyfill fixes that
|
||||
* and it most importantly ensures we can poll() standard input, which
|
||||
* would otherwise have been impossible. We aren't using threads. What
|
||||
* we do instead is have termios behaviors e.g. canonical mode editing
|
||||
* happen on demand as a side effect of read/poll/ioctl activity.
|
||||
*/
|
||||
|
||||
struct VirtualKey {
|
||||
int vk;
|
||||
int normal_str;
|
||||
int shift_str;
|
||||
int ctrl_str;
|
||||
int shift_ctrl_str;
|
||||
} kVirtualKey[] = {
|
||||
};
|
||||
|
||||
#define S(s) W(s "\0\0")
|
||||
#define W(s) (s[3] << 24 | s[2] << 16 | s[1] << 8 | s[0])
|
||||
|
||||
static const struct VirtualKey kVirtualKey[] = {
|
||||
{kNtVkUp, S("A"), S("1;2A"), S("1;5A"), S("1;6A")},
|
||||
{kNtVkDown, S("B"), S("1;2B"), S("1;5B"), S("1;6B")},
|
||||
{kNtVkLeft, S("D"), S("1;2D"), S("1;5D"), S("1;6D")},
|
||||
{kNtVkRight, S("C"), S("1;2C"), S("1;5C"), S("1;6C")},
|
||||
{kNtVkLeft, S("D"), S("1;2D"), S("1;5D"), S("1;6D")},
|
||||
{kNtVkInsert, S("2~"), S("2;2~"), S("2;5~"), S("2;6~")},
|
||||
{kNtVkDelete, S("3~"), S("3;2~"), S("3;5~"), S("3;6~")},
|
||||
{kNtVkHome, S("H"), S("1;2H"), S("1;5H"), S("1;6H")},
|
||||
|
@ -87,8 +104,18 @@ static const struct {
|
|||
{kNtVkF10, S("21~"), S("34~"), S("21^"), S("34^")},
|
||||
{kNtVkF11, S("23~"), S("23$"), S("23^"), S("23@")},
|
||||
{kNtVkF12, S("24~"), S("24$"), S("24^"), S("24@")},
|
||||
#undef W
|
||||
#undef S
|
||||
{0},
|
||||
};
|
||||
|
||||
// TODO: How can we configure `less` to not need this bloat?
|
||||
static const struct VirtualKey kDecckm[] = {
|
||||
{kNtVkUp, -S("OA"), -S("OA"), S("A"), S("A")},
|
||||
{kNtVkDown, -S("OB"), -S("OB"), S("B"), S("B")},
|
||||
{kNtVkRight, -S("OC"), -S("OC"), S("C"), S("C")},
|
||||
{kNtVkLeft, -S("OD"), -S("OD"), S("D"), S("D")},
|
||||
{kNtVkPrior, S("5~"), S("5;2~"), S("5;5~"), S("5;6~")},
|
||||
{kNtVkNext, S("6~"), S("6;2~"), S("6;5~"), S("6;6~")},
|
||||
{0},
|
||||
};
|
||||
|
||||
#define KEYSTROKE_CONTAINER(e) DLL_CONTAINER(struct Keystroke, elem, e)
|
||||
|
@ -100,6 +127,8 @@ struct Keystroke {
|
|||
};
|
||||
|
||||
struct Keystrokes {
|
||||
atomic_uint once;
|
||||
int64_t cin, cot;
|
||||
struct Dll *list;
|
||||
struct Dll *line;
|
||||
struct Dll *free;
|
||||
|
@ -108,10 +137,27 @@ struct Keystrokes {
|
|||
uint16_t utf16hs;
|
||||
pthread_mutex_t lock;
|
||||
struct Keystroke pool[512];
|
||||
const struct VirtualKey *vkt;
|
||||
};
|
||||
|
||||
static struct Keystrokes __keystroke;
|
||||
|
||||
textwindows void __keystroke_wipe(void) {
|
||||
bzero(&__keystroke, sizeof(__keystroke));
|
||||
}
|
||||
|
||||
static textwindows void OpenConsole(void) {
|
||||
__keystroke.vkt = kVirtualKey;
|
||||
__keystroke.cin = CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite,
|
||||
kNtFileShareRead, 0, kNtOpenExisting, 0, 0);
|
||||
__keystroke.cot = CreateFile(u"CONOUT$", kNtGenericRead | kNtGenericWrite,
|
||||
kNtFileShareWrite, 0, kNtOpenExisting, 0, 0);
|
||||
}
|
||||
|
||||
static textwindows void InitConsole(void) {
|
||||
cosmo_once(&__keystroke.once, OpenConsole);
|
||||
}
|
||||
|
||||
static textwindows void LockKeystrokes(void) {
|
||||
pthread_mutex_lock(&__keystroke.lock);
|
||||
}
|
||||
|
@ -120,30 +166,34 @@ static textwindows void UnlockKeystrokes(void) {
|
|||
pthread_mutex_unlock(&__keystroke.lock);
|
||||
}
|
||||
|
||||
static textwindows uint64_t BlockSignals(void) {
|
||||
return atomic_exchange(&__get_tls()->tib_sigmask, -1);
|
||||
textwindows int64_t GetConsoleInputHandle(void) {
|
||||
InitConsole();
|
||||
return __keystroke.cin;
|
||||
}
|
||||
|
||||
static textwindows void UnblockSignals(uint64_t mask) {
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, mask, memory_order_release);
|
||||
textwindows int64_t GetConsoleOutputHandle(void) {
|
||||
InitConsole();
|
||||
return __keystroke.cot;
|
||||
}
|
||||
|
||||
static textwindows int RaiseSignal(int sig) {
|
||||
__get_tls()->tib_sigpending |= 1ull << (sig - 1);
|
||||
return 0;
|
||||
static textwindows bool IsMouseModeCommand(int x) {
|
||||
return x == 1000 || // SET_VT200_MOUSE
|
||||
x == 1002 || // SET_BTN_EVENT_MOUSE
|
||||
x == 1006 || // SET_SGR_EXT_MODE_MOUSE
|
||||
x == 1015; // SET_URXVT_EXT_MODE_MOUSE
|
||||
}
|
||||
|
||||
static textwindows int GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
|
||||
for (int i = 0; i < ARRAYLEN(kVirtualKey); ++i) {
|
||||
if (kVirtualKey[i].vk == vk) {
|
||||
for (int i = 0; __keystroke.vkt[i].vk; ++i) {
|
||||
if (__keystroke.vkt[i].vk == vk) {
|
||||
if (shift && ctrl) {
|
||||
return kVirtualKey[i].shift_ctrl_str;
|
||||
return __keystroke.vkt[i].shift_ctrl_str;
|
||||
} else if (shift) {
|
||||
return kVirtualKey[i].shift_str;
|
||||
return __keystroke.vkt[i].shift_str;
|
||||
} else if (ctrl) {
|
||||
return kVirtualKey[i].ctrl_str;
|
||||
return __keystroke.vkt[i].ctrl_str;
|
||||
} else {
|
||||
return kVirtualKey[i].normal_str;
|
||||
return __keystroke.vkt[i].normal_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +202,7 @@ static textwindows int GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
|
|||
|
||||
static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
||||
|
||||
uint16_t c = r->Event.KeyEvent.uChar.UnicodeChar;
|
||||
uint32_t c = r->Event.KeyEvent.uChar.UnicodeChar;
|
||||
uint16_t vk = r->Event.KeyEvent.wVirtualKeyCode;
|
||||
uint16_t cks = r->Event.KeyEvent.dwControlKeyState;
|
||||
|
||||
|
@ -162,6 +212,7 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
|||
}
|
||||
|
||||
#if 0
|
||||
// this code is useful for troubleshooting why keys don't work
|
||||
kprintf("bKeyDown=%hhhd wVirtualKeyCode=%s wVirtualScanCode=%s "
|
||||
"UnicodeChar=%#x[%#lc] dwControlKeyState=%s\n",
|
||||
r->Event.KeyEvent.bKeyDown,
|
||||
|
@ -172,7 +223,7 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
|||
DescribeControlKeyState(r->Event.KeyEvent.dwControlKeyState));
|
||||
#endif
|
||||
|
||||
// process arrow keys, function keys, etc.
|
||||
// turn arrow/function keys into vt100/ansi/xterm byte sequences
|
||||
int n = 0;
|
||||
int v = GetVirtualKey(vk, !!(cks & kNtShiftPressed),
|
||||
!!(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed)));
|
||||
|
@ -191,31 +242,28 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
|||
return n;
|
||||
}
|
||||
|
||||
// ^/ should be interpreted as ^_
|
||||
// ^/ (crtl+slash) maps to ^_ (ctrl-hyphen) on linux
|
||||
if (vk == kNtVkOem_2 && (cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))) {
|
||||
p[n++] = 037;
|
||||
return n;
|
||||
}
|
||||
|
||||
// everything needs a unicode mapping from here on out
|
||||
// handle some stuff microsoft doesn't encode, e.g. ctrl+alt+b
|
||||
// handle cases where win32 doesn't provide character
|
||||
if (!c) {
|
||||
if (isgraph(vk) && (cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))) {
|
||||
c = CTRL(vk);
|
||||
if (vk == '2' && (cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))) {
|
||||
c = 0; // ctrl-2 → "\000"
|
||||
} else if (isascii(vk) && isdigit(vk) &&
|
||||
(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))) {
|
||||
c = 030 + (vk - '0'); // e.g. ctrl-3 → "\033"
|
||||
} else if (isascii(vk) && isgraph(vk) &&
|
||||
(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))) {
|
||||
c = vk ^ 0100; // e.g. ctrl-alt-b → "\033\002"
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// shift-tab is backtab or ^[Z
|
||||
if (vk == kNtVkTab && (cks & (kNtShiftPressed))) {
|
||||
p[n++] = 033;
|
||||
p[n++] = '[';
|
||||
p[n++] = 'Z';
|
||||
return n;
|
||||
}
|
||||
|
||||
// translate utf-16 into utf-32
|
||||
// convert utf-16 to utf-32
|
||||
if (IsHighSurrogate(c)) {
|
||||
__keystroke.utf16hs = c;
|
||||
return 0;
|
||||
|
@ -224,29 +272,30 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
|||
c = MergeUtf16(__keystroke.utf16hs, c);
|
||||
}
|
||||
|
||||
// enter sends \r in a raw terminals
|
||||
// enter sends \r with raw terminals
|
||||
// make it a multics newline instead
|
||||
if (c == '\r' && !(__ttyconf.magic & kTtyNoCr2Nl)) {
|
||||
c = '\n';
|
||||
}
|
||||
|
||||
// microsoft doesn't encode ctrl-space (^@) as nul
|
||||
// detecting it is also impossible w/ kNtEnableVirtualTerminalInput
|
||||
// ctrl-space (^@) is literally zero
|
||||
if (c == ' ' && (cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))) {
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
// make it possible to distinguish ctrl-h (^H) from backspace (^?)
|
||||
// make backspace (^?) distinguishable from ctrl-h (^H)
|
||||
if (c == kNtVkBack && !(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))) {
|
||||
c = 0177;
|
||||
}
|
||||
|
||||
// handle ctrl-c and ctrl-\, which tcsetattr() is able to remap
|
||||
// handle ctrl-\ and ctrl-c
|
||||
// note we define _POSIX_VDISABLE as zero
|
||||
// tcsetattr() lets anyone reconfigure these keybindings
|
||||
if (c && !(__ttyconf.magic & kTtyNoIsigs)) {
|
||||
if (c == __ttyconf.vintr) {
|
||||
return RaiseSignal(SIGINT);
|
||||
return __sig_enqueue(SIGINT);
|
||||
} else if (c == __ttyconf.vquit) {
|
||||
return RaiseSignal(SIGQUIT);
|
||||
return __sig_enqueue(SIGQUIT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,12 +312,14 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
|||
}
|
||||
|
||||
// insert esc prefix when alt is held
|
||||
// for example "h" becomes "\033h" (alt-h)
|
||||
// if up arrow is "\033[A" then alt-up is "\033\033[A"
|
||||
if ((cks & (kNtLeftAltPressed | kNtRightAltPressed)) &&
|
||||
r->Event.KeyEvent.bKeyDown) {
|
||||
p[n++] = 033;
|
||||
}
|
||||
|
||||
// convert utf-32 to utf-8
|
||||
// finally apply thompson-pike varint encoding
|
||||
uint64_t w = tpenc(c);
|
||||
do p[n++] = w;
|
||||
while ((w >>= 8));
|
||||
|
@ -343,7 +394,7 @@ static textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
|
|||
case kNtMouseEvent:
|
||||
return ProcessMouseEvent(r, p);
|
||||
case kNtWindowBufferSizeEvent:
|
||||
return RaiseSignal(SIGWINCH);
|
||||
return __sig_enqueue(SIGWINCH);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -377,60 +428,51 @@ static textwindows struct Keystroke *NewKeystroke(void) {
|
|||
return k;
|
||||
}
|
||||
|
||||
static textwindows void WriteTty(struct Fd *f, const char *p, size_t n) {
|
||||
int64_t hOutput;
|
||||
uint32_t dwConsoleMode;
|
||||
if (f->kind == kFdConsole) {
|
||||
hOutput = f->extra;
|
||||
} else if (g_fds.p[1].kind == kFdFile &&
|
||||
GetConsoleMode(g_fds.p[1].handle, &dwConsoleMode)) {
|
||||
hOutput = g_fds.p[1].handle;
|
||||
} else if (g_fds.p[2].kind == kFdFile &&
|
||||
GetConsoleMode(g_fds.p[2].handle, &dwConsoleMode)) {
|
||||
hOutput = g_fds.p[2].handle;
|
||||
} else {
|
||||
hOutput = g_fds.p[1].handle;
|
||||
}
|
||||
WriteFile(hOutput, p, n, 0, 0);
|
||||
static textwindows void WriteTty(const char *p, size_t n) {
|
||||
WriteFile(__keystroke.cot, p, n, 0, 0);
|
||||
}
|
||||
|
||||
static textwindows bool IsCtl(int c) {
|
||||
return isascii(c) && iscntrl(c) && c != '\n' && c != '\t';
|
||||
}
|
||||
|
||||
static textwindows void WriteTtyCtl(struct Fd *f, const char *p, size_t n) {
|
||||
static textwindows void WriteCtl(const char *p, size_t n) {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (IsCtl(p[i])) {
|
||||
char ctl[2];
|
||||
ctl[0] = '^';
|
||||
ctl[1] = p[i] ^ 0100;
|
||||
WriteTty(f, ctl, 2);
|
||||
WriteTty(ctl, 2);
|
||||
} else {
|
||||
WriteTty(f, p + i, 1);
|
||||
WriteTty(p + i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows void EchoTty(struct Fd *f, const char *p, size_t n) {
|
||||
static textwindows void EchoTty(const char *p, size_t n) {
|
||||
if (__ttyconf.magic & kTtyEchoRaw) {
|
||||
WriteTty(f, p, n);
|
||||
WriteTty(p, n);
|
||||
} else {
|
||||
WriteTtyCtl(f, p, n);
|
||||
WriteCtl(p, n);
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows bool EraseKeystroke(struct Fd *f) {
|
||||
static textwindows void EraseCharacter(void) {
|
||||
WriteTty("\b \b", 3);
|
||||
}
|
||||
|
||||
static textwindows bool EraseKeystroke(void) {
|
||||
struct Dll *e;
|
||||
if ((e = dll_last(__keystroke.line))) {
|
||||
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
|
||||
dll_remove(&__keystroke.line, e);
|
||||
dll_make_first(&__keystroke.free, e);
|
||||
for (int i = k->buflen; i--;) {
|
||||
if ((k->buf[i] & 0300) == 0200) continue;
|
||||
WriteTty(f, "\b \b", 3);
|
||||
if ((k->buf[i] & 0300) == 0200) continue; // utf-8 cont
|
||||
EraseCharacter();
|
||||
if (!(__ttyconf.magic & kTtyEchoRaw) && IsCtl(k->buf[i])) {
|
||||
WriteTty(f, "\b \b", 3);
|
||||
EraseCharacter();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -439,8 +481,7 @@ static textwindows bool EraseKeystroke(struct Fd *f) {
|
|||
}
|
||||
}
|
||||
|
||||
static textwindows void IngestConsoleInputRecord(struct Fd *f,
|
||||
struct NtInputRecord *r) {
|
||||
static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) {
|
||||
|
||||
// convert win32 console event into ansi
|
||||
int len;
|
||||
|
@ -453,7 +494,7 @@ static textwindows void IngestConsoleInputRecord(struct Fd *f,
|
|||
if (len == 1 && buf[0] && //
|
||||
(buf[0] & 255) == __ttyconf.verase && //
|
||||
!(__ttyconf.magic & kTtyUncanon)) {
|
||||
EraseKeystroke(f);
|
||||
EraseKeystroke();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -461,15 +502,15 @@ static textwindows void IngestConsoleInputRecord(struct Fd *f,
|
|||
if (len == 1 && buf[0] && //
|
||||
(buf[0] & 255) == __ttyconf.vkill && //
|
||||
!(__ttyconf.magic & kTtyUncanon)) {
|
||||
while (EraseKeystroke(f)) {
|
||||
while (EraseKeystroke()) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// allocate an object to hold this keystroke
|
||||
// allocate object to hold keystroke
|
||||
struct Keystroke *k;
|
||||
if (!(k = NewKeystroke())) {
|
||||
STRACE("ran out of memory to hold keystroke %#.*s", len, buf);
|
||||
STRACE("out of keystroke memory");
|
||||
return;
|
||||
}
|
||||
memcpy(k->buf, buf, sizeof(k->buf));
|
||||
|
@ -478,7 +519,7 @@ static textwindows void IngestConsoleInputRecord(struct Fd *f,
|
|||
// echo input if it was successfully recorded
|
||||
// assuming the win32 console isn't doing it already
|
||||
if (!(__ttyconf.magic & kTtySilence)) {
|
||||
EchoTty(f, buf, len);
|
||||
EchoTty(buf, len);
|
||||
}
|
||||
|
||||
// save keystroke to appropriate list
|
||||
|
@ -487,70 +528,64 @@ static textwindows void IngestConsoleInputRecord(struct Fd *f,
|
|||
} else {
|
||||
dll_make_last(&__keystroke.line, &k->elem);
|
||||
|
||||
// handle end-of-line in canonical mode
|
||||
// handle enter in canonical mode
|
||||
if (len == 1 && buf[0] &&
|
||||
((buf[0] & 255) == '\n' || //
|
||||
(buf[0] & 255) == __ttyconf.veol || //
|
||||
(buf[0] & 255) == __ttyconf.veol2)) {
|
||||
dll_make_last(&__keystroke.list, __keystroke.line);
|
||||
__keystroke.line = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows void IngestConsoleInput(struct Fd *f) {
|
||||
static textwindows void IngestConsoleInput(void) {
|
||||
uint32_t i, n;
|
||||
struct NtInputRecord records[16];
|
||||
if (!__keystroke.end_of_file) {
|
||||
do {
|
||||
if (GetNumberOfConsoleInputEvents(f->handle, &n)) {
|
||||
if (n) {
|
||||
n = MIN(ARRAYLEN(records), n);
|
||||
if (ReadConsoleInput(f->handle, records, n, &n)) {
|
||||
for (i = 0; i < n && !__keystroke.end_of_file; ++i) {
|
||||
IngestConsoleInputRecord(f, records + i);
|
||||
}
|
||||
} else {
|
||||
STRACE("ReadConsoleInput failed w/ %d", GetLastError());
|
||||
__keystroke.end_of_file = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
STRACE("GetNumberOfConsoleInputRecords failed w/ %d", GetLastError());
|
||||
__keystroke.end_of_file = true;
|
||||
break;
|
||||
}
|
||||
} while (n == ARRAYLEN(records));
|
||||
for (;;) {
|
||||
if (__keystroke.end_of_file) return;
|
||||
if (!GetNumberOfConsoleInputEvents(__keystroke.cin, &n)) {
|
||||
goto UnexpectedEof;
|
||||
}
|
||||
if (!n) return;
|
||||
n = MIN(ARRAYLEN(records), n);
|
||||
if (!ReadConsoleInput(__keystroke.cin, records, n, &n)) {
|
||||
goto UnexpectedEof;
|
||||
}
|
||||
for (i = 0; i < n && !__keystroke.end_of_file; ++i) {
|
||||
IngestConsoleInputRecord(records + i);
|
||||
}
|
||||
}
|
||||
UnexpectedEof:
|
||||
STRACE("console read error %d", GetLastError());
|
||||
__keystroke.end_of_file = true;
|
||||
}
|
||||
|
||||
textwindows int FlushConsoleInputBytes(int64_t handle) {
|
||||
// Discards all unread stdin bytes.
|
||||
textwindows int FlushConsoleInputBytes(void) {
|
||||
int rc;
|
||||
uint64_t m;
|
||||
m = BlockSignals();
|
||||
BLOCK_SIGNALS;
|
||||
InitConsole();
|
||||
LockKeystrokes();
|
||||
if (FlushConsoleInputBuffer(handle)) {
|
||||
dll_make_first(&__keystroke.free, __keystroke.list);
|
||||
__keystroke.list = 0;
|
||||
dll_make_first(&__keystroke.free, __keystroke.line);
|
||||
__keystroke.line = 0;
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
FlushConsoleInputBuffer(__keystroke.cin);
|
||||
dll_make_first(&__keystroke.free, __keystroke.list);
|
||||
__keystroke.list = 0;
|
||||
dll_make_first(&__keystroke.free, __keystroke.line);
|
||||
__keystroke.line = 0;
|
||||
rc = 0;
|
||||
UnlockKeystrokes();
|
||||
UnblockSignals(m);
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows int CountConsoleInputBytes(struct Fd *f) {
|
||||
int count = 0;
|
||||
// Returns number of stdin bytes that may be read without blocking.
|
||||
textwindows int CountConsoleInputBytes(void) {
|
||||
struct Dll *e;
|
||||
uint64_t m = BlockSignals();
|
||||
int count = 0;
|
||||
BLOCK_SIGNALS;
|
||||
InitConsole();
|
||||
LockKeystrokes();
|
||||
IngestConsoleInput(f);
|
||||
IngestConsoleInput();
|
||||
for (e = dll_first(__keystroke.list); e; e = dll_next(__keystroke.list, e)) {
|
||||
count += KEYSTROKE_CONTAINER(e)->buflen;
|
||||
}
|
||||
|
@ -558,10 +593,75 @@ textwindows int CountConsoleInputBytes(struct Fd *f) {
|
|||
count = -1;
|
||||
}
|
||||
UnlockKeystrokes();
|
||||
UnblockSignals(m);
|
||||
ALLOW_SIGNALS;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Intercept ANSI TTY commands that enable features.
|
||||
textwindows void InterceptTerminalCommands(const char *data, size_t size) {
|
||||
int i;
|
||||
unsigned x;
|
||||
bool ismouse;
|
||||
uint32_t cm, cm2;
|
||||
enum { ASC, ESC, CSI, CMD } t;
|
||||
GetConsoleMode(GetConsoleInputHandle(), &cm), cm2 = cm;
|
||||
for (ismouse = false, x = i = t = 0; i < size; ++i) {
|
||||
switch (t) {
|
||||
case ASC:
|
||||
if (data[i] == 033) {
|
||||
t = ESC;
|
||||
}
|
||||
break;
|
||||
case ESC:
|
||||
if (data[i] == '[') {
|
||||
t = CSI;
|
||||
} else {
|
||||
t = ASC;
|
||||
}
|
||||
break;
|
||||
case CSI:
|
||||
if (data[i] == '?') {
|
||||
t = CMD;
|
||||
x = 0;
|
||||
} else {
|
||||
t = ASC;
|
||||
}
|
||||
break;
|
||||
case CMD:
|
||||
if ('0' <= data[i] && data[i] <= '9') {
|
||||
x *= 10;
|
||||
x += data[i] - '0';
|
||||
} else if (data[i] == ';') {
|
||||
ismouse |= IsMouseModeCommand(x);
|
||||
x = 0;
|
||||
} else if (data[i] == 'h') {
|
||||
if (x == 1) {
|
||||
__keystroke.vkt = kDecckm; // \e[?1h decckm on
|
||||
} else if ((ismouse |= IsMouseModeCommand(x))) {
|
||||
__ttyconf.magic |= kTtyXtMouse;
|
||||
cm2 |= kNtEnableMouseInput;
|
||||
cm2 &= ~kNtEnableQuickEditMode; // take mouse
|
||||
}
|
||||
t = ASC;
|
||||
} else if (data[i] == 'l') {
|
||||
if (x == 1) {
|
||||
__keystroke.vkt = kVirtualKey; // \e[?1l decckm off
|
||||
} else if ((ismouse |= IsMouseModeCommand(x))) {
|
||||
__ttyconf.magic &= ~kTtyXtMouse;
|
||||
cm2 |= kNtEnableQuickEditMode; // release mouse
|
||||
}
|
||||
t = ASC;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
if (cm2 != cm) {
|
||||
SetConsoleMode(GetConsoleInputHandle(), cm2);
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows bool DigestConsoleInput(char *data, size_t size, int *rc) {
|
||||
|
||||
// handle eof once available input is consumed
|
||||
|
@ -604,137 +704,69 @@ static textwindows bool DigestConsoleInput(char *data, size_t size, int *rc) {
|
|||
}
|
||||
}
|
||||
|
||||
static textwindows ssize_t ReadFromWindowsConsole(struct Fd *f, void *data,
|
||||
size_t size) {
|
||||
int rc = -1;
|
||||
for (;;) {
|
||||
bool done = false;
|
||||
uint64_t m;
|
||||
m = BlockSignals();
|
||||
LockKeystrokes();
|
||||
IngestConsoleInput(f);
|
||||
done = DigestConsoleInput(data, size, &rc);
|
||||
UnlockKeystrokes();
|
||||
UnblockSignals(m);
|
||||
if (done) break;
|
||||
if (f->flags & O_NONBLOCK) return eagain();
|
||||
uint32_t ms = __SIG_POLL_INTERVAL_MS;
|
||||
if (!__ttyconf.vmin) {
|
||||
if (!__ttyconf.vtime) {
|
||||
return 0;
|
||||
} else {
|
||||
ms = __ttyconf.vtime * 100;
|
||||
}
|
||||
}
|
||||
if (_check_interrupts(kSigOpRestartable)) return -1;
|
||||
if (__pause_thread(ms)) {
|
||||
if (errno == EAGAIN) {
|
||||
errno = EINTR; // TODO(jart): Why do we need it?
|
||||
}
|
||||
return -1;
|
||||
static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
|
||||
int rc;
|
||||
sigset_t m;
|
||||
int64_t sem;
|
||||
uint32_t ms = -1u;
|
||||
if (!__ttyconf.vmin) {
|
||||
if (!__ttyconf.vtime) {
|
||||
return 0; // non-blocking w/o raising eagain
|
||||
} else {
|
||||
ms = __ttyconf.vtime * 100;
|
||||
}
|
||||
}
|
||||
if (f->flags & O_NONBLOCK) {
|
||||
return eagain(); // standard unix non-blocking
|
||||
}
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->pt_flags |= PT_RESTARTABLE;
|
||||
pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0);
|
||||
pthread_cleanup_push((void *)CloseHandle, (void *)sem);
|
||||
atomic_store_explicit(&pt->pt_futex, 0, memory_order_release);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release);
|
||||
m = __sig_beginwait(waitmask);
|
||||
if ((rc = _check_cancel()) != -1 && (rc = _check_signal(true)) != -1) {
|
||||
WaitForMultipleObjects(2, (int64_t[2]){sem, __keystroke.cin}, 0, ms);
|
||||
if (~pt->pt_flags & PT_RESTARTABLE) rc = eintr();
|
||||
if (rc == -1 && errno == EINTR) _check_cancel();
|
||||
}
|
||||
__sig_finishwait(m);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU, memory_order_release);
|
||||
pthread_cleanup_pop(true);
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static textwindows ssize_t ReadFromConsole(struct Fd *f, void *data,
|
||||
size_t size, sigset_t waitmask) {
|
||||
int rc;
|
||||
bool done = false;
|
||||
InitConsole();
|
||||
do {
|
||||
LockKeystrokes();
|
||||
IngestConsoleInput();
|
||||
done = DigestConsoleInput(data, size, &rc);
|
||||
UnlockKeystrokes();
|
||||
} while (!done && !(rc = WaitForConsole(f, waitmask)));
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
|
||||
int64_t offset) {
|
||||
int64_t offset, sigset_t waitmask) {
|
||||
|
||||
bool32 ok;
|
||||
struct Fd *f;
|
||||
uint32_t got;
|
||||
int64_t handle;
|
||||
struct PosixThread *pt;
|
||||
|
||||
f = g_fds.p + fd;
|
||||
handle = f->handle;
|
||||
pt = _pthread_self();
|
||||
pt->abort_errno = EAGAIN;
|
||||
size = MIN(size, 0x7ffff000);
|
||||
|
||||
bool pwriting = offset != -1;
|
||||
bool seekable = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk;
|
||||
bool nonblock = !!(f->flags & O_NONBLOCK);
|
||||
|
||||
if (pwriting && !seekable) {
|
||||
return espipe();
|
||||
}
|
||||
if (!pwriting) {
|
||||
offset = 0;
|
||||
// switch to terminal polyfill if reading from win32 console
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
if (f->kind == kFdConsole) {
|
||||
return ReadFromConsole(f, data, size, waitmask);
|
||||
}
|
||||
|
||||
uint32_t cm;
|
||||
if (!seekable && (f->kind == kFdConsole || GetConsoleMode(handle, &cm))) {
|
||||
return ReadFromWindowsConsole(f, data, size);
|
||||
}
|
||||
|
||||
if (!pwriting && seekable) {
|
||||
pthread_mutex_lock(&f->lock);
|
||||
offset = f->pointer;
|
||||
}
|
||||
|
||||
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0),
|
||||
.Pointer = offset};
|
||||
// the win32 manual says it's important to *not* put &got here
|
||||
// since for overlapped i/o, we always use GetOverlappedResult
|
||||
ok = ReadFile(handle, data, size, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||
BlockingOperation:
|
||||
if (!nonblock) {
|
||||
pt->ioverlap = &overlap;
|
||||
pt->iohandle = handle;
|
||||
}
|
||||
if (nonblock) {
|
||||
CancelIoEx(handle, &overlap);
|
||||
} else if (_check_interrupts(kSigOpRestartable)) {
|
||||
Interrupted:
|
||||
pt->abort_errno = errno;
|
||||
CancelIoEx(handle, &overlap);
|
||||
} else {
|
||||
for (;;) {
|
||||
uint32_t i;
|
||||
if (g_fds.stdin.inisem) {
|
||||
ReleaseSemaphore(g_fds.stdin.inisem, 1, 0);
|
||||
}
|
||||
i = WaitForSingleObject(overlap.hEvent, __SIG_IO_INTERVAL_MS);
|
||||
if (i == kNtWaitTimeout) {
|
||||
if (_check_interrupts(kSigOpRestartable)) {
|
||||
goto Interrupted;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pt->ioverlap = 0;
|
||||
pt->iohandle = 0;
|
||||
ok = true;
|
||||
}
|
||||
if (ok) {
|
||||
// overlapped is allocated on stack, so it's important we wait
|
||||
// for windows to acknowledge that it's done using that memory
|
||||
ok = GetOverlappedResult(handle, &overlap, &got, nonblock);
|
||||
if (!ok && GetLastError() == kNtErrorIoIncomplete) {
|
||||
goto BlockingOperation;
|
||||
}
|
||||
}
|
||||
CloseHandle(overlap.hEvent);
|
||||
|
||||
if (!pwriting && seekable) {
|
||||
if (ok) f->pointer = offset + got;
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
return got;
|
||||
}
|
||||
|
||||
errno_t err;
|
||||
if (_weaken(pthread_testcancel_np) &&
|
||||
(err = _weaken(pthread_testcancel_np)())) {
|
||||
return ecanceled();
|
||||
}
|
||||
// perform heavy lifting
|
||||
ssize_t rc;
|
||||
rc = sys_readwrite_nt(fd, data, size, offset, f->handle, waitmask, ReadFile);
|
||||
if (rc != -2) return rc;
|
||||
|
||||
// mops up win32 errors
|
||||
switch (GetLastError()) {
|
||||
case kNtErrorBrokenPipe: // broken pipe
|
||||
case kNtErrorNoData: // closing named pipe
|
||||
|
@ -742,25 +774,23 @@ textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
|
|||
return 0; //
|
||||
case kNtErrorAccessDenied: // read doesn't return EACCESS
|
||||
return ebadf(); //
|
||||
case kNtErrorOperationAborted:
|
||||
errno = pt->abort_errno;
|
||||
return -1;
|
||||
default:
|
||||
return __winerr();
|
||||
}
|
||||
}
|
||||
|
||||
textwindows ssize_t sys_read_nt(int fd, const struct iovec *iov, size_t iovlen,
|
||||
int64_t opt_offset) {
|
||||
static textwindows ssize_t sys_read_nt2(int fd, const struct iovec *iov,
|
||||
size_t iovlen, int64_t opt_offset,
|
||||
sigset_t waitmask) {
|
||||
ssize_t rc;
|
||||
size_t i, total;
|
||||
if (opt_offset < -1) return einval();
|
||||
while (iovlen && !iov[0].iov_len) iov++, iovlen--;
|
||||
if (iovlen) {
|
||||
for (total = i = 0; i < iovlen; ++i) {
|
||||
// TODO(jart): disable cancelations after first iteration
|
||||
if (!iov[i].iov_len) continue;
|
||||
rc = sys_read_nt_impl(fd, iov[i].iov_base, iov[i].iov_len, opt_offset);
|
||||
rc = sys_read_nt_impl(fd, iov[i].iov_base, iov[i].iov_len, opt_offset,
|
||||
waitmask);
|
||||
if (rc == -1) {
|
||||
if (total && errno != ECANCELED) {
|
||||
return total;
|
||||
|
@ -771,11 +801,21 @@ textwindows ssize_t sys_read_nt(int fd, const struct iovec *iov, size_t iovlen,
|
|||
total += rc;
|
||||
if (opt_offset != -1) opt_offset += rc;
|
||||
if (rc < iov[i].iov_len) break;
|
||||
waitmask = -1; // disable eintr/ecanceled for remaining iovecs
|
||||
}
|
||||
return total;
|
||||
} else {
|
||||
return sys_read_nt_impl(fd, NULL, 0, opt_offset);
|
||||
return sys_read_nt_impl(fd, NULL, 0, opt_offset, waitmask);
|
||||
}
|
||||
}
|
||||
|
||||
textwindows ssize_t sys_read_nt(int fd, const struct iovec *iov, size_t iovlen,
|
||||
int64_t opt_offset) {
|
||||
ssize_t rc;
|
||||
sigset_t m = __sig_block();
|
||||
rc = sys_read_nt2(fd, iov, iovlen, opt_offset, m);
|
||||
__sig_unblock(m);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -58,14 +58,14 @@
|
|||
* or `SO_RCVTIMEO` is in play and the time interval elapsed
|
||||
* @raise ENOBUFS is specified by POSIX
|
||||
* @raise ENXIO is specified by POSIX
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
*/
|
||||
ssize_t read(int fd, void *buf, size_t size) {
|
||||
ssize_t rc;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (fd < 0) {
|
||||
rc = ebadf();
|
||||
|
@ -87,7 +87,7 @@ ssize_t read(int fd, void *buf, size_t size) {
|
|||
rc = enosys();
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
DATATRACE("read(%d, [%#.*hhs%s], %'zu) → %'zd% m", fd,
|
||||
(int)MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, rc);
|
||||
return rc;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
|
@ -35,8 +36,8 @@
|
|||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf,
|
||||
size_t bufsiz) {
|
||||
static textwindows ssize_t sys_readlinkat_nt_impl(int dirfd, const char *path,
|
||||
char *buf, size_t bufsiz) {
|
||||
|
||||
char16_t path16[PATH_MAX];
|
||||
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
|
||||
|
@ -128,3 +129,12 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf,
|
|||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf,
|
||||
size_t bufsiz) {
|
||||
ssize_t rc;
|
||||
BLOCK_SIGNALS;
|
||||
rc = sys_readlinkat_nt_impl(dirfd, path, buf, bufsiz);
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -43,12 +43,12 @@
|
|||
* performance boost in the case of a single small iovec.
|
||||
*
|
||||
* @return number of bytes actually read, or -1 w/ errno
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @restartable
|
||||
*/
|
||||
ssize_t readv(int fd, const struct iovec *iov, int iovlen) {
|
||||
ssize_t rc;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (fd < 0) {
|
||||
rc = ebadf();
|
||||
|
@ -75,7 +75,7 @@ ssize_t readv(int fd, const struct iovec *iov, int iovlen) {
|
|||
rc = enosys();
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("readv(%d, [%s], %d) → %'ld% m", fd, DescribeIovec(rc, iov, iovlen),
|
||||
iovlen, rc);
|
||||
return rc;
|
||||
|
|
209
libc/calls/readwrite-nt.c
Normal file
209
libc/calls/readwrite-nt.c
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/events.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
struct ReadwriteResources {
|
||||
int64_t handle;
|
||||
struct NtOverlapped *overlap;
|
||||
};
|
||||
|
||||
static void UnwindReadwrite(void *arg) {
|
||||
uint32_t got;
|
||||
struct ReadwriteResources *rwc = arg;
|
||||
CancelIoEx(rwc->handle, rwc->overlap);
|
||||
GetOverlappedResult(rwc->handle, rwc->overlap, &got, true);
|
||||
CloseHandle(rwc->overlap->hEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs code that's common to read/write/pread/pwrite/etc on Windows.
|
||||
*
|
||||
* @return bytes exchanged, or -1 w/ errno, or -2 if operation failed
|
||||
* and caller needs to do more work, examining the GetLastError()
|
||||
*/
|
||||
textwindows ssize_t
|
||||
sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
|
||||
int64_t handle, uint64_t waitmask,
|
||||
bool32 ReadOrWriteFile(int64_t, void *, uint32_t, uint32_t *,
|
||||
struct NtOverlapped *)) {
|
||||
bool32 ok;
|
||||
uint64_t m;
|
||||
uint32_t exchanged;
|
||||
bool eagained = false;
|
||||
bool eintered = false;
|
||||
bool canceled = false;
|
||||
bool olderror = errno;
|
||||
struct PosixThread *pt;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
|
||||
// win32 i/o apis generally take 32-bit values thus we implicitly
|
||||
// truncate outrageously large sizes. linux actually does it too!
|
||||
size = MIN(size, 0x7ffff000);
|
||||
|
||||
// pread() and pwrite() perform an implicit lseek() operation, so
|
||||
// similar to the lseek() system call, they too raise ESPIPE when
|
||||
// operating on a non-seekable file.
|
||||
bool pwriting = offset != -1;
|
||||
bool seekable = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk;
|
||||
if (pwriting && !seekable) {
|
||||
return espipe();
|
||||
}
|
||||
|
||||
// when a file is opened in overlapped mode win32 requires that we
|
||||
// take over full responsibility for managing our own file pointer
|
||||
// which is fine, because the one win32 has was never very good in
|
||||
// the sense that it behaves so differently from linux, that using
|
||||
// win32 i/o required more compatibilty toil than doing it by hand
|
||||
if (!pwriting) {
|
||||
if (seekable) {
|
||||
offset = f->pointer;
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// managing an overlapped i/o operation is tricky to do using just
|
||||
// imperative procedural logic. its design lends itself more to be
|
||||
// something that's abstracted in an object-oriented design, which
|
||||
// easily manages the unusual lifecycles requirements of the thing
|
||||
// the game here is to not return until win32 is done w/ `overlap`
|
||||
// next we need to allow signal handlers to re-enter this function
|
||||
// while we're performing a read in the same thread. this needs to
|
||||
// be thread safe too of course. read() / write() are cancelation
|
||||
// points so pthread_cancel() might teleport the execution here to
|
||||
// pthread_exit(), so we need cleanup handlers that pthread_exit()
|
||||
// can call, pushed onto the stack, so we don't leak win32 handles
|
||||
// or worse trash the thread stack containing `overlap` that win32
|
||||
// temporarily owns while the overlapped i/o op is being performed
|
||||
// we implement a non-blocking iop by optimistically performing io
|
||||
// and then aborting the operation if win32 says it needs to block
|
||||
// with cancelation points, we need to be able to raise eintr when
|
||||
// this thread is pre-empted to run a signal handler but only when
|
||||
// that signal handler wasn't installed using this SA_RESTART flag
|
||||
// in which case read() and write() will keep going afterwards. we
|
||||
// support a second kind of eintr in cosmo/musl which is ecanceled
|
||||
// and it's mission critical that it be relayed properly, since it
|
||||
// can only be returned by a single system call in a thread's life
|
||||
// another thing we do is check if any pending signals exist, then
|
||||
// running as many of them as possible before entering a wait call
|
||||
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0),
|
||||
.Pointer = offset};
|
||||
struct ReadwriteResources rwc = {handle, &overlap};
|
||||
pthread_cleanup_push(UnwindReadwrite, &rwc);
|
||||
ok = ReadOrWriteFile(handle, data, size, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||
BlockingOperation:
|
||||
pt = _pthread_self();
|
||||
pt->pt_iohandle = handle;
|
||||
pt->pt_ioverlap = &overlap;
|
||||
pt->pt_flags |= PT_RESTARTABLE;
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_IO, memory_order_release);
|
||||
m = __sig_beginwait(waitmask);
|
||||
if (f->flags & O_NONBLOCK) {
|
||||
CancelIoEx(handle, &overlap);
|
||||
eagained = true;
|
||||
} else if (_check_cancel()) {
|
||||
CancelIoEx(handle, &overlap);
|
||||
canceled = true;
|
||||
} else if (_check_signal(true)) {
|
||||
CancelIoEx(handle, &overlap);
|
||||
eintered = true;
|
||||
} else {
|
||||
WaitForSingleObject(overlap.hEvent, -1u);
|
||||
}
|
||||
__sig_finishwait(m);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU,
|
||||
memory_order_release);
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
pt->pt_ioverlap = 0;
|
||||
pt->pt_iohandle = 0;
|
||||
ok = true;
|
||||
}
|
||||
if (ok) {
|
||||
bool32 should_wait = canceled || eagained;
|
||||
ok = GetOverlappedResult(handle, &overlap, &exchanged, should_wait);
|
||||
if (!ok && GetLastError() == kNtErrorIoIncomplete) {
|
||||
goto BlockingOperation;
|
||||
}
|
||||
}
|
||||
CloseHandle(overlap.hEvent);
|
||||
pthread_cleanup_pop(false);
|
||||
|
||||
// if we acknowledged a pending masked mode cancelation request then
|
||||
// we must pass it to the caller immediately now that cleanup's done
|
||||
if (canceled) {
|
||||
return ecanceled();
|
||||
}
|
||||
|
||||
// sudden success trumps interrupts and/or failed i/o abort attempts
|
||||
// plenty of code above might clobber errno, so we always restore it
|
||||
if (ok) {
|
||||
if (!pwriting && seekable) {
|
||||
f->pointer = offset + exchanged;
|
||||
}
|
||||
errno = olderror;
|
||||
return exchanged;
|
||||
}
|
||||
|
||||
// if we backed out of the i/o operation intentionally ignore errors
|
||||
if (eagained) {
|
||||
return eagain();
|
||||
}
|
||||
|
||||
// if another thread canceled our win32 i/o operation then we should
|
||||
// check and see if it was pthread_cancel() which committed the deed
|
||||
// in which case _check_cancel() can acknowledge the cancelation now
|
||||
// it's also fine to do nothing here; punt to next cancelation point
|
||||
if (GetLastError() == kNtErrorOperationAborted && _check_cancel() == -1) {
|
||||
return ecanceled();
|
||||
}
|
||||
|
||||
// if we chose to process a pending signal earlier then we preserve
|
||||
// that original error explicitly here even though aborted == eintr
|
||||
if (eintered) {
|
||||
return eintr();
|
||||
}
|
||||
|
||||
// read() and write() have generally different error-handling paths
|
||||
return -2;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -1,98 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/extend.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
|
||||
// TODO(jart): make more of this code lockless
|
||||
|
||||
static volatile size_t mapsize;
|
||||
|
||||
/**
|
||||
* Grows file descriptor array memory if needed.
|
||||
*
|
||||
* @see libc/runtime/memtrack64.txt
|
||||
* @see libc/runtime/memtrack32.txt
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int __ensurefds_unlocked(int fd) {
|
||||
size_t n;
|
||||
if (fd < g_fds.n) return fd;
|
||||
n = fd + 1;
|
||||
g_fds.e = _extend(g_fds.p, n * sizeof(*g_fds.p), g_fds.e, MAP_PRIVATE,
|
||||
kMemtrackFdsStart + kMemtrackFdsSize);
|
||||
g_fds.n = n;
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grows file descriptor array memory if needed.
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int __ensurefds(int fd) {
|
||||
__fds_lock();
|
||||
fd = __ensurefds_unlocked(fd);
|
||||
__fds_unlock();
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds open file descriptor slot.
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int __reservefd_unlocked(int start) {
|
||||
int fd, f1, f2;
|
||||
for (;;) {
|
||||
f1 = atomic_load_explicit(&g_fds.f, memory_order_acquire);
|
||||
for (fd = MAX(start, f1); fd < g_fds.n; ++fd) {
|
||||
if (!g_fds.p[fd].kind) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
fd = __ensurefds_unlocked(fd);
|
||||
bzero(g_fds.p + fd, sizeof(*g_fds.p));
|
||||
if (_cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
|
||||
// g_fds.f isn't guarded by our mutex
|
||||
do {
|
||||
f2 = MAX(fd + 1, f1);
|
||||
} while (!atomic_compare_exchange_weak_explicit(
|
||||
&g_fds.f, &f1, f2, memory_order_release, memory_order_relaxed));
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds open file descriptor slot.
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int __reservefd(int start) {
|
||||
int fd;
|
||||
__fds_lock();
|
||||
fd = __reservefd_unlocked(start);
|
||||
__fds_unlock();
|
||||
return fd;
|
||||
}
|
|
@ -61,9 +61,9 @@ void __restore_tty(void) {
|
|||
int e;
|
||||
if (__isrestorable && !__isworker && !__nocolor) {
|
||||
e = errno;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
sys_write(0, ANSI_RESTORE, __strlen(ANSI_RESTORE));
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
tcsetattr(0, TCSAFLUSH, &__oldtermios);
|
||||
errno = e;
|
||||
}
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sched-sysv.internal.h"
|
||||
#include "libc/calls/struct/cpuset.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/enum/processaccess.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static dontinline textwindows int sys_sched_getaffinity_nt(int pid, size_t size,
|
||||
cpu_set_t *bitset) {
|
||||
int rc;
|
||||
int64_t h, closeme = -1;
|
||||
uint64_t SystemAffinityMask;
|
||||
|
||||
if (!pid || pid == getpid()) {
|
||||
h = GetCurrentProcess();
|
||||
} else if (__isfdkind(pid, kFdProcess)) {
|
||||
h = g_fds.p[pid].handle;
|
||||
} else {
|
||||
h = OpenProcess(kNtProcessQueryInformation, false, pid);
|
||||
if (!h) return __winerr();
|
||||
closeme = h;
|
||||
}
|
||||
|
||||
if (GetProcessAffinityMask(h, bitset->__bits, &SystemAffinityMask)) {
|
||||
rc = 8;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
|
||||
if (closeme != -1) {
|
||||
CloseHandle(closeme);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets CPU affinity for process.
|
||||
*
|
||||
* @param pid is the process id (or 0 for caller)
|
||||
* @param size is bytes in bitset, which should be `sizeof(cpuset_t)`
|
||||
* @param bitset receives bitset and should be uint64_t[16] in order to
|
||||
* work on older versions of Linux
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ENOSYS if not Linux, FreeBSD, NetBSD, or Windows
|
||||
* @see pthread_getaffinity_np() for threads
|
||||
*/
|
||||
int sched_getaffinity(int pid, size_t size, cpu_set_t *bitset) {
|
||||
int rc;
|
||||
if (size != sizeof(cpu_set_t)) {
|
||||
rc = einval();
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_sched_getaffinity_nt(pid, size, bitset);
|
||||
} else if (IsFreebsd()) {
|
||||
if (!sys_sched_getaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid, 32,
|
||||
bitset)) {
|
||||
rc = 32;
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
} else if (IsNetbsd()) {
|
||||
if (!sys_sched_getaffinity_netbsd(P_ALL_LWPS, pid, 32, bitset)) {
|
||||
rc = 32;
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
} else {
|
||||
rc = sys_sched_getaffinity(pid, size, bitset);
|
||||
}
|
||||
if (rc > 0) {
|
||||
if (rc < size) {
|
||||
bzero((char *)bitset + rc, size - rc);
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
STRACE("sched_getaffinity(%d, %'zu, %p) → %d% m", pid, size, bitset, rc);
|
||||
return rc;
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sched-sysv.internal.h"
|
||||
#include "libc/calls/struct/cpuset.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/enum/processaccess.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static dontinline textwindows int sys_sched_setaffinity_nt(
|
||||
int pid, uint64_t size, const cpu_set_t *bitset) {
|
||||
int rc;
|
||||
int64_t h, closeme = -1;
|
||||
|
||||
if (!pid || pid == getpid()) {
|
||||
h = GetCurrentProcess();
|
||||
} else if (__isfdkind(pid, kFdProcess)) {
|
||||
h = g_fds.p[pid].handle;
|
||||
} else {
|
||||
h = OpenProcess(kNtProcessSetInformation | kNtProcessQueryInformation,
|
||||
false, pid);
|
||||
if (!h) return __winerr();
|
||||
closeme = h;
|
||||
}
|
||||
|
||||
if (SetProcessAffinityMask(h, bitset->__bits[0])) {
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
|
||||
if (closeme != -1) {
|
||||
CloseHandle(closeme);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks kernel to only schedule process on particular CPUs.
|
||||
*
|
||||
* Affinity masks are inherited across fork() and execve() boundaries.
|
||||
*
|
||||
* @param pid is the process or process id (or 0 for caller)
|
||||
* @param size is bytes in bitset, which should be `sizeof(cpuset_t)`
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ENOSYS if not Linux, FreeBSD, NetBSD, or Windows
|
||||
* @see pthread_getaffinity_np() for threads
|
||||
*/
|
||||
int sched_setaffinity(int pid, size_t size, const cpu_set_t *bitset) {
|
||||
int rc;
|
||||
if (size != sizeof(cpu_set_t)) {
|
||||
rc = einval();
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_sched_setaffinity_nt(pid, size, bitset);
|
||||
} else if (IsFreebsd()) {
|
||||
rc = sys_sched_setaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid, 32,
|
||||
bitset);
|
||||
} else if (IsNetbsd()) {
|
||||
rc = sys_sched_setaffinity_netbsd(P_ALL_LWPS, pid, 32, bitset);
|
||||
} else {
|
||||
rc = sys_sched_setaffinity(pid, size, bitset);
|
||||
}
|
||||
STRACE("sched_setaffinity(%d, %'zu, %p) → %d% m", pid, size, bitset, rc);
|
||||
return rc;
|
||||
}
|
|
@ -18,11 +18,8 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Changes process group for process.
|
||||
|
@ -33,29 +30,11 @@
|
|||
* @vforksafe
|
||||
*/
|
||||
int setpgid(int pid, int pgid) {
|
||||
int rc, me;
|
||||
int rc;
|
||||
if (!IsWindows()) {
|
||||
rc = sys_setpgid(pid, pgid);
|
||||
} else {
|
||||
me = getpid();
|
||||
if ((!pid || pid == me) && (!pgid || pgid == me)) {
|
||||
// "When a process is created with kNtCreateNewProcessGroup
|
||||
// specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)
|
||||
// is made on behalf of the new process; this means that the new
|
||||
// process has CTRL+C disabled. This lets shells handle CTRL+C
|
||||
// themselves, and selectively pass that signal on to
|
||||
// sub-processes. CTRL+BREAK is not disabled, and may be used to
|
||||
// interrupt the process/process group."
|
||||
// ──Quoth MSDN § CreateProcessW()
|
||||
if (SetConsoleCtrlHandler(0, 1)) {
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
} else {
|
||||
// avoid bash printing scary warnings for now
|
||||
rc = 0;
|
||||
}
|
||||
rc = 0; // not sure what to do on windows yet
|
||||
}
|
||||
STRACE("setpgid(%d, %d) → %d% m", pid, pgid, rc);
|
||||
return rc;
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/enum/processaccess.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sysv/consts/prio.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_setpriority_nt(int which, unsigned pid, int nice) {
|
||||
int rc;
|
||||
uint32_t tier;
|
||||
int64_t h, closeme = -1;
|
||||
|
||||
if (which != PRIO_PROCESS) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
if (!pid || pid == getpid()) {
|
||||
h = GetCurrentProcess();
|
||||
} else if (__isfdkind(pid, kFdProcess)) {
|
||||
h = g_fds.p[pid].handle;
|
||||
} else {
|
||||
h = OpenProcess(kNtProcessSetInformation | kNtProcessQueryInformation,
|
||||
false, pid);
|
||||
if (!h) return __winerr();
|
||||
closeme = h;
|
||||
}
|
||||
|
||||
if (nice <= -15) {
|
||||
tier = kNtRealtimePriorityClass;
|
||||
} else if (nice <= -9) {
|
||||
tier = kNtHighPriorityClass;
|
||||
} else if (nice <= -3) {
|
||||
tier = kNtAboveNormalPriorityClass;
|
||||
} else if (nice <= 3) {
|
||||
tier = kNtNormalPriorityClass;
|
||||
} else if (nice <= 12) {
|
||||
tier = kNtBelowNormalPriorityClass;
|
||||
} else {
|
||||
tier = kNtIdlePriorityClass;
|
||||
}
|
||||
|
||||
if (SetPriorityClass(h, tier)) {
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
|
||||
if (closeme != -1) {
|
||||
CloseHandle(closeme);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
|
||||
/**
|
||||
* Sets nice value of thing.
|
||||
*
|
||||
* On Windows, there's only six priority classes. We define them as -16
|
||||
* (realtime), -10 (high), -5 (above), 0 (normal), 5 (below), 15 (idle)
|
||||
* which are the only values that'll roundtrip getpriority/setpriority.
|
||||
*
|
||||
* @param which can be one of:
|
||||
* - `PRIO_PROCESS` is supported universally
|
||||
* - `PRIO_PGRP` is supported on unix
|
||||
* - `PRIO_USER` is supported on unix
|
||||
* @param who is the pid, pgid, or uid, 0 meaning current
|
||||
* @param value ∈ [-NZERO,NZERO) which is clamped automatically
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EINVAL if `which` was invalid or unsupported
|
||||
* @error EACCES if `value` lower that `RLIMIT_NICE`
|
||||
* @error EACCES on Linux without `CAP_SYS_NICE`
|
||||
* @raise EPERM if access to process was denied
|
||||
* @raise ESRCH if the process didn't exist
|
||||
* @see getpriority()
|
||||
*/
|
||||
int setpriority(int which, unsigned who, int value) {
|
||||
int rc;
|
||||
if (!IsWindows()) {
|
||||
rc = sys_setpriority(which, who, value);
|
||||
} else {
|
||||
rc = sys_setpriority_nt(which, who, value);
|
||||
}
|
||||
STRACE("setpriority(%s, %u, %d) → %d% m", DescribeWhichPrio(which), who,
|
||||
value, rc);
|
||||
return rc;
|
||||
}
|
164
libc/calls/sig.c
164
libc/calls/sig.c
|
@ -18,9 +18,8 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
|
@ -56,10 +55,14 @@
|
|||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/ss.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
/**
|
||||
* @fileoverview Cosmopolitan Signals for Windows.
|
||||
*/
|
||||
|
||||
struct SignalFrame {
|
||||
struct PosixThread *pt;
|
||||
struct NtContext *nc;
|
||||
|
@ -86,6 +89,17 @@ textwindows bool __sig_ignored(int sig) {
|
|||
__sig_ignored_by_default(sig));
|
||||
}
|
||||
|
||||
textwindows void __sig_delete(int sig) {
|
||||
struct Dll *e;
|
||||
__sig.pending &= ~(1ull << (sig - 1));
|
||||
_pthread_lock();
|
||||
for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e)) {
|
||||
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
|
||||
pt->tib->tib_sigpending &= ~(1ull << (sig - 1));
|
||||
}
|
||||
_pthread_unlock();
|
||||
}
|
||||
|
||||
static textwindows bool __sig_should_use_altstack(unsigned flags,
|
||||
struct CosmoTib *tib) {
|
||||
if (!(flags & SA_ONSTACK)) {
|
||||
|
@ -141,53 +155,72 @@ static textwindows sigaction_f __sig_handler(unsigned rva) {
|
|||
|
||||
textwindows int __sig_raise(int sig, int sic) {
|
||||
unsigned rva, flags;
|
||||
struct CosmoTib *tib = __get_tls();
|
||||
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
|
||||
ucontext_t ctx = {.uc_sigmask.__bits[0] = tib->tib_sigmask};
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
ucontext_t ctx = {.uc_sigmask = pt->tib->tib_sigmask};
|
||||
if (!__sig_start(pt, sig, &rva, &flags)) return 0;
|
||||
siginfo_t si = {.si_signo = sig, .si_code = sic};
|
||||
struct NtContext nc;
|
||||
nc.ContextFlags = kNtContextAll;
|
||||
GetThreadContext(GetCurrentThread(), &nc);
|
||||
_ntcontext2linux(&ctx, &nc);
|
||||
pt->tib->tib_sigmask |= __sighandmask[sig];
|
||||
if (!(flags & SA_NODEFER)) {
|
||||
tib->tib_sigmask |= 1ull << (sig - 1);
|
||||
pt->tib->tib_sigmask |= 1ull << (sig - 1);
|
||||
}
|
||||
STRACE("entering raise(%G) signal handler %t with mask %s → %s", sig,
|
||||
__sig_handler(rva), DescribeSigset(0, &ctx.uc_sigmask),
|
||||
DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}));
|
||||
NTTRACE("entering raise(%G) signal handler %t with mask %s → %s", sig,
|
||||
__sig_handler(rva), DescribeSigset(0, &ctx.uc_sigmask),
|
||||
DescribeSigset(0, (sigset_t *)&pt->tib->tib_sigmask));
|
||||
__sig_handler(rva)(sig, &si, &ctx);
|
||||
STRACE("leaving raise(%G) signal handler %t with mask %s → %s", sig,
|
||||
__sig_handler(rva),
|
||||
DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}),
|
||||
DescribeSigset(0, &ctx.uc_sigmask));
|
||||
tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
||||
NTTRACE("leaving raise(%G) signal handler %t with mask %s → %s", sig,
|
||||
__sig_handler(rva),
|
||||
DescribeSigset(0, (sigset_t *)&pt->tib->tib_sigmask),
|
||||
DescribeSigset(0, &ctx.uc_sigmask));
|
||||
pt->tib->tib_sigmask = ctx.uc_sigmask;
|
||||
return (flags & SA_RESTART) ? 2 : 1;
|
||||
}
|
||||
|
||||
textwindows void __sig_cancel(struct PosixThread *pt, unsigned flags) {
|
||||
atomic_int *futex;
|
||||
if (_weaken(WakeByAddressSingle) &&
|
||||
(futex = atomic_load_explicit(&pt->pt_futex, memory_order_acquire))) {
|
||||
STRACE("canceling futex");
|
||||
_weaken(WakeByAddressSingle)(futex);
|
||||
} else if (!(flags & SA_RESTART) && pt->iohandle > 0) {
|
||||
STRACE("canceling i/o operation");
|
||||
if (!CancelIoEx(pt->iohandle, pt->ioverlap)) {
|
||||
int err = GetLastError();
|
||||
if (err != kNtErrorNotFound) {
|
||||
STRACE("CancelIoEx() failed w/ %d", err);
|
||||
}
|
||||
}
|
||||
} else if (pt->pt_flags & PT_INSEMAPHORE) {
|
||||
STRACE("canceling semaphore");
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (!ReleaseSemaphore(pt->semaphore, 1, 0)) {
|
||||
STRACE("ReleaseSemaphore() failed w/ %d", GetLastError());
|
||||
}
|
||||
} else {
|
||||
STRACE("canceling asynchronously");
|
||||
// cancels blocking operations being performed by signaled thread
|
||||
textwindows void __sig_cancel(struct PosixThread *pt, int sig, unsigned flags) {
|
||||
bool should_restart;
|
||||
atomic_int *blocker;
|
||||
// cancelation points need to set pt_blocker before entering a wait op
|
||||
blocker = atomic_load_explicit(&pt->pt_blocker, memory_order_acquire);
|
||||
// cancelation points should set it back to this after blocking
|
||||
// however, code that longjmps might mess it up a tolerable bit
|
||||
if (blocker == PT_BLOCKER_CPU) {
|
||||
STRACE("%G delivered to %d asynchronously", sig, _pthread_tid(pt));
|
||||
return;
|
||||
}
|
||||
// most cancelation points can be safely restarted w/o raising eintr
|
||||
should_restart = (flags & SA_RESTART) && (pt->pt_flags & PT_RESTARTABLE);
|
||||
// we can cancel another thread's overlapped i/o op after the freeze
|
||||
if (blocker == PT_BLOCKER_IO) {
|
||||
if (should_restart) {
|
||||
STRACE("%G restarting %d's i/o op", sig, _pthread_tid(pt));
|
||||
} else {
|
||||
STRACE("%G interupting %d's i/o op", sig, _pthread_tid(pt));
|
||||
CancelIoEx(pt->pt_iohandle, pt->pt_ioverlap);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// threads can create semaphores on an as-needed basis
|
||||
if (blocker == PT_BLOCKER_SEM) {
|
||||
if (should_restart) {
|
||||
STRACE("%G restarting %d's semaphore", sig, _pthread_tid(pt));
|
||||
} else {
|
||||
STRACE("%G releasing %d's semaphore", sig, _pthread_tid(pt));
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
ReleaseSemaphore(pt->pt_semaphore, 1, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// all other blocking ops that aren't overlap should use futexes
|
||||
// we force restartable futexes to churn by waking w/o releasing
|
||||
STRACE("%G waking %d's futex", sig, _pthread_tid(pt));
|
||||
if (!should_restart) {
|
||||
atomic_store_explicit(blocker, 1, memory_order_release);
|
||||
}
|
||||
WakeByAddressSingle(blocker);
|
||||
}
|
||||
|
||||
static textwindows wontreturn void __sig_panic(const char *msg) {
|
||||
|
@ -207,23 +240,21 @@ static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
|||
ucontext_t ctx = {0};
|
||||
int sig = sf->si.si_signo;
|
||||
_ntcontext2linux(&ctx, sf->nc);
|
||||
ctx.uc_sigmask.__bits[0] =
|
||||
atomic_load_explicit(&sf->pt->tib->tib_sigmask, memory_order_acquire);
|
||||
ctx.uc_sigmask = sf->pt->tib->tib_sigmask;
|
||||
sf->pt->tib->tib_sigmask |= __sighandmask[sig];
|
||||
if (!(sf->flags & SA_NODEFER)) {
|
||||
sf->pt->tib->tib_sigmask |= 1ull << (sig - 1);
|
||||
}
|
||||
++__sig.count;
|
||||
STRACE("entering __sig_tramp(%G, %t) with mask %s → %s", sig,
|
||||
__sig_handler(sf->rva), DescribeSigset(0, &ctx.uc_sigmask),
|
||||
DescribeSigset(0, &(sigset_t){{sf->pt->tib->tib_sigmask}}));
|
||||
NTTRACE("entering __sig_tramp(%G, %t) with mask %s → %s", sig,
|
||||
__sig_handler(sf->rva), DescribeSigset(0, &ctx.uc_sigmask),
|
||||
DescribeSigset(0, (sigset_t *)&sf->pt->tib->tib_sigmask));
|
||||
__sig_handler(sf->rva)(sig, &sf->si, &ctx);
|
||||
STRACE("leaving __sig_tramp(%G, %t) with mask %s → %s", sig,
|
||||
__sig_handler(sf->rva),
|
||||
DescribeSigset(0, &(sigset_t){{sf->pt->tib->tib_sigmask}}),
|
||||
DescribeSigset(0, &ctx.uc_sigmask));
|
||||
atomic_store_explicit(&sf->pt->tib->tib_sigmask, ctx.uc_sigmask.__bits[0],
|
||||
memory_order_release);
|
||||
// TDOO(jart): Do we need to do a __sig_check() here?
|
||||
NTTRACE("leaving __sig_tramp(%G, %t) with mask %s → %s", sig,
|
||||
__sig_handler(sf->rva),
|
||||
DescribeSigset(0, (sigset_t *)&sf->pt->tib->tib_sigmask),
|
||||
DescribeSigset(0, &ctx.uc_sigmask));
|
||||
sf->pt->tib->tib_sigmask = ctx.uc_sigmask;
|
||||
_ntlinux2context(sf->nc, &ctx);
|
||||
SetThreadContext(GetCurrentThread(), sf->nc);
|
||||
__sig_panic("SetThreadContext(GetCurrentThread)");
|
||||
|
@ -275,18 +306,15 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
|||
STRACE("SetThreadContext failed w/ %d", GetLastError());
|
||||
return ESRCH;
|
||||
}
|
||||
ResumeThread(th); // doesn't actually resume if doing blocking i/o
|
||||
pt->abort_errno = EINTR;
|
||||
__sig_cancel(pt, flags); // we can wake it up immediately by canceling it
|
||||
ResumeThread(th);
|
||||
__sig_cancel(pt, sig, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
|
||||
int rc;
|
||||
BLOCK_SIGNALS;
|
||||
BLOCK_CANCELLATIONS;
|
||||
rc = __sig_killer(pt, sig, sic);
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_SIGNALS;
|
||||
return rc;
|
||||
}
|
||||
|
@ -302,22 +330,25 @@ textwindows void __sig_generate(int sig, int sic) {
|
|||
STRACE("terminating on %G due to no handler", sig);
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
pthread_spin_lock(&_pthread_lock);
|
||||
BLOCK_SIGNALS;
|
||||
_pthread_lock();
|
||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||
pt = POSIXTHREAD_CONTAINER(e);
|
||||
if (atomic_load_explicit(&pt->status, memory_order_acquire) <
|
||||
if (atomic_load_explicit(&pt->pt_status, memory_order_acquire) <
|
||||
kPosixThreadTerminated &&
|
||||
!(pt->tib->tib_sigmask & (1ull << (sig - 1)))) {
|
||||
mark = pt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_spin_unlock(&_pthread_lock);
|
||||
_pthread_unlock();
|
||||
ALLOW_SIGNALS;
|
||||
if (mark) {
|
||||
STRACE("generating %G by killing %d", sig, _pthread_tid(mark));
|
||||
__sig_kill(mark, sig, sic);
|
||||
} else {
|
||||
STRACE("all threads block %G so adding to pending signals of process", sig);
|
||||
__sig.pending |= sig;
|
||||
__sig.pending |= 1ull << (sig - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,8 +411,8 @@ static int __sig_crash_sig(struct NtExceptionPointers *ep, int *code) {
|
|||
}
|
||||
}
|
||||
|
||||
static void __sig_crasher(struct NtExceptionPointers *ep, int code, int sig,
|
||||
struct CosmoTib *tib) {
|
||||
static void __sig_unmaskable(struct NtExceptionPointers *ep, int code, int sig,
|
||||
struct CosmoTib *tib) {
|
||||
|
||||
// increment the signal count for getrusage()
|
||||
++__sig.count;
|
||||
|
@ -436,9 +467,13 @@ static void __sig_crasher(struct NtExceptionPointers *ep, int code, int sig,
|
|||
ucontext_t ctx = {0};
|
||||
siginfo_t si = {.si_signo = sig, .si_code = code, .si_addr = si_addr};
|
||||
_ntcontext2linux(&ctx, ep->ContextRecord);
|
||||
ctx.uc_sigmask.__bits[0] = tib->tib_sigmask;
|
||||
ctx.uc_sigmask = tib->tib_sigmask;
|
||||
tib->tib_sigmask |= __sighandmask[sig];
|
||||
if (!(flags & SA_NODEFER)) {
|
||||
tib->tib_sigmask |= 1ull << (sig - 1);
|
||||
}
|
||||
__sig_handler(rva)(sig, &si, &ctx);
|
||||
tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
||||
tib->tib_sigmask = ctx.uc_sigmask;
|
||||
_ntlinux2context(ep->ContextRecord, &ctx);
|
||||
}
|
||||
|
||||
|
@ -470,10 +505,10 @@ __msabi dontinstrument unsigned __sig_crash(struct NtExceptionPointers *ep) {
|
|||
struct CosmoTib *tib = __get_tls();
|
||||
unsigned flags = __sighandflags[sig];
|
||||
if (__sig_should_use_altstack(flags, tib)) {
|
||||
__stack_call(ep, code, sig, tib, __sig_crasher,
|
||||
__stack_call(ep, code, sig, tib, __sig_unmaskable,
|
||||
tib->tib_sigstack_addr + tib->tib_sigstack_size);
|
||||
} else {
|
||||
__sig_crasher(ep, code, sig, tib);
|
||||
__sig_unmaskable(ep, code, sig, tib);
|
||||
}
|
||||
|
||||
// resume running user program
|
||||
|
@ -517,6 +552,7 @@ static textwindows int __sig_checkem(atomic_ulong *sigs, struct CosmoTib *tib,
|
|||
for (int sig = 1; sig <= 64; ++sig) {
|
||||
if ((deliverable & (1ull << (sig - 1))) &&
|
||||
atomic_fetch_and(sigs, ~(1ull << (sig - 1))) & (1ull << (sig - 1))) {
|
||||
STRACE("found pending %G we can raise now", sig);
|
||||
handler_was_called |= __sig_raise(sig, SI_KERNEL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ int __sig_check(void);
|
|||
int __sig_kill(struct PosixThread *, int, int);
|
||||
int __sig_mask(int, const sigset_t *, sigset_t *);
|
||||
int __sig_raise(int, int);
|
||||
void __sig_cancel(struct PosixThread *, unsigned);
|
||||
void __sig_delete(int);
|
||||
void __sig_generate(int, int);
|
||||
void __sig_init(void);
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
|
@ -27,7 +26,6 @@
|
|||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigaction.internal.h"
|
||||
#include "libc/calls/struct/siginfo.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
|
@ -35,6 +33,7 @@
|
|||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
|
@ -47,56 +46,53 @@
|
|||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#if SupportsWindows()
|
||||
__static_yoink("__sig_ctor");
|
||||
#endif
|
||||
|
||||
#define SA_RESTORER 0x04000000
|
||||
|
||||
static void sigaction_cosmo2native(union metasigaction *sa) {
|
||||
void *handler;
|
||||
uint64_t flags;
|
||||
void *restorer;
|
||||
uint32_t mask[4];
|
||||
uint32_t masklo;
|
||||
uint32_t maskhi;
|
||||
if (!sa) return;
|
||||
flags = sa->cosmo.sa_flags;
|
||||
handler = sa->cosmo.sa_handler;
|
||||
restorer = sa->cosmo.sa_restorer;
|
||||
mask[0] = sa->cosmo.sa_mask.__bits[0];
|
||||
mask[1] = sa->cosmo.sa_mask.__bits[0] >> 32;
|
||||
mask[2] = sa->cosmo.sa_mask.__bits[1];
|
||||
mask[3] = sa->cosmo.sa_mask.__bits[1] >> 32;
|
||||
masklo = sa->cosmo.sa_mask;
|
||||
maskhi = sa->cosmo.sa_mask >> 32;
|
||||
if (IsLinux()) {
|
||||
sa->linux.sa_flags = flags;
|
||||
sa->linux.sa_handler = handler;
|
||||
sa->linux.sa_restorer = restorer;
|
||||
sa->linux.sa_mask[0] = mask[0];
|
||||
sa->linux.sa_mask[1] = mask[1];
|
||||
sa->linux.sa_mask[0] = masklo;
|
||||
sa->linux.sa_mask[1] = maskhi;
|
||||
} else if (IsXnu()) {
|
||||
sa->xnu_in.sa_flags = flags;
|
||||
sa->xnu_in.sa_handler = handler;
|
||||
sa->xnu_in.sa_restorer = restorer;
|
||||
sa->xnu_in.sa_mask[0] = mask[0];
|
||||
sa->xnu_in.sa_mask[0] = masklo;
|
||||
} else if (IsFreebsd()) {
|
||||
sa->freebsd.sa_flags = flags;
|
||||
sa->freebsd.sa_handler = handler;
|
||||
sa->freebsd.sa_mask[0] = mask[0];
|
||||
sa->freebsd.sa_mask[1] = mask[1];
|
||||
sa->freebsd.sa_mask[2] = mask[2];
|
||||
sa->freebsd.sa_mask[3] = mask[3];
|
||||
sa->freebsd.sa_mask[0] = masklo;
|
||||
sa->freebsd.sa_mask[1] = maskhi;
|
||||
sa->freebsd.sa_mask[2] = 0;
|
||||
sa->freebsd.sa_mask[3] = 0;
|
||||
} else if (IsOpenbsd()) {
|
||||
sa->openbsd.sa_flags = flags;
|
||||
sa->openbsd.sa_handler = handler;
|
||||
sa->openbsd.sa_mask[0] = mask[0];
|
||||
sa->openbsd.sa_mask[0] = masklo;
|
||||
} else if (IsNetbsd()) {
|
||||
sa->netbsd.sa_flags = flags;
|
||||
sa->netbsd.sa_handler = handler;
|
||||
sa->netbsd.sa_mask[0] = mask[0];
|
||||
sa->netbsd.sa_mask[1] = mask[1];
|
||||
sa->netbsd.sa_mask[2] = mask[2];
|
||||
sa->netbsd.sa_mask[3] = mask[3];
|
||||
sa->netbsd.sa_mask[0] = masklo;
|
||||
sa->netbsd.sa_mask[1] = maskhi;
|
||||
sa->netbsd.sa_mask[2] = 0;
|
||||
sa->netbsd.sa_mask[3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,44 +100,40 @@ static void sigaction_native2cosmo(union metasigaction *sa) {
|
|||
void *handler;
|
||||
uint64_t flags;
|
||||
void *restorer = 0;
|
||||
uint32_t mask[4] = {0};
|
||||
uint32_t masklo;
|
||||
uint32_t maskhi = 0;
|
||||
if (!sa) return;
|
||||
if (IsLinux()) {
|
||||
flags = sa->linux.sa_flags;
|
||||
handler = sa->linux.sa_handler;
|
||||
restorer = sa->linux.sa_restorer;
|
||||
mask[0] = sa->linux.sa_mask[0];
|
||||
mask[1] = sa->linux.sa_mask[1];
|
||||
masklo = sa->linux.sa_mask[0];
|
||||
maskhi = sa->linux.sa_mask[1];
|
||||
} else if (IsXnu()) {
|
||||
flags = sa->xnu_out.sa_flags;
|
||||
handler = sa->xnu_out.sa_handler;
|
||||
mask[0] = sa->xnu_out.sa_mask[0];
|
||||
masklo = sa->xnu_out.sa_mask[0];
|
||||
} else if (IsFreebsd()) {
|
||||
flags = sa->freebsd.sa_flags;
|
||||
handler = sa->freebsd.sa_handler;
|
||||
mask[0] = sa->freebsd.sa_mask[0];
|
||||
mask[1] = sa->freebsd.sa_mask[1];
|
||||
mask[2] = sa->freebsd.sa_mask[2];
|
||||
mask[3] = sa->freebsd.sa_mask[3];
|
||||
masklo = sa->freebsd.sa_mask[0];
|
||||
maskhi = sa->freebsd.sa_mask[1];
|
||||
} else if (IsOpenbsd()) {
|
||||
flags = sa->openbsd.sa_flags;
|
||||
handler = sa->openbsd.sa_handler;
|
||||
mask[0] = sa->openbsd.sa_mask[0];
|
||||
masklo = sa->openbsd.sa_mask[0];
|
||||
} else if (IsNetbsd()) {
|
||||
flags = sa->netbsd.sa_flags;
|
||||
handler = sa->netbsd.sa_handler;
|
||||
mask[0] = sa->netbsd.sa_mask[0];
|
||||
mask[1] = sa->netbsd.sa_mask[1];
|
||||
mask[2] = sa->netbsd.sa_mask[2];
|
||||
mask[3] = sa->netbsd.sa_mask[3];
|
||||
masklo = sa->netbsd.sa_mask[0];
|
||||
maskhi = sa->netbsd.sa_mask[1];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
sa->cosmo.sa_flags = flags;
|
||||
sa->cosmo.sa_handler = handler;
|
||||
sa->cosmo.sa_restorer = restorer;
|
||||
sa->cosmo.sa_mask.__bits[0] = mask[0] | (uint64_t)mask[1] << 32;
|
||||
sa->cosmo.sa_mask.__bits[1] = mask[2] | (uint64_t)mask[3] << 32;
|
||||
sa->cosmo.sa_mask = masklo | (uint64_t)maskhi << 32;
|
||||
}
|
||||
|
||||
static int __sigaction(int sig, const struct sigaction *act,
|
||||
|
@ -257,14 +249,10 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
if (rc != -1 && !__vforked) {
|
||||
if (act) {
|
||||
__sighandrvas[sig] = rva;
|
||||
__sighandmask[sig] = act->sa_mask;
|
||||
__sighandflags[sig] = act->sa_flags;
|
||||
if (IsWindows()) {
|
||||
if (__sig_ignored(sig)) {
|
||||
__sig.pending &= ~(1ull << (sig - 1));
|
||||
if (__tls_enabled) {
|
||||
__get_tls()->tib_sigpending &= ~(1ull << (sig - 1));
|
||||
}
|
||||
}
|
||||
if (IsWindows() && __sig_ignored(sig)) {
|
||||
__sig_delete(sig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -486,7 +474,7 @@ static int __sigaction(int sig, const struct sigaction *act,
|
|||
* handler doesn't save / restore the `errno` global when calling wait,
|
||||
* then any i/o logic in the main program that checks `errno` will most
|
||||
* likely break. This is rare in practice, since systems usually design
|
||||
* signals to favor delivery from cancellation points before they block
|
||||
* signals to favor delivery from cancelation points before they block
|
||||
* however that's not guaranteed.
|
||||
*
|
||||
* @return 0 on success or -1 w/ errno
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
|
||||
sigset_t _sigblockall(void) {
|
||||
sigset_t ss = {{-1, -1}};
|
||||
return _sigsetmask(ss);
|
||||
}
|
|
@ -57,8 +57,7 @@ privileged void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo,
|
|||
g.uc.uc_stack.ss_sp = ctx->uc_stack.ss_sp;
|
||||
g.uc.uc_stack.ss_size = ctx->uc_stack.ss_size;
|
||||
g.uc.uc_stack.ss_flags = ctx->uc_stack.ss_flags;
|
||||
__repmovsb(&g.uc.uc_sigmask, ctx->uc_sigmask,
|
||||
MIN(sizeof(g.uc.uc_sigmask), sizeof(ctx->uc_sigmask)));
|
||||
g.uc.uc_sigmask = ctx->uc_sigmask[0] | (uint64_t)ctx->uc_sigmask[0] << 32;
|
||||
g.uc.uc_mcontext.r8 = ctx->uc_mcontext.mc_r8;
|
||||
g.uc.uc_mcontext.r9 = ctx->uc_mcontext.mc_r9;
|
||||
g.uc.uc_mcontext.r10 = ctx->uc_mcontext.mc_r10;
|
||||
|
@ -88,8 +87,8 @@ privileged void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo,
|
|||
ctx->uc_stack.ss_size = g.uc.uc_stack.ss_size;
|
||||
ctx->uc_stack.ss_flags = g.uc.uc_stack.ss_flags;
|
||||
ctx->uc_flags = g.uc.uc_flags;
|
||||
__repmovsb(ctx->uc_sigmask, &g.uc.uc_sigmask,
|
||||
MIN(sizeof(g.uc.uc_sigmask), sizeof(ctx->uc_sigmask)));
|
||||
ctx->uc_sigmask[0] = g.uc.uc_sigmask;
|
||||
ctx->uc_sigmask[1] = g.uc.uc_sigmask >> 32;
|
||||
ctx->uc_mcontext.mc_rdi = g.uc.uc_mcontext.rdi;
|
||||
ctx->uc_mcontext.mc_rsi = g.uc.uc_mcontext.rsi;
|
||||
ctx->uc_mcontext.mc_rdx = g.uc.uc_mcontext.rdx;
|
||||
|
|
|
@ -55,8 +55,7 @@ privileged void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo,
|
|||
__repstosb(&g.uc, 0, sizeof(g.uc));
|
||||
__siginfo2cosmo(&g.si, (void *)openbsdinfo);
|
||||
g.uc.uc_mcontext.fpregs = &g.uc.__fpustate;
|
||||
g.uc.uc_sigmask.__bits[0] = ctx->sc_mask;
|
||||
g.uc.uc_sigmask.__bits[1] = 0;
|
||||
g.uc.uc_sigmask = ctx->sc_mask;
|
||||
g.uc.uc_mcontext.rdi = ctx->sc_rdi;
|
||||
g.uc.uc_mcontext.rsi = ctx->sc_rsi;
|
||||
g.uc.uc_mcontext.rdx = ctx->sc_rdx;
|
||||
|
@ -83,7 +82,7 @@ privileged void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo,
|
|||
sizeof(*ctx->sc_fpstate));
|
||||
}
|
||||
((sigaction_f)(__executable_start + rva))(sig, &g.si, &g.uc);
|
||||
ctx->sc_mask = g.uc.uc_sigmask.__bits[0];
|
||||
ctx->sc_mask = g.uc.uc_sigmask;
|
||||
ctx->sc_rdi = g.uc.uc_mcontext.rdi;
|
||||
ctx->sc_rsi = g.uc.uc_mcontext.rsi;
|
||||
ctx->sc_rdx = g.uc.uc_mcontext.rdx;
|
||||
|
|
|
@ -507,8 +507,7 @@ privileged void __sigenter_xnu(void *fn, int infostyle, int sig,
|
|||
__repstosb(&g, 0, sizeof(g));
|
||||
|
||||
if (xnuctx) {
|
||||
g.uc.uc_sigmask.__bits[0] = xnuctx->uc_sigmask;
|
||||
g.uc.uc_sigmask.__bits[1] = 0;
|
||||
g.uc.uc_sigmask = xnuctx->uc_sigmask;
|
||||
g.uc.uc_stack.ss_sp = xnuctx->uc_stack.ss_sp;
|
||||
g.uc.uc_stack.ss_flags = xnuctx->uc_stack.ss_flags;
|
||||
g.uc.uc_stack.ss_size = xnuctx->uc_stack.ss_size;
|
||||
|
@ -546,7 +545,7 @@ privileged void __sigenter_xnu(void *fn, int infostyle, int sig,
|
|||
xnuctx->uc_stack.ss_sp = g.uc.uc_stack.ss_sp;
|
||||
xnuctx->uc_stack.ss_flags = g.uc.uc_stack.ss_flags;
|
||||
xnuctx->uc_stack.ss_size = g.uc.uc_stack.ss_size;
|
||||
xnuctx->uc_sigmask = g.uc.uc_sigmask.__bits[0];
|
||||
xnuctx->uc_sigmask = g.uc.uc_sigmask;
|
||||
#ifdef __x86_64__
|
||||
if (xnuctx->uc_mcontext) {
|
||||
if (xnuctx->uc_mcsize >=
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define GetSigBit(x) (1ull << (((x)-1) & 63))
|
||||
|
||||
textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) {
|
||||
|
||||
// validate api usage
|
||||
if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
// get address of sigset to modify
|
||||
_Atomic(uint64_t) *mask = &__get_tls()->tib_sigmask;
|
||||
|
||||
// these signals are precious to cosmopolitan
|
||||
uint64_t precious = 0
|
||||
#define M(x) | GetSigBit(x)
|
||||
#include "libc/intrin/sigisprecious.inc"
|
||||
;
|
||||
|
||||
// handle read-only case
|
||||
uint64_t oldmask;
|
||||
if (neu) {
|
||||
uint64_t input = neu->__bits[0] & ~precious;
|
||||
if (how == SIG_BLOCK) {
|
||||
oldmask = atomic_fetch_or_explicit(mask, input, memory_order_acq_rel);
|
||||
} else if (how == SIG_UNBLOCK) {
|
||||
oldmask = atomic_fetch_and_explicit(mask, input, memory_order_acq_rel);
|
||||
} else { // SIG_SETMASK
|
||||
oldmask = atomic_exchange_explicit(mask, input, memory_order_acq_rel);
|
||||
}
|
||||
} else {
|
||||
oldmask = atomic_load_explicit(mask, memory_order_acquire);
|
||||
}
|
||||
|
||||
// return old signal mask to caller
|
||||
if (old) {
|
||||
old->__bits[0] = oldmask;
|
||||
old->__bits[1] = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -41,24 +41,19 @@ int sigpending(sigset_t *pending) {
|
|||
rc = efault();
|
||||
} else if (IsLinux() || IsNetbsd() || IsOpenbsd() || IsFreebsd() || IsXnu()) {
|
||||
// 128 signals on NetBSD and FreeBSD, 64 on Linux, 32 on OpenBSD and XNU
|
||||
rc = sys_sigpending(pending, 8);
|
||||
// OpenBSD passes signal sets in words rather than pointers
|
||||
uint64_t mem[2];
|
||||
rc = sys_sigpending(mem, 8);
|
||||
if (IsOpenbsd()) {
|
||||
pending->__bits[0] = (unsigned)rc;
|
||||
rc = 0;
|
||||
*pending = rc;
|
||||
} else {
|
||||
*pending = mem[0];
|
||||
}
|
||||
// only modify memory on success
|
||||
if (!rc) {
|
||||
// clear unsupported bits
|
||||
if (IsXnu()) {
|
||||
pending->__bits[0] &= 0xFFFFFFFF;
|
||||
}
|
||||
if (IsLinux() || IsOpenbsd() || IsXnu()) {
|
||||
pending->__bits[1] = 0;
|
||||
}
|
||||
if (IsXnu() || IsOpenbsd()) {
|
||||
*pending &= 0xffffffff;
|
||||
}
|
||||
rc = 0;
|
||||
} else if (IsWindows()) {
|
||||
*pending = (sigset_t){{__sig.pending | __get_tls()->tib_sigpending}};
|
||||
*pending = __sig.pending | __get_tls()->tib_sigpending;
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = enosys();
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
int sys_sigprocmask(int how, const sigset_t *opt_set,
|
||||
sigset_t *opt_out_oldset) {
|
||||
int res, rc, arg1;
|
||||
sigset_t old = {0};
|
||||
const sigset_t *arg2;
|
||||
if (!IsOpenbsd()) {
|
||||
rc = __sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0, 8);
|
||||
} else {
|
||||
if (opt_set) {
|
||||
// openbsd only supports 32 signals so it passses them in a reg
|
||||
arg1 = how;
|
||||
arg2 = (sigset_t *)(uintptr_t)(*(uint32_t *)opt_set);
|
||||
} else {
|
||||
arg1 = how; // SIG_BLOCK
|
||||
arg2 = 0; // changes nothing
|
||||
}
|
||||
if ((res = __sys_sigprocmask(arg1, arg2, 0, 0)) != -1) {
|
||||
memcpy(&old, &res, sizeof(res));
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rc != -1 && opt_out_oldset) {
|
||||
*opt_out_oldset = old;
|
||||
}
|
||||
return rc;
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Changes signal blocking state of calling thread, e.g.:
|
||||
*
|
||||
* sigset_t neu,old;
|
||||
* sigfillset(&neu);
|
||||
* sigprocmask(SIG_BLOCK, &neu, &old);
|
||||
* sigprocmask(SIG_SETMASK, &old, NULL);
|
||||
*
|
||||
* @param how can be SIG_BLOCK (U), SIG_UNBLOCK (/), SIG_SETMASK (=)
|
||||
* @param set is the new mask content (optional)
|
||||
* @param oldset will receive the old mask (optional) and can't overlap
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EFAULT if `set` or `oldset` is bad memory
|
||||
* @raise EINVAL if `how` is invalid
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
*/
|
||||
int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
|
||||
int rc;
|
||||
sigset_t old = {0};
|
||||
if (IsAsan() &&
|
||||
((opt_set && !__asan_is_valid(opt_set, sizeof(*opt_set))) ||
|
||||
(opt_out_oldset &&
|
||||
!__asan_is_valid(opt_out_oldset, sizeof(*opt_out_oldset))))) {
|
||||
rc = efault();
|
||||
} else if (IsMetal() || IsWindows()) {
|
||||
rc = __sig_mask(how, opt_set, &old);
|
||||
if (_weaken(__sig_check)) {
|
||||
_weaken(__sig_check)();
|
||||
}
|
||||
} else {
|
||||
rc = sys_sigprocmask(how, opt_set, opt_out_oldset ? &old : 0);
|
||||
}
|
||||
if (rc != -1 && opt_out_oldset) {
|
||||
*opt_out_oldset = old;
|
||||
}
|
||||
STRACE("sigprocmask(%s, %s, [%s]) → %d% m", DescribeHow(how),
|
||||
DescribeSigset(0, opt_set), DescribeSigset(rc, opt_out_oldset), rc);
|
||||
return rc;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
sigset_t _sigsetmask(sigset_t neu) {
|
||||
sigset_t res;
|
||||
if (IsMetal() || IsWindows()) {
|
||||
__sig_mask(SIG_SETMASK, &neu, &res);
|
||||
} else {
|
||||
sys_sigprocmask(SIG_SETMASK, &neu, &res);
|
||||
}
|
||||
return res;
|
||||
}
|
|
@ -24,10 +24,12 @@
|
|||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
/**
|
||||
* Blocks until SIG ∉ MASK is delivered to thread.
|
||||
|
@ -38,47 +40,29 @@
|
|||
* @param ignore is a bitset of signals to block temporarily, which if
|
||||
* NULL is equivalent to passing an empty signal set
|
||||
* @return -1 w/ EINTR (or possibly EFAULT)
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @norestart
|
||||
*/
|
||||
int sigsuspend(const sigset_t *ignore) {
|
||||
int rc;
|
||||
const sigset_t *arg;
|
||||
sigset_t save, mask = {0};
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (IsAsan() && ignore && !__asan_is_valid(ignore, sizeof(*ignore))) {
|
||||
rc = efault();
|
||||
} else if (IsXnu() || IsOpenbsd()) {
|
||||
// openbsd and xnu only support 32 signals
|
||||
// they use a register calling convention for sigsuspend
|
||||
if (ignore) {
|
||||
arg = (sigset_t *)(uintptr_t)(*(uint32_t *)ignore);
|
||||
} else {
|
||||
arg = 0;
|
||||
}
|
||||
rc = sys_sigsuspend(arg, 8);
|
||||
} else if (IsLinux() || IsFreebsd() || IsNetbsd() || IsWindows()) {
|
||||
if (ignore) {
|
||||
arg = ignore;
|
||||
} else {
|
||||
arg = &mask;
|
||||
}
|
||||
if (!IsWindows()) {
|
||||
rc = sys_sigsuspend(arg, 8);
|
||||
} else {
|
||||
__sig_mask(SIG_SETMASK, arg, &save);
|
||||
while (!(rc = _check_interrupts(0))) {
|
||||
if ((rc = __pause_thread(__SIG_SIG_INTERVAL_MS))) break;
|
||||
}
|
||||
__sig_mask(SIG_SETMASK, &save, 0);
|
||||
}
|
||||
// openbsd and xnu use a 32 signal register convention
|
||||
rc = sys_sigsuspend(ignore ? (void *)(intptr_t)(uint32_t)*ignore : 0, 8);
|
||||
} else {
|
||||
rc = enosys();
|
||||
sigset_t waitmask = ignore ? *ignore : 0;
|
||||
if (IsWindows() || IsMetal()) {
|
||||
while (!(rc = _park_norestart(-1u, waitmask))) donothing;
|
||||
} else {
|
||||
rc = sys_sigsuspend((uint64_t[2]){waitmask}, 8);
|
||||
}
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("sigsuspend(%s) → %d% m", DescribeSigset(0, ignore), rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue