Fix XNU / FreeBSD / OpenBSD / RHEL5 / NT bugs

For the first time ever, all tests in this codebase now pass, when
run automatically on macos, freebsd, openbsd, rhel5, rhel7, alpine
and windows via the network using the runit and runitd build tools

- Fix vfork exec path etc.
- Add XNU opendir() support
- Add OpenBSD opendir() support
- Add Linux history to syscalls.sh
- Use copy_file_range on FreeBSD 13+
- Fix system calls with 7+ arguments
- Fix Windows with greater than 16 FDs
- Fix RUNIT.COM and RUNITD.COM flakiness
- Fix OpenBSD munmap() when files are mapped
- Fix long double so it's actually long on Windows
- Fix OpenBSD truncate() and ftruncate() thunk typo
- Let Windows fcntl() be used on socket files descriptors
- Fix Windows fstat() which had an accidental printf statement
- Fix RHEL5 CLOCK_MONOTONIC by not aliasing to CLOCK_MONOTONIC_RAW

This is wonderful. I never could have dreamed it would be possible
to get it working so well on so many platforms with tiny binaries.

Fixes #31
Fixes #25
Fixes #14
This commit is contained in:
Justine Tunney
2021-01-25 13:08:05 -08:00
parent c20dad3534
commit 45b72485ad
1032 changed files with 6083 additions and 2348 deletions

View File

@@ -32,14 +32,19 @@
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
struct dirent$freebsd {
uint32_t d_fileno;
uint16_t d_reclen;
uint8_t d_type;
uint8_t d_namlen;
char d_name[256];
};
/**
* @fileoverview Directory Streams for Linux+Mac+Windows+FreeBSD+OpenBSD.
*
* System interfaces for listing the contents of file system directories
* are famously incompatible across platforms. Most native projects that
* have been around a long time implement wrappers for this. Normally it
* will only be for DOS or Windows support. So this is the first time it
* has been done for five platforms, having a remarkably tiny footprint.
*/
/**
* Directory stream object.
*/
struct dirstream {
int64_t tell;
int64_t fd;
@@ -57,6 +62,17 @@ struct dirstream {
};
};
/**
* FreeBSD getdents() and XNU getdirentries() ABI.
*/
struct dirent$bsd {
uint32_t d_fileno;
uint16_t d_reclen;
uint8_t d_type;
uint8_t d_namlen;
char d_name[256];
};
static textwindows noinline DIR *opendir$nt(const char *name) {
int len;
DIR *res;
@@ -132,17 +148,14 @@ static textwindows noinline struct dirent *readdir$nt(DIR *dir) {
DIR *opendir(const char *name) {
int fd;
DIR *res;
if (!IsWindows() && !IsXnu()) {
if (!IsWindows()) {
res = NULL;
if ((fd = open(name, O_RDONLY | O_DIRECTORY | O_CLOEXEC)) != -1) {
if (!(res = fdopendir(fd))) close(fd);
}
return res;
} else if (IsWindows()) {
return opendir$nt(name);
} else {
enosys(); /* TODO(jart): Implement me! */
return NULL;
return opendir$nt(name);
}
}
@@ -156,7 +169,7 @@ DIR *opendir(const char *name) {
*/
DIR *fdopendir(int fd) {
DIR *dir;
if (!IsWindows() && !IsXnu()) {
if (!IsWindows()) {
if ((dir = calloc(1, sizeof(*dir)))) {
dir->fd = fd;
return dir;
@@ -168,7 +181,7 @@ DIR *fdopendir(int fd) {
}
/**
* Reads next entry from DIR.
* Reads next entry from directory stream.
*
* This API doesn't define any particular ordering.
*
@@ -178,31 +191,30 @@ DIR *fdopendir(int fd) {
*/
struct dirent *readdir(DIR *dir) {
int rc;
long basep;
struct dirent *ent;
struct dirent$freebsd *freebsd;
struct dirent$bsd *bsd;
if (!IsWindows()) {
if (dir->buf_pos >= dir->buf_end) {
if (!(rc = getdents(dir->fd, dir->buf,
sizeof(dir->buf) - sizeof(dir->ent.d_name))) ||
rc == -1) {
return NULL;
}
basep = dir->tell; /* <- what does xnu do */
rc = getdents(dir->fd, dir->buf, sizeof(dir->buf) - 256, &basep);
if (!rc || rc == -1) return NULL;
dir->buf_pos = 0;
dir->buf_end = rc;
}
if (IsLinux()) {
if (IsLinux() || IsOpenbsd()) {
ent = (struct dirent *)(dir->buf + dir->buf_pos);
dir->buf_pos += ent->d_reclen;
dir->tell = ent->d_off;
} else {
freebsd = (struct dirent$freebsd *)(dir->buf + dir->buf_pos);
dir->buf_pos += freebsd->d_reclen;
bsd = (struct dirent$bsd *)(dir->buf + dir->buf_pos);
dir->buf_pos += bsd->d_reclen;
ent = &dir->ent;
ent->d_ino = freebsd->d_fileno;
ent->d_off = dir->tell++;
ent->d_reclen = freebsd->d_reclen;
ent->d_type = freebsd->d_type;
memcpy(ent->d_name, freebsd->d_name, freebsd->d_namlen + 1);
ent->d_ino = bsd->d_fileno;
ent->d_off = IsXnu() ? (dir->tell = basep) : dir->tell++;
ent->d_reclen = bsd->d_reclen;
ent->d_type = bsd->d_type;
memcpy(ent->d_name, bsd->d_name, bsd->d_namlen + 1);
}
return ent;
} else {

View File

@@ -1,46 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/calls/hefty/mkvarargv.h"
#include "libc/calls/calls.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
* @notasyncsignalsafe (TODO)
*/
int execl(const char *exe, const char *arg, ... /*, NULL*/) {
va_list va;
void *argv;
va_start(va, arg);
if ((argv = mkvarargv(arg, va))) {
execve(exe, argv, environ);
free(argv);
}
va_end(va);
return -1;
}

View File

@@ -1,47 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/hefty/mkvarargv.h"
#include "libc/mem/mem.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
* @notasyncsignalsafe (TODO)
*/
int execle(const char *exe, const char *arg,
... /*, NULL, char *const envp[] */) {
va_list va;
void *argv;
va_start(va, arg);
if ((argv = mkvarargv(arg, va))) {
execve(exe, argv, va_arg(va, char *const *));
free(argv);
}
va_end(va);
return -1;
}

View File

@@ -1,50 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/hefty/mkvarargv.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.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
* @notasyncsignalsafe
*/
int execlp(const char *prog, const char *arg, ... /*, NULL*/) {
char *exe;
char pathbuf[PATH_MAX];
if ((exe = commandv(prog, pathbuf))) {
va_list va;
void *argv;
va_start(va, arg);
if ((argv = mkvarargv(arg, va))) {
execve(exe, argv, environ);
free(argv);
}
va_end(va);
}
return -1;
}

View File

@@ -1,28 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/runtime/runtime.h"
#include "libc/calls/calls.h"
/**
* Replaces process with specific program, using default environment.
* @asyncsignalsafe
*/
int execv(const char *exe, char *const argv[]) {
return execve(exe, argv, environ);
}

View File

@@ -1,67 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/hefty/internal.h"
#include "libc/calls/hefty/ntspawn.h"
#include "libc/calls/internal.h"
#include "libc/nt/accounting.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/nt/synchronization.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sock.h"
textwindows int execve$nt(const char *program, char *const argv[],
char *const envp[]) {
int i;
uint32_t dwExitCode;
struct NtStartupInfo startinfo;
struct NtProcessInformation procinfo;
memset(&startinfo, 0, sizeof(startinfo));
startinfo.cb = sizeof(struct NtStartupInfo);
startinfo.dwFlags = kNtStartfUsestdhandles;
startinfo.hStdInput = g_fds.p[0].handle;
startinfo.hStdOutput = g_fds.p[1].handle;
startinfo.hStdError = g_fds.p[2].handle;
for (i = 2; i < g_fds.n; ++i) {
if (g_fds.p[i].kind != kFdEmpty && (g_fds.p[i].flags & O_CLOEXEC)) {
close(i);
}
}
if (ntspawn(program, argv, envp, NULL, NULL, true, 0, NULL, &startinfo,
&procinfo) == -1) {
return -1;
}
CloseHandle(procinfo.hThread);
for (i = 0; i < g_fds.n; ++i) {
if (g_fds.p[i].kind != kFdEmpty) {
close(i);
}
}
do {
WaitForSingleObject(procinfo.hProcess, -1);
dwExitCode = kNtStillActive;
GetExitCodeProcess(procinfo.hProcess, &dwExitCode);
} while (dwExitCode == kNtStillActive);
ExitProcess(dwExitCode);
}

View File

@@ -1,42 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/hefty/internal.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
/**
* Replaces current process with program.
*
* @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
* @asyncsignalsafe
*/
int execve(const char *program, char *const argv[], char *const envp[]) {
if (!IsWindows()) {
return execve$sysv(program, argv, envp);
} else {
return execve$nt(program, argv, envp);
}
}

View File

@@ -1,28 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/runtime/runtime.h"
#include "libc/calls/calls.h"
/**
* Replaces process, with path search, using default environment.
* @notasyncsignalsafe
*/
int execvp(const char *file, char *const argv[]) {
return execvpe(file, argv, environ);
}

View File

@@ -1,40 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/mem/mem.h"
/**
* Executes program, with path environment search.
*
* The current process is replaced with the executed one.
*
* @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
* @notasyncsignalsafe
*/
int execvpe(const char *prog, char *const argv[], char *const *envp) {
char *exe;
char pathbuf[PATH_MAX];
if ((exe = commandv(prog, pathbuf))) {
execve(exe, argv, envp);
}
return -1;
}

View File

@@ -17,18 +17,24 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/ntspawn.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/internal.h"
#include "libc/calls/ntspawn.h"
#include "libc/fmt/itoa.h"
#include "libc/nexgen32e/nt2sysv.h"
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/enum/wt.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/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/runtime/directmap.h"
#include "libc/runtime/memtrack.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
@@ -71,9 +77,11 @@ textwindows void WinMainForked(void) {
jmp_buf jb;
char16_t *p;
uint64_t size;
uint32_t i, varlen;
struct DirectMap dm;
char16_t var[21 + 1 + 21 + 1];
uint32_t i, varlen, protect, access, oldprot;
varlen = GetEnvironmentVariable(u"_FORK", var, ARRAYLEN(var));
SetEnvironmentVariable(u"_FORK", NULL);
if (!varlen || varlen >= ARRAYLEN(var)) return;
p = var;
h = ParseInt(&p);
@@ -84,39 +92,27 @@ textwindows void WinMainForked(void) {
ReadAll(h, &_mmi.p[i], sizeof(_mmi.p[i]));
addr = (void *)((uint64_t)_mmi.p[i].x << 16);
size = ((uint64_t)(_mmi.p[i].y - _mmi.p[i].x) << 16) + FRAMESIZE;
switch (_mmi.p[i].prot & (PROT_READ | PROT_WRITE | PROT_EXEC)) {
case PROT_READ | PROT_WRITE | PROT_EXEC:
protect = kNtPageExecuteReadwrite;
access = kNtFileMapRead | kNtFileMapWrite | kNtFileMapExecute;
break;
case PROT_READ | PROT_WRITE:
protect = kNtPageReadwrite;
access = kNtFileMapRead | kNtFileMapWrite;
break;
case PROT_READ:
protect = kNtPageReadonly;
access = kNtFileMapRead;
break;
default:
protect = kNtPageNoaccess;
access = 0;
break;
}
if (_mmi.p[i].flags & MAP_PRIVATE) {
MapViewOfFileExNuma((_mmi.p[i].h = CreateFileMappingNuma(
-1, NULL, kNtPageExecuteReadwrite, 0, size, NULL,
kNtNumaNoPreferredNode)),
kNtFileMapRead | kNtFileMapWrite | kNtFileMapExecute,
0, 0, size, addr, kNtNumaNoPreferredNode);
CloseHandle(_mmi.p[i].h);
_mmi.p[i].h =
__mmap$nt(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC, -1, 0)
.maphandle;
ReadAll(h, addr, size);
VirtualProtect(addr, size, protect, &oldprot);
} else {
MapViewOfFileExNuma(_mmi.p[i].h, access, 0, 0, size, addr,
kNtNumaNoPreferredNode);
MapViewOfFileExNuma(
_mmi.p[i].h,
(_mmi.p[i].prot & PROT_WRITE)
? kNtFileMapWrite | kNtFileMapExecute | kNtFileMapRead
: kNtFileMapExecute | kNtFileMapRead,
0, 0, size, addr, kNtNumaNoPreferredNode);
}
}
ReadAll(h, _edata, _end - _edata);
CloseHandle(h);
unsetenv("_FORK");
if (weaken(__wincrash$nt)) {
AddVectoredExceptionHandler(1, (void *)weaken(__wincrash$nt));
}
longjmp(jb, 1);
}
@@ -141,13 +137,18 @@ textwindows int fork$nt(void) {
startinfo.hStdInput = g_fds.p[0].handle;
startinfo.hStdOutput = g_fds.p[1].handle;
startinfo.hStdError = g_fds.p[2].handle;
if (ntspawn(g_argv[0], g_argv, environ, &kNtIsInheritable, NULL, true, 0,
NULL, &startinfo, &procinfo) != -1) {
if (ntspawn(g_argv, environ, &kNtIsInheritable, NULL, true, 0, NULL,
&startinfo, &procinfo) != -1) {
CloseHandle(reader);
CloseHandle(procinfo.hThread);
g_fds.p[pid].kind = kFdProcess;
g_fds.p[pid].handle = procinfo.hProcess;
g_fds.p[pid].flags = O_CLOEXEC;
if (weaken(g_sighandrvas) &&
weaken(g_sighandrvas)[SIGCHLD] == SIG_IGN) {
CloseHandle(procinfo.hProcess);
} else {
g_fds.p[pid].kind = kFdProcess;
g_fds.p[pid].handle = procinfo.hProcess;
g_fds.p[pid].flags = O_CLOEXEC;
}
WriteAll(writer, jb, sizeof(jb));
WriteAll(writer, &_mmi.i, sizeof(_mmi.i));
for (i = 0; i < _mmi.i; ++i) {

View File

@@ -1,15 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_HEFTY_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_HEFTY_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int faccessat$nt(int, const char *, int, uint32_t) hidden;
int execve$nt(const char *, char *const[], char *const[]) hidden;
int spawnve$nt(unsigned, int[3], const char *, char *const[],
char *const[]) hidden;
int spawnve$sysv(unsigned, int[3], const char *, char *const[],
char *const[]) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_HEFTY_INTERNAL_H_ */

View File

@@ -1,97 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/alg/arraylist2.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/ntspawn.h"
#include "libc/fmt/conv.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/hascharacter.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/oldutf16.internal.h"
#include "libc/str/str.h"
#include "libc/str/tpdecode.internal.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/errfuns.h"
#define APPENDCHAR(C) \
do { \
if (mkntcmdline_append(&cmdline_p, &cmdline_i, &cmdline_n, C) == -1) { \
goto error; \
} \
} while (0)
static int mkntcmdline_append(char16_t **p, size_t *i, size_t *n, char16_t c) {
return APPEND(p, i, n, &c);
}
/**
* Converts System V argv to Windows-style command line.
*
* Escaping is performed and it's designed to round-trip with
* GetDosArgv() or GetDosArgv(). This function does NOT escape
* command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd).
*
* @param argv is an a NULL-terminated array of UTF-8 strings
* @return freshly allocated lpCommandLine or NULL w/ errno
* @kudos Daniel Colascione for teaching how to quote
* @see libc/runtime/dosargv.c
*/
hidden textwindows nodiscard char16_t *mkntcmdline(char *const argv[]) {
wint_t wc;
size_t i, j;
char16_t cbuf[2];
char16_t *cmdline_p = NULL;
size_t cmdline_i = 0;
size_t cmdline_n = 0;
if (argv[0]) {
for (i = 0; argv[i]; ++i) {
if (i) APPENDCHAR(u' ');
bool needsquote = !argv[i] || !!argv[i][strcspn(argv[i], " \t\n\v\"")];
if (needsquote) APPENDCHAR(u'"');
for (j = 0;;) {
if (needsquote) {
int slashes = 0;
while (argv[i][j] && argv[i][j] == u'\\') slashes++, j++;
slashes <<= 1;
if (argv[i][j] == u'"') slashes++;
while (slashes--) APPENDCHAR(u'\\');
}
if (!argv[i][j]) break;
j += abs(tpdecode(&argv[i][j], &wc));
if (CONCAT(&cmdline_p, &cmdline_i, &cmdline_n, cbuf,
abs(pututf16(cbuf, ARRAYLEN(cbuf), wc, false))) == -1) {
goto error;
}
}
if (needsquote) APPENDCHAR(u'"');
}
APPENDCHAR(u'\0');
if (cmdline_i > CMD_MAX) {
e2big();
goto error;
}
} else {
einval();
}
return cmdline_p;
error:
free(cmdline_p);
return NULL;
}

View File

@@ -1,66 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/alg/arraylist2.internal.h"
#include "libc/calls/hefty/ntspawn.h"
#include "libc/fmt/conv.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/str/oldutf16.internal.h"
#include "libc/str/str.h"
#include "libc/str/tpdecode.internal.h"
#include "libc/sysv/errfuns.h"
/**
* Copies sorted environment variable block for Windows.
*
* This is designed to meet the requirements of CreateProcess().
*
* @param envp is an a NULL-terminated array of UTF-8 strings
* @return freshly allocated lpEnvironment or NULL w/ errno
*/
textwindows char16_t *mkntenvblock(char *const envp[]) {
wint_t wc;
size_t i, j, bi, bn;
char16_t *bp, cbuf[2];
unsigned consumed, produced;
bi = 0;
bn = 8;
bp = NULL;
if ((envp = sortenvp(envp)) && (bp = calloc(bn, sizeof(char16_t)))) {
for (i = 0; envp[i]; ++i) {
for (j = 0;; j += consumed) {
consumed = abs(tpdecode(&envp[i][j], &wc));
produced = abs(pututf16(cbuf, ARRAYLEN(cbuf), wc, false));
if (CONCAT(&bp, &bi, &bn, cbuf, produced) == -1) goto error;
if (!wc) break;
}
}
++bi;
if (bi > ENV_MAX) {
e2big();
goto error;
}
free(envp);
return bp;
}
error:
free(envp);
free(bp);
return NULL;
}

View File

@@ -1,32 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_HEFTY_MKVARARGV_H_
#define COSMOPOLITAN_LIBC_CALLS_HEFTY_MKVARARGV_H_
#include "libc/alg/arraylist2.internal.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* Turns variadic program arguments into array.
*
* This is a support function for execl(), execlp(), spawnl(), etc.
*
* @note type signatures are fubar for these functions
*/
forceinline void *mkvarargv(const char *arg1, va_list va) {
size_t i, n;
const char **p, *arg;
i = 0;
n = 0;
p = NULL;
arg = arg1;
do {
if (APPEND(&p, &i, &n, &arg) == -1) {
free(p);
return NULL;
}
} while ((arg = va_arg(va, const char *)));
return p;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_HEFTY_MKVARARGV_H_ */

View File

@@ -1,88 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/alg/alg.h"
#include "libc/alg/arraylist.internal.h"
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/ntspawn.h"
#include "libc/calls/internal.h"
#include "libc/fmt/conv.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/errfuns.h"
/**
* 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 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 specifies program arguments
* @param argv[n-1] is NULL
* @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 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 *program, char *const argv[], char *const envp[],
struct NtSecurityAttributes *opt_lpProcessAttributes,
struct NtSecurityAttributes *opt_lpThreadAttributes, bool32 bInheritHandles,
uint32_t dwCreationFlags, const char16_t *opt_lpCurrentDirectory,
/*nodiscard*/ const struct NtStartupInfo *lpStartupInfo,
struct NtProcessInformation *opt_out_lpProcessInformation) {
int rc;
char16_t program16[PATH_MAX], *lpCommandLine, *lpEnvironment;
lpCommandLine = NULL;
lpEnvironment = NULL;
if (__mkntpath(program, program16) != -1 &&
(lpCommandLine = mkntcmdline(&argv[1])) &&
(lpEnvironment = mkntenvblock(envp))) {
if (CreateProcess(program16, lpCommandLine, opt_lpProcessAttributes,
opt_lpThreadAttributes, bInheritHandles,
dwCreationFlags | kNtCreateUnicodeEnvironment,
lpEnvironment, opt_lpCurrentDirectory, lpStartupInfo,
opt_out_lpProcessInformation)) {
rc = 0;
} else {
rc = __winerr();
}
} else {
rc = -1;
}
free(lpCommandLine);
free(lpEnvironment);
return rc;
}

View File

@@ -1,20 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_HEFTY_NTSPAWN_H_
#define COSMOPOLITAN_LIBC_CALLS_HEFTY_NTSPAWN_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct NtProcessInformation;
struct NtSecurityAttributes;
struct NtStartupInfo;
char **sortenvp(char *const[]) hidden nodiscard paramsnonnull();
char16_t *mkntcmdline(char *const[]) hidden nodiscard paramsnonnull();
char16_t *mkntenvblock(char *const[]) hidden nodiscard paramsnonnull();
int ntspawn(const char *, char *const[], char *const[],
struct NtSecurityAttributes *, struct NtSecurityAttributes *,
bool32, uint32_t, const char16_t *, const struct NtStartupInfo *,
struct NtProcessInformation *) paramsnonnull((1, 2, 3, 9)) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_HEFTY_NTSPAWN_H_ */

View File

@@ -1,64 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/alg/alg.h"
#include "libc/alg/arraylist.internal.h"
#include "libc/calls/hefty/ntspawn.h"
#include "libc/dce.h"
#include "libc/str/str.h"
static int CompareStrings(const char *l, const char *r) {
size_t i = 0;
while (l[i] == r[i] && r[i]) ++i;
return (l[i] & 0xff) - (r[i] & 0xff);
}
static void SortStrings(char **a, size_t n) {
char *t;
size_t i, j;
for (i = 1; i < n; ++i) {
for (t = a[i], j = i; j > 0 && CompareStrings(t, a[j - 1]) < 0; --j) {
a[j] = a[j - 1];
}
a[j] = t;
}
}
/**
* Copies environment variable pointers and sorts them.
*
* This is useful for (a) binary searching; and (b) keeping the NT
* Executive happy, which wants strings to be ordered by UNICODE
* codepoint identifiers. That's basically what uint8_t comparisons on
* UTF8-encoded data gives us.
*
* @param envp is a NULL-terminated string array
* @return newly allocated sorted copy of envp pointer array
*/
hidden textwindows nodiscard char **sortenvp(char *const envp[]) {
char **copy;
size_t n, size;
n = 0;
while (envp[n]) n++;
size = (n + 1) * sizeof(char *);
if ((copy = malloc(size))) {
memcpy(copy, envp, size);
SortStrings(copy, n);
}
return copy;
}

View File

@@ -1,26 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_HEFTY_SPAWN_H_
#define COSMOPOLITAN_LIBC_CALLS_HEFTY_SPAWN_H_
#define SPAWN_DETACH 1
#define SPAWN_TABULARASA 2
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int spawnve(unsigned, int[3], const char *, char *const[], char *const[])
paramsnonnull((3, 4, 5));
int spawnl(unsigned, int[3], const char *, const char *, ...) nullterminated()
paramsnonnull((3, 4));
int spawnlp(unsigned, int[3], const char *, const char *, ...) nullterminated()
paramsnonnull((3, 4));
int spawnle(unsigned, int[3], const char *, const char *, ...)
nullterminated((1)) paramsnonnull((3, 4));
int spawnv(unsigned, int[3], const char *, char *const[]) paramsnonnull((3, 4));
int spawnvp(unsigned, int[3], const char *, char *const[])
paramsnonnull((3, 4));
int spawnvpe(unsigned, int[3], const char *, char *const[], char *const[])
paramsnonnull((3, 4, 5));
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_HEFTY_SPAWN_H_ */

View File

@@ -1,48 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/hefty/mkvarargv.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
/**
* Launches program, with current environment.
*
* @param stdiofds may optionally be passed to customize standard i/o
* @param stdiofds[𝑖] may be -1 to receive a pipe() fd
* @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 pid of child, or -1 w/ errno
*/
int spawnl(unsigned flags, int stdiofds[3], const char *exe, const char *arg,
... /*, NULL*/) {
int rc;
va_list va;
void *argv;
rc = -1;
va_start(va, arg);
if ((argv = mkvarargv(arg, va))) {
rc = spawnve(flags, stdiofds, exe, argv, environ);
free(argv);
}
va_end(va);
return rc;
}

View File

@@ -1,53 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/hefty/mkvarargv.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
/**
* Launches program, with PATH search and current environment.
*
* @param stdiofds may optionally be passed to customize standard i/o
* @param stdiofds[𝑖] may be -1 to receive a pipe() fd
* @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 pid of child, or -1 w/ errno
*/
nodiscard int spawnlp(unsigned flags, int stdiofds[3], const char *prog,
const char *arg, ... /*, NULL*/) {
int pid;
char *exe;
va_list va;
void *argv;
char pathbuf[PATH_MAX];
pid = -1;
if ((exe = commandv(prog, pathbuf))) {
va_start(va, arg);
if ((argv = mkvarargv(arg, va))) {
pid = spawnve(flags, stdiofds, exe, argv, environ);
free(argv);
}
va_end(va);
}
return pid;
}

View File

@@ -1,98 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/bits/xchg.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/internal.h"
#include "libc/calls/hefty/ntspawn.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/internal.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/files.h"
#include "libc/nt/ipc.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/startupinfo.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h"
textwindows int spawnve$nt(unsigned flags, int stdiofds[3], const char *program,
char *const argv[], char *const envp[]) {
int pid;
size_t i;
int tubes[3];
int64_t handle, h, *x, *y;
struct NtStartupInfo sti;
struct NtProcessInformation procinfo;
handle = 0;
memset(&sti, 0, sizeof(sti));
sti.cb = sizeof(sti);
sti.dwFlags = kNtStartfUsestdhandles;
if ((pid = __getemptyfd()) == -1) return -1;
for (i = 0; i < 3; ++i) {
if (stdiofds[i] == -1) {
x = &h;
y = &sti.stdiofds[i];
if (kIoMotion[i]) xchg(&x, &y);
if ((tubes[i] = __getemptyfd()) != -1 &&
CreatePipe(x, y, &kNtIsInheritable, 0)) {
g_fds.p[tubes[i]].handle = h;
} else {
handle = -1;
}
} else {
sti.stdiofds[i] = g_fds.p[stdiofds[i]].handle;
}
}
if (handle != -1 &&
ntspawn(program, argv, envp, &kNtIsInheritable, NULL,
(flags & SPAWN_TABULARASA) ? false : true,
(flags & SPAWN_DETACH)
? (kNtCreateNewProcessGroup | kNtDetachedProcess |
kNtCreateBreakawayFromJob)
: 0,
NULL, &sti, &procinfo) != -1) {
CloseHandle(procinfo.hThread);
handle = procinfo.hProcess;
}
for (i = 0; i < 3; ++i) {
if (stdiofds[i] == -1) {
if (handle != -1) {
stdiofds[i] = tubes[i];
g_fds.p[tubes[i]].kind = kFdFile;
g_fds.p[tubes[i]].flags = 0;
CloseHandle(sti.stdiofds[i]);
} else {
CloseHandle(tubes[i]);
}
}
}
g_fds.p[pid].kind = kFdProcess;
g_fds.p[pid].handle = handle;
g_fds.p[pid].flags = O_CLOEXEC;
return pid;
}

View File

@@ -1,120 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/internal.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/fmt/conv.h"
#include "libc/mem/mem.h"
#include "libc/paths.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h"
int spawnve$sysv(unsigned flags, int stdiofds[3], const char *program,
char *const argv[], char *const envp[]) {
int rc, pid, fd;
size_t i, j, len;
int32_t tubes[3][2];
char **argv2, MZqFpD[8];
void *argv3;
pid = 0;
argv2 = NULL;
argv3 = argv;
/*
* αcτµαlly pδrταblε εxεcµταblε w/ thompson shell script
* morphology needs to be launched via command interpreter.
*/
if (endswith(program, ".com") || endswith(program, ".exe")) {
memset(MZqFpD, 0, sizeof(MZqFpD));
fd = openat$sysv(AT_FDCWD, program, O_RDONLY, 0);
read$sysv(fd, MZqFpD, sizeof(MZqFpD));
close$sysv(fd);
if (memcmp(MZqFpD, "MZqFpD", 6) == 0) {
/*
* If we got this:
*
* spawn(/bin/echo, [echo, hi, there])
*
* It will become this:
*
* spawn(/bin/sh, [sh, /bin/echo, hi, there])
*/
len = 1;
while (argv[len]) len++;
if ((argv2 = malloc((2 + len + 1) * sizeof(char *)))) {
i = 0, j = 1;
argv2[i++] = "sh";
argv2[i++] = program;
while (j < len) argv2[i++] = argv[j++];
argv2[i] = NULL;
argv3 = argv2;
program = "/bin/sh";
}
}
}
for (i = 0; i < 3; ++i) {
if (stdiofds[i] == -1) {
pid |= pipe$sysv(tubes[i]);
}
}
if (pid != -1) {
if ((pid = vfork()) == 0) {
if (flags & SPAWN_DETACH) {
if (setsid() == -1) abort();
if ((rc = fork$sysv()) == -1) abort();
if (rc > 0) _exit(0);
}
for (i = 0; i < 3; ++i) {
if (stdiofds[i] == -1) {
close$sysv(tubes[i][kIoMotion[i]]);
fd = tubes[i][!kIoMotion[i]];
} else {
fd = stdiofds[i];
}
dup2$sysv(fd, i);
}
execve$sysv(program, argv3, envp);
abort();
}
}
for (i = 0; i < 3; ++i) {
if (stdiofds[i] == -1) {
close$sysv(tubes[i][!kIoMotion[i]]);
fd = tubes[i][kIoMotion[i]];
if (pid != -1) {
stdiofds[i] = fd;
} else {
close$sysv(fd);
}
}
}
if (argv2) free(argv2);
return pid;
}

View File

@@ -1,63 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/bits/pushpop.h"
#include "libc/calls/hefty/internal.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/dce.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/errfuns.h"
/**
* Launches program, e.g.
*
* char buf[2];
* int ws, pid, fds[3] = {-1, -1, STDERR_FILENO};
* CHECK_NE(-1, (pid = spawnve(0, fds, commandv("ssh"),
* (char *const[]){"ssh", hostname, "cat", 0},
* environ)));
* CHECK_EQ(+2, write(fds[0], "hi", 2));
* CHECK_NE(-1, close(fds[0]));
* CHECK_EQ(+2, read(fds[1], buf, 2)));
* CHECK_NE(-1, close(fds[1]));
* CHECK_EQ(+0, memcmp(buf, "hi", 2)));
* CHECK_NE(-1, waitpid(pid, &ws, 0));
* CHECK_EQ(+0, WEXITSTATUS(ws));
*
* @param stdiofds may optionally be passed to customize standard i/o
* @param stdiofds[𝑖] may be -1 to receive a pipe() fd
* @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 pid of child, or -1 w/ errno
* @deprecated just use vfork() and execve()
*/
int spawnve(unsigned flags, int stdiofds[3], const char *program,
char *const argv[], char *const envp[]) {
if (!argv[0]) return einval();
int defaultfds[3] = {pushpop(STDIN_FILENO), pushpop(STDOUT_FILENO),
pushpop(STDERR_FILENO)};
if (!IsWindows()) {
return spawnve$sysv(flags, stdiofds ?: defaultfds, program, argv, envp);
} else {
return spawnve$nt(flags, stdiofds ?: defaultfds, program, argv, envp);
}
}

View File

@@ -22,28 +22,50 @@
/ Forks process without copying page tables.
/
/ This is the same as fork() except it's optimized for the case
/ where the caller invokes exec() immediately afterwards.
/ where the caller invokes execve() immediately afterwards. You
/ can also call functions like close(), dup2(), etc. You cannot
/ call read() safely but you can call pread(). Call _exit() but
/ don't call exit(). Look for the vforksafe function annotation
/
/ @return pid of child process or 0 if forked process
/ @returnstwice
vfork: testb IsWindows()
/ @vforksafe
vfork:
#if SupportsWindows()
testb IsWindows()
jnz fork$nt
#endif
mov __NR_vfork(%rip),%eax
cmp $-1,%eax
je systemfive.enosys
pop %rsi
pop %rsi # saves return address in a register
#if SupportsBsd()
testb IsBsd()
jnz vfork.bsd
#endif
syscall
push %rsi
cmp $-4095,%rax
push %rsi # note it happens twice in same page
cmp $-4095,%eax
jae systemfive.error
ret
0: ezlea __vforked,di
test %eax,%eax
jz 1f
decl (%rdi)
jns 2f # openbsd doesn't actually share mem
1: incl (%rdi)
2: ret
.endfn vfork,globl
#if SupportsBsd()
vfork.bsd:
syscall
push %rsi
jc systemfive.errno
ret
#if SupportsXnu()
testb IsXnu()
jz 0b
neg %edx # edx is 0 for parent and 1 for child
not %edx # eax always returned with childs pid
and %edx,%eax
#endif /* XNU */
jmp 0b
.endfn vfork.bsd
#endif /* BSD */