mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-27 13:00:28 +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
66
libc/proc/clock.c
Normal file
66
libc/proc/clock.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*-*- 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;
|
||||
}
|
|
@ -590,13 +590,17 @@ static int Shift(int i) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int Fake(int main(int, char **)) {
|
||||
static int Fake(int main(int, char **), bool wantexec) {
|
||||
int pid;
|
||||
if (wantexec) {
|
||||
goto RunProgram;
|
||||
}
|
||||
if ((pid = fork()) == -1) {
|
||||
perror("fork");
|
||||
return 127;
|
||||
}
|
||||
if (!pid) {
|
||||
RunProgram:
|
||||
// TODO(jart): Maybe nuke stdio too?
|
||||
if (_weaken(optind)) {
|
||||
*_weaken(optind) = 1;
|
||||
|
@ -660,7 +664,7 @@ static wontreturn void Exec(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static int TryBuiltin(void) {
|
||||
static int TryBuiltin(bool wantexec) {
|
||||
if (!n) return exitstatus;
|
||||
if (!strcmp(args[0], "exit")) Exit();
|
||||
if (!strcmp(args[0], "exec")) Exec();
|
||||
|
@ -686,16 +690,24 @@ static int TryBuiltin(void) {
|
|||
if (!strcmp(args[0], "mktemp")) return Mktemp();
|
||||
if (!strcmp(args[0], "usleep")) return Usleep();
|
||||
if (!strcmp(args[0], "toupper")) return Toupper();
|
||||
if (_weaken(_tr) && !strcmp(args[0], "tr")) return Fake(_weaken(_tr));
|
||||
if (_weaken(_sed) && !strcmp(args[0], "sed")) return Fake(_weaken(_sed));
|
||||
if (_weaken(_awk) && !strcmp(args[0], "awk")) return Fake(_weaken(_awk));
|
||||
if (_weaken(_curl) && !strcmp(args[0], "curl")) return Fake(_weaken(_curl));
|
||||
if (_weaken(_tr) && !strcmp(args[0], "tr")) {
|
||||
return Fake(_weaken(_tr), wantexec);
|
||||
}
|
||||
if (_weaken(_sed) && !strcmp(args[0], "sed")) {
|
||||
return Fake(_weaken(_sed), wantexec);
|
||||
}
|
||||
if (_weaken(_awk) && !strcmp(args[0], "awk")) {
|
||||
return Fake(_weaken(_awk), wantexec);
|
||||
}
|
||||
if (_weaken(_curl) && !strcmp(args[0], "curl")) {
|
||||
return Fake(_weaken(_curl), wantexec);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ShellExec(void) {
|
||||
int rc;
|
||||
if ((rc = TryBuiltin()) == -1) {
|
||||
if ((rc = TryBuiltin(true)) == -1) {
|
||||
rc = SystemExec();
|
||||
}
|
||||
return (n = 0), rc;
|
||||
|
@ -718,14 +730,15 @@ static void Pipe(void) {
|
|||
if (pfds[1] != 1) unassert(!close(pfds[1]));
|
||||
_Exit(ShellExec());
|
||||
}
|
||||
unassert(!dup2(pfds[0], 0));
|
||||
if (pfds[1]) unassert(!close(pfds[1]));
|
||||
unassert(dup2(pfds[0], 0) == 0);
|
||||
if (pfds[0] != 0) unassert(!close(pfds[0]));
|
||||
if (pfds[1] != 0) unassert(!close(pfds[1]));
|
||||
n = 0;
|
||||
}
|
||||
|
||||
static int ShellSpawn(void) {
|
||||
int rc, pid;
|
||||
if ((rc = TryBuiltin()) == -1) {
|
||||
if ((rc = TryBuiltin(false)) == -1) {
|
||||
switch ((pid = fork())) {
|
||||
case 0:
|
||||
_Exit(SystemExec());
|
||||
|
|
162
libc/proc/describefds.c
Normal file
162
libc/proc/describefds.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*-*- 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/assert.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
#define FDS_VAR "_COSMO_FDS="
|
||||
|
||||
#define MAX_ENTRY_BYTES 256
|
||||
|
||||
/**
|
||||
* @fileoverview fd/handle inheritance for execve() and posix_spawn()
|
||||
*/
|
||||
|
||||
struct StringBuilder {
|
||||
char *p;
|
||||
int i, n;
|
||||
};
|
||||
|
||||
// returns true if fd can't be inherited by anything
|
||||
textwindows bool __is_cloexec(const struct Fd *f) {
|
||||
if (f->kind == kFdEmpty) return true;
|
||||
if (f->kind == kFdReserved) return true;
|
||||
if (f->kind == kFdZip) return true;
|
||||
if (f->kind == kFdEpoll) return true;
|
||||
if (f->flags & O_CLOEXEC) return true;
|
||||
if (f->handle == -1) return true;
|
||||
if (!f->handle) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// this must be called after ntspawn() returns
|
||||
// we perform critical cleanup that _exit() can't do
|
||||
textwindows void __undescribe_fds(int64_t hCreatorProcess,
|
||||
int64_t *lpExplicitHandles,
|
||||
uint32_t dwExplicitHandleCount) {
|
||||
if (lpExplicitHandles) {
|
||||
for (uint32_t i = 0; i < dwExplicitHandleCount; ++i) {
|
||||
DuplicateHandle(hCreatorProcess, lpExplicitHandles[i], 0, 0, 0, false,
|
||||
kNtDuplicateCloseSource);
|
||||
}
|
||||
free(lpExplicitHandles);
|
||||
}
|
||||
}
|
||||
|
||||
// serializes file descriptors and generates child handle array
|
||||
// 1. serialize file descriptor table to environment variable str
|
||||
// 2. generate array that'll tell CreateProcess() what to inherit
|
||||
textwindows char *__describe_fds(const struct Fd *fds, size_t fdslen,
|
||||
struct NtStartupInfo *lpStartupInfo,
|
||||
int64_t hCreatorProcess,
|
||||
int64_t **out_lpExplicitHandles,
|
||||
uint32_t *out_lpExplicitHandleCount) {
|
||||
char *b, *p;
|
||||
uint32_t hi = 0;
|
||||
struct StringBuilder sb;
|
||||
int64_t *handles, handle;
|
||||
uint32_t handlecount = 0;
|
||||
|
||||
// setup memory for environment variable
|
||||
if (!(sb.p = strdup(FDS_VAR))) return 0;
|
||||
sb.i = sizeof(FDS_VAR) - 1;
|
||||
sb.n = sizeof(FDS_VAR);
|
||||
|
||||
// setup memory for explicitly inherited handle list
|
||||
for (int fd = 0; fd < fdslen; ++fd) {
|
||||
const struct Fd *f = fds + fd;
|
||||
if (__is_cloexec(f)) continue;
|
||||
++handlecount;
|
||||
}
|
||||
if (!(handles = calloc(handlecount, sizeof(*handles)))) {
|
||||
OnFailure:
|
||||
__undescribe_fds(hCreatorProcess, handles, hi);
|
||||
free(sb.p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// serialize file descriptors
|
||||
for (int fd = 0; fd < fdslen; ++fd) {
|
||||
const struct Fd *f = fds + fd;
|
||||
if (__is_cloexec(f)) continue;
|
||||
|
||||
// make inheritable version of handle exist in creator process
|
||||
if (!DuplicateHandle(GetCurrentProcess(), f->handle, hCreatorProcess,
|
||||
&handle, 0, true, kNtDuplicateSameAccess)) {
|
||||
STRACE("__describe_fds() DuplicateHandle() failed w/ %d", GetLastError());
|
||||
__winerr();
|
||||
goto OnFailure;
|
||||
}
|
||||
for (uint32_t i = 0; i < 3; ++i) {
|
||||
if (lpStartupInfo->stdiofds[i] == f->handle) {
|
||||
lpStartupInfo->stdiofds[i] = handle;
|
||||
}
|
||||
}
|
||||
handles[hi++] = handle;
|
||||
|
||||
// ensure output string has enough space for new entry
|
||||
if (sb.i + MAX_ENTRY_BYTES > sb.n) {
|
||||
char *p2;
|
||||
sb.n += sb.n >> 1;
|
||||
sb.n += MAX_ENTRY_BYTES;
|
||||
if ((p2 = realloc(sb.p, sb.n))) {
|
||||
sb.p = p2;
|
||||
} else {
|
||||
goto OnFailure;
|
||||
}
|
||||
}
|
||||
|
||||
// serialize file descriptor
|
||||
p = b = sb.p + sb.i;
|
||||
p = FormatInt64(p, fd);
|
||||
*p++ = '_';
|
||||
p = FormatInt64(p, handle);
|
||||
*p++ = '_';
|
||||
p = FormatInt64(p, f->kind);
|
||||
*p++ = '_';
|
||||
p = FormatInt64(p, f->flags);
|
||||
*p++ = '_';
|
||||
p = FormatInt64(p, f->mode);
|
||||
*p++ = '_';
|
||||
p = FormatInt64(p, f->pointer);
|
||||
*p++ = '_';
|
||||
p = FormatInt64(p, f->type);
|
||||
*p++ = '_';
|
||||
p = FormatInt64(p, f->family);
|
||||
*p++ = '_';
|
||||
p = FormatInt64(p, f->protocol);
|
||||
*p++ = ';';
|
||||
unassert(p - b < MAX_ENTRY_BYTES);
|
||||
sb.i += p - b;
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
// return result
|
||||
*out_lpExplicitHandles = handles;
|
||||
*out_lpExplicitHandleCount = hi;
|
||||
unassert(hi == handlecount);
|
||||
return sb.p;
|
||||
}
|
15
libc/proc/describefds.internal.h
Normal file
15
libc/proc/describefds.internal.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_PROC_DESCRIBEFDS_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_PROC_DESCRIBEFDS_INTERNAL_H_
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
bool __is_cloexec(const struct Fd *);
|
||||
void __undescribe_fds(int64_t, int64_t *, uint32_t);
|
||||
char *__describe_fds(const struct Fd *, size_t, struct NtStartupInfo *, int64_t,
|
||||
int64_t **, uint32_t *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_PROC_DESCRIBEFDS_INTERNAL_H_ */
|
|
@ -16,105 +16,46 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/sigaction.internal.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/handlock.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/enum/processaccess.h"
|
||||
#include "libc/nt/enum/startf.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/enum/wait.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/struct/msg.h"
|
||||
#include "libc/nt/struct/processinformation.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/proc/describefds.internal.h"
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/itimer.internal.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define keywords textwindows dontinstrument
|
||||
textwindows int sys_execve_nt(const char *program, char *const argv[],
|
||||
char *const envp[]) {
|
||||
|
||||
// clang-format off
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
__msabi extern typeof(DuplicateHandle) *const __imp_DuplicateHandle;
|
||||
__msabi extern typeof(GenerateConsoleCtrlEvent) *const __imp_GenerateConsoleCtrlEvent;
|
||||
__msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId;
|
||||
__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess;
|
||||
__msabi extern typeof(GetLastError) *const __imp_GetLastError;
|
||||
__msabi extern typeof(OpenThread) *const __imp_OpenThread;
|
||||
__msabi extern typeof(SetConsoleCtrlHandler) *const __imp_SetConsoleCtrlHandler;
|
||||
__msabi extern typeof(SetHandleInformation) *const __imp_SetHandleInformation;
|
||||
__msabi extern typeof(TerminateThread) *const __imp_TerminateThread;
|
||||
__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile;
|
||||
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
|
||||
// clang-format on
|
||||
// execve() needs to be @asyncsignalsafe
|
||||
sigset_t m = __sig_block();
|
||||
_pthread_lock();
|
||||
|
||||
extern long __klog_handle;
|
||||
static void sys_execve_nt_relay(intptr_t, long, long, long);
|
||||
void __stack_call(intptr_t, long, long, long,
|
||||
void (*)(intptr_t, intptr_t, long, long),
|
||||
intptr_t) wontreturn;
|
||||
|
||||
static keywords void PurgeHandle(intptr_t h) {
|
||||
if (!h) return;
|
||||
if (h == -1) return;
|
||||
__imp_CloseHandle(h);
|
||||
}
|
||||
|
||||
static keywords void PurgeThread(intptr_t h) {
|
||||
if (h && h != -1) {
|
||||
__imp_TerminateThread(h, SIGKILL);
|
||||
__imp_CloseHandle(h);
|
||||
}
|
||||
}
|
||||
|
||||
static keywords void sys_execve_inherit(int64_t hands[3], bool32 bInherit) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (hands[i] != -1) {
|
||||
__imp_SetHandleInformation(hands[i], kNtHandleFlagInherit, bInherit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keywords int sys_execve_nt(const char *program, char *const argv[],
|
||||
char *const envp[]) {
|
||||
size_t i;
|
||||
|
||||
__hand_lock();
|
||||
pthread_spin_lock(&_pthread_lock);
|
||||
|
||||
// pass bitmask telling child which fds are sockets
|
||||
int bits;
|
||||
char buf[32], *v = 0;
|
||||
if (_weaken(socket)) {
|
||||
for (bits = i = 0; i < 3; ++i) {
|
||||
if (g_fds.p[i].kind == kFdSocket) {
|
||||
bits |= 1 << i;
|
||||
}
|
||||
}
|
||||
FormatInt32(stpcpy(buf, "__STDIO_SOCKETS="), bits);
|
||||
v = buf;
|
||||
// new process should be a child of our parent
|
||||
int64_t hParentProcess;
|
||||
int ppid = sys_getppid_nt();
|
||||
if (!(hParentProcess = OpenProcess(
|
||||
kNtProcessDupHandle | kNtProcessCreateProcess, false, ppid))) {
|
||||
_pthread_unlock();
|
||||
__sig_unblock(m);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// define stdio handles for the spawned subprocess
|
||||
|
@ -122,96 +63,55 @@ keywords int sys_execve_nt(const char *program, char *const argv[],
|
|||
.cb = sizeof(struct NtStartupInfo),
|
||||
.dwFlags = kNtStartfUsestdhandles,
|
||||
};
|
||||
for (i = 0; i <= 2; ++i) {
|
||||
if (g_fds.p[i].kind != kFdEmpty && //
|
||||
!(g_fds.p[i].flags & O_CLOEXEC)) {
|
||||
si.stdiofds[i] = g_fds.p[i].handle;
|
||||
for (int fd = 0; fd < 3; ++fd) {
|
||||
if (!__is_cloexec(g_fds.p + fd)) {
|
||||
si.stdiofds[fd] = g_fds.p[fd].handle;
|
||||
} else {
|
||||
si.stdiofds[i] = -1;
|
||||
si.stdiofds[fd] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// pass serialized file descriptor table in environment
|
||||
char *fdspec;
|
||||
int64_t *lpExplicitHandles;
|
||||
uint32_t dwExplicitHandleCount;
|
||||
if (!(fdspec = __describe_fds(g_fds.p, g_fds.n, &si, hParentProcess,
|
||||
&lpExplicitHandles, &dwExplicitHandleCount))) {
|
||||
CloseHandle(hParentProcess);
|
||||
_pthread_unlock();
|
||||
__sig_unblock(m);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// launch the process
|
||||
struct NtProcessInformation pi;
|
||||
sys_execve_inherit(si.stdiofds, true);
|
||||
int rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &si, &pi);
|
||||
int rc =
|
||||
ntspawn(program, argv, envp, (char *[]){fdspec, 0}, 0, 0, hParentProcess,
|
||||
lpExplicitHandles, dwExplicitHandleCount, &si, &pi);
|
||||
__undescribe_fds(hParentProcess, lpExplicitHandles, dwExplicitHandleCount);
|
||||
if (rc == -1) {
|
||||
sys_execve_inherit(si.stdiofds, false);
|
||||
__hand_unlock();
|
||||
if (__imp_GetLastError() == kNtErrorSharingViolation) {
|
||||
free(fdspec);
|
||||
CloseHandle(hParentProcess);
|
||||
__undescribe_fds(hParentProcess, lpExplicitHandles, dwExplicitHandleCount);
|
||||
_pthread_unlock();
|
||||
__sig_unblock(m);
|
||||
if (GetLastError() == kNtErrorSharingViolation) {
|
||||
return etxtbsy();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
PurgeHandle(pi.hThread);
|
||||
|
||||
// kill siblings
|
||||
struct Dll *e;
|
||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
|
||||
if ((pthread_t)pt == __get_tls()->tib_pthread) continue;
|
||||
PurgeThread(pt->tib->tib_syshand);
|
||||
PurgeHandle(pt->semaphore);
|
||||
// give child to libc/proc/proc.c worker thread in parent
|
||||
int64_t handle;
|
||||
if (!DuplicateHandle(GetCurrentProcess(), pi.hProcess, hParentProcess,
|
||||
&handle, 0, false, kNtDuplicateSameAccess)) {
|
||||
kprintf("failed to duplicate handle from %P into %d due to %s\n", ppid,
|
||||
strerror(GetLastError()));
|
||||
_Exit(1);
|
||||
}
|
||||
if (_weaken(__itimer)) {
|
||||
PurgeThread(_weaken(__itimer)->thread);
|
||||
}
|
||||
if (_weaken(__proc)) {
|
||||
PurgeThread(_weaken(__proc)->thread);
|
||||
PurgeHandle(_weaken(__proc)->onstart);
|
||||
}
|
||||
|
||||
// retreat to original win32-provided stack memory
|
||||
__ftrace = 0;
|
||||
__stack_call(pi.hProcess, 0, 0, 0, sys_execve_nt_relay, __oldstack);
|
||||
}
|
||||
|
||||
// child is in same process group so wait for it to get killed by this
|
||||
__msabi static keywords bool32 sys_execve_nt_event(uint32_t dwCtrlType) {
|
||||
return true; // tell win32 we handled signal
|
||||
}
|
||||
|
||||
// this function runs on the original tiny stack that windows gave us
|
||||
// we need to keep the original process alive simply to pass an int32
|
||||
// so we unmap all memory to avoid getting a double whammy after fork
|
||||
static keywords void sys_execve_nt_relay(intptr_t h, long b, long c, long d) {
|
||||
uint32_t i, dwExitCode;
|
||||
|
||||
// close more handles
|
||||
__imp_SetConsoleCtrlHandler((void *)sys_execve_nt_event, 1);
|
||||
for (i = 0; i < g_fds.n; ++i) {
|
||||
if (g_fds.p[i].kind != kFdEmpty) {
|
||||
PurgeHandle(g_fds.p[i].handle);
|
||||
if (g_fds.p[i].kind == kFdConsole) {
|
||||
PurgeHandle(g_fds.p[i].extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free all the memory mmap created
|
||||
for (i = 0; i < _mmi.i; ++i) {
|
||||
__imp_UnmapViewOfFile((void *)((uintptr_t)_mmi.p[i].x << 16));
|
||||
PurgeHandle(_mmi.p[i].h);
|
||||
}
|
||||
|
||||
// wait for process to terminate
|
||||
//
|
||||
// WaitForSingleObject can return kNtWaitAbandoned which MSDN
|
||||
// describes as a "sort of" successful status which indicates
|
||||
// someone else didn't free a mutex and you should check that
|
||||
// persistent resources haven't been left corrupted. not sure
|
||||
// what those resources would be for process objects, however
|
||||
// this status has actually been observed when waiting on 'em
|
||||
do {
|
||||
dwExitCode = 255;
|
||||
if (__imp_WaitForSingleObject(h, -1) != kNtWaitFailed) {
|
||||
__imp_GetExitCodeProcess(h, &dwExitCode);
|
||||
}
|
||||
} while (dwExitCode == kNtStillActive);
|
||||
|
||||
// propagate child exit status to parent
|
||||
TerminateThisProcess(dwExitCode);
|
||||
unassert(!(handle & 0xFFFFFFFFFF000000));
|
||||
TerminateThisProcess(0x23000000u | handle);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
#include "ape/ape.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/cp.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void __execve_lock(void);
|
||||
void __execve_unlock(void);
|
||||
bool IsApeLoadable(char[8]);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
|
@ -18,11 +18,10 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/proc/execve.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/stat.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
|
@ -35,6 +34,7 @@
|
|||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/proc/execve.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
|
@ -201,14 +201,14 @@ int fexecve(int fd, char *const argv[], char *const envp[]) {
|
|||
}
|
||||
if (!__isfdkind(fd, kFdZip)) {
|
||||
bool memfdReq;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
BLOCK_SIGNALS;
|
||||
strace_enabled(-1);
|
||||
memfdReq = ((rc = fcntl(fd, F_GETFD)) != -1) && (rc & FD_CLOEXEC) &&
|
||||
IsAPEFd(fd);
|
||||
strace_enabled(+1);
|
||||
ALLOW_SIGNALS;
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
if (rc == -1) {
|
||||
break;
|
||||
} else if (!memfdReq) {
|
||||
|
@ -221,13 +221,13 @@ int fexecve(int fd, char *const argv[], char *const envp[]) {
|
|||
}
|
||||
int newfd;
|
||||
char *path = alloca(PATH_MAX);
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
BLOCK_SIGNALS;
|
||||
strace_enabled(-1);
|
||||
newfd = fd_to_mem_fd(fd, path);
|
||||
strace_enabled(+1);
|
||||
ALLOW_SIGNALS;
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
if (newfd == -1) {
|
||||
break;
|
||||
}
|
||||
|
@ -242,13 +242,13 @@ int fexecve(int fd, char *const argv[], char *const envp[]) {
|
|||
if (!savedErr) {
|
||||
savedErr = errno;
|
||||
}
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
BLOCK_SIGNALS;
|
||||
strace_enabled(-1);
|
||||
close(newfd);
|
||||
strace_enabled(+1);
|
||||
ALLOW_SIGNALS;
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
} while (0);
|
||||
if (savedErr) {
|
||||
errno = savedErr;
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/proc/describefds.internal.h"
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
|
@ -68,9 +69,8 @@
|
|||
|
||||
#ifdef __x86_64__
|
||||
|
||||
extern long __klog_handle;
|
||||
extern int64_t __wincrashearly;
|
||||
bool32 __onntconsoleevent(uint32_t);
|
||||
void __keystroke_wipe(void);
|
||||
|
||||
static textwindows wontreturn void AbortFork(const char *func) {
|
||||
#ifdef SYSDEBUG
|
||||
|
@ -130,7 +130,6 @@ static textwindows dontinline void ReadOrDie(int64_t h, void *buf, size_t n) {
|
|||
if (!ForkIo2(h, buf, n, ReadFile, "ReadFile", true)) {
|
||||
AbortFork("ReadFile1");
|
||||
}
|
||||
if (_weaken(__klog_handle)) *_weaken(__klog_handle) = 0;
|
||||
#ifndef NDEBUG
|
||||
size_t got;
|
||||
if (!ForkIo2(h, &got, sizeof(got), ReadFile, "ReadFile", true)) {
|
||||
|
@ -283,15 +282,6 @@ textwindows void WinMainForked(void) {
|
|||
fds->p[1].handle = GetStdHandle(kNtStdOutputHandle);
|
||||
fds->p[2].handle = GetStdHandle(kNtStdErrorHandle);
|
||||
|
||||
// untrack children of parent since we specify with both
|
||||
// CreateProcess() and CreateThread() as non-inheritable
|
||||
for (i = 0; i < fds->n; ++i) {
|
||||
if (fds->p[i].kind == kFdProcess) {
|
||||
fds->p[i].kind = 0;
|
||||
atomic_store_explicit(&fds->f, MIN(i, fds->f), memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
// restore the crash reporting stuff
|
||||
#ifdef SYSDEBUG
|
||||
RemoveVectoredExceptionHandler(oncrash);
|
||||
|
@ -304,24 +294,6 @@ textwindows void WinMainForked(void) {
|
|||
longjmp(jb, 1);
|
||||
}
|
||||
|
||||
static void __hand_inherit(bool32 bInherit) {
|
||||
struct CosmoTib *tib = __get_tls();
|
||||
SetHandleInformation(tib->tib_syshand, kNtHandleFlagInherit, bInherit);
|
||||
SetHandleInformation(tib->tib_syshand, kNtHandleFlagInherit, bInherit);
|
||||
for (int i = 0; i < _mmi.i; ++i) {
|
||||
if ((_mmi.p[i].flags & MAP_TYPE) == MAP_SHARED) {
|
||||
SetHandleInformation(_mmi.p[i].h, kNtHandleFlagInherit, bInherit);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < g_fds.n; ++i) {
|
||||
if (g_fds.p[i].kind == kFdEmpty) continue;
|
||||
SetHandleInformation(g_fds.p[i].handle, kNtHandleFlagInherit, bInherit);
|
||||
if (g_fds.p[i].kind == kFdConsole) {
|
||||
SetHandleInformation(g_fds.p[i].extra, kNtHandleFlagInherit, bInherit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
||||
char ok;
|
||||
jmp_buf jb;
|
||||
|
@ -333,8 +305,8 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
|||
char16_t pipename[64];
|
||||
int64_t reader, writer;
|
||||
struct NtStartupInfo startinfo;
|
||||
char *p, forkvar[6 + 21 + 1 + 21 + 1];
|
||||
struct NtProcessInformation procinfo;
|
||||
char *p, forkvar[6 + 21 + 1 + 21 + 1];
|
||||
tib = __get_tls();
|
||||
ftrace_enabled(-1);
|
||||
strace_enabled(-1);
|
||||
|
@ -372,12 +344,10 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
|||
args = args2;
|
||||
}
|
||||
#endif
|
||||
__hand_inherit(true);
|
||||
NTTRACE("STARTING SPAWN");
|
||||
int spawnrc =
|
||||
ntspawn(GetProgramExecutableName(), args, environ, forkvar, 0, 0,
|
||||
true, dwCreationFlags, 0, &startinfo, &procinfo);
|
||||
__hand_inherit(false);
|
||||
int spawnrc = ntspawn(GetProgramExecutableName(), args, environ,
|
||||
(char *[]){forkvar, 0}, dwCreationFlags, 0, 0, 0, 0,
|
||||
&startinfo, &procinfo);
|
||||
if (spawnrc != -1) {
|
||||
CloseHandle(procinfo.hThread);
|
||||
ok = WriteAll(writer, jb, sizeof(jb)) &&
|
||||
|
@ -435,9 +405,11 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
|||
if (ftrace_stackdigs) {
|
||||
_weaken(__hook)(_weaken(ftrace_hook), _weaken(GetSymbolTable)());
|
||||
}
|
||||
// reset console
|
||||
__keystroke_wipe();
|
||||
// reset alarms
|
||||
if (_weaken(__itimer_reset)) {
|
||||
_weaken(__itimer_reset)();
|
||||
if (_weaken(__itimer_wipe)) {
|
||||
_weaken(__itimer_wipe)();
|
||||
}
|
||||
}
|
||||
if (rc == -1) {
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
|
@ -44,9 +42,9 @@ int _fork(uint32_t dwCreationFlags) {
|
|||
struct CosmoTib *tib;
|
||||
int ax, dx, tid, parent;
|
||||
struct PosixThread *me, *other;
|
||||
parent = __pid;
|
||||
(void)parent;
|
||||
BLOCK_SIGNALS;
|
||||
BLOCK_CANCELLATIONS;
|
||||
if (IsWindows()) __proc_lock();
|
||||
if (__threaded && _weaken(_pthread_onfork_prepare)) {
|
||||
_weaken(_pthread_onfork_prepare)();
|
||||
|
@ -62,27 +60,26 @@ int _fork(uint32_t dwCreationFlags) {
|
|||
} else {
|
||||
dx = GetCurrentProcessId();
|
||||
}
|
||||
parent = __pid;
|
||||
__pid = dx;
|
||||
tib = __get_tls();
|
||||
me = (struct PosixThread *)tib->tib_pthread;
|
||||
dll_remove(&_pthread_list, &me->list);
|
||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||
other = POSIXTHREAD_CONTAINER(e);
|
||||
atomic_store_explicit(&other->status, kPosixThreadZombie,
|
||||
atomic_store_explicit(&other->pt_status, kPosixThreadZombie,
|
||||
memory_order_relaxed);
|
||||
other->tib->tib_syshand = 0;
|
||||
}
|
||||
dll_make_first(&_pthread_list, &me->list);
|
||||
tid = IsLinux() || IsXnuSilicon() ? dx : sys_gettid();
|
||||
atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed);
|
||||
atomic_store_explicit(&me->ptid, tid, memory_order_relaxed);
|
||||
atomic_store_explicit(&me->cancelled, false, memory_order_relaxed);
|
||||
if (IsWindows()) npassert((me->semaphore = CreateSemaphore(0, 0, 1, 0)));
|
||||
atomic_store_explicit(&me->pt_canceled, false, memory_order_relaxed);
|
||||
if (__threaded && _weaken(_pthread_onfork_child)) {
|
||||
_weaken(_pthread_onfork_child)();
|
||||
}
|
||||
if (IsWindows()) __proc_wipe();
|
||||
if (IsWindows()) {
|
||||
__proc_wipe();
|
||||
}
|
||||
STRACE("fork() → 0 (child of %d)", parent);
|
||||
} else {
|
||||
if (__threaded && _weaken(_pthread_onfork_parent)) {
|
||||
|
@ -91,7 +88,6 @@ int _fork(uint32_t dwCreationFlags) {
|
|||
if (IsWindows()) __proc_unlock();
|
||||
STRACE("fork() → %d% m", ax);
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_SIGNALS;
|
||||
return ax;
|
||||
}
|
||||
|
|
71
libc/proc/getpriority-nt.c
Normal file
71
libc/proc/getpriority-nt.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*-*- 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/syscall-nt.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/sysv/consts/prio.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_getpriority_nt(int which, unsigned pid) {
|
||||
|
||||
if (which != PRIO_PROCESS) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
int64_t handle;
|
||||
if (!(handle = __proc_handle(pid))) {
|
||||
return esrch();
|
||||
}
|
||||
|
||||
uint32_t tier;
|
||||
switch ((tier = GetPriorityClass(handle))) {
|
||||
case kNtRealtimePriorityClass:
|
||||
return -16;
|
||||
break;
|
||||
case kNtHighPriorityClass:
|
||||
return -10;
|
||||
break;
|
||||
case kNtAboveNormalPriorityClass:
|
||||
return -5;
|
||||
break;
|
||||
case kNtNormalPriorityClass:
|
||||
return 0;
|
||||
break;
|
||||
case kNtBelowNormalPriorityClass:
|
||||
return 5;
|
||||
break;
|
||||
case kNtIdlePriorityClass:
|
||||
return 15;
|
||||
break;
|
||||
case 0:
|
||||
STRACE("GetPriorityClass() failed with %d", GetLastError());
|
||||
if (GetLastError() == kNtErrorInvalidHandle) {
|
||||
return esrch();
|
||||
} else {
|
||||
return eperm();
|
||||
}
|
||||
default:
|
||||
STRACE("unknown win32 priority class %d", tier);
|
||||
return 0;
|
||||
}
|
||||
}
|
89
libc/proc/getpriority.c
Normal file
89
libc/proc/getpriority.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*-*- 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;
|
||||
}
|
90
libc/proc/getrusage-nt.c
Normal file
90
libc/proc/getrusage-nt.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*-*- 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.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/proc/proc.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/rusage.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
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 (who == RUSAGE_CHILDREN) {
|
||||
if (usage) {
|
||||
__proc_lock();
|
||||
*usage = __proc.ruchlds;
|
||||
__proc_unlock();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (who == RUSAGE_SELF || who == RUSAGE_BOTH) {
|
||||
me = GetCurrentProcess();
|
||||
} else if (who == RUSAGE_THREAD) {
|
||||
me = GetCurrentThread();
|
||||
} else {
|
||||
return einval();
|
||||
}
|
||||
|
||||
if (!usage) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(who == RUSAGE_THREAD ? GetThreadTimes : GetProcessTimes)(
|
||||
me, &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,
|
||||
};
|
||||
|
||||
if (who == RUSAGE_BOTH) {
|
||||
__proc_lock();
|
||||
rusage_add(usage, &__proc.ruchlds);
|
||||
__proc_unlock();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
35
libc/proc/getrusage-sysv.c
Normal file
35
libc/proc/getrusage-sysv.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*-*- 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;
|
||||
}
|
46
libc/proc/getrusage.c
Normal file
46
libc/proc/getrusage.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*-*- 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() && usage && !__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;
|
||||
}
|
35
libc/proc/handle.c
Normal file
35
libc/proc/handle.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*-*- 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/intrin/weaken.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
|
||||
// retrieves handle of process
|
||||
// supports only current process and processes we created
|
||||
// returns owned win32 handle, or zero without setting errno
|
||||
textwindows int64_t __proc_handle(int pid) {
|
||||
if (!pid || pid == getpid()) {
|
||||
return GetCurrentProcess();
|
||||
} else if (_weaken(__proc_search)) {
|
||||
return _weaken(__proc_search)(pid);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -17,67 +17,59 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
static textwindows int sys_kill_nt_impl(int pid, int sig) {
|
||||
int err;
|
||||
bool32 ok;
|
||||
struct Dll *e;
|
||||
struct Proc *pr = 0;
|
||||
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) {
|
||||
if (pid == PROC_CONTAINER(e)->pid) {
|
||||
pr = PROC_CONTAINER(e);
|
||||
}
|
||||
}
|
||||
if (!pr) {
|
||||
return esrch();
|
||||
}
|
||||
if (sig) {
|
||||
err = errno;
|
||||
ok = TerminateProcess(pr->handle, sig);
|
||||
if (!ok && GetLastError() == kNtErrorAccessDenied) {
|
||||
ok = true; // cargo culting other codebases here
|
||||
errno = err;
|
||||
}
|
||||
}
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
textwindows int sys_kill_nt(int pid, int sig) {
|
||||
if (!(0 <= sig && sig <= 64)) return einval();
|
||||
|
||||
// validate api usage
|
||||
if (!(0 <= sig && sig <= 64)) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
// XXX: NT doesn't really have process groups. For instance the
|
||||
// CreateProcess() flag for starting a process group actually
|
||||
// just does an "ignore ctrl-c" internally.
|
||||
if (pid < -1) pid = -pid;
|
||||
|
||||
if (pid == -1) return einval(); // no support for kill all yet
|
||||
|
||||
// If we're targeting current process group then just call raise().
|
||||
if (pid <= 0 || pid == getpid()) {
|
||||
if (!sig) return 0; // ability check passes
|
||||
return raise(sig);
|
||||
if (pid < -1) {
|
||||
pid = -pid;
|
||||
}
|
||||
|
||||
int rc;
|
||||
uint64_t m;
|
||||
m = atomic_exchange(&__get_tls()->tib_sigmask, -1);
|
||||
__proc_lock();
|
||||
pthread_cleanup_push((void *)__proc_unlock, 0);
|
||||
rc = sys_kill_nt_impl(pid, sig);
|
||||
pthread_cleanup_pop(true);
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
|
||||
// no support for kill all yet
|
||||
if (pid == -1) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
return rc;
|
||||
// just call raise() if we're targeting self
|
||||
if (pid <= 0 || pid == getpid()) {
|
||||
if (sig) {
|
||||
return raise(sig);
|
||||
} else {
|
||||
return 0; // ability check passes
|
||||
}
|
||||
}
|
||||
|
||||
// find existing handle we own for process
|
||||
int64_t handle;
|
||||
if (!(handle = __proc_handle(pid))) {
|
||||
return esrch();
|
||||
}
|
||||
|
||||
// perform actual kill
|
||||
// process will report WIFSIGNALED with WTERMSIG(sig)
|
||||
if (TerminateProcess(handle, sig)) return 0;
|
||||
STRACE("TerminateProcess() failed with %d", GetLastError());
|
||||
switch (GetLastError()) {
|
||||
case kNtErrorInvalidHandle:
|
||||
return esrch();
|
||||
default:
|
||||
return eperm();
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -1,146 +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/proc/ntspawn.h"
|
||||
#include "libc/mem/mem.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 { \
|
||||
cmdline[k++] = c; \
|
||||
if (k == ARG_MAX / 2) { \
|
||||
return e2big(); \
|
||||
} \
|
||||
} 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).
|
||||
//
|
||||
// TODO(jart): this needs fuzzing and security review
|
||||
//
|
||||
// @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
|
||||
textwindows int mkntcmdline(char16_t cmdline[ARG_MAX / 2], char *const argv[]) {
|
||||
uint64_t w;
|
||||
wint_t x, y;
|
||||
int slashes, n;
|
||||
bool needsquote;
|
||||
char *ansiargv[2];
|
||||
size_t i, j, k, s;
|
||||
if (!argv[0]) {
|
||||
bzero(ansiargv, sizeof(ansiargv));
|
||||
argv = ansiargv;
|
||||
}
|
||||
for (k = i = 0; argv[i]; ++i) {
|
||||
if (i) APPEND(u' ');
|
||||
if ((needsquote = NeedsQuotes(argv[i]))) APPEND(u'"');
|
||||
for (slashes = j = 0;;) {
|
||||
x = argv[i][j++] & 255;
|
||||
if (x >= 0300) {
|
||||
n = ThomPikeLen(x);
|
||||
x = ThomPikeByte(x);
|
||||
while (--n) {
|
||||
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;
|
||||
w = EncodeUtf16(x);
|
||||
do {
|
||||
APPEND(w);
|
||||
} while ((w >>= 16));
|
||||
}
|
||||
}
|
||||
for (s = 0; s < (slashes << needsquote); ++s) {
|
||||
APPEND(u'\\');
|
||||
}
|
||||
if (needsquote) {
|
||||
APPEND(u'"');
|
||||
}
|
||||
}
|
||||
cmdline[k] = u'\0';
|
||||
return 0;
|
||||
}
|
|
@ -1,202 +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/proc/ntspawn.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/getenv.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/mem/arraylist2.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/thompike.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
static inline char *StrChr(const char *s, int c) {
|
||||
for (;; ++s) {
|
||||
if ((*s & 255) == (c & 255)) return (char *)s;
|
||||
if (!*s) return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows inline int CompareStrings(const char *l, const char *r) {
|
||||
int a, b;
|
||||
size_t i = 0;
|
||||
while ((a = ToUpper(l[i] & 255)) == (b = ToUpper(r[i] & 255)) && r[i]) ++i;
|
||||
return a - b;
|
||||
}
|
||||
|
||||
static textwindows void FixPath(char *path) {
|
||||
char *p;
|
||||
|
||||
// skip over variable name
|
||||
while (*path++) {
|
||||
if (path[-1] == '=') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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] == '/' || p[0] == '\\') && IsAlpha(p[1]) &&
|
||||
(p[2] == '/' || p[2] == '\\')) {
|
||||
p[0] = p[1];
|
||||
p[1] = ':';
|
||||
}
|
||||
for (; *p; ++p) {
|
||||
if (p[0] == ';' && (p[1] == '/' || p[1] == '\\') && IsAlpha(p[2]) &&
|
||||
(p[3] == '/' || p[3] == '\\')) {
|
||||
p[1] = p[2];
|
||||
p[2] = ':';
|
||||
}
|
||||
}
|
||||
|
||||
// turn slash into backslash
|
||||
for (p = path; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\\';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows void InsertString(char **a, size_t i, const char *s,
|
||||
char buf[ARG_MAX], size_t *bufi,
|
||||
bool *have_systemroot) {
|
||||
char *v;
|
||||
size_t j, k;
|
||||
|
||||
v = StrChr(s, '=');
|
||||
|
||||
// apply fixups to var=/c/...
|
||||
if (v && v[1] == '/' && IsAlpha(v[2]) && v[3] == '/') {
|
||||
v = buf + *bufi;
|
||||
for (k = 0; s[k]; ++k) {
|
||||
if (*bufi + 1 < ARG_MAX) {
|
||||
buf[(*bufi)++] = s[k];
|
||||
}
|
||||
}
|
||||
if (*bufi < ARG_MAX) {
|
||||
buf[(*bufi)++] = 0;
|
||||
FixPath(v);
|
||||
s = v;
|
||||
}
|
||||
}
|
||||
|
||||
// append to sorted list
|
||||
for (j = i; j > 0 && CompareStrings(s, a[j - 1]) < 0; --j) {
|
||||
a[j] = a[j - 1];
|
||||
}
|
||||
a[j] = (char *)s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies sorted environment variable block for Windows.
|
||||
*
|
||||
* This is designed to meet the requirements of CreateProcess().
|
||||
*
|
||||
* @param envvars 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 exceeded ARG_MAX/2 (32767)
|
||||
*/
|
||||
textwindows int mkntenvblock(char16_t envvars[ARG_MAX / 2], char *const envp[],
|
||||
const char *extravar, char buf[ARG_MAX]) {
|
||||
bool v;
|
||||
uint64_t w;
|
||||
char **vars;
|
||||
wint_t x, y;
|
||||
bool have_systemroot = false;
|
||||
size_t i, j, k, n, m, bufi = 0;
|
||||
for (n = 0; envp[n];) n++;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
int nbytes = (n + 1) * sizeof(char *);
|
||||
vars = alloca(nbytes);
|
||||
CheckLargeStackAllocation(vars, nbytes);
|
||||
#pragma GCC pop_options
|
||||
for (i = 0; i < n; ++i) {
|
||||
InsertString(vars, i, envp[i], buf, &bufi, &have_systemroot);
|
||||
}
|
||||
if (extravar) {
|
||||
InsertString(vars, n++, extravar, buf, &bufi, &have_systemroot);
|
||||
}
|
||||
if (!have_systemroot && environ) {
|
||||
// https://jpassing.com/2009/12/28/the-hidden-danger-of-forgetting-to-specify-systemroot-in-a-custom-environment-block/
|
||||
struct Env systemroot;
|
||||
systemroot = __getenv(environ, "SYSTEMROOT");
|
||||
if (systemroot.s) {
|
||||
InsertString(vars, n++, environ[systemroot.i], buf, &bufi,
|
||||
&have_systemroot);
|
||||
}
|
||||
}
|
||||
for (k = i = 0; i < n; ++i) {
|
||||
j = 0;
|
||||
v = false;
|
||||
do {
|
||||
x = vars[i][j++] & 0xff;
|
||||
if (x >= 0200) {
|
||||
if (x < 0300) continue;
|
||||
m = ThomPikeLen(x);
|
||||
x = ThomPikeByte(x);
|
||||
while (--m) {
|
||||
if ((y = vars[i][j++] & 0xff)) {
|
||||
x = ThomPikeMerge(x, y);
|
||||
} else {
|
||||
x = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!v) {
|
||||
if (x != '=') {
|
||||
x = ToUpper(x);
|
||||
} else {
|
||||
v = true;
|
||||
}
|
||||
}
|
||||
w = EncodeUtf16(x);
|
||||
do {
|
||||
envvars[k++] = w & 0xffff;
|
||||
if (k == ARG_MAX / 2) {
|
||||
return e2big();
|
||||
}
|
||||
} while ((w >>= 16));
|
||||
} while (x);
|
||||
}
|
||||
envvars[k] = u'\0';
|
||||
return 0;
|
||||
}
|
48
libc/proc/nice.c
Normal file
48
libc/proc/nice.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*-*- 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;
|
||||
}
|
|
@ -1,111 +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 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/assert.h"
|
||||
#include "libc/calls/struct/sigaction.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/pushpop.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/enum/filemapflags.h"
|
||||
#include "libc/nt/enum/pageflags.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/memory.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/processinformation.h"
|
||||
#include "libc/nt/struct/securityattributes.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
struct SpawnBlock {
|
||||
union {
|
||||
struct {
|
||||
char16_t cmdline[ARG_MAX / 2];
|
||||
char16_t envvars[ARG_MAX / 2];
|
||||
char buf[ARG_MAX];
|
||||
};
|
||||
char __pad[ROUNDUP(ARG_MAX / 2 * 3 * sizeof(char16_t), FRAMESIZE)];
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 extravar is added to envp to avoid setenv() in caller
|
||||
* @param bInheritHandles means handles already marked inheritable will
|
||||
* be inherited; which, assuming the System V wrapper functions are
|
||||
* being used, should mean (1) all files and sockets that weren't
|
||||
* opened with O_CLOEXEC; and (2) all memory mappings
|
||||
* @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
|
||||
*/
|
||||
textwindows int ntspawn(
|
||||
const char *prog, char *const argv[], char *const envp[],
|
||||
const char *extravar,
|
||||
const struct NtSecurityAttributes *opt_lpProcessAttributes,
|
||||
const struct NtSecurityAttributes *opt_lpThreadAttributes,
|
||||
bool32 bInheritHandles, uint32_t dwCreationFlags,
|
||||
const char16_t *opt_lpCurrentDirectory,
|
||||
const struct NtStartupInfo *lpStartupInfo,
|
||||
struct NtProcessInformation *opt_out_lpProcessInformation) {
|
||||
int rc = -1;
|
||||
int64_t handle;
|
||||
struct SpawnBlock *block = 0;
|
||||
char16_t prog16[PATH_MAX + 5];
|
||||
if (__mkntpath(prog, prog16) == -1) return -1;
|
||||
if ((handle = CreateFileMapping(-1, 0, pushpop(kNtPageReadwrite), 0,
|
||||
sizeof(*block), 0)) &&
|
||||
(block = MapViewOfFileEx(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0,
|
||||
sizeof(*block), 0)) &&
|
||||
mkntcmdline(block->cmdline, argv) != -1 &&
|
||||
mkntenvblock(block->envvars, envp, extravar, block->buf) != -1) {
|
||||
if (CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes,
|
||||
opt_lpThreadAttributes, bInheritHandles,
|
||||
dwCreationFlags | kNtCreateUnicodeEnvironment |
|
||||
kNtInheritParentAffinity,
|
||||
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
|
||||
opt_out_lpProcessInformation)) {
|
||||
rc = 0;
|
||||
}
|
||||
} else if (GetLastError() == kNtErrorSharingViolation) {
|
||||
etxtbsy();
|
||||
}
|
||||
if (block) UnmapViewOfFile(block);
|
||||
if (handle) CloseHandle(handle);
|
||||
return __fix_enotdir(rc, prog16);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -1,21 +1,16 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_
|
||||
#include "libc/limits.h"
|
||||
#ifndef COSMOPOLITAN_NTSPAWN_H_
|
||||
#define COSMOPOLITAN_NTSPAWN_H_
|
||||
#include "libc/nt/struct/processinformation.h"
|
||||
#include "libc/nt/struct/securityattributes.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int mkntcmdline(char16_t[ARG_MAX / 2], char *const[]);
|
||||
int mkntenvblock(char16_t[ARG_MAX / 2], char *const[], const char *,
|
||||
char[ARG_MAX]);
|
||||
int ntspawn(const char *, char *const[], char *const[], const char *,
|
||||
const struct NtSecurityAttributes *,
|
||||
const struct NtSecurityAttributes *, bool32, uint32_t,
|
||||
const char16_t *, const struct NtStartupInfo *,
|
||||
struct NtProcessInformation *);
|
||||
int mkntcmdline(char16_t[32767], char *const[]);
|
||||
int mkntenvblock(char16_t[32767], char *const[], char *const[], char[32767]);
|
||||
int ntspawn(const char *, char *const[], char *const[], char *const[], uint32_t,
|
||||
const char16_t *, int64_t, int64_t *, uint32_t,
|
||||
const struct NtStartupInfo *, struct NtProcessInformation *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_ */
|
||||
#endif /* COSMOPOLITAN_NTSPAWN_H_ */
|
||||
|
|
|
@ -38,11 +38,11 @@
|
|||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/handlock.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/enum/startf.h"
|
||||
|
@ -50,6 +50,7 @@
|
|||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/processinformation.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#include "libc/proc/describefds.internal.h"
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
@ -57,6 +58,7 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
|
@ -65,6 +67,7 @@
|
|||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
|
@ -86,156 +89,222 @@
|
|||
#define sigprocmask sys_sigprocmask
|
||||
#endif
|
||||
|
||||
#define CLOSER_CONTAINER(e) DLL_CONTAINER(struct Closer, elem, e)
|
||||
|
||||
struct Closer {
|
||||
int64_t handle;
|
||||
struct Dll elem;
|
||||
};
|
||||
|
||||
struct SpawnFds {
|
||||
int n;
|
||||
struct Fd *p;
|
||||
struct Dll *closers;
|
||||
};
|
||||
|
||||
static atomic_bool has_vfork; // i.e. not qemu/wsl/xnu/openbsd
|
||||
|
||||
static void posix_spawn_unhand(int64_t hands[3]) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (hands[i] != -1) {
|
||||
CloseHandle(hands[i]);
|
||||
}
|
||||
static textwindows int64_t spawnfds_handle(struct SpawnFds *fds, int fd) {
|
||||
if (__is_cloexec(fds->p + fd)) return -1;
|
||||
return fds->p[fd].handle;
|
||||
}
|
||||
|
||||
static textwindows errno_t spawnfds_ensure(struct SpawnFds *fds, int fd) {
|
||||
int n2;
|
||||
struct Fd *p2;
|
||||
if (fd < 0) return EBADF;
|
||||
if (fd < fds->n) return 0;
|
||||
n2 = fd + 1;
|
||||
if (!(p2 = realloc(fds->p, n2 * sizeof(*fds->p)))) return ENOMEM;
|
||||
bzero(p2 + fds->n, (n2 - fds->n) * sizeof(*fds->p));
|
||||
fds->p = p2;
|
||||
fds->n = n2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows void spawnfds_destroy(struct SpawnFds *fds) {
|
||||
struct Dll *e;
|
||||
while ((e = dll_first(fds->closers))) {
|
||||
struct Closer *closer = CLOSER_CONTAINER(e);
|
||||
dll_remove(&fds->closers, e);
|
||||
CloseHandle(closer->handle);
|
||||
free(closer);
|
||||
}
|
||||
free(fds->p);
|
||||
}
|
||||
|
||||
static textwindows int spawnfds_closelater(struct SpawnFds *fds,
|
||||
int64_t handle) {
|
||||
struct Closer *closer;
|
||||
if (!(closer = malloc(sizeof(struct Closer)))) return ENOMEM;
|
||||
closer->handle = handle;
|
||||
dll_init(&closer->elem);
|
||||
dll_make_last(&fds->closers, &closer->elem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows bool spawnfds_exists(struct SpawnFds *fds, int fildes) {
|
||||
return fildes + 0u < fds->n && fds->p[fildes].kind;
|
||||
}
|
||||
|
||||
static textwindows void spawnfds_close(struct SpawnFds *fds, int fildes) {
|
||||
if (spawnfds_exists(fds, fildes)) {
|
||||
fds->p[fildes] = (struct Fd){0};
|
||||
}
|
||||
}
|
||||
|
||||
static void posix_spawn_inherit(int64_t hands[3], bool32 bInherit) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (hands[i] != -1) {
|
||||
SetHandleInformation(hands[i], kNtHandleFlagInherit, bInherit);
|
||||
}
|
||||
static textwindows errno_t spawnfds_dup2(struct SpawnFds *fds, int fildes,
|
||||
int newfildes) {
|
||||
errno_t err;
|
||||
struct Fd *old;
|
||||
if (spawnfds_exists(fds, fildes)) {
|
||||
old = fds->p + fildes;
|
||||
} else if (__isfdopen(fildes)) {
|
||||
old = g_fds.p + fildes;
|
||||
} else {
|
||||
return EBADF;
|
||||
}
|
||||
if ((err = spawnfds_ensure(fds, newfildes))) return err;
|
||||
struct Fd *neu = fds->p + newfildes;
|
||||
memcpy(neu, old, sizeof(struct Fd));
|
||||
neu->flags &= ~O_CLOEXEC;
|
||||
if (!DuplicateHandle(GetCurrentProcess(), neu->handle, GetCurrentProcess(),
|
||||
&neu->handle, 0, true, kNtDuplicateSameAccess)) {
|
||||
return EMFILE;
|
||||
}
|
||||
spawnfds_closelater(fds, neu->handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows errno_t spawnfds_open(struct SpawnFds *fds, int fildes,
|
||||
const char *path, int oflag,
|
||||
int mode) {
|
||||
int64_t h;
|
||||
errno_t err;
|
||||
char16_t path16[PATH_MAX];
|
||||
uint32_t perm, share, disp, attr;
|
||||
if ((err = spawnfds_ensure(fds, fildes))) return err;
|
||||
if (__mkntpathat(AT_FDCWD, path, 0, path16) != -1 &&
|
||||
GetNtOpenFlags(oflag, mode, &perm, &share, &disp, &attr) != -1 &&
|
||||
(h = CreateFile(path16, perm, share, &kNtIsInheritable, disp, attr, 0))) {
|
||||
spawnfds_closelater(fds, h);
|
||||
fds->p[fildes].kind = kFdFile;
|
||||
fds->p[fildes].flags = oflag;
|
||||
fds->p[fildes].mode = mode;
|
||||
fds->p[fildes].handle = h;
|
||||
return 0;
|
||||
} else {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows errno_t posix_spawn_windows_impl(
|
||||
static textwindows errno_t posix_spawn_nt_impl(
|
||||
int *pid, const char *path, const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) {
|
||||
int i;
|
||||
|
||||
// create file descriptor work area
|
||||
char stdio_kind[3] = {kFdEmpty, kFdEmpty, kFdEmpty};
|
||||
intptr_t stdio_handle[3] = {-1, -1, -1};
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (g_fds.p[i].kind != kFdEmpty && !(g_fds.p[i].flags & O_CLOEXEC)) {
|
||||
stdio_kind[i] = g_fds.p[i].kind;
|
||||
stdio_handle[i] = g_fds.p[i].handle;
|
||||
}
|
||||
}
|
||||
// signals, locks, and resources
|
||||
char *fdspec = 0;
|
||||
errno_t e = errno;
|
||||
struct Proc *proc = 0;
|
||||
struct SpawnFds fds = {0};
|
||||
int64_t *lpExplicitHandles = 0;
|
||||
uint32_t dwExplicitHandleCount = 0;
|
||||
int64_t hCreatorProcess = GetCurrentProcess();
|
||||
sigset_t m = __sig_block();
|
||||
|
||||
// reserve object for tracking proces
|
||||
struct Proc *proc;
|
||||
// reserve process tracking object
|
||||
__proc_lock();
|
||||
proc = __proc_new();
|
||||
__proc_unlock();
|
||||
if (!proc) return -1;
|
||||
|
||||
// apply user file actions
|
||||
intptr_t close_handle[3] = {-1, -1, -1};
|
||||
if (file_actions) {
|
||||
int err = 0;
|
||||
for (struct _posix_faction *a = *file_actions; a && !err; a = a->next) {
|
||||
switch (a->action) {
|
||||
case _POSIX_SPAWN_CLOSE:
|
||||
unassert(a->fildes < 3u);
|
||||
stdio_kind[a->fildes] = kFdEmpty;
|
||||
stdio_handle[a->fildes] = -1;
|
||||
break;
|
||||
case _POSIX_SPAWN_DUP2:
|
||||
unassert(a->newfildes < 3u);
|
||||
if (__isfdopen(a->fildes)) {
|
||||
stdio_kind[a->newfildes] = g_fds.p[a->fildes].kind;
|
||||
stdio_handle[a->newfildes] = g_fds.p[a->fildes].handle;
|
||||
} else {
|
||||
err = EBADF;
|
||||
}
|
||||
break;
|
||||
case _POSIX_SPAWN_OPEN: {
|
||||
int64_t hand;
|
||||
int e = errno;
|
||||
char16_t path16[PATH_MAX];
|
||||
uint32_t perm, share, disp, attr;
|
||||
unassert(a->fildes < 3u);
|
||||
if (__mkntpathat(AT_FDCWD, a->path, 0, path16) != -1 &&
|
||||
GetNtOpenFlags(a->oflag, a->mode, //
|
||||
&perm, &share, &disp, &attr) != -1 &&
|
||||
(hand = CreateFile(path16, perm, share, 0, disp, attr, 0))) {
|
||||
stdio_kind[a->fildes] = kFdFile;
|
||||
close_handle[a->fildes] = hand;
|
||||
stdio_handle[a->fildes] = hand;
|
||||
} else {
|
||||
err = errno;
|
||||
errno = e;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
posix_spawn_unhand(close_handle);
|
||||
// setup return path
|
||||
errno_t err;
|
||||
if (!proc) {
|
||||
err = ENOMEM;
|
||||
ReturnErr:
|
||||
__undescribe_fds(hCreatorProcess, lpExplicitHandles, dwExplicitHandleCount);
|
||||
free(fdspec);
|
||||
if (proc) {
|
||||
__proc_lock();
|
||||
__proc_free(proc);
|
||||
__proc_unlock();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// create the windows process start info
|
||||
int bits;
|
||||
char buf[32], *v = 0;
|
||||
if (_weaken(socket)) {
|
||||
for (bits = i = 0; i < 3; ++i) {
|
||||
if (stdio_kind[i] == kFdSocket) {
|
||||
bits |= 1 << i;
|
||||
}
|
||||
}
|
||||
FormatInt32(stpcpy(buf, "__STDIO_SOCKETS="), bits);
|
||||
v = buf;
|
||||
}
|
||||
struct NtStartupInfo startinfo = {
|
||||
.cb = sizeof(struct NtStartupInfo),
|
||||
.dwFlags = kNtStartfUsestdhandles,
|
||||
.hStdInput = stdio_handle[0],
|
||||
.hStdOutput = stdio_handle[1],
|
||||
.hStdError = stdio_handle[2],
|
||||
};
|
||||
|
||||
// figure out the flags
|
||||
short flags = 0;
|
||||
bool bInheritHandles = false;
|
||||
uint32_t dwCreationFlags = 0;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
bInheritHandles |= stdio_handle[i] != -1;
|
||||
}
|
||||
if (attrp && *attrp) {
|
||||
flags = (*attrp)->flags;
|
||||
if (flags & POSIX_SPAWN_SETSID) {
|
||||
dwCreationFlags |= kNtDetachedProcess;
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETPGROUP) {
|
||||
dwCreationFlags |= kNtCreateNewProcessGroup;
|
||||
}
|
||||
}
|
||||
|
||||
// launch the process
|
||||
int rc, e = errno;
|
||||
struct NtProcessInformation procinfo;
|
||||
if (!envp) envp = environ;
|
||||
__hand_rlock();
|
||||
posix_spawn_inherit(stdio_handle, true);
|
||||
rc = ntspawn(path, argv, envp, v, 0, 0, bInheritHandles, dwCreationFlags, 0,
|
||||
&startinfo, &procinfo);
|
||||
posix_spawn_inherit(stdio_handle, false);
|
||||
posix_spawn_unhand(close_handle);
|
||||
__hand_runlock();
|
||||
if (rc == -1) {
|
||||
int err = errno;
|
||||
__proc_lock();
|
||||
__proc_free(proc);
|
||||
__proc_unlock();
|
||||
spawnfds_destroy(&fds);
|
||||
__sig_unblock(m);
|
||||
errno = e;
|
||||
return err;
|
||||
}
|
||||
|
||||
// return the result
|
||||
// fork file descriptor table
|
||||
for (int fd = g_fds.n; fd--;) {
|
||||
if (__is_cloexec(g_fds.p + fd)) continue;
|
||||
if ((err = spawnfds_ensure(&fds, fd))) goto ReturnErr;
|
||||
fds.p[fd] = g_fds.p[fd];
|
||||
}
|
||||
|
||||
// apply user file actions
|
||||
if (file_actions) {
|
||||
for (struct _posix_faction *a = *file_actions; a && !err; a = a->next) {
|
||||
switch (a->action) {
|
||||
case _POSIX_SPAWN_CLOSE:
|
||||
spawnfds_close(&fds, a->fildes);
|
||||
break;
|
||||
case _POSIX_SPAWN_DUP2:
|
||||
err = spawnfds_dup2(&fds, a->fildes, a->newfildes);
|
||||
if (err) {
|
||||
STRACE("spawnfds_dup2(%d, %d) failed", a->fildes, a->newfildes);
|
||||
goto ReturnErr;
|
||||
}
|
||||
break;
|
||||
case _POSIX_SPAWN_OPEN:
|
||||
err = spawnfds_open(&fds, a->fildes, a->path, a->oflag, a->mode);
|
||||
if (err) {
|
||||
STRACE("spawnfds_open(%d, %#s) failed", a->fildes, a->path);
|
||||
goto ReturnErr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// figure out flags
|
||||
uint32_t dwCreationFlags = 0;
|
||||
if (attrp && *attrp) {
|
||||
if ((*attrp)->flags & POSIX_SPAWN_SETSID) {
|
||||
dwCreationFlags |= kNtDetachedProcess;
|
||||
}
|
||||
if ((*attrp)->flags & POSIX_SPAWN_SETPGROUP) {
|
||||
dwCreationFlags |= kNtCreateNewProcessGroup;
|
||||
}
|
||||
}
|
||||
|
||||
// create process startinfo
|
||||
struct NtStartupInfo startinfo = {
|
||||
.cb = sizeof(struct NtStartupInfo),
|
||||
.dwFlags = kNtStartfUsestdhandles,
|
||||
.hStdInput = spawnfds_handle(&fds, 0),
|
||||
.hStdOutput = spawnfds_handle(&fds, 1),
|
||||
.hStdError = spawnfds_handle(&fds, 2),
|
||||
};
|
||||
|
||||
// launch process
|
||||
int rc = -1;
|
||||
struct NtProcessInformation procinfo;
|
||||
if (!envp) envp = environ;
|
||||
if ((fdspec = __describe_fds(fds.p, fds.n, &startinfo, hCreatorProcess,
|
||||
&lpExplicitHandles, &dwExplicitHandleCount))) {
|
||||
rc = ntspawn(path, argv, envp, (char *[]){fdspec, 0}, dwCreationFlags, 0, 0,
|
||||
lpExplicitHandles, dwExplicitHandleCount, &startinfo,
|
||||
&procinfo);
|
||||
}
|
||||
if (rc == -1) {
|
||||
err = errno;
|
||||
goto ReturnErr;
|
||||
}
|
||||
|
||||
// return result
|
||||
CloseHandle(procinfo.hThread);
|
||||
proc->pid = procinfo.dwProcessId;
|
||||
proc->handle = procinfo.hProcess;
|
||||
|
@ -243,7 +312,9 @@ static textwindows errno_t posix_spawn_windows_impl(
|
|||
__proc_lock();
|
||||
__proc_add(proc);
|
||||
__proc_unlock();
|
||||
return 0;
|
||||
proc = 0;
|
||||
err = 0;
|
||||
goto ReturnErr;
|
||||
}
|
||||
|
||||
static const char *DescribePid(char buf[12], int err, int *pid) {
|
||||
|
@ -253,7 +324,7 @@ static const char *DescribePid(char buf[12], int err, int *pid) {
|
|||
return buf;
|
||||
}
|
||||
|
||||
static textwindows dontinline errno_t posix_spawn_windows(
|
||||
static textwindows dontinline errno_t posix_spawn_nt(
|
||||
int *pid, const char *path, const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) {
|
||||
int err;
|
||||
|
@ -263,7 +334,7 @@ static textwindows dontinline errno_t posix_spawn_windows(
|
|||
(envp && !__asan_is_valid_strlist(envp))))) {
|
||||
err = EFAULT;
|
||||
} else {
|
||||
err = posix_spawn_windows_impl(pid, path, file_actions, attrp, argv, envp);
|
||||
err = posix_spawn_nt_impl(pid, path, file_actions, attrp, argv, envp);
|
||||
}
|
||||
STRACE("posix_spawn([%s], %#s, %s, %s) → %s",
|
||||
DescribePid(alloca(12), err, pid), path, DescribeStringList(argv),
|
||||
|
@ -272,7 +343,21 @@ static textwindows dontinline errno_t posix_spawn_windows(
|
|||
}
|
||||
|
||||
/**
|
||||
* Spawns process, the POSIX way.
|
||||
* Spawns process, the POSIX way, e.g.
|
||||
*
|
||||
* int pid, status;
|
||||
* posix_spawnattr_t sa;
|
||||
* posix_spawnattr_init(&sa);
|
||||
* posix_spawnattr_setflags(&sa, POSIX_SPAWN_SETPGROUP);
|
||||
* posix_spawn_file_actions_t fa;
|
||||
* posix_spawn_file_actions_init(&fa);
|
||||
* posix_spawn_file_actions_addopen(&fa, 0, "/dev/null", O_RDWR, 0644);
|
||||
* posix_spawn_file_actions_adddup2(&fa, 0, 1);
|
||||
* posix_spawnp(&pid, "lol", &fa, &sa, (char *[]){"lol", 0}, 0);
|
||||
* posix_spawnp(&pid, "cat", &fa, &sa, (char *[]){"cat", 0}, 0);
|
||||
* posix_spawn_file_actions_destroy(&fa);
|
||||
* posix_spawnattr_destroy(&sa);
|
||||
* while (wait(&status) != -1);
|
||||
*
|
||||
* This provides superior process creation performance across systems
|
||||
*
|
||||
|
@ -311,7 +396,7 @@ errno_t posix_spawn(int *pid, const char *path,
|
|||
const posix_spawnattr_t *attrp, char *const argv[],
|
||||
char *const envp[]) {
|
||||
if (IsWindows()) {
|
||||
return posix_spawn_windows(pid, path, file_actions, attrp, argv, envp);
|
||||
return posix_spawn_nt(pid, path, file_actions, attrp, argv, envp);
|
||||
}
|
||||
int pfds[2];
|
||||
bool use_pipe;
|
||||
|
|
107
libc/proc/proc.c
107
libc/proc/proc.c
|
@ -16,21 +16,32 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/leaky.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/enum/processaccess.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/enum/wait.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/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
|
@ -42,11 +53,37 @@
|
|||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
/**
|
||||
* @fileoverview Windows Subprocess Management.
|
||||
*/
|
||||
|
||||
struct Procs __proc;
|
||||
|
||||
static textwindows void GetProcessStats(int64_t h, struct rusage *ru) {
|
||||
bzero(ru, sizeof(*ru));
|
||||
struct NtProcessMemoryCountersEx memcount = {sizeof(memcount)};
|
||||
unassert(GetProcessMemoryInfo(h, &memcount, sizeof(memcount)));
|
||||
ru->ru_maxrss = memcount.PeakWorkingSetSize / 1024;
|
||||
ru->ru_majflt = memcount.PageFaultCount;
|
||||
struct NtFileTime createtime, exittime;
|
||||
struct NtFileTime kerneltime, usertime;
|
||||
unassert(GetProcessTimes(h, &createtime, &exittime, &kerneltime, &usertime));
|
||||
ru->ru_utime = WindowsDurationToTimeVal(ReadFileTime(usertime));
|
||||
ru->ru_stime = WindowsDurationToTimeVal(ReadFileTime(kerneltime));
|
||||
struct NtIoCounters iocount;
|
||||
unassert(GetProcessIoCounters(h, &iocount));
|
||||
ru->ru_inblock = iocount.ReadOperationCount;
|
||||
ru->ru_oublock = iocount.WriteOperationCount;
|
||||
}
|
||||
|
||||
static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
|
||||
__bootstrap_tls(&__proc.tls, __builtin_frame_address(0));
|
||||
for (;;) {
|
||||
|
||||
// assemble a group of processes to wait on. if more than 64
|
||||
// children exist, then we'll use a small timeout and select
|
||||
// processes with a shifting window via a double linked list
|
||||
struct rusage ru;
|
||||
int64_t handles[64];
|
||||
int sic, dosignal = 0;
|
||||
struct Proc *pr, *objects[64];
|
||||
|
@ -64,33 +101,53 @@ static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
|
|||
}
|
||||
dll_make_last(&__proc.list, samples);
|
||||
__proc_unlock();
|
||||
|
||||
// wait for win32 to report any status change
|
||||
millis = n == 64 ? __SIG_PROC_INTERVAL_MS : -1u;
|
||||
i = WaitForMultipleObjects(n, handles, false, millis);
|
||||
if (i == -1u) {
|
||||
STRACE("PROC WORKER DYING: WAIT FAILED: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
i &= ~kNtWaitAbandoned;
|
||||
if (!i || i == kNtWaitTimeout) continue;
|
||||
GetExitCodeProcess(handles[i], &status);
|
||||
if (status == kNtStillActive) continue;
|
||||
GetProcessStats(handles[i], &ru);
|
||||
|
||||
// update data structures and notify folks
|
||||
__proc_lock();
|
||||
pr = objects[i];
|
||||
if (status == 0xc9af3d51u) status = kNtStillActive;
|
||||
pr->wstatus = status;
|
||||
if ((__sighandrvas[SIGCHLD] == (uintptr_t)SIG_IGN ||
|
||||
(__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) &&
|
||||
(!pr->waiters && !__proc.waiters)) {
|
||||
rusage_add(&pr->ru, &ru);
|
||||
rusage_add(&__proc.ruchlds, &ru);
|
||||
if ((status & 0xFF000000u) == 0x23000000u) {
|
||||
// handle child execve()
|
||||
CloseHandle(pr->handle);
|
||||
dll_remove(&__proc.list, &pr->elem);
|
||||
dll_make_first(&__proc.free, &pr->elem);
|
||||
pr->handle = status & 0x00FFFFFF;
|
||||
} else {
|
||||
pr->iszombie = 1;
|
||||
dll_remove(&__proc.list, &pr->elem);
|
||||
dll_make_first(&__proc.zombies, &pr->elem);
|
||||
if (pr->waiters) {
|
||||
nsync_cv_broadcast(&pr->onexit);
|
||||
} else if (__proc.waiters) {
|
||||
nsync_cv_signal(&__proc.onexit);
|
||||
// handle child _exit()
|
||||
CloseHandle(pr->handle);
|
||||
if (status == 0xc9af3d51u) {
|
||||
status = kNtStillActive;
|
||||
}
|
||||
pr->wstatus = status;
|
||||
if ((__sighandrvas[SIGCHLD] == (uintptr_t)SIG_IGN ||
|
||||
(__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) &&
|
||||
(!pr->waiters && !__proc.waiters)) {
|
||||
dll_remove(&__proc.list, &pr->elem);
|
||||
dll_make_first(&__proc.free, &pr->elem);
|
||||
} else {
|
||||
dosignal = 1;
|
||||
sic = WIFSIGNALED(status) ? CLD_KILLED : CLD_EXITED;
|
||||
pr->iszombie = 1;
|
||||
dll_remove(&__proc.list, &pr->elem);
|
||||
dll_make_first(&__proc.zombies, &pr->elem);
|
||||
if (pr->waiters) {
|
||||
nsync_cv_broadcast(&pr->onexit);
|
||||
} else if (__proc.waiters) {
|
||||
nsync_cv_signal(&__proc.onexit);
|
||||
} else {
|
||||
dosignal = 1;
|
||||
sic = WIFSIGNALED(status) ? CLD_KILLED : CLD_EXITED;
|
||||
}
|
||||
}
|
||||
}
|
||||
__proc_unlock();
|
||||
|
@ -189,4 +246,22 @@ textwindows void __proc_free(struct Proc *proc) {
|
|||
dll_make_first(&__proc.free, &proc->elem);
|
||||
}
|
||||
|
||||
// returns owned handle of direct child process
|
||||
// this is intended for the __proc_handle() implementation
|
||||
textwindows int64_t __proc_search(int pid) {
|
||||
struct Dll *e;
|
||||
int64_t handle = 0;
|
||||
BLOCK_SIGNALS;
|
||||
__proc_lock();
|
||||
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) {
|
||||
if (pid == PROC_CONTAINER(e)->pid) {
|
||||
handle = PROC_CONTAINER(e)->handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
__proc_unlock();
|
||||
ALLOW_SIGNALS;
|
||||
return handle;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -21,6 +21,7 @@ struct Proc {
|
|||
int64_t handle;
|
||||
struct Dll elem;
|
||||
nsync_cv onexit;
|
||||
struct rusage ru;
|
||||
};
|
||||
|
||||
struct Procs {
|
||||
|
@ -36,6 +37,7 @@ struct Procs {
|
|||
struct Proc pool[8];
|
||||
unsigned allocated;
|
||||
struct CosmoTib tls;
|
||||
struct rusage ruchlds;
|
||||
};
|
||||
|
||||
extern struct Procs __proc;
|
||||
|
@ -43,6 +45,8 @@ extern struct Procs __proc;
|
|||
void __proc_wipe(void);
|
||||
void __proc_lock(void);
|
||||
void __proc_unlock(void);
|
||||
int64_t __proc_handle(int);
|
||||
int64_t __proc_search(int);
|
||||
struct Proc *__proc_new(void);
|
||||
void __proc_add(struct Proc *);
|
||||
void __proc_free(struct Proc *);
|
||||
|
|
88
libc/proc/sched_getaffinity.c
Normal file
88
libc/proc/sched_getaffinity.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*-*- 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/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/errors.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/proc/proc.internal.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) {
|
||||
int64_t handle;
|
||||
uint64_t SystemAffinityMask;
|
||||
if (!(handle = __proc_handle(pid))) {
|
||||
return esrch();
|
||||
}
|
||||
if (GetProcessAffinityMask(handle, bitset->__bits, &SystemAffinityMask)) {
|
||||
return 8;
|
||||
} else if (GetLastError() == kNtErrorInvalidHandle) {
|
||||
return esrch();
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
72
libc/proc/sched_setaffinity.c
Normal file
72
libc/proc/sched_setaffinity.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*-*- 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/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/errors.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static dontinline textwindows int sys_sched_setaffinity_nt(
|
||||
int pid, uint64_t size, const cpu_set_t *bitset) {
|
||||
int64_t handle;
|
||||
if (!(handle = __proc_handle(pid))) {
|
||||
return esrch();
|
||||
}
|
||||
if (SetProcessAffinityMask(handle, bitset->__bits[0])) {
|
||||
return 0;
|
||||
} else if (GetLastError() == kNtErrorInvalidHandle) {
|
||||
return esrch();
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
64
libc/proc/setpriority-nt.c
Normal file
64
libc/proc/setpriority-nt.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*-*- 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/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/sysv/consts/prio.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_setpriority_nt(int which, unsigned pid, int nice) {
|
||||
|
||||
if (which != PRIO_PROCESS) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
int64_t handle;
|
||||
if (!(handle = __proc_handle(pid))) {
|
||||
return esrch();
|
||||
}
|
||||
|
||||
uint32_t tier;
|
||||
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(handle, tier)) return 0;
|
||||
STRACE("SetPriorityClass() failed with %d", GetLastError());
|
||||
switch (GetLastError()) {
|
||||
case kNtErrorInvalidHandle:
|
||||
return esrch(); // Otherwise EBADF would be returned.
|
||||
default:
|
||||
return __winerr(); // TODO: Does this get EPERM/EACCES right?
|
||||
}
|
||||
}
|
57
libc/proc/setpriority.c
Normal file
57
libc/proc/setpriority.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*-*- 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;
|
||||
}
|
|
@ -82,14 +82,14 @@ int system(const char *cmdline) {
|
|||
sigemptyset(&ignore.sa_mask);
|
||||
sigaction(SIGINT, &ignore, &saveint);
|
||||
sigaction(SIGQUIT, &ignore, &savequit);
|
||||
BLOCK_CANCELLATIONS;
|
||||
BLOCK_CANCELATION;
|
||||
while (wait4(pid, &wstatus, 0, 0) == -1) {
|
||||
if (errno != EINTR) {
|
||||
wstatus = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_CANCELATION;
|
||||
sigaction(SIGQUIT, &savequit, 0);
|
||||
sigaction(SIGINT, &saveint, 0);
|
||||
}
|
||||
|
|
|
@ -73,14 +73,14 @@ int systemvpe(const char *prog, char *const argv[], char *const envp[]) {
|
|||
sigemptyset(&ignore.sa_mask);
|
||||
sigaction(SIGINT, &ignore, &saveint);
|
||||
sigaction(SIGQUIT, &ignore, &savequit);
|
||||
BLOCK_CANCELLATIONS;
|
||||
BLOCK_CANCELATION;
|
||||
while (wait4(pid, &wstatus, 0, 0) == -1) {
|
||||
if (errno != EINTR) {
|
||||
wstatus = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_CANCELATION;
|
||||
sigaction(SIGQUIT, &savequit, 0);
|
||||
sigaction(SIGINT, &saveint, 0);
|
||||
}
|
||||
|
|
67
libc/proc/times.c
Normal file
67
libc/proc/times.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*-*- 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/timeval.h"
|
||||
#include "libc/calls/struct/tms.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/fmt/wintime.internal.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/clktck.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sysv/consts/rusage.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
static dontinline long ConvertMicros(struct timeval tv) {
|
||||
return tv.tv_sec * CLK_TCK + tv.tv_usec / (1000000 / CLK_TCK);
|
||||
}
|
||||
|
||||
static dontinline long times2(struct tms *out_times, struct rusage *ru) {
|
||||
struct timeval tv;
|
||||
struct NtFileTime CreationTime, ExitTime, KernelTime, UserTime;
|
||||
if (!IsWindows()) {
|
||||
if (getrusage(RUSAGE_SELF, ru) == -1) return -1;
|
||||
out_times->tms_utime = ConvertMicros(ru->ru_utime);
|
||||
out_times->tms_stime = ConvertMicros(ru->ru_stime);
|
||||
if (getrusage(RUSAGE_CHILDREN, ru) == -1) return -1;
|
||||
out_times->tms_cutime = ConvertMicros(ru->ru_utime);
|
||||
out_times->tms_cstime = ConvertMicros(ru->ru_stime);
|
||||
} else {
|
||||
if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime,
|
||||
&KernelTime, &UserTime)) {
|
||||
return __winerr();
|
||||
}
|
||||
out_times->tms_utime = ReadFileTime(UserTime);
|
||||
out_times->tms_stime = ReadFileTime(KernelTime);
|
||||
out_times->tms_cutime = 0;
|
||||
out_times->tms_cstime = 0;
|
||||
}
|
||||
if (gettimeofday(&tv, NULL) == -1) return -1;
|
||||
return ConvertMicros(tv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns accounting data for process on time-sharing system.
|
||||
*/
|
||||
long times(struct tms *out_times) {
|
||||
struct rusage ru;
|
||||
return times2(out_times, &ru);
|
||||
}
|
41
libc/proc/verynice.c
Normal file
41
libc/proc/verynice.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*-*- 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/struct/sched_param.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/sysv/consts/ioprio.h"
|
||||
#include "libc/sysv/consts/prio.h"
|
||||
#include "libc/sysv/consts/sched.h"
|
||||
|
||||
/**
|
||||
* Makes current process as low-priority as possible.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @note error reporting currently not implemented
|
||||
*/
|
||||
int verynice(void) {
|
||||
int e = errno;
|
||||
setpriority(PRIO_PROCESS, 0, 10);
|
||||
sys_ioprio_set(IOPRIO_WHO_PROCESS, 0,
|
||||
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
|
||||
struct sched_param param = {sched_get_priority_min(SCHED_IDLE)};
|
||||
sched_setscheduler(0, SCHED_IDLE, ¶m);
|
||||
errno = e;
|
||||
return 0;
|
||||
}
|
|
@ -24,7 +24,7 @@
|
|||
* @param opt_out_wstatus optionally returns status code, and *wstatus
|
||||
* may be inspected using WEEXITSTATUS(), etc.
|
||||
* @return process id of terminated child or -1 w/ errno
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
* @param options can have WNOHANG, WUNTRACED, WCONTINUED, etc.
|
||||
* @param opt_out_rusage optionally returns accounting data
|
||||
* @return process id of terminated child or -1 w/ errno
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/bo.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/errno.h"
|
||||
|
@ -37,32 +37,14 @@
|
|||
#include "libc/nt/struct/processmemorycounters.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/w.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
static textwindows void GetProcessStats(int64_t h, struct rusage *ru) {
|
||||
bzero(ru, sizeof(*ru));
|
||||
struct NtProcessMemoryCountersEx memcount = {sizeof(memcount)};
|
||||
unassert(GetProcessMemoryInfo(h, &memcount, sizeof(memcount)));
|
||||
ru->ru_maxrss = memcount.PeakWorkingSetSize / 1024;
|
||||
ru->ru_majflt = memcount.PageFaultCount;
|
||||
struct NtFileTime createtime, exittime;
|
||||
struct NtFileTime kerneltime, usertime;
|
||||
unassert(GetProcessTimes(h, &createtime, &exittime, &kerneltime, &usertime));
|
||||
ru->ru_utime = WindowsDurationToTimeVal(ReadFileTime(usertime));
|
||||
ru->ru_stime = WindowsDurationToTimeVal(ReadFileTime(kerneltime));
|
||||
struct NtIoCounters iocount;
|
||||
unassert(GetProcessIoCounters(h, &iocount));
|
||||
ru->ru_inblock = iocount.ReadOperationCount;
|
||||
ru->ru_oublock = iocount.WriteOperationCount;
|
||||
}
|
||||
|
||||
static textwindows struct timespec GetNextDeadline(struct timespec deadline) {
|
||||
if (__tls_enabled && __get_tls()->tib_sigmask == -1) return timespec_max;
|
||||
if (timespec_iszero(deadline)) deadline = timespec_real();
|
||||
struct timespec delay = timespec_frommillis(__SIG_PROC_INTERVAL_MS);
|
||||
return timespec_add(deadline, delay);
|
||||
|
@ -74,10 +56,9 @@ static textwindows int ReapZombie(struct Proc *pr, int *wstatus,
|
|||
*wstatus = pr->wstatus;
|
||||
}
|
||||
if (opt_out_rusage) {
|
||||
GetProcessStats(pr->handle, opt_out_rusage);
|
||||
*opt_out_rusage = pr->ru;
|
||||
}
|
||||
if (!pr->waiters) {
|
||||
CloseHandle(pr->handle);
|
||||
dll_remove(&__proc.zombies, &pr->elem);
|
||||
dll_make_first(&__proc.free, &pr->elem);
|
||||
}
|
||||
|
@ -99,8 +80,15 @@ static textwindows int CheckZombies(int pid, int *wstatus,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static textwindows void UnwindWaiterCount(void *arg) {
|
||||
int *waiters = arg;
|
||||
--*waiters;
|
||||
}
|
||||
|
||||
static textwindows int WaitForProcess(int pid, int *wstatus, int options,
|
||||
struct rusage *rusage, uint64_t *m) {
|
||||
struct rusage *rusage,
|
||||
uint64_t waitmask) {
|
||||
uint64_t m;
|
||||
int rc, *wv;
|
||||
nsync_cv *cv;
|
||||
struct Dll *e;
|
||||
|
@ -137,19 +125,22 @@ static textwindows int WaitForProcess(int pid, int *wstatus, int options,
|
|||
|
||||
// wait for status change
|
||||
if (options & WNOHANG) return 0;
|
||||
CheckForInterrupt:
|
||||
if (_check_interrupts(kSigOpRestartable) == -1) return -1;
|
||||
WaitMore:
|
||||
deadline = GetNextDeadline(deadline);
|
||||
SpuriousWakeup:
|
||||
++*wv;
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, *m, memory_order_release);
|
||||
rc = nsync_cv_wait_with_deadline(cv, &__proc.lock, deadline, 0);
|
||||
*m = atomic_exchange(&__get_tls()->tib_sigmask, -1);
|
||||
--*wv;
|
||||
pthread_cleanup_push(UnwindWaiterCount, wv);
|
||||
m = __sig_beginwait(waitmask);
|
||||
if ((rc = _check_signal(true)) != -1) {
|
||||
rc = nsync_cv_wait_with_deadline(cv, &__proc.lock, deadline, 0);
|
||||
}
|
||||
__sig_finishwait(m);
|
||||
pthread_cleanup_pop(true);
|
||||
if (rc == -1) return -1;
|
||||
if (rc == ETIMEDOUT) goto WaitMore;
|
||||
if (rc == ECANCELED) return ecanceled();
|
||||
if (pr && pr->iszombie) return ReapZombie(pr, wstatus, rusage);
|
||||
if (rc == ETIMEDOUT) goto CheckForInterrupt;
|
||||
unassert(!rc);
|
||||
unassert(!rc); // i have to follow my dreams however crazy they seem
|
||||
if (!pr && (rc = CheckZombies(pid, wstatus, rusage))) return rc;
|
||||
goto SpuriousWakeup;
|
||||
}
|
||||
|
@ -157,20 +148,21 @@ SpuriousWakeup:
|
|||
textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
int rc;
|
||||
if (options & ~WNOHANG) {
|
||||
return einval(); // no support for WCONTINUED and WUNTRACED yet
|
||||
}
|
||||
uint64_t m;
|
||||
// no support for WCONTINUED and WUNTRACED yet
|
||||
if (options & ~WNOHANG) return einval();
|
||||
// XXX: NT doesn't really have process groups. For instance the
|
||||
// CreateProcess() flag for starting a process group actually
|
||||
// just does an "ignore ctrl-c" internally.
|
||||
if (pid == 0) pid = -1;
|
||||
if (pid < -1) pid = -pid;
|
||||
uint64_t m = atomic_exchange(&__get_tls()->tib_sigmask, -1);
|
||||
m = __sig_block();
|
||||
__proc_lock();
|
||||
pthread_cleanup_push((void *)__proc_unlock, 0);
|
||||
rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage, &m);
|
||||
rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage,
|
||||
m | 1ull << (SIGCHLD - 1));
|
||||
pthread_cleanup_pop(true);
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
|
||||
__sig_unblock(m);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,14 +35,14 @@
|
|||
* @param options can have WNOHANG, WUNTRACED, WCONTINUED, etc.
|
||||
* @param opt_out_rusage optionally returns accounting data
|
||||
* @return process id of terminated child or -1 w/ errno
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
int wait4(int pid, int *opt_out_wstatus, int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
int rc, ws = 0;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (IsAsan() &&
|
||||
((opt_out_wstatus &&
|
||||
|
@ -59,7 +59,7 @@ int wait4(int pid, int *opt_out_wstatus, int options,
|
|||
*opt_out_wstatus = ws;
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
END_CANCELATION_POINT;
|
||||
STRACE("wait4(%d, [%#x], %d, %p) → %d% m", pid, ws, options, opt_out_rusage,
|
||||
rc);
|
||||
return rc;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
* may be inspected using WEXITSTATUS(), etc.
|
||||
* @param options can have WNOHANG, WUNTRACED, WCONTINUED, etc.
|
||||
* @return process id of terminated child or -1 w/ errno
|
||||
* @cancellationpoint
|
||||
* @cancelationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue