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:
Justine Tunney 2023-09-18 20:44:45 -07:00
parent c4eb838516
commit ec480f5aa0
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
638 changed files with 7925 additions and 8282 deletions

1069
libc/proc/cocmd.c Normal file

File diff suppressed because it is too large Load diff

68
libc/proc/daemon.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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_ */

View 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_ */

View 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;
}

View 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,
});
}

View 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,
});
}

View 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,
});
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}
}

View 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;
}

View 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;
}

View 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;
}

View 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
View 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
View 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
View file

53
libc/proc/proc.internal.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}