mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-18 16:40:32 +00:00
Make improvements
- Every unit test now passes on Apple Silicon. The final piece of this puzzle was porting our POSIX threads cancelation support, since that works differently on ARM64 XNU vs. AMD64. Our semaphore support on Apple Silicon is also superior now compared to AMD64, thanks to the grand central dispatch library which lets *NSYNC locks go faster. - The Cosmopolitan runtime is now more stable, particularly on Windows. To do this, thread local storage is mandatory at all runtime levels, and the innermost packages of the C library is no longer being built using ASAN. TLS is being bootstrapped with a 128-byte TIB during the process startup phase, and then later on the runtime re-allocates it either statically or dynamically to support code using _Thread_local. fork() and execve() now do a better job cooperating with threads. We can now check how much stack memory is left in the process or thread when functions like kprintf() / execve() etc. call alloca(), so that ENOMEM can be raised, reduce a buffer size, or just print a warning. - POSIX signal emulation is now implemented the same way kernels do it with pthread_kill() and raise(). Any thread can interrupt any other thread, regardless of what it's doing. If it's blocked on read/write then the killer thread will cancel its i/o operation so that EINTR can be returned in the mark thread immediately. If it's doing a tight CPU bound operation, then that's also interrupted by the signal delivery. Signal delivery works now by suspending a thread and pushing context data structures onto its stack, and redirecting its execution to a trampoline function, which calls SetThreadContext(GetCurrentThread()) when it's done. - We're now doing a better job managing locks and handles. On NetBSD we now close semaphore file descriptors in forked children. Semaphores on Windows can now be canceled immediately, which means mutexes/condition variables will now go faster. Apple Silicon semaphores can be canceled too. We're now using Apple's pthread_yield() funciton. Apple _nocancel syscalls are now used on XNU when appropriate to ensure pthread_cancel requests aren't lost. The MbedTLS library has been updated to support POSIX thread cancelations. See tool/build/runitd.c for an example of how it can be used for production multi-threaded tls servers. Handles on Windows now leak less often across processes. All i/o operations on Windows are now overlapped, which means file pointers can no longer be inherited across dup() and fork() for the time being. - We now spawn a thread on Windows to deliver SIGCHLD and wakeup wait4() which means, for example, that posix_spawn() now goes 3x faster. POSIX spawn is also now more correct. Like Musl, it's now able to report the failure code of execve() via a pipe although our approach favors using shared memory to do that on systems that have a true vfork() function. - We now spawn a thread to deliver SIGALRM to threads when setitimer() is used. This enables the most precise wakeups the OS makes possible. - The Cosmopolitan runtime now uses less memory. On NetBSD for example, it turned out the kernel would actually commit the PT_GNU_STACK size which caused RSS to be 6mb for every process. Now it's down to ~4kb. On Apple Silicon, we reduce the mandatory upstream thread size to the smallest possible size to reduce the memory overhead of Cosmo threads. The examples directory has a program called greenbean which can spawn a web server on Linux with 10,000 worker threads and have the memory usage of the process be ~77mb. The 1024 byte overhead of POSIX-style thread-local storage is now optional; it won't be allocated until the pthread_setspecific/getspecific functions are called. On Windows, the threads that get spawned which are internal to the libc implementation use reserve rather than commit memory, which shaves a few hundred kb. - sigaltstack() is now supported on Windows, however it's currently not able to be used to handle stack overflows, since crash signals are still generated by WIN32. However the crash handler will still switch to the alt stack, which is helpful in environments with tiny threads. - Test binaries are now smaller. Many of the mandatory dependencies of the test runner have been removed. This ensures many programs can do a better job only linking the the thing they're testing. This caused the test binaries for LIBC_FMT for example, to decrease from 200kb to 50kb - long double is no longer used in the implementation details of libc, except in the APIs that define it. The old code that used long double for time (instead of struct timespec) has now been thoroughly removed. - ShowCrashReports() is now much tinier in MODE=tiny. Instead of doing backtraces itself, it'll just print a command you can run on the shell using our new `cosmoaddr2line` program to view the backtrace. - Crash report signal handling now works in a much better way. Instead of terminating the process, it now relies on SA_RESETHAND so that the default SIG_IGN behavior can terminate the process if necessary. - Our pledge() functionality has now been fully ported to AARCH64 Linux.
This commit is contained in:
parent
c4eb838516
commit
ec480f5aa0
638 changed files with 7925 additions and 8282 deletions
1069
libc/proc/cocmd.c
Normal file
1069
libc/proc/cocmd.c
Normal file
File diff suppressed because it is too large
Load diff
68
libc/proc/daemon.c
Normal file
68
libc/proc/daemon.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Runs process in background.
|
||||
*
|
||||
* On Unix this calls fork() and setsid(). On Windows this is
|
||||
* implemented using CreateProcess(kNtDetachedProcess).
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
*/
|
||||
int daemon(int nochdir, int noclose) {
|
||||
int fd;
|
||||
|
||||
switch (_fork(kNtDetachedProcess)) {
|
||||
case -1:
|
||||
return -1;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
_Exit(0);
|
||||
}
|
||||
|
||||
if (!IsWindows()) {
|
||||
if (setsid() == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nochdir) {
|
||||
unassert(!chdir("/"));
|
||||
}
|
||||
|
||||
if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR)) != -1) {
|
||||
unassert(dup2(fd, 0) == 0);
|
||||
unassert(dup2(fd, 1) == 1);
|
||||
unassert(dup2(fd, 2) == 2);
|
||||
if (fd > 2) {
|
||||
unassert(!close(fd));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
59
libc/proc/execl.c
Normal file
59
libc/proc/execl.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*-*- 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/mem/alloca.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Executes program, with current environment.
|
||||
*
|
||||
* The current process is replaced with the executed one.
|
||||
*
|
||||
* @param prog will not be PATH searched, see execlp()
|
||||
* @param arg[0] is the name of the program to run
|
||||
* @param arg[1,n-2] optionally specify program arguments
|
||||
* @param arg[n-1] is NULL
|
||||
* @return doesn't return on success, otherwise -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int execl(const char *exe, const char *arg, ... /*, NULL*/) {
|
||||
int i;
|
||||
va_list va, vb;
|
||||
va_copy(vb, va);
|
||||
va_start(va, arg);
|
||||
for (i = 0; va_arg(va, const char *); ++i) donothing;
|
||||
va_end(va);
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
int nbytes = (i + 2) * sizeof(char *);
|
||||
if (__get_safe_size(nbytes, 4096) < nbytes) return enomem();
|
||||
char **argv = alloca(nbytes);
|
||||
CheckLargeStackAllocation(argv, nbytes);
|
||||
#pragma GCC pop_options
|
||||
va_start(vb, arg);
|
||||
argv[0] = (char *)arg;
|
||||
for (i = 1;; ++i) {
|
||||
if (!(argv[i] = va_arg(vb, char *))) break;
|
||||
}
|
||||
va_end(vb);
|
||||
return execv(exe, argv);
|
||||
}
|
63
libc/proc/execle.c
Normal file
63
libc/proc/execle.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*-*- 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/mem/alloca.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Executes program, with custom environment.
|
||||
*
|
||||
* The current process is replaced with the executed one.
|
||||
*
|
||||
* @param prog will not be PATH searched, see commandv()
|
||||
* @param arg[0] is the name of the program to run
|
||||
* @param arg[1,n-3] optionally specify program arguments
|
||||
* @param arg[n-2] is NULL
|
||||
* @param arg[n-1] is a pointer to a ["key=val",...,NULL] array
|
||||
* @return doesn't return on success, otherwise -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int execle(const char *exe, const char *arg,
|
||||
... /*, NULL, char *const envp[] */) {
|
||||
int i;
|
||||
va_list va, vb;
|
||||
char **argv, **envp;
|
||||
va_copy(vb, va);
|
||||
va_start(va, arg);
|
||||
for (i = 0; va_arg(va, const char *); ++i) donothing;
|
||||
envp = va_arg(va, char **);
|
||||
va_end(va);
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
int nbytes = (i + 2) * sizeof(char *);
|
||||
if (__get_safe_size(nbytes, 4096) < nbytes) return enomem();
|
||||
argv = alloca(nbytes);
|
||||
CheckLargeStackAllocation(argv, nbytes);
|
||||
#pragma GCC pop_options
|
||||
va_start(vb, arg);
|
||||
argv[0] = (char *)arg;
|
||||
for (i = 1;; ++i) {
|
||||
if (!(argv[i] = va_arg(vb, char *))) break;
|
||||
}
|
||||
va_end(vb);
|
||||
return execve(exe, argv, envp);
|
||||
}
|
84
libc/proc/execlp.c
Normal file
84
libc/proc/execlp.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*-*- 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/limits.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Executes program, with PATH search and current environment.
|
||||
*
|
||||
* The current process is replaced with the executed one.
|
||||
*
|
||||
* @param prog is program to launch (may be PATH searched)
|
||||
* @param arg[0] is the name of the program to run
|
||||
* @param arg[1,n-2] optionally specify program arguments
|
||||
* @param arg[n-1] is NULL
|
||||
* @return doesn't return on success, otherwise -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int execlp(const char *prog, const char *arg, ... /*, NULL*/) {
|
||||
int i;
|
||||
char *exe;
|
||||
char **argv;
|
||||
va_list va, vb;
|
||||
char pathbuf[PATH_MAX];
|
||||
|
||||
// turn varargs into array
|
||||
va_copy(vb, va);
|
||||
va_start(va, arg);
|
||||
for (i = 0; va_arg(va, const char *); ++i) (void)0;
|
||||
va_end(va);
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
int nbytes = (i + 2) * sizeof(char *);
|
||||
if (__get_safe_size(nbytes, 4096) < nbytes) return enomem();
|
||||
argv = alloca(nbytes);
|
||||
CheckLargeStackAllocation(argv, nbytes);
|
||||
#pragma GCC pop_options
|
||||
va_start(vb, arg);
|
||||
argv[0] = (char *)arg;
|
||||
for (i = 1;; ++i) {
|
||||
if (!(argv[i] = va_arg(vb, char *))) break;
|
||||
}
|
||||
va_end(vb);
|
||||
|
||||
if (strchr(prog, '/')) {
|
||||
return execv(prog, argv);
|
||||
}
|
||||
|
||||
// resolve path of executable
|
||||
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// change argv[0] to resolved path if it's ambiguous
|
||||
// otherwise the program won't have much luck finding itself
|
||||
if (argv[0] && *prog != '/' && *exe == '/' && !strcmp(prog, argv[0])) {
|
||||
argv[0] = exe;
|
||||
}
|
||||
|
||||
// execute program
|
||||
// tail call shouldn't be possible
|
||||
return execv(exe, argv);
|
||||
}
|
29
libc/proc/execv.c
Normal file
29
libc/proc/execv.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- 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/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
* Replaces process with specific program, using default environment.
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int execv(const char *exe, char *const argv[]) {
|
||||
return execve(exe, argv, environ);
|
||||
}
|
223
libc/proc/execve-nt.greg.c
Normal file
223
libc/proc/execve-nt.greg.c
Normal file
|
@ -0,0 +1,223 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/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/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/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/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"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define keywords textwindows dontinstrument
|
||||
|
||||
// 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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// define stdio handles for the spawned subprocess
|
||||
struct NtStartupInfo si = {
|
||||
.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;
|
||||
} else {
|
||||
si.stdiofds[i] = -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);
|
||||
if (rc == -1) {
|
||||
sys_execve_inherit(si.stdiofds, false);
|
||||
__hand_unlock();
|
||||
if (__imp_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);
|
||||
}
|
||||
if (_weaken(__sigwinch_thread)) {
|
||||
PurgeThread(*_weaken(__sigwinch_thread));
|
||||
}
|
||||
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);
|
||||
PurgeThread(g_fds.stdin.thread);
|
||||
PurgeHandle(g_fds.stdin.reader);
|
||||
PurgeHandle(g_fds.stdin.writer);
|
||||
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);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
149
libc/proc/execve-sysv.c
Normal file
149
libc/proc/execve-sysv.c
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*-*- 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 "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/proc/execve.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define ELIBBAD_LINUX 80
|
||||
#define EBADEXEC_XNU 85
|
||||
#define EBADARCH_XNU 86
|
||||
|
||||
static struct {
|
||||
atomic_uint once;
|
||||
const char *home;
|
||||
const char *tmpdir;
|
||||
} g_execve;
|
||||
|
||||
static bool IsApeFile(const char *path) {
|
||||
if (!endswith(path, ".com")) {
|
||||
return true;
|
||||
} else {
|
||||
bool res = false;
|
||||
BLOCK_CANCELLATIONS;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
int fd;
|
||||
char buf[8];
|
||||
int flags = O_RDONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC;
|
||||
if ((fd = sys_openat(AT_FDCWD, path, flags, 0)) != -1) {
|
||||
res = sys_pread(fd, buf, 8, 0, 0) == 8 && IsApeLoadable(buf);
|
||||
sys_close(fd);
|
||||
}
|
||||
END_CANCELLATION_POINT;
|
||||
ALLOW_CANCELLATIONS;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *Join(const char *a, const char *b, char buf[PATH_MAX]) {
|
||||
size_t n, m;
|
||||
if (a && *a) {
|
||||
n = strlen(a);
|
||||
m = strlen(b);
|
||||
if (n + m + 1 < PATH_MAX) {
|
||||
stpcpy(stpcpy(buf, a), b);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void RetryExecve(const char *prog, char **argv, char *const *envp) {
|
||||
if ((argv[0] = (char *)prog)) {
|
||||
STRACE("execve(%#s, %s) due to %s", prog, DescribeStringList(argv),
|
||||
_strerrno(errno));
|
||||
__sys_execve(prog, argv, envp);
|
||||
}
|
||||
}
|
||||
|
||||
static void SetupExecve(void) {
|
||||
g_execve.home = getenv("HOME");
|
||||
g_execve.tmpdir = getenv("TMPDIR");
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void InitExecve(void) {
|
||||
cosmo_once(&g_execve.once, SetupExecve);
|
||||
}
|
||||
|
||||
int sys_execve(const char *prog, char *const argv[], char *const envp[]) {
|
||||
|
||||
// try kernel
|
||||
// this also checks execute bit
|
||||
__sys_execve(prog, argv, envp);
|
||||
if (!(errno == ENOEXEC || (IsLinux() && errno == ELIBBAD_LINUX))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// allocate memory
|
||||
int argc;
|
||||
for (argc = 0; argv[argc];) ++argc;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
int nbytes = (argc + 4) * sizeof(char *);
|
||||
int ntotal = nbytes + PATH_MAX;
|
||||
if (__get_safe_size(ntotal, 4096) < ntotal) return enomem();
|
||||
char **shargs = alloca(nbytes);
|
||||
CheckLargeStackAllocation(shargs, nbytes);
|
||||
#pragma GCC pop_options
|
||||
|
||||
// try ape loader
|
||||
if (IsApeFile(prog)) {
|
||||
shargs[1] = (char *)"-";
|
||||
shargs[2] = (char *)prog;
|
||||
memcpy(shargs + 3, argv, (argc + 1) * sizeof(char *));
|
||||
RetryExecve("/usr/bin/ape", shargs, envp);
|
||||
char *buf = alloca(PATH_MAX);
|
||||
const char *name = "/.ape-" APE_VERSION_STR;
|
||||
InitExecve();
|
||||
RetryExecve(Join(g_execve.tmpdir, name, buf), shargs, envp);
|
||||
RetryExecve(Join(g_execve.home, name, buf), shargs, envp);
|
||||
RetryExecve(Join(".", name, buf), shargs, envp);
|
||||
}
|
||||
|
||||
// try bourne shell
|
||||
shargs[0] = _PATH_BSHELL;
|
||||
shargs[1] = (char *)prog;
|
||||
memcpy(shargs + 2, argv + 1, argc * sizeof(char *));
|
||||
RetryExecve(shargs[0], shargs, envp);
|
||||
|
||||
enoexec();
|
||||
return -1;
|
||||
}
|
97
libc/proc/execve.c
Normal file
97
libc/proc/execve.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*-*- 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/pledge.h"
|
||||
#include "libc/calls/pledge.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/likely.h"
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/runtime/zipos.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Replaces current process with program.
|
||||
*
|
||||
* On Windows, `argv` and `envp` can't contain binary strings. They need
|
||||
* to be valid UTF-8 in order to round-trip the WIN32 API, without being
|
||||
* corrupted.
|
||||
*
|
||||
* On Windows, only file descriptors 0, 1 and 2 can be passed to a child
|
||||
* process in such a way that allows them to be automatically discovered
|
||||
* when the child process initializes. Cosmpolitan currently treats your
|
||||
* other file descriptors as implicitly O_CLOEXEC.
|
||||
*
|
||||
* @param program will not be PATH searched, see commandv()
|
||||
* @param argv[0] is the name of the program to run
|
||||
* @param argv[1,n-2] optionally specify program arguments
|
||||
* @param argv[n-1] is NULL
|
||||
* @param envp[0,n-2] specifies "foo=bar" environment variables
|
||||
* @param envp[n-1] is NULL
|
||||
* @return doesn't return, or -1 w/ errno
|
||||
* @raise ETXTBSY if another process has `prog` open in write mode
|
||||
* @raise ENOEXEC if file is executable but not a valid format
|
||||
* @raise ENOMEM if remaining stack memory is insufficient
|
||||
* @raise EACCES if execute permission was denied
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int execve(const char *prog, char *const argv[], char *const envp[]) {
|
||||
int rc;
|
||||
struct ZiposUri uri;
|
||||
if (!prog || !argv || !envp ||
|
||||
(IsAsan() && (!__asan_is_valid_str(prog) || //
|
||||
!__asan_is_valid_strlist(argv) || //
|
||||
!__asan_is_valid_strlist(envp)))) {
|
||||
rc = efault();
|
||||
} else {
|
||||
STRACE("execve(%#s, %s, %s)", prog, DescribeStringList(argv),
|
||||
DescribeStringList(envp));
|
||||
rc = 0;
|
||||
if (IsLinux() && __execpromises && _weaken(sys_pledge_linux)) {
|
||||
rc = _weaken(sys_pledge_linux)(__execpromises, __pledge_mode);
|
||||
}
|
||||
if (!rc) {
|
||||
if (0 && _weaken(__zipos_parseuri) &&
|
||||
(_weaken(__zipos_parseuri)(prog, &uri) != -1)) {
|
||||
rc = _weaken(__zipos_open)(&uri, O_RDONLY | O_CLOEXEC);
|
||||
if (rc != -1) {
|
||||
const int zipFD = rc;
|
||||
strace_enabled(-1);
|
||||
rc = fexecve(zipFD, argv, envp);
|
||||
close(zipFD);
|
||||
strace_enabled(+1);
|
||||
}
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_execve(prog, argv, envp);
|
||||
} else {
|
||||
rc = sys_execve_nt(prog, argv, envp);
|
||||
}
|
||||
}
|
||||
}
|
||||
STRACE("execve(%#s) failed %d% m", prog, rc);
|
||||
return rc;
|
||||
}
|
12
libc/proc/execve.internal.h
Normal file
12
libc/proc/execve.internal.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void __execve_lock(void);
|
||||
void __execve_unlock(void);
|
||||
bool IsApeLoadable(char[8]);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_EXECVE_SYSV_H_ */
|
29
libc/proc/execvp.c
Normal file
29
libc/proc/execvp.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- 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/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
* Replaces process, with path search, using default environment.
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int execvp(const char *file, char *const argv[]) {
|
||||
return execvpe(file, argv, environ);
|
||||
}
|
83
libc/proc/execvpe.c
Normal file
83
libc/proc/execvpe.c
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*-*- 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/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Executes program, with path environment search.
|
||||
*
|
||||
* This function is a wrapper of the execve() system call that does path
|
||||
* resolution. The `PATH` environment variable is taken from your global
|
||||
* `environ` rather than the `envp` argument.
|
||||
*
|
||||
* @param prog is the program to launch
|
||||
* @param argv is [file,argv₁..argvₙ₋₁,NULL]
|
||||
* @param envp is ["key=val",...,NULL]
|
||||
* @return doesn't return on success, otherwise -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int execvpe(const char *prog, char *const argv[], char *const *envp) {
|
||||
size_t i;
|
||||
char *exe, **argv2;
|
||||
char pathbuf[PATH_MAX];
|
||||
|
||||
// validate memory
|
||||
if (IsAsan() &&
|
||||
(!__asan_is_valid_str(prog) || !__asan_is_valid_strlist(argv))) {
|
||||
return efault();
|
||||
}
|
||||
|
||||
if (strchr(prog, '/')) {
|
||||
return execve(prog, argv, envp);
|
||||
}
|
||||
|
||||
// resolve path of executable
|
||||
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// change argv[0] to resolved path if it's ambiguous
|
||||
// otherwise the program won't have much luck finding itself
|
||||
if (argv[0] && *prog != '/' && *exe == '/' && !strcmp(prog, argv[0])) {
|
||||
for (i = 0; argv[i++];) (void)0;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
int nbytes = i * sizeof(*argv);
|
||||
if (__get_safe_size(nbytes, 4096) < nbytes) return enomem();
|
||||
argv2 = alloca(nbytes);
|
||||
CheckLargeStackAllocation(argv2, nbytes);
|
||||
#pragma GCC pop_options
|
||||
memcpy(argv2, argv, nbytes);
|
||||
argv2[0] = exe;
|
||||
argv = argv2;
|
||||
}
|
||||
|
||||
// execute program
|
||||
// tail call shouldn't be possible
|
||||
return execve(exe, argv, envp);
|
||||
}
|
455
libc/proc/fork-nt.c
Normal file
455
libc/proc/fork-nt.c
Normal file
|
@ -0,0 +1,455 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/wincrash.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/directmap.internal.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/filemapflags.h"
|
||||
#include "libc/nt/enum/pageflags.h"
|
||||
#include "libc/nt/enum/startf.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/ipc.h"
|
||||
#include "libc/nt/memory.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/signals.h"
|
||||
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/limits.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.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"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
extern long __klog_handle;
|
||||
extern int64_t __wincrashearly;
|
||||
bool32 __onntconsoleevent(uint32_t);
|
||||
|
||||
static textwindows wontreturn void AbortFork(const char *func) {
|
||||
#ifdef SYSDEBUG
|
||||
kprintf("fork() %s() failed with win32 error %d\n", func, GetLastError());
|
||||
#endif
|
||||
TerminateThisProcess(SIGSTKFLT);
|
||||
}
|
||||
|
||||
static textwindows char16_t *ParseInt(char16_t *p, int64_t *x) {
|
||||
*x = 0;
|
||||
while (*p == ' ') p++;
|
||||
while ('0' <= *p && *p <= '9') {
|
||||
*x *= 10;
|
||||
*x += *p++ - '0';
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline textwindows ssize_t ForkIo(int64_t h, char *p, size_t n,
|
||||
bool32 (*f)()) {
|
||||
size_t i;
|
||||
uint32_t x;
|
||||
for (i = 0; i < n; i += x) {
|
||||
if (!f(h, p + i, n - i, &x, NULL)) {
|
||||
return __winerr();
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static dontinline textwindows bool ForkIo2(int64_t h, void *buf, size_t n,
|
||||
bool32 (*fn)(), const char *sf,
|
||||
bool ischild) {
|
||||
ssize_t rc = ForkIo(h, buf, n, fn);
|
||||
if (ischild) __tls_enabled_set(false); // prevent tls crash in kprintf
|
||||
NTTRACE("%s(%ld, %p, %'zu) → %'zd% m", sf, h, buf, n, rc);
|
||||
return rc != -1;
|
||||
}
|
||||
|
||||
static dontinline textwindows bool WriteAll(int64_t h, void *buf, size_t n) {
|
||||
bool ok;
|
||||
ok = ForkIo2(h, buf, n, WriteFile, "WriteFile", false);
|
||||
#ifndef NDEBUG
|
||||
if (ok) ok = ForkIo2(h, &n, sizeof(n), WriteFile, "WriteFile", false);
|
||||
#endif
|
||||
#ifdef SYSDEBUG
|
||||
if (!ok) {
|
||||
kprintf("failed to write %zu bytes to forked child: %d\n", n,
|
||||
GetLastError());
|
||||
}
|
||||
#endif
|
||||
// Sleep(10);
|
||||
return ok;
|
||||
}
|
||||
|
||||
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)) {
|
||||
AbortFork("ReadFile2");
|
||||
}
|
||||
if (got != n) {
|
||||
AbortFork("ReadFile_SIZE_CHECK");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static textwindows int64_t MapOrDie(uint32_t prot, uint64_t size) {
|
||||
int64_t h;
|
||||
for (;;) {
|
||||
if ((h = CreateFileMapping(-1, 0, prot, size >> 32, size, 0))) {
|
||||
return h;
|
||||
}
|
||||
if (GetLastError() == kNtErrorAccessDenied) {
|
||||
switch (prot) {
|
||||
case kNtPageExecuteWritecopy:
|
||||
prot = kNtPageWritecopy;
|
||||
continue;
|
||||
case kNtPageExecuteReadwrite:
|
||||
prot = kNtPageReadwrite;
|
||||
continue;
|
||||
case kNtPageExecuteRead:
|
||||
prot = kNtPageReadonly;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
AbortFork("MapOrDie");
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows void ViewOrDie(int64_t h, uint32_t access, size_t pos,
|
||||
size_t size, void *base) {
|
||||
TryAgain:
|
||||
if (!MapViewOfFileEx(h, access, pos >> 32, pos, size, base)) {
|
||||
if ((access & kNtFileMapExecute) &&
|
||||
GetLastError() == kNtErrorAccessDenied) {
|
||||
access &= ~kNtFileMapExecute;
|
||||
goto TryAgain;
|
||||
}
|
||||
AbortFork("ViewOrDie");
|
||||
}
|
||||
}
|
||||
|
||||
static __msabi textwindows int OnForkCrash(struct NtExceptionPointers *ep) {
|
||||
kprintf("error: fork() child crashed!%n"
|
||||
"\tExceptionCode = %#x%n"
|
||||
"\tRip = %x%n",
|
||||
ep->ExceptionRecord->ExceptionCode,
|
||||
ep->ContextRecord ? ep->ContextRecord->Rip : -1);
|
||||
TerminateThisProcess(SIGSTKFLT);
|
||||
}
|
||||
|
||||
textwindows void WinMainForked(void) {
|
||||
jmp_buf jb;
|
||||
int64_t reader;
|
||||
int64_t savetsc;
|
||||
char *addr, *shad;
|
||||
uint64_t size, upsize;
|
||||
struct MemoryInterval *maps;
|
||||
char16_t fvar[21 + 1 + 21 + 1];
|
||||
uint32_t i, varlen, oldprot, savepid;
|
||||
long mapcount, mapcapacity, specialz;
|
||||
|
||||
struct StdinRelay stdin;
|
||||
struct Fds *fds = __veil("r", &g_fds);
|
||||
stdin = fds->stdin;
|
||||
|
||||
// check to see if the process was actually forked
|
||||
// this variable should have the pipe handle numba
|
||||
varlen = GetEnvironmentVariable(u"_FORK", fvar, ARRAYLEN(fvar));
|
||||
if (!varlen || varlen >= ARRAYLEN(fvar)) return;
|
||||
NTTRACE("WinMainForked()");
|
||||
SetEnvironmentVariable(u"_FORK", NULL);
|
||||
#ifdef SYSDEBUG
|
||||
int64_t oncrash = AddVectoredExceptionHandler(1, (void *)OnForkCrash);
|
||||
#endif
|
||||
ParseInt(fvar, &reader);
|
||||
|
||||
// read the cpu state from the parent process & plus
|
||||
// read the list of mappings from the parent process
|
||||
// this is stored in a special secretive memory map!
|
||||
// read ExtendMemoryIntervals for further details :|
|
||||
maps = (void *)kMemtrackStart;
|
||||
ReadOrDie(reader, jb, sizeof(jb));
|
||||
ReadOrDie(reader, &mapcount, sizeof(_mmi.i));
|
||||
ReadOrDie(reader, &mapcapacity, sizeof(_mmi.n));
|
||||
specialz = ROUNDUP(mapcapacity * sizeof(_mmi.p[0]), kMemtrackGran);
|
||||
ViewOrDie(MapOrDie(kNtPageReadwrite, specialz), kNtFileMapWrite, 0, specialz,
|
||||
maps);
|
||||
ReadOrDie(reader, maps, mapcount * sizeof(_mmi.p[0]));
|
||||
if (IsAsan()) {
|
||||
shad = (char *)(((intptr_t)maps >> 3) + 0x7fff8000);
|
||||
size = ROUNDUP(specialz >> 3, FRAMESIZE);
|
||||
ViewOrDie(MapOrDie(kNtPageReadwrite, size), kNtFileMapWrite, 0, size, shad);
|
||||
ReadOrDie(reader, shad, (mapcount * sizeof(_mmi.p[0])) >> 3);
|
||||
}
|
||||
|
||||
// read the heap mappings from the parent process
|
||||
for (i = 0; i < mapcount; ++i) {
|
||||
addr = (char *)((uint64_t)maps[i].x << 16);
|
||||
size = maps[i].size;
|
||||
if ((maps[i].flags & MAP_TYPE) != MAP_SHARED) {
|
||||
upsize = ROUNDUP(size, FRAMESIZE);
|
||||
// we don't need to close the map handle because sys_mmap_nt
|
||||
// doesn't mark it inheritable across fork() for MAP_PRIVATE
|
||||
ViewOrDie((maps[i].h = MapOrDie(kNtPageExecuteReadwrite, upsize)),
|
||||
kNtFileMapWrite | kNtFileMapExecute, 0, upsize, addr);
|
||||
ReadOrDie(reader, addr, size);
|
||||
} else {
|
||||
// we can however safely inherit MAP_SHARED with zero copy
|
||||
ViewOrDie(maps[i].h,
|
||||
maps[i].readonlyfile ? kNtFileMapRead | kNtFileMapExecute
|
||||
: kNtFileMapWrite | kNtFileMapExecute,
|
||||
maps[i].offset, size, addr);
|
||||
}
|
||||
}
|
||||
|
||||
// read the .data and .bss program image sections
|
||||
savepid = __pid;
|
||||
savetsc = kStartTsc;
|
||||
ReadOrDie(reader, __data_start, __data_end - __data_start);
|
||||
ReadOrDie(reader, __bss_start, __bss_end - __bss_start);
|
||||
__pid = savepid;
|
||||
kStartTsc = savetsc;
|
||||
__threaded = false;
|
||||
__tls_index = 0;
|
||||
__tls_enabled_set(false);
|
||||
|
||||
// apply fixups and reapply memory protections
|
||||
_mmi.p = maps;
|
||||
_mmi.n = specialz / sizeof(_mmi.p[0]);
|
||||
for (i = 0; i < mapcount; ++i) {
|
||||
if (!VirtualProtect((void *)((uint64_t)maps[i].x << 16), maps[i].size,
|
||||
__prot2nt(maps[i].prot, maps[i].iscow), &oldprot)) {
|
||||
AbortFork("VirtualProtect");
|
||||
}
|
||||
}
|
||||
|
||||
// mitosis complete
|
||||
if (!CloseHandle(reader)) {
|
||||
AbortFork("CloseHandle");
|
||||
}
|
||||
|
||||
// rewrap the stdin named pipe hack
|
||||
// since the handles closed on fork
|
||||
fds->stdin = stdin;
|
||||
fds->p[0].handle = GetStdHandle(kNtStdInputHandle);
|
||||
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);
|
||||
#endif
|
||||
if (_weaken(__sig_init)) {
|
||||
_weaken(__sig_init)();
|
||||
}
|
||||
|
||||
// jump back into function below
|
||||
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;
|
||||
uint32_t op;
|
||||
char **args;
|
||||
int i, rc = -1;
|
||||
struct Proc *proc;
|
||||
struct CosmoTib *tib;
|
||||
char16_t pipename[64];
|
||||
int64_t reader, writer;
|
||||
struct NtStartupInfo startinfo;
|
||||
char *p, forkvar[6 + 21 + 1 + 21 + 1];
|
||||
struct NtProcessInformation procinfo;
|
||||
tib = __get_tls();
|
||||
ftrace_enabled(-1);
|
||||
strace_enabled(-1);
|
||||
if (!(proc = __proc_new())) return -1;
|
||||
if (!setjmp(jb)) {
|
||||
reader = CreateNamedPipe(__create_pipe_name(pipename), kNtPipeAccessInbound,
|
||||
kNtPipeTypeByte | kNtPipeReadmodeByte, 1, PIPE_BUF,
|
||||
PIPE_BUF, 0, &kNtIsInheritable);
|
||||
writer = CreateFile(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting, 0, 0);
|
||||
if (reader != -1 && writer != -1) {
|
||||
p = stpcpy(forkvar, "_FORK=");
|
||||
p = FormatUint64(p, reader);
|
||||
bzero(&startinfo, sizeof(startinfo));
|
||||
startinfo.cb = sizeof(struct NtStartupInfo);
|
||||
startinfo.dwFlags = kNtStartfUsestdhandles;
|
||||
startinfo.hStdInput = __getfdhandleactual(0);
|
||||
startinfo.hStdOutput = __getfdhandleactual(1);
|
||||
startinfo.hStdError = __getfdhandleactual(2);
|
||||
args = __argv;
|
||||
#ifdef SYSDEBUG
|
||||
// If --strace was passed to this program, then propagate it the
|
||||
// forked process since the flag was removed by __intercept_flag
|
||||
if (strace_enabled(0) > 0) {
|
||||
int n;
|
||||
for (n = 0; args[n];) ++n;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
int nbytes = (n + 2) * sizeof(char *);
|
||||
char **args2 = alloca(nbytes);
|
||||
CheckLargeStackAllocation(args2, nbytes);
|
||||
#pragma GCC pop_options
|
||||
for (i = 0; i < n; ++i) args2[i] = args[i];
|
||||
args2[i++] = "--strace";
|
||||
args2[i] = 0;
|
||||
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);
|
||||
if (spawnrc != -1) {
|
||||
CloseHandle(procinfo.hThread);
|
||||
ok = WriteAll(writer, jb, sizeof(jb)) &&
|
||||
WriteAll(writer, &_mmi.i, sizeof(_mmi.i)) &&
|
||||
WriteAll(writer, &_mmi.n, sizeof(_mmi.n)) &&
|
||||
WriteAll(writer, _mmi.p, _mmi.i * sizeof(_mmi.p[0]));
|
||||
if (IsAsan() && ok) {
|
||||
ok = WriteAll(writer, (char *)(((intptr_t)_mmi.p >> 3) + 0x7fff8000),
|
||||
(_mmi.i * sizeof(_mmi.p[0])) >> 3);
|
||||
}
|
||||
for (i = 0; i < _mmi.i && ok; ++i) {
|
||||
if ((_mmi.p[i].flags & MAP_TYPE) != MAP_SHARED) {
|
||||
char *p = (char *)((uint64_t)_mmi.p[i].x << 16);
|
||||
// XXX: forking destroys thread guard pages currently
|
||||
VirtualProtect(
|
||||
p, _mmi.p[i].size,
|
||||
__prot2nt(_mmi.p[i].prot | PROT_READ, _mmi.p[i].iscow), &op);
|
||||
ok = WriteAll(writer, p, _mmi.p[i].size);
|
||||
}
|
||||
}
|
||||
if (ok) ok = WriteAll(writer, __data_start, __data_end - __data_start);
|
||||
if (ok) ok = WriteAll(writer, __bss_start, __bss_end - __bss_start);
|
||||
if (ok) {
|
||||
if (!CloseHandle(writer)) ok = false;
|
||||
writer = -1;
|
||||
}
|
||||
if (ok) {
|
||||
proc->wasforked = true;
|
||||
proc->handle = procinfo.hProcess;
|
||||
rc = proc->pid = procinfo.dwProcessId;
|
||||
__proc_add(proc);
|
||||
} else {
|
||||
TerminateProcess(procinfo.hProcess, SIGKILL);
|
||||
CloseHandle(procinfo.hProcess);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reader != -1) CloseHandle(reader);
|
||||
if (writer != -1) CloseHandle(writer);
|
||||
if (rc == -1 && errno != ENOMEM) {
|
||||
eagain(); // posix fork() only specifies two errors
|
||||
}
|
||||
} else {
|
||||
rc = 0;
|
||||
// re-apply code morphing for thread-local storage
|
||||
__set_tls(tib);
|
||||
__morph_tls();
|
||||
__tls_enabled_set(true);
|
||||
// clear pending signals
|
||||
tib->tib_sigpending = 0;
|
||||
__sig.pending = 0;
|
||||
// re-enable threads
|
||||
__enable_threads();
|
||||
// re-apply code morphing for function tracing
|
||||
if (ftrace_stackdigs) {
|
||||
_weaken(__hook)(_weaken(ftrace_hook), _weaken(GetSymbolTable)());
|
||||
}
|
||||
// reset alarms
|
||||
if (_weaken(__itimer_reset)) {
|
||||
_weaken(__itimer_reset)();
|
||||
}
|
||||
}
|
||||
if (rc == -1) {
|
||||
__proc_free(proc);
|
||||
}
|
||||
ftrace_enabled(+1);
|
||||
strace_enabled(+1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
71
libc/proc/fork-sysv.c
Normal file
71
libc/proc/fork-sysv.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 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/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int sys_fork(void) {
|
||||
#ifdef __x86_64__
|
||||
|
||||
axdx_t ad;
|
||||
int ax, dx;
|
||||
ad = __sys_fork();
|
||||
ax = ad.ax;
|
||||
dx = ad.dx;
|
||||
if (IsXnu() && ax != -1) {
|
||||
// eax always returned with childs pid
|
||||
// edx is 0 for parent and 1 for child
|
||||
ax &= dx - 1;
|
||||
}
|
||||
return ax;
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
if (IsLinux()) {
|
||||
int flags = 17; // SIGCHLD
|
||||
void *child_stack = 0;
|
||||
void *parent_tidptr = 0;
|
||||
void *newtls = 0;
|
||||
void *child_tidptr = 0;
|
||||
register long r0 asm("x0") = (long)flags;
|
||||
register long r1 asm("x1") = (long)child_stack;
|
||||
register long r2 asm("x2") = (long)parent_tidptr;
|
||||
register long r3 asm("x3") = (long)newtls;
|
||||
register long r4 asm("x4") = (long)child_tidptr;
|
||||
register int res_x0 asm("x0");
|
||||
asm volatile("mov\tx8,%1\n\t"
|
||||
"svc\t0"
|
||||
: "=r"(res_x0)
|
||||
: "i"(220), "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4)
|
||||
: "x8", "x16", "memory");
|
||||
return _sysret(res_x0);
|
||||
} else if (__syslib) {
|
||||
return _sysret(__syslib->__fork());
|
||||
} else {
|
||||
return enosys();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
return enosys();
|
||||
|
||||
#endif
|
||||
}
|
110
libc/proc/fork.c
Normal file
110
libc/proc/fork.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/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"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
int _fork(uint32_t dwCreationFlags) {
|
||||
struct Dll *e;
|
||||
struct CosmoTib *tib;
|
||||
int ax, dx, tid, parent;
|
||||
struct PosixThread *me, *other;
|
||||
(void)parent;
|
||||
BLOCK_SIGNALS;
|
||||
BLOCK_CANCELLATIONS;
|
||||
if (IsWindows()) __proc_lock();
|
||||
if (__threaded && _weaken(_pthread_onfork_prepare)) {
|
||||
_weaken(_pthread_onfork_prepare)();
|
||||
}
|
||||
if (!IsWindows()) {
|
||||
ax = sys_fork();
|
||||
} else {
|
||||
ax = sys_fork_nt(dwCreationFlags);
|
||||
}
|
||||
if (!ax) {
|
||||
if (!IsWindows()) {
|
||||
dx = sys_getpid().ax;
|
||||
} 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,
|
||||
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)));
|
||||
if (__threaded && _weaken(_pthread_onfork_child)) {
|
||||
_weaken(_pthread_onfork_child)();
|
||||
}
|
||||
if (IsWindows()) __proc_wipe();
|
||||
STRACE("fork() → 0 (child of %d)", parent);
|
||||
} else {
|
||||
if (__threaded && _weaken(_pthread_onfork_parent)) {
|
||||
_weaken(_pthread_onfork_parent)();
|
||||
}
|
||||
if (IsWindows()) __proc_unlock();
|
||||
STRACE("fork() → %d% m", ax);
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_SIGNALS;
|
||||
return ax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new process.
|
||||
*
|
||||
* @return 0 to child, child pid to parent, or -1 w/ errno
|
||||
* @raise EAGAIN if `RLIMIT_NPROC` was exceeded or system lacked resources
|
||||
* @raise ENOMEM if we require more vespene gas
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
*/
|
||||
int fork(void) {
|
||||
return _fork(0);
|
||||
}
|
76
libc/proc/kill-nt.c
Normal file
76
libc/proc/kill-nt.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 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/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/sysv/errfuns.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) {
|
||||
int rc;
|
||||
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);
|
||||
}
|
||||
|
||||
__proc_lock();
|
||||
rc = sys_kill_nt_impl(pid, sig);
|
||||
__proc_unlock();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
56
libc/proc/kill.c
Normal file
56
libc/proc/kill.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*-*- 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/strace.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Sends signal to process.
|
||||
*
|
||||
* The impact of this action can be terminating the process, or
|
||||
* interrupting it to request something happen.
|
||||
*
|
||||
* @param pid can be:
|
||||
* >0 signals one process by id
|
||||
* =0 signals all processes in current process group
|
||||
* -1 signals all processes possible (except init)
|
||||
* <-1 signals all processes in -pid process group
|
||||
* @param sig can be:
|
||||
* >0 can be SIGINT, SIGTERM, SIGKILL, SIGUSR1, etc.
|
||||
* =0 checks both if pid exists and we can signal it
|
||||
* @return 0 if something was accomplished, or -1 w/ errno
|
||||
* @raise ESRCH if `pid` couldn't be found
|
||||
* @raise EPERM if lacked permission to signal process
|
||||
* @raise EPERM if pledge() is in play without `proc` promised
|
||||
* @raise EINVAL if the provided `sig` is invalid or unsupported
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int kill(int pid, int sig) {
|
||||
int rc;
|
||||
if (!IsWindows()) {
|
||||
rc = sys_kill(pid, sig, 1);
|
||||
} else {
|
||||
rc = sys_kill_nt(pid, sig);
|
||||
}
|
||||
STRACE("kill(%d, %G) → %d% m", pid, sig, rc);
|
||||
return rc;
|
||||
}
|
30
libc/proc/killpg.c
Normal file
30
libc/proc/killpg.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*-*- 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/dce.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Sends signal to process group.
|
||||
*/
|
||||
int killpg(int pgrp, int sig) {
|
||||
if (!(0 < sig && sig < NSIG)) return einval();
|
||||
if (pgrp == 1 || pgrp < 0) return esrch();
|
||||
return kill(IsWindows() ? pgrp : -pgrp, sig);
|
||||
}
|
128
libc/proc/ntspawn.c
Normal file
128
libc/proc/ntspawn.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*-*- 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) {
|
||||
int64_t handle;
|
||||
int i, e, rc = -1;
|
||||
struct SpawnBlock *block = 0;
|
||||
char16_t prog16[PATH_MAX + 5], *p;
|
||||
char16_t suffixes[][5] = {u"", u".com", u".exe"};
|
||||
|
||||
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) {
|
||||
p = prog16 + strlen16(prog16);
|
||||
for (i = 0; i < ARRAYLEN(suffixes); ++i) {
|
||||
if (suffixes[i][0] && endswith16(prog16, suffixes[i])) {
|
||||
p -= strlen16(suffixes[i]);
|
||||
*p = 0;
|
||||
} else {
|
||||
strcpy16(p, suffixes[i]);
|
||||
}
|
||||
e = errno;
|
||||
if (CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes,
|
||||
opt_lpThreadAttributes, bInheritHandles,
|
||||
dwCreationFlags | kNtCreateUnicodeEnvironment |
|
||||
kNtInheritParentAffinity,
|
||||
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
|
||||
opt_out_lpProcessInformation)) {
|
||||
rc = 0;
|
||||
break;
|
||||
} else if (errno == ENOENT) {
|
||||
errno = e;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (GetLastError() == kNtErrorSharingViolation) {
|
||||
etxtbsy();
|
||||
}
|
||||
if (block) UnmapViewOfFile(block);
|
||||
if (handle) CloseHandle(handle);
|
||||
return __fix_enotdir(rc, prog16);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
21
libc/proc/ntspawn.h
Normal file
21
libc/proc/ntspawn.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_
|
||||
#include "libc/limits.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 *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_NTSPAWN_H_ */
|
57
libc/proc/paginate.c
Normal file
57
libc/proc/paginate.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 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/temp.h"
|
||||
|
||||
/**
|
||||
* Displays wall of text in terminal with pagination.
|
||||
*/
|
||||
void __paginate(int fd, const char *s) {
|
||||
int tfd, pid;
|
||||
char *args[3] = {0};
|
||||
char tmppath[] = "/tmp/paginate.XXXXXX";
|
||||
char progpath[PATH_MAX];
|
||||
if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) &&
|
||||
((args[0] = commandv("less", progpath, sizeof(progpath))) ||
|
||||
(args[0] = commandv("more", progpath, sizeof(progpath))))) {
|
||||
if ((tfd = mkstemp(tmppath)) != -1) {
|
||||
write(tfd, s, strlen(s));
|
||||
close(tfd);
|
||||
args[1] = tmppath;
|
||||
if ((pid = fork()) != -1) {
|
||||
putenv("LC_ALL=C.UTF-8");
|
||||
putenv("LESSCHARSET=utf-8");
|
||||
putenv("LESS=-RS");
|
||||
if (!pid) {
|
||||
execv(args[0], args);
|
||||
_Exit(127);
|
||||
}
|
||||
waitpid(pid, 0, 0);
|
||||
unlink(tmppath);
|
||||
return;
|
||||
}
|
||||
unlink(tmppath);
|
||||
}
|
||||
}
|
||||
write(fd, s, strlen(s));
|
||||
}
|
474
libc/proc/posix_spawn.c
Normal file
474
libc/proc/posix_spawn.c
Normal file
|
@ -0,0 +1,474 @@
|
|||
/*-*- 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/posix_spawn.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/struct/rlimit.internal.h"
|
||||
#include "libc/calls/struct/rusage.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
#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/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/enum/startf.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/processinformation.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/limits.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifndef SYSDEBUG
|
||||
#define read sys_read
|
||||
#define write sys_write
|
||||
#define close sys_close
|
||||
#define pipe2 sys_pipe2
|
||||
#define getgid sys_getgid
|
||||
#define setgid sys_setgid
|
||||
#define getuid sys_getuid
|
||||
#define setuid sys_setuid
|
||||
#define setsid sys_setsid
|
||||
#define setpgid sys_setpgid
|
||||
#define fcntl __sys_fcntl
|
||||
#define wait4 __sys_wait4
|
||||
#define openat __sys_openat
|
||||
#define setrlimit sys_setrlimit
|
||||
#define sigprocmask sys_sigprocmask
|
||||
#endif
|
||||
|
||||
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 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 posix_spawn_windows_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;
|
||||
}
|
||||
}
|
||||
|
||||
// reserve object for tracking proces
|
||||
struct Proc *proc;
|
||||
__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);
|
||||
__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();
|
||||
errno = e;
|
||||
return err;
|
||||
}
|
||||
|
||||
// return the result
|
||||
CloseHandle(procinfo.hThread);
|
||||
proc->pid = procinfo.dwProcessId;
|
||||
proc->handle = procinfo.hProcess;
|
||||
if (pid) *pid = proc->pid;
|
||||
__proc_lock();
|
||||
__proc_add(proc);
|
||||
__proc_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *DescribePid(char buf[12], int err, int *pid) {
|
||||
if (err) return "n/a";
|
||||
if (!pid) return "NULL";
|
||||
FormatInt32(buf, *pid);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static textwindows dontinline errno_t posix_spawn_windows(
|
||||
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;
|
||||
if (!path || !argv ||
|
||||
(IsAsan() && (!__asan_is_valid_str(path) || //
|
||||
!__asan_is_valid_strlist(argv) || //
|
||||
(envp && !__asan_is_valid_strlist(envp))))) {
|
||||
err = EFAULT;
|
||||
} else {
|
||||
err = posix_spawn_windows_impl(pid, path, file_actions, attrp, argv, envp);
|
||||
}
|
||||
STRACE("posix_spawn([%s], %#s, %s, %s) → %s",
|
||||
DescribePid(alloca(12), err, pid), path, DescribeStringList(argv),
|
||||
DescribeStringList(envp), !err ? "0" : _strerrno(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns process, the POSIX way.
|
||||
*
|
||||
* This provides superior process creation performance across systems
|
||||
*
|
||||
* Processes are normally spawned by calling fork() and execve(), but
|
||||
* that goes slow on Windows if the caller has allocated a nontrivial
|
||||
* number of memory mappings, all of which need to be copied into the
|
||||
* forked child, only to be destroyed a moment later. On UNIX systems
|
||||
* fork() bears a similar cost that's 100x less bad, which is copying
|
||||
* the page tables. So what this implementation does is on Windows it
|
||||
* calls CreateProcess() directly and on UNIX it uses vfork() if it's
|
||||
* possible (XNU and OpenBSD don't have it). On UNIX this API has the
|
||||
* benefit of avoiding the footguns of using vfork() directly because
|
||||
* this implementation will ensure signal handlers can't be called in
|
||||
* the child process since that'd likely corrupt the parent's memory.
|
||||
* On systems with a real vfork() implementation, the execve() status
|
||||
* code is returned by this function via shared memory; otherwise, it
|
||||
* gets passed via a temporary pipe (on systems like QEmu, Blink, and
|
||||
* XNU/OpenBSD) whose support is auto-detected at runtime.
|
||||
*
|
||||
* @param pid if non-null shall be set to child pid on success
|
||||
* @param path is resolved path of program which is not `$PATH` searched
|
||||
* @param file_actions specifies close(), dup2(), and open() operations
|
||||
* @param attrp specifies signal masks, user ids, scheduling, etc.
|
||||
* @param envp is environment variables, or `environ` if null
|
||||
* @return 0 on success or error number on failure
|
||||
* @raise ETXTBSY if another process has `path` open in write mode
|
||||
* @raise ENOEXEC if file is executable but not a valid format
|
||||
* @raise ENOMEM if remaining stack memory is insufficient
|
||||
* @raise EACCES if execute permission was denied
|
||||
* @see posix_spawnp() for `$PATH` searching
|
||||
* @returnserrno
|
||||
* @tlsrequired
|
||||
* @threadsafe
|
||||
*/
|
||||
errno_t posix_spawn(int *pid, const char *path,
|
||||
const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *attrp, char *const argv[],
|
||||
char *const envp[]) {
|
||||
if (IsWindows()) {
|
||||
return posix_spawn_windows(pid, path, file_actions, attrp, argv, envp);
|
||||
}
|
||||
int pfds[2];
|
||||
bool use_pipe;
|
||||
volatile int status = 0;
|
||||
sigset_t blockall, oldmask;
|
||||
int child, res, cs, e = errno;
|
||||
volatile bool can_clobber = false;
|
||||
sigfillset(&blockall);
|
||||
sigprocmask(SIG_SETMASK, &blockall, &oldmask);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
||||
if ((use_pipe = !atomic_load_explicit(&has_vfork, memory_order_acquire))) {
|
||||
if (pipe2(pfds, O_CLOEXEC)) {
|
||||
res = errno;
|
||||
goto ParentFailed;
|
||||
}
|
||||
}
|
||||
if (!(child = vfork())) {
|
||||
can_clobber = true;
|
||||
sigset_t *childmask;
|
||||
bool lost_cloexec = 0;
|
||||
struct sigaction dfl = {0};
|
||||
short flags = attrp && *attrp ? (*attrp)->flags : 0;
|
||||
if (use_pipe) close(pfds[0]);
|
||||
for (int sig = 1; sig < _NSIG; sig++) {
|
||||
if (__sighandrvas[sig] != (long)SIG_DFL &&
|
||||
(__sighandrvas[sig] != (long)SIG_IGN ||
|
||||
((flags & POSIX_SPAWN_SETSIGDEF) &&
|
||||
sigismember(&(*attrp)->sigdefault, sig) == 1))) {
|
||||
sigaction(sig, &dfl, 0);
|
||||
}
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETSID) {
|
||||
setsid();
|
||||
}
|
||||
if ((flags & POSIX_SPAWN_SETPGROUP) && setpgid(0, (*attrp)->pgroup)) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
if ((flags & POSIX_SPAWN_RESETIDS) && setgid(getgid())) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
if ((flags & POSIX_SPAWN_RESETIDS) && setuid(getuid())) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
if (file_actions) {
|
||||
struct _posix_faction *a;
|
||||
for (a = *file_actions; a; a = a->next) {
|
||||
if (use_pipe && pfds[1] == a->fildes) {
|
||||
int p2;
|
||||
if ((p2 = dup(pfds[1])) == -1) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
lost_cloexec = true;
|
||||
close(pfds[1]);
|
||||
pfds[1] = p2;
|
||||
}
|
||||
switch (a->action) {
|
||||
case _POSIX_SPAWN_CLOSE:
|
||||
if (close(a->fildes)) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
break;
|
||||
case _POSIX_SPAWN_DUP2:
|
||||
if (dup2(a->fildes, a->newfildes) == -1) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
break;
|
||||
case _POSIX_SPAWN_OPEN: {
|
||||
int t;
|
||||
if ((t = openat(AT_FDCWD, a->path, a->oflag, a->mode)) == -1) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
if (t != a->fildes) {
|
||||
if (dup2(t, a->fildes) == -1) {
|
||||
close(t);
|
||||
goto ChildFailed;
|
||||
}
|
||||
if (close(t)) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (IsLinux() || IsFreebsd() || IsNetbsd()) {
|
||||
if (flags & POSIX_SPAWN_SETSCHEDULER) {
|
||||
if (sched_setscheduler(0, (*attrp)->schedpolicy,
|
||||
&(*attrp)->schedparam) == -1) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETSCHEDPARAM) {
|
||||
if (sched_setparam(0, &(*attrp)->schedparam)) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETRLIMIT) {
|
||||
for (int rez = 0; rez <= ARRAYLEN((*attrp)->rlim); ++rez) {
|
||||
if ((*attrp)->rlimset & (1u << rez)) {
|
||||
if (setrlimit(rez, (*attrp)->rlim + rez)) {
|
||||
goto ChildFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lost_cloexec) {
|
||||
fcntl(pfds[1], F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETSIGMASK) {
|
||||
childmask = &(*attrp)->sigmask;
|
||||
} else {
|
||||
childmask = &oldmask;
|
||||
}
|
||||
sigprocmask(SIG_SETMASK, childmask, 0);
|
||||
if (!envp) envp = environ;
|
||||
execve(path, argv, envp);
|
||||
ChildFailed:
|
||||
res = errno;
|
||||
if (!use_pipe) {
|
||||
status = res;
|
||||
} else {
|
||||
write(pfds[1], &res, sizeof(res));
|
||||
}
|
||||
_Exit(127);
|
||||
}
|
||||
if (use_pipe) {
|
||||
close(pfds[1]);
|
||||
}
|
||||
if (child != -1) {
|
||||
if (!use_pipe) {
|
||||
res = status;
|
||||
} else {
|
||||
if (can_clobber) {
|
||||
atomic_store_explicit(&has_vfork, true, memory_order_release);
|
||||
}
|
||||
res = 0;
|
||||
read(pfds[0], &res, sizeof(res));
|
||||
}
|
||||
if (!res) {
|
||||
if (pid) *pid = child;
|
||||
} else {
|
||||
wait4(child, 0, 0, 0);
|
||||
}
|
||||
} else {
|
||||
res = errno;
|
||||
}
|
||||
if (use_pipe) {
|
||||
close(pfds[0]);
|
||||
}
|
||||
ParentFailed:
|
||||
sigprocmask(SIG_SETMASK, &oldmask, 0);
|
||||
pthread_setcancelstate(cs, 0);
|
||||
errno = e;
|
||||
return res;
|
||||
}
|
56
libc/proc/posix_spawn.h
Normal file
56
libc/proc/posix_spawn.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_STDIO_SPAWN_H_
|
||||
#define COSMOPOLITAN_LIBC_STDIO_SPAWN_H_
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/struct/sched_param.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
|
||||
#define POSIX_SPAWN_USEVFORK 0
|
||||
#define POSIX_SPAWN_RESETIDS 1
|
||||
#define POSIX_SPAWN_SETPGROUP 2
|
||||
#define POSIX_SPAWN_SETSIGDEF 4
|
||||
#define POSIX_SPAWN_SETSIGMASK 8
|
||||
#define POSIX_SPAWN_SETSCHEDPARAM 16
|
||||
#define POSIX_SPAWN_SETSCHEDULER 32
|
||||
#define POSIX_SPAWN_SETSID 128
|
||||
#define POSIX_SPAWN_SETRLIMIT 256
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
typedef struct _posix_spawna *posix_spawnattr_t;
|
||||
typedef struct _posix_faction *posix_spawn_file_actions_t;
|
||||
|
||||
int posix_spawn(int *, const char *, const posix_spawn_file_actions_t *,
|
||||
const posix_spawnattr_t *, char *const[], char *const[]);
|
||||
int posix_spawnp(int *, const char *, const posix_spawn_file_actions_t *,
|
||||
const posix_spawnattr_t *, char *const[], char *const[]);
|
||||
|
||||
int posix_spawn_file_actions_init(posix_spawn_file_actions_t *);
|
||||
int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *);
|
||||
int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *, int);
|
||||
int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *, int, int);
|
||||
int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *, int,
|
||||
const char *, int, unsigned);
|
||||
|
||||
int posix_spawnattr_init(posix_spawnattr_t *);
|
||||
int posix_spawnattr_destroy(posix_spawnattr_t *);
|
||||
int posix_spawnattr_getflags(const posix_spawnattr_t *, short *);
|
||||
int posix_spawnattr_setflags(posix_spawnattr_t *, short);
|
||||
int posix_spawnattr_getpgroup(const posix_spawnattr_t *, int *);
|
||||
int posix_spawnattr_setpgroup(posix_spawnattr_t *, int);
|
||||
int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *, int *);
|
||||
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *, int);
|
||||
int posix_spawnattr_getschedparam(const posix_spawnattr_t *,
|
||||
struct sched_param *);
|
||||
int posix_spawnattr_setschedparam(posix_spawnattr_t *,
|
||||
const struct sched_param *);
|
||||
int posix_spawnattr_getsigmask(const posix_spawnattr_t *, sigset_t *);
|
||||
int posix_spawnattr_setsigmask(posix_spawnattr_t *, const sigset_t *);
|
||||
int posix_spawnattr_getsigdefault(const posix_spawnattr_t *, sigset_t *);
|
||||
int posix_spawnattr_setsigdefault(posix_spawnattr_t *, const sigset_t *);
|
||||
int posix_spawnattr_getrlimit(const posix_spawnattr_t *, int, struct rlimit *);
|
||||
int posix_spawnattr_setrlimit(posix_spawnattr_t *, int, const struct rlimit *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_STDIO_SPAWN_H_ */
|
45
libc/proc/posix_spawn.internal.h
Normal file
45
libc/proc/posix_spawn.internal.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/struct/sched_param.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
|
||||
#define _POSIX_SPAWN_CLOSE 1
|
||||
#define _POSIX_SPAWN_DUP2 2
|
||||
#define _POSIX_SPAWN_OPEN 3
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct _posix_spawna {
|
||||
short flags;
|
||||
bool schedparam_isset;
|
||||
bool schedpolicy_isset;
|
||||
int pgroup;
|
||||
int rlimset;
|
||||
int schedpolicy;
|
||||
struct sched_param schedparam;
|
||||
sigset_t sigmask;
|
||||
sigset_t sigdefault;
|
||||
struct rlimit rlim[16];
|
||||
};
|
||||
|
||||
struct _posix_faction {
|
||||
struct _posix_faction *next;
|
||||
int action;
|
||||
int fildes;
|
||||
int oflag;
|
||||
union {
|
||||
int newfildes;
|
||||
unsigned mode;
|
||||
};
|
||||
char *path;
|
||||
};
|
||||
|
||||
int __posix_spawn_add_file_action(posix_spawn_file_actions_t *,
|
||||
struct _posix_faction);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_ */
|
32
libc/proc/posix_spawn_add_file_action.c
Normal file
32
libc/proc/posix_spawn_add_file_action.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- 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/errno.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
int __posix_spawn_add_file_action(posix_spawn_file_actions_t *l,
|
||||
struct _posix_faction a) {
|
||||
struct _posix_faction *ap;
|
||||
if (!(ap = malloc(sizeof(*ap)))) return ENOMEM;
|
||||
*ap = a;
|
||||
while (*l) l = &(*l)->next;
|
||||
*l = ap;
|
||||
return 0;
|
||||
}
|
41
libc/proc/posix_spawn_file_actions_addclose.c
Normal file
41
libc/proc/posix_spawn_file_actions_addclose.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/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Add a close action to object.
|
||||
*
|
||||
* @param file_actions was initialized by posix_spawn_file_actions_init()
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ENOMEM if we require more vespene gas
|
||||
* @raise EBADF if `fildes` is negative
|
||||
*/
|
||||
int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions,
|
||||
int fildes) {
|
||||
if (fildes < 0) return EBADF;
|
||||
if (IsWindows() && fildes > 2) return 0;
|
||||
return __posix_spawn_add_file_action(file_actions,
|
||||
(struct _posix_faction){
|
||||
.action = _POSIX_SPAWN_CLOSE,
|
||||
.fildes = fildes,
|
||||
});
|
||||
}
|
43
libc/proc/posix_spawn_file_actions_adddup2.c
Normal file
43
libc/proc/posix_spawn_file_actions_adddup2.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*-*- 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/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Add a dup2 action to object.
|
||||
*
|
||||
* @param file_actions was initialized by posix_spawn_file_actions_init()
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ENOMEM if we require more vespene gas
|
||||
* @raise EBADF if 'fildes' or `newfildes` is negative
|
||||
* @raise ENOTSUP if `newfildes` isn't 0, 1, or 2 on Windows
|
||||
*/
|
||||
int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions,
|
||||
int fildes, int newfildes) {
|
||||
if (fildes < 0 || newfildes < 0) return EBADF;
|
||||
if (IsWindows() && newfildes > 2) return ENOTSUP;
|
||||
return __posix_spawn_add_file_action(file_actions,
|
||||
(struct _posix_faction){
|
||||
.action = _POSIX_SPAWN_DUP2,
|
||||
.fildes = fildes,
|
||||
.newfildes = newfildes,
|
||||
});
|
||||
}
|
51
libc/proc/posix_spawn_file_actions_addopen.c
Normal file
51
libc/proc/posix_spawn_file_actions_addopen.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*-*- 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/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Add an open action to object.
|
||||
*
|
||||
* @param file_actions was initialized by posix_spawn_file_actions_init()
|
||||
* @param fildes is what open() result gets duplicated to
|
||||
* @param path will be safely copied
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ENOMEM if we require more vespene gas
|
||||
* @raise EBADF if `fildes` is negative
|
||||
* @raise ENOTSUP if `fildes` isn't 0, 1, or 2 on Windows
|
||||
*/
|
||||
int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *file_actions,
|
||||
int fildes, const char *path, int oflag,
|
||||
unsigned mode) {
|
||||
char *path2;
|
||||
if (fildes < 0) return EBADF;
|
||||
if (IsWindows() && fildes > 2) return ENOTSUP;
|
||||
if (!(path2 = strdup(path))) return ENOMEM;
|
||||
return __posix_spawn_add_file_action(file_actions,
|
||||
(struct _posix_faction){
|
||||
.action = _POSIX_SPAWN_OPEN,
|
||||
.fildes = fildes,
|
||||
.path = path2,
|
||||
.oflag = oflag,
|
||||
.mode = mode,
|
||||
});
|
||||
}
|
39
libc/proc/posix_spawn_file_actions_destroy.c
Normal file
39
libc/proc/posix_spawn_file_actions_destroy.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*-*- 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/mem/mem.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Destroys posix_spawn() file actions list.
|
||||
*
|
||||
* This function is safe to call multiple times.
|
||||
*
|
||||
* @param file_actions was initialized by posix_spawn_file_actions_init()
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions) {
|
||||
if (*file_actions) {
|
||||
posix_spawn_file_actions_destroy(&(*file_actions)->next);
|
||||
free((*file_actions)->path);
|
||||
free(*file_actions);
|
||||
*file_actions = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
33
libc/proc/posix_spawn_file_actions_init.c
Normal file
33
libc/proc/posix_spawn_file_actions_init.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*-*- 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/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Initializes posix_spawn() file actions list.
|
||||
*
|
||||
* File actions get applied in the same order as they're registered.
|
||||
*
|
||||
* @param file_actions will need posix_spawn_file_actions_destroy()
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions) {
|
||||
*file_actions = 0;
|
||||
return 0;
|
||||
}
|
36
libc/proc/posix_spawnattr_destroy.c
Normal file
36
libc/proc/posix_spawnattr_destroy.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*-*- 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/mem/mem.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
|
||||
/**
|
||||
* Destroys posix_spawn() attributes object.
|
||||
*
|
||||
* This function is safe to call multiple times.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_destroy(posix_spawnattr_t *attr) {
|
||||
if (*attr) {
|
||||
free(*attr);
|
||||
*attr = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
31
libc/proc/posix_spawnattr_getflags.c
Normal file
31
libc/proc/posix_spawnattr_getflags.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*-*- 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/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Gets posix_spawn() flags.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) {
|
||||
*flags = (*attr)->flags;
|
||||
return 0;
|
||||
}
|
32
libc/proc/posix_spawnattr_getpgroup.c
Normal file
32
libc/proc/posix_spawnattr_getpgroup.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- 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/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Gets process group id associated with attributes.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param pgroup receives the result on success
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, int *pgroup) {
|
||||
*pgroup = (*attr)->pgroup;
|
||||
return 0;
|
||||
}
|
44
libc/proc/posix_spawnattr_getrlimit.c
Normal file
44
libc/proc/posix_spawnattr_getrlimit.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*-*- 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/struct/rlimit.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Gets resource limit for spawned process.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EINVAL if `resource` is invalid or unsupported by host
|
||||
* @raise ENOENT if `resource` is absent
|
||||
*/
|
||||
int posix_spawnattr_getrlimit(const posix_spawnattr_t *attr, int resource,
|
||||
struct rlimit *rlim) {
|
||||
if ((0 <= resource && resource < ARRAYLEN((*attr)->rlim))) {
|
||||
if (((*attr)->rlimset & (1u << resource))) {
|
||||
*rlim = (*attr)->rlim[resource];
|
||||
return 0;
|
||||
} else {
|
||||
return ENOENT;
|
||||
}
|
||||
} else {
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
34
libc/proc/posix_spawnattr_getschedparam.c
Normal file
34
libc/proc/posix_spawnattr_getschedparam.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*-*- 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/struct/sched_param.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Gets scheduler parameter.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedparam receives the result
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr,
|
||||
struct sched_param *schedparam) {
|
||||
*schedparam = (*attr)->schedparam;
|
||||
return 0;
|
||||
}
|
33
libc/proc/posix_spawnattr_getschedpolicy.c
Normal file
33
libc/proc/posix_spawnattr_getschedpolicy.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*-*- 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/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Gets scheduler policy that'll be used for spawned process.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedpolicy receives the result
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr,
|
||||
int *schedpolicy) {
|
||||
*schedpolicy = (*attr)->schedpolicy;
|
||||
return 0;
|
||||
}
|
32
libc/proc/posix_spawnattr_getsigdefault.c
Normal file
32
libc/proc/posix_spawnattr_getsigdefault.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- 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/struct/sigset.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Retrieves which signals will be restored to `SIG_DFL`.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr,
|
||||
sigset_t *sigdefault) {
|
||||
*sigdefault = (*attr)->sigdefault;
|
||||
return 0;
|
||||
}
|
36
libc/proc/posix_spawnattr_getsigmask.c
Normal file
36
libc/proc/posix_spawnattr_getsigmask.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*-*- 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/struct/sigset.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Gets signal mask for sigprocmask() in child process.
|
||||
*
|
||||
* The signal mask is applied to the child process in such a way that
|
||||
* signal handlers from the parent process can't get triggered in the
|
||||
* child process.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr,
|
||||
sigset_t *sigmask) {
|
||||
*sigmask = (*attr)->sigmask;
|
||||
return 0;
|
||||
}
|
42
libc/proc/posix_spawnattr_init.c
Normal file
42
libc/proc/posix_spawnattr_init.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*-*- 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/errno.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Initialize posix_spawn() attributes object with default values.
|
||||
*
|
||||
* @param attr needs to be passed to posix_spawnattr_destroy() later
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ENOMEM if we require more vespene gas
|
||||
*/
|
||||
int posix_spawnattr_init(posix_spawnattr_t *attr) {
|
||||
int rc, e = errno;
|
||||
struct _posix_spawna *a;
|
||||
if ((a = calloc(1, sizeof(struct _posix_spawna)))) {
|
||||
*attr = a;
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = errno;
|
||||
errno = e;
|
||||
}
|
||||
return rc;
|
||||
}
|
47
libc/proc/posix_spawnattr_setflags.c
Normal file
47
libc/proc/posix_spawnattr_setflags.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*-*- 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/errno.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Sets posix_spawn() flags.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param flags may have any of the following
|
||||
* - `POSIX_SPAWN_RESETIDS`
|
||||
* - `POSIX_SPAWN_SETPGROUP`
|
||||
* - `POSIX_SPAWN_SETSIGDEF`
|
||||
* - `POSIX_SPAWN_SETSIGMASK`
|
||||
* - `POSIX_SPAWN_SETSCHEDPARAM`
|
||||
* - `POSIX_SPAWN_SETSCHEDULER`
|
||||
* - `POSIX_SPAWN_SETSID`
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EINVAL if `flags` has invalid bits
|
||||
*/
|
||||
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
|
||||
if (flags &
|
||||
~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF |
|
||||
POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSCHEDPARAM |
|
||||
POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETSID)) {
|
||||
return EINVAL;
|
||||
}
|
||||
(*attr)->flags = flags;
|
||||
return 0;
|
||||
}
|
38
libc/proc/posix_spawnattr_setpgroup.c
Normal file
38
libc/proc/posix_spawnattr_setpgroup.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*-*- 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/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Specifies process group into which child process is placed.
|
||||
*
|
||||
* Setting `pgroup` to zero will ensure newly created processes are
|
||||
* placed within their own brand new process group.
|
||||
*
|
||||
* This setter also sets the `POSIX_SPAWN_SETPGROUP` flag.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param pgroup is the process group id, or 0 for self
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, int pgroup) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETPGROUP;
|
||||
(*attr)->pgroup = pgroup;
|
||||
return 0;
|
||||
}
|
43
libc/proc/posix_spawnattr_setrlimit.c
Normal file
43
libc/proc/posix_spawnattr_setrlimit.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*-*- 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/struct/rlimit.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Sets resource limit on spawned process.
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETRLIMIT`.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EINVAL if resource is invalid
|
||||
*/
|
||||
int posix_spawnattr_setrlimit(posix_spawnattr_t *attr, int resource,
|
||||
const struct rlimit *rlim) {
|
||||
if (0 <= resource && resource < ARRAYLEN((*attr)->rlim)) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETRLIMIT;
|
||||
(*attr)->rlimset |= 1u << resource;
|
||||
(*attr)->rlim[resource] = *rlim;
|
||||
return 0;
|
||||
} else {
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
37
libc/proc/posix_spawnattr_setschedparam.c
Normal file
37
libc/proc/posix_spawnattr_setschedparam.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*-*- 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/struct/sched_param.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Specifies scheduler parameter override for spawned process.
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETSCHEDPARAM`.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedparam receives the result
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
|
||||
const struct sched_param *schedparam) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSCHEDPARAM;
|
||||
(*attr)->schedparam = *schedparam;
|
||||
return 0;
|
||||
}
|
34
libc/proc/posix_spawnattr_setschedpolicy.c
Normal file
34
libc/proc/posix_spawnattr_setschedpolicy.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*-*- 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/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Specifies scheduler policy override for spawned process.
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETSCHEDULER`.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSCHEDULER;
|
||||
(*attr)->schedpolicy = schedpolicy;
|
||||
return 0;
|
||||
}
|
44
libc/proc/posix_spawnattr_setsigdefault.c
Normal file
44
libc/proc/posix_spawnattr_setsigdefault.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*-*- 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/struct/sigset.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Specifies which signals should be restored to `SIG_DFL`.
|
||||
*
|
||||
* This routine isn't necessary in most cases, since posix_spawn() by
|
||||
* default will try to avoid vfork() race conditions by tracking what
|
||||
* signals have a handler function and then resets them automatically
|
||||
* within the child process, before applying the child's signal mask.
|
||||
* This function may be used to ensure the `SIG_IGN` disposition will
|
||||
* not propagate across execve in cases where this process explicitly
|
||||
* set the signals to `SIG_IGN` earlier (since posix_spawn() will not
|
||||
* issue O(128) system calls just to be totally pedantic about that).
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETSIGDEF`.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr,
|
||||
const sigset_t *sigdefault) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSIGDEF;
|
||||
(*attr)->sigdefault = *sigdefault;
|
||||
return 0;
|
||||
}
|
35
libc/proc/posix_spawnattr_setsigmask.c
Normal file
35
libc/proc/posix_spawnattr_setsigmask.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/struct/sigset.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/proc/posix_spawn.internal.h"
|
||||
|
||||
/**
|
||||
* Specifies signal mask for sigprocmask() in child process.
|
||||
*
|
||||
* This setter also sets the `POSIX_SPAWN_SETSIGMASK` flag.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
|
||||
const sigset_t *sigmask) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSIGMASK;
|
||||
(*attr)->sigmask = *sigmask;
|
||||
return 0;
|
||||
}
|
38
libc/proc/posix_spawnp.c
Normal file
38
libc/proc/posix_spawnp.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*-*- 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/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
|
||||
/**
|
||||
* Spawns process the POSIX way w/ PATH search.
|
||||
*
|
||||
* @param pid is non-NULL and will be set to child pid in parent
|
||||
* @param path of executable is PATH searched unless it contains a slash
|
||||
* @return 0 on success or error number on failure
|
||||
*/
|
||||
int posix_spawnp(int *pid, const char *path,
|
||||
const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *attrp, char *const argv[],
|
||||
char *const envp[]) {
|
||||
char pathbuf[PATH_MAX];
|
||||
if (!(path = commandv(path, pathbuf, sizeof(pathbuf)))) return errno;
|
||||
return posix_spawn(pid, path, file_actions, attrp, argv, envp);
|
||||
}
|
192
libc/proc/proc.c
Normal file
192
libc/proc/proc.c
Normal file
|
@ -0,0 +1,192 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/leaky.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
struct Procs __proc;
|
||||
|
||||
static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
|
||||
__bootstrap_tls(&__proc.tls, __builtin_frame_address(0));
|
||||
for (;;) {
|
||||
int64_t handles[64];
|
||||
int sic, dosignal = 0;
|
||||
struct Proc *pr, *objects[64];
|
||||
struct Dll *e, *e2, *samples = 0;
|
||||
uint32_t millis, status, i, n = 1;
|
||||
__proc_lock();
|
||||
handles[0] = __proc.onstart;
|
||||
for (e = dll_first(__proc.list); e && n < 64; ++n, e = e2) {
|
||||
pr = PROC_CONTAINER(e);
|
||||
e2 = dll_next(__proc.list, e);
|
||||
dll_remove(&__proc.list, e);
|
||||
dll_make_last(&samples, e);
|
||||
handles[n] = pr->handle;
|
||||
objects[n] = pr;
|
||||
}
|
||||
dll_make_last(&__proc.list, samples);
|
||||
__proc_unlock();
|
||||
millis = n == 64 ? __SIG_PROC_INTERVAL_MS : -1u;
|
||||
i = WaitForMultipleObjects(n, handles, false, millis);
|
||||
i &= ~kNtWaitAbandoned;
|
||||
if (!i || i == kNtWaitTimeout) continue;
|
||||
GetExitCodeProcess(handles[i], &status);
|
||||
if (status == kNtStillActive) continue;
|
||||
__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)) {
|
||||
CloseHandle(pr->handle);
|
||||
dll_remove(&__proc.list, &pr->elem);
|
||||
dll_make_first(&__proc.free, &pr->elem);
|
||||
} 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);
|
||||
} else {
|
||||
dosignal = 1;
|
||||
sic = WIFSIGNALED(status) ? CLD_KILLED : CLD_EXITED;
|
||||
}
|
||||
}
|
||||
__proc_unlock();
|
||||
if (dosignal) {
|
||||
__sig_generate(SIGCHLD, sic);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy initializes process tracker data structures and worker.
|
||||
*/
|
||||
static textwindows void __proc_setup(void) {
|
||||
__proc.onstart = CreateSemaphore(0, 0, 1, 0);
|
||||
__proc.thread = CreateThread(0, 65536, __proc_worker, 0,
|
||||
kNtStackSizeParamIsAReservation, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks process tracker.
|
||||
*/
|
||||
textwindows void __proc_lock(void) {
|
||||
cosmo_once(&__proc.once, __proc_setup);
|
||||
nsync_mu_lock(&__proc.lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks process tracker.
|
||||
*/
|
||||
textwindows void __proc_unlock(void) {
|
||||
nsync_mu_unlock(&__proc.lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets process tracker from forked child.
|
||||
*/
|
||||
textwindows void __proc_wipe(void) {
|
||||
bzero(&__proc, sizeof(__proc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates object for new process.
|
||||
*
|
||||
* The returned memory is not tracked by any list. It must be filled in
|
||||
* with system process information and then added back to the system by
|
||||
* calling __proc_add(). If process creation fails, then it needs to be
|
||||
* released using __proc_free().
|
||||
*/
|
||||
textwindows struct Proc *__proc_new(void) {
|
||||
struct Dll *e;
|
||||
struct Proc *proc = 0;
|
||||
int i, n = ARRAYLEN(__proc.pool);
|
||||
if (atomic_load_explicit(&__proc.allocated, memory_order_relaxed) < n &&
|
||||
(i = atomic_fetch_add(&__proc.allocated, 1)) < n) {
|
||||
proc = __proc.pool + i;
|
||||
} else {
|
||||
if ((e = dll_first(__proc.free))) {
|
||||
proc = PROC_CONTAINER(e);
|
||||
dll_remove(&__proc.free, &proc->elem);
|
||||
}
|
||||
if (!proc) {
|
||||
if (_weaken(malloc)) {
|
||||
proc = _weaken(malloc)(sizeof(struct Proc));
|
||||
} else {
|
||||
enomem();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (proc) {
|
||||
bzero(proc, sizeof(*proc));
|
||||
dll_init(&proc->elem);
|
||||
}
|
||||
return proc;
|
||||
}
|
||||
|
||||
IGNORE_LEAKS(__proc_new)
|
||||
|
||||
/**
|
||||
* Adds process to active list.
|
||||
*
|
||||
* The handle and pid must be filled in before calling this.
|
||||
*/
|
||||
textwindows void __proc_add(struct Proc *proc) {
|
||||
dll_make_first(&__proc.list, &proc->elem);
|
||||
ReleaseSemaphore(__proc.onstart, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees process allocation.
|
||||
*
|
||||
* Process must not be currently tracked in the active or zombies list.
|
||||
*/
|
||||
textwindows void __proc_free(struct Proc *proc) {
|
||||
dll_make_first(&__proc.free, &proc->elem);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
0
libc/proc/proc.h
Executable file
0
libc/proc/proc.h
Executable file
53
libc/proc/proc.internal.h
Normal file
53
libc/proc/proc.internal.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_PROC_H_
|
||||
#define COSMOPOLITAN_LIBC_PROC_H_
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "third_party/nsync/cv.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define PROC_CONTAINER(e) DLL_CONTAINER(struct Proc, elem, e)
|
||||
|
||||
struct Proc {
|
||||
int pid;
|
||||
int waiters;
|
||||
bool iszombie;
|
||||
bool wasforked;
|
||||
uint32_t wstatus;
|
||||
int64_t handle;
|
||||
struct Dll elem;
|
||||
nsync_cv onexit;
|
||||
};
|
||||
|
||||
struct Procs {
|
||||
int waiters;
|
||||
atomic_uint once;
|
||||
nsync_mu lock;
|
||||
nsync_cv onexit;
|
||||
intptr_t thread;
|
||||
intptr_t onstart;
|
||||
struct Dll *list;
|
||||
struct Dll *free;
|
||||
struct Dll *zombies;
|
||||
struct Proc pool[8];
|
||||
unsigned allocated;
|
||||
struct CosmoTib tls;
|
||||
};
|
||||
|
||||
extern struct Procs __proc;
|
||||
|
||||
void __proc_wipe(void);
|
||||
void __proc_lock(void);
|
||||
void __proc_unlock(void);
|
||||
struct Proc *__proc_new(void);
|
||||
void __proc_add(struct Proc *);
|
||||
void __proc_free(struct Proc *);
|
||||
int sys_wait4_nt(int, int *, int, struct rusage *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_PROC_H_ */
|
70
libc/proc/proc.mk
Normal file
70
libc/proc/proc.mk
Normal file
|
@ -0,0 +1,70 @@
|
|||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
|
||||
|
||||
PKGS += LIBC_PROC
|
||||
|
||||
LIBC_PROC_ARTIFACTS += LIBC_PROC_A
|
||||
LIBC_PROC = $(LIBC_PROC_A_DEPS) $(LIBC_PROC_A)
|
||||
LIBC_PROC_A = o/$(MODE)/libc/proc/proc.a
|
||||
LIBC_PROC_A_FILES := $(wildcard libc/proc/*) $(wildcard libc/proc/unlocked/*)
|
||||
LIBC_PROC_A_HDRS = $(filter %.h,$(LIBC_PROC_A_FILES))
|
||||
LIBC_PROC_A_SRCS_S = $(filter %.S,$(LIBC_PROC_A_FILES))
|
||||
LIBC_PROC_A_SRCS_C = $(filter %.c,$(LIBC_PROC_A_FILES))
|
||||
|
||||
LIBC_PROC_A_SRCS = \
|
||||
$(LIBC_PROC_A_SRCS_S) \
|
||||
$(LIBC_PROC_A_SRCS_C)
|
||||
|
||||
LIBC_PROC_A_OBJS = \
|
||||
$(LIBC_PROC_A_SRCS_S:%.S=o/$(MODE)/%.o) \
|
||||
$(LIBC_PROC_A_SRCS_C:%.c=o/$(MODE)/%.o)
|
||||
|
||||
LIBC_PROC_A_CHECKS = \
|
||||
$(LIBC_PROC_A).pkg \
|
||||
$(LIBC_PROC_A_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
LIBC_PROC_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_NT_PSAPI \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
THIRD_PARTY_NSYNC \
|
||||
THIRD_PARTY_NSYNC_MEM
|
||||
|
||||
LIBC_PROC_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(LIBC_PROC_A_DIRECTDEPS),$($(x))))
|
||||
|
||||
$(LIBC_PROC_A):libc/proc/ \
|
||||
$(LIBC_PROC_A).pkg \
|
||||
$(LIBC_PROC_A_OBJS)
|
||||
|
||||
$(LIBC_PROC_A).pkg: \
|
||||
$(LIBC_PROC_A_OBJS) \
|
||||
$(foreach x,$(LIBC_PROC_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
$(LIBC_PROC_A_OBJS): private \
|
||||
COPTS += \
|
||||
-fno-sanitize=address \
|
||||
-Wframe-larger-than=4096 \
|
||||
-Walloca-larger-than=4096
|
||||
|
||||
# aarch64 friendly assembly code
|
||||
o/$(MODE)/libc/proc/vfork.o: libc/proc/vfork.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
|
||||
LIBC_PROC_LIBS = $(foreach x,$(LIBC_PROC_ARTIFACTS),$($(x)))
|
||||
LIBC_PROC_SRCS = $(foreach x,$(LIBC_PROC_ARTIFACTS),$($(x)_SRCS))
|
||||
LIBC_PROC_HDRS = $(foreach x,$(LIBC_PROC_ARTIFACTS),$($(x)_HDRS))
|
||||
LIBC_PROC_CHECKS = $(foreach x,$(LIBC_PROC_ARTIFACTS),$($(x)_CHECKS))
|
||||
LIBC_PROC_OBJS = $(foreach x,$(LIBC_PROC_ARTIFACTS),$($(x)_OBJS))
|
||||
$(LIBC_PROC_OBJS): $(BUILD_FILES) libc/proc/proc.mk
|
||||
|
||||
.PHONY: o/$(MODE)/libc/proc
|
||||
o/$(MODE)/libc/proc: $(LIBC_PROC_CHECKS)
|
98
libc/proc/system.c
Normal file
98
libc/proc/system.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*-*- 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/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Launches program with system command interpreter.
|
||||
*
|
||||
* This implementation embeds the Cosmopolitan Command Interpreter which
|
||||
* provides Bourne-like syntax on all platforms, including Windows. Many
|
||||
* builtin commands are included, e.g. exit, cd, rm, [, cat, wait, exec,
|
||||
* env, echo, read, true, test, kill, touch, rmdir, mkdir, false, mktemp
|
||||
* and usleep. It's also possible to __static_yoink() the symbols `_tr`,
|
||||
* `_sed`, `_awk`, and `_curl` for the tr, sed, awk and curl commands if
|
||||
* you're using the Cosmopolitan mono-repo.
|
||||
*
|
||||
* If you just have a program name and arguments, and you don't need the
|
||||
* full power of a UNIX-like shell, then consider using the Cosmopolitan
|
||||
* provided API systemvpe() instead. It provides a safer alternative for
|
||||
* variable arguments than shell script escaping. It lets you clean your
|
||||
* environment variables, for even more safety. Finally it's 10x faster.
|
||||
*
|
||||
* It's important to check the returned status code. For example, if you
|
||||
* press CTRL-C while running your program you'll expect it to terminate
|
||||
* however that won't be the case if the SIGINT gets raised while inside
|
||||
* the system() function. If the child process doesn't handle the signal
|
||||
* then this will return e.g. WIFSIGNALED(ws) && WTERMSIG(ws) == SIGINT.
|
||||
*
|
||||
* @param cmdline is a unix shell script
|
||||
* @return -1 if child process couldn't be created, otherwise a wait
|
||||
* status that can be accessed using macros like WEXITSTATUS(s),
|
||||
* WIFSIGNALED(s), WTERMSIG(s), etc.
|
||||
* @see systemve()
|
||||
* @threadsafe
|
||||
*/
|
||||
int system(const char *cmdline) {
|
||||
int pid, wstatus;
|
||||
sigset_t chldmask, savemask;
|
||||
if (!cmdline) return 1;
|
||||
sigemptyset(&chldmask);
|
||||
sigaddset(&chldmask, SIGINT);
|
||||
sigaddset(&chldmask, SIGQUIT);
|
||||
sigaddset(&chldmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
|
||||
if (!(pid = fork())) {
|
||||
sigprocmask(SIG_SETMASK, &savemask, 0);
|
||||
_Exit(_cocmd(3, (char *[]){"system", "-c", (char *)cmdline, 0}, environ));
|
||||
} else if (pid == -1) {
|
||||
wstatus = -1;
|
||||
} else {
|
||||
struct sigaction ignore, saveint, savequit;
|
||||
ignore.sa_flags = 0;
|
||||
ignore.sa_handler = SIG_IGN;
|
||||
sigemptyset(&ignore.sa_mask);
|
||||
sigaction(SIGINT, &ignore, &saveint);
|
||||
sigaction(SIGQUIT, &ignore, &savequit);
|
||||
BLOCK_CANCELLATIONS;
|
||||
while (wait4(pid, &wstatus, 0, 0) == -1) {
|
||||
if (errno != EINTR) {
|
||||
wstatus = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
sigaction(SIGQUIT, &savequit, 0);
|
||||
sigaction(SIGINT, &saveint, 0);
|
||||
}
|
||||
sigprocmask(SIG_SETMASK, &savemask, 0);
|
||||
return wstatus;
|
||||
}
|
90
libc/proc/systemvpe.c
Normal file
90
libc/proc/systemvpe.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 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/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
/**
|
||||
* Executes program and waits for it to complete, e.g.
|
||||
*
|
||||
* systemvpe("ls", (char *[]){"ls", dir, 0}, environ);
|
||||
*
|
||||
* This function is designed to do the same thing as system() except
|
||||
* rather than taking a shell script argument it accepts an array of
|
||||
* strings which are safely passed directly to execve().
|
||||
*
|
||||
* This function is 5x faster than system() and generally safer, for
|
||||
* most command running use cases that don't need to control the i/o
|
||||
* file descriptors.
|
||||
*
|
||||
* @param prog is path searched (if it doesn't contain a slash) from
|
||||
* the $PATH environment variable in `environ` (not your `envp`)
|
||||
* @return -1 if child process couldn't be created, otherwise a wait
|
||||
* status that can be accessed using macros like WEXITSTATUS(s),
|
||||
* WIFSIGNALED(s), WTERMSIG(s), etc.
|
||||
* @see system()
|
||||
* @threadsafe
|
||||
*/
|
||||
int systemvpe(const char *prog, char *const argv[], char *const envp[]) {
|
||||
char *exe;
|
||||
int pid, wstatus;
|
||||
char pathbuf[PATH_MAX + 1];
|
||||
sigset_t chldmask, savemask;
|
||||
if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) {
|
||||
return -1;
|
||||
}
|
||||
sigemptyset(&chldmask);
|
||||
sigaddset(&chldmask, SIGINT);
|
||||
sigaddset(&chldmask, SIGQUIT);
|
||||
sigaddset(&chldmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
|
||||
if (!(pid = vfork())) {
|
||||
sigprocmask(SIG_SETMASK, &savemask, 0);
|
||||
execve(prog, argv, envp);
|
||||
_Exit(127);
|
||||
} else if (pid == -1) {
|
||||
wstatus = -1;
|
||||
} else {
|
||||
struct sigaction ignore, saveint, savequit;
|
||||
ignore.sa_flags = 0;
|
||||
ignore.sa_handler = SIG_IGN;
|
||||
sigemptyset(&ignore.sa_mask);
|
||||
sigaction(SIGINT, &ignore, &saveint);
|
||||
sigaction(SIGQUIT, &ignore, &savequit);
|
||||
BLOCK_CANCELLATIONS;
|
||||
while (wait4(pid, &wstatus, 0, 0) == -1) {
|
||||
if (errno != EINTR) {
|
||||
wstatus = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
sigaction(SIGQUIT, &savequit, 0);
|
||||
sigaction(SIGINT, &saveint, 0);
|
||||
}
|
||||
sigprocmask(SIG_SETMASK, &savemask, 0);
|
||||
return wstatus;
|
||||
}
|
128
libc/proc/vfork.S
Normal file
128
libc/proc/vfork.S
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 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/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
// Forks process without copying page tables.
|
||||
//
|
||||
// This function lets a process spawn another process without
|
||||
// copying the page tables. The parent process gets suspended
|
||||
// until the child calls either execve() or _Exit(), and they
|
||||
// share memory during that time. That's at least how we want
|
||||
// vfork() to work. Support for these behaviors is patchy. It
|
||||
// is also error-prone to use this function in an environment
|
||||
// with signal handlers and threads. The best way to use this
|
||||
// is by calling posix_spawn() which works around the dangers
|
||||
// and determines at runtime the best way to pass error codes
|
||||
//
|
||||
// @return pid of child process or 0 if forked process
|
||||
// @returnstwice
|
||||
// @threadsafe
|
||||
// @vforksafe
|
||||
.ftrace1
|
||||
vfork:
|
||||
.ftrace2
|
||||
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
jmp fork
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#if SupportsWindows()
|
||||
// these platforms disagree with vfork
|
||||
testb $_HOSTXNU|_HOSTOPENBSD|_HOSTWINDOWS,__hostos(%rip)
|
||||
jnz fork
|
||||
#endif
|
||||
|
||||
#if !IsTiny()
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
#ifdef SYSDEBUG
|
||||
ezlea .Llog,di
|
||||
call __stracef
|
||||
#endif
|
||||
pop %rbp
|
||||
#endif
|
||||
mov %fs:0,%r9 // get thread information block
|
||||
mov 0x3c(%r9),%r8d // avoid question of @vforksafe errno
|
||||
pop %rsi // saves return address in a register
|
||||
mov __NR_vfork(%rip),%eax
|
||||
#if SupportsBsd()
|
||||
clc
|
||||
#endif
|
||||
syscall
|
||||
#if SupportsBsd()
|
||||
jnc 0f
|
||||
neg %rax
|
||||
0:
|
||||
#endif
|
||||
push %rsi // note it happens twice in same page
|
||||
cmp $-4095,%eax
|
||||
jae systemfive_error
|
||||
mov %r8d,0x3c(%r9) // restore errno
|
||||
1: test %eax,%eax
|
||||
jnz .Lpar
|
||||
.Lchi: orb $TIB_FLAG_VFORKED,0x40(%r9)
|
||||
ret
|
||||
.Lpar: andb $~TIB_FLAG_VFORKED,0x40(%r9)
|
||||
ret
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
adrp x0,__hostos
|
||||
ldr w0,[x0,#:lo12:__hostos]
|
||||
tbz x0,3,1f // bit 3 is xnu
|
||||
b fork // which doesn't support vfork()
|
||||
1: mov x8,#220 // __NR_clone
|
||||
mov x0,#0x4111 // SIGCHLD | CLONE_VM | CLONE_VFORK
|
||||
mov x1,#0
|
||||
svc 0
|
||||
|
||||
// if (!rc) {
|
||||
// __get_tls()->tib_flags |= TIB_FLAG_VFORKED;
|
||||
// } else {
|
||||
// __get_tls()->tib_flags &= ~TIB_FLAG_VFORKED;
|
||||
// }
|
||||
sub x1,x28,#0x80 // RELIES ON TLS TIB ABI!
|
||||
ldr x2,[x1,64]
|
||||
cbnz x0,2f
|
||||
orr x2,x2,#TIB_FLAG_VFORKED
|
||||
1: str x2,[x1,64]
|
||||
b 3f
|
||||
2: and x2,x2,#~TIB_FLAG_VFORKED
|
||||
b 1b
|
||||
|
||||
// if (rc < 0) errno = -rc, rc = -1;
|
||||
3: .hidden _sysret
|
||||
b _sysret
|
||||
|
||||
#else
|
||||
#error "architecture unsupported"
|
||||
#endif
|
||||
.endfn vfork,globl
|
||||
|
||||
#ifdef SYSDEBUG
|
||||
.rodata.str1.1
|
||||
.Llog: .ascii STRACE_PROLOGUE
|
||||
.asciz "vfork()\n"
|
||||
.previous
|
||||
#endif /* DEBUGSYS */
|
34
libc/proc/wait.c
Normal file
34
libc/proc/wait.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*-*- 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"
|
||||
|
||||
/**
|
||||
* Waits for status to change on any child process.
|
||||
*
|
||||
* @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
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
*/
|
||||
int wait(int *opt_out_wstatus) {
|
||||
return wait4(-1, opt_out_wstatus, 0, NULL);
|
||||
}
|
36
libc/proc/wait3.c
Normal file
36
libc/proc/wait3.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*-*- 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/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Waits for status to change on any child process.
|
||||
*
|
||||
* @param opt_out_wstatus optionally returns status code, and *wstatus
|
||||
* may be inspected using WEEXITSTATUS(), etc.
|
||||
* @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
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
int wait3(int *opt_out_wstatus, int options, struct rusage *opt_out_rusage) {
|
||||
return wait4(-1, opt_out_wstatus, options, opt_out_rusage);
|
||||
}
|
172
libc/proc/wait4-nt.c
Normal file
172
libc/proc/wait4-nt.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/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/timespec.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.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/processmemorycounters.h"
|
||||
#include "libc/proc/proc.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/w.h"
|
||||
#include "libc/sysv/errfuns.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);
|
||||
}
|
||||
|
||||
static textwindows int ReapZombie(struct Proc *pr, int *wstatus,
|
||||
struct rusage *opt_out_rusage) {
|
||||
if (wstatus) {
|
||||
*wstatus = pr->wstatus;
|
||||
}
|
||||
if (opt_out_rusage) {
|
||||
GetProcessStats(pr->handle, opt_out_rusage);
|
||||
}
|
||||
if (!pr->waiters) {
|
||||
CloseHandle(pr->handle);
|
||||
dll_remove(&__proc.zombies, &pr->elem);
|
||||
dll_make_first(&__proc.free, &pr->elem);
|
||||
}
|
||||
return pr->pid;
|
||||
}
|
||||
|
||||
static textwindows int CheckZombies(int pid, int *wstatus,
|
||||
struct rusage *opt_out_rusage) {
|
||||
struct Dll *e;
|
||||
for (e = dll_first(__proc.zombies); e; e = dll_next(__proc.zombies, e)) {
|
||||
struct Proc *pr = PROC_CONTAINER(e);
|
||||
if (pid == -1 && pr->waiters) {
|
||||
continue; // this zombie has been claimed
|
||||
}
|
||||
if (pid == -1 || pid == pr->pid) {
|
||||
return ReapZombie(pr, wstatus, opt_out_rusage);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows int WaitForProcess(int pid, int *wstatus, int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
int rc, *wv;
|
||||
nsync_cv *cv;
|
||||
struct Dll *e;
|
||||
struct Proc *pr;
|
||||
struct timespec deadline = timespec_zero;
|
||||
|
||||
// check list of processes that've already exited
|
||||
if ((rc = CheckZombies(pid, wstatus, opt_out_rusage))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
// find the mark
|
||||
pr = 0;
|
||||
if (pid == -1) {
|
||||
if (dll_is_empty(__proc.list)) {
|
||||
return echild();
|
||||
}
|
||||
cv = &__proc.onexit;
|
||||
wv = &__proc.waiters;
|
||||
} else {
|
||||
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) {
|
||||
unassert(!pr->iszombie);
|
||||
cv = &pr->onexit;
|
||||
wv = &pr->waiters;
|
||||
} else {
|
||||
return echild();
|
||||
}
|
||||
}
|
||||
|
||||
// wait for status change
|
||||
if (options & WNOHANG) return 0;
|
||||
CheckForInterrupt:
|
||||
if (_check_interrupts(kSigOpRestartable) == -1) return -1;
|
||||
deadline = GetNextDeadline(deadline);
|
||||
SpuriousWakeup:
|
||||
BEGIN_BLOCKING_OPERATION;
|
||||
++*wv;
|
||||
rc = nsync_cv_wait_with_deadline(cv, &__proc.lock, deadline, 0);
|
||||
--*wv;
|
||||
END_BLOCKING_OPERATION;
|
||||
if (rc == ECANCELED) return ecanceled();
|
||||
if (pr && pr->iszombie) return ReapZombie(pr, wstatus, opt_out_rusage);
|
||||
if (rc == ETIMEDOUT) goto CheckForInterrupt;
|
||||
unassert(!rc);
|
||||
if (!pr && (rc = CheckZombies(pid, wstatus, opt_out_rusage))) return rc;
|
||||
goto 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
|
||||
}
|
||||
// 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;
|
||||
__proc_lock();
|
||||
rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage);
|
||||
__proc_unlock();
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
32
libc/proc/wait4-sysv.c
Normal file
32
libc/proc/wait4-sysv.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- 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/calls/calls.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/rusage.internal.h"
|
||||
|
||||
int sys_wait4(int pid, int *opt_out_wstatus, int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
int rc;
|
||||
if ((rc = __sys_wait4(pid, opt_out_wstatus, options, opt_out_rusage)) != -1) {
|
||||
if (opt_out_rusage) {
|
||||
__rusage2linux(opt_out_rusage);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
66
libc/proc/wait4.c
Normal file
66
libc/proc/wait4.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/calls/calls.h"
|
||||
#include "libc/calls/cp.internal.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/proc/proc.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Waits for status to change on process.
|
||||
*
|
||||
* @param pid >0 targets specific process, =0 means any proc in a group,
|
||||
* -1 means any child process, <-1 means any proc in specific group
|
||||
* @param opt_out_wstatus optionally returns status code, and *wstatus
|
||||
* may be inspected using WEEXITSTATUS(), etc.
|
||||
* @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
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
int wait4(int pid, int *opt_out_wstatus, int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
int rc, ws = 0;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
|
||||
if (IsAsan() &&
|
||||
((opt_out_wstatus &&
|
||||
!__asan_is_valid(opt_out_wstatus, sizeof(*opt_out_wstatus))) ||
|
||||
(opt_out_rusage &&
|
||||
!__asan_is_valid(opt_out_rusage, sizeof(*opt_out_rusage))))) {
|
||||
rc = efault();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_wait4(pid, &ws, options, opt_out_rusage);
|
||||
} else {
|
||||
rc = sys_wait4_nt(pid, &ws, options, opt_out_rusage);
|
||||
}
|
||||
if (rc != -1 && opt_out_wstatus) {
|
||||
*opt_out_wstatus = ws;
|
||||
}
|
||||
|
||||
END_CANCELLATION_POINT;
|
||||
STRACE("wait4(%d, [%#x], %d, %p) → %d% m", pid, ws, options, opt_out_rusage,
|
||||
rc);
|
||||
return rc;
|
||||
}
|
36
libc/proc/waitpid.c
Normal file
36
libc/proc/waitpid.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*-*- 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"
|
||||
|
||||
/**
|
||||
* Waits for status to change on process.
|
||||
*
|
||||
* @param pid >0 targets specific process, =0 means any proc in a group,
|
||||
* -1 means any child process, <-1 means any proc in specific group
|
||||
* @param opt_out_wstatus optionally returns status code, and *wstatus
|
||||
* 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
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
int waitpid(int pid, int *opt_out_wstatus, int options) {
|
||||
return wait4(pid, opt_out_wstatus, options, NULL);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue