mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-12 14:09:12 +00:00
Make improvements
- Improved async signal safety of read() particularly for longjmp() - Started adding cancel cleanup handlers for locks / etc on Windows - Make /dev/tty work better particularly for uses like `foo | less` - Eagerly read console input into a linked list, so poll can signal - Fix some libc definitional bugs, which configure scripts detected
This commit is contained in:
parent
d6c2830850
commit
0c5dd7b342
85 changed files with 1062 additions and 671 deletions
|
@ -180,9 +180,6 @@ static keywords void sys_execve_nt_relay(intptr_t h, long b, long c, long d) {
|
|||
|
||||
// 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);
|
||||
|
|
|
@ -58,8 +58,7 @@ static bool IsApeFile(const char *path) {
|
|||
return true;
|
||||
} else {
|
||||
bool res = false;
|
||||
BLOCK_CANCELLATIONS;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BLOCK_SIGNALS;
|
||||
int fd;
|
||||
char buf[8];
|
||||
int flags = O_RDONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC;
|
||||
|
@ -67,8 +66,7 @@ static bool IsApeFile(const char *path) {
|
|||
res = sys_pread(fd, buf, 8, 0, 0) == 8 && IsApeLoadable(buf);
|
||||
sys_close(fd);
|
||||
}
|
||||
END_CANCELLATION_POINT;
|
||||
ALLOW_CANCELLATIONS;
|
||||
ALLOW_SIGNALS;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
260
libc/proc/fexecve.c
Normal file
260
libc/proc/fexecve.c
Normal file
|
@ -0,0 +1,260 @@
|
|||
/*-*- 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/blockcancel.internal.h"
|
||||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/proc/execve.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/stat.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/mfd.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/shm.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static bool IsAPEFd(const int fd) {
|
||||
char buf[8];
|
||||
return (sys_pread(fd, buf, 8, 0, 0) == 8) && IsApeLoadable(buf);
|
||||
}
|
||||
|
||||
static int fexecve_impl(const int fd, char *const argv[], char *const envp[]) {
|
||||
int rc;
|
||||
if (IsLinux()) {
|
||||
char path[14 + 12];
|
||||
FormatInt32(stpcpy(path, "/proc/self/fd/"), fd);
|
||||
rc = __sys_execve(path, argv, envp);
|
||||
} else if (IsFreebsd()) {
|
||||
rc = sys_fexecve(fd, argv, envp);
|
||||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
PTF_NUM = 1 << 0,
|
||||
PTF_NUM2 = 1 << 1,
|
||||
PTF_NUM3 = 1 << 2,
|
||||
PTF_ANY = 1 << 3
|
||||
} PTF_PARSE;
|
||||
|
||||
static bool ape_to_elf(void *ape, const size_t apesize) {
|
||||
static const char printftok[] = "printf '";
|
||||
static const size_t printftoklen = sizeof(printftok) - 1;
|
||||
const char *tok = memmem(ape, apesize, printftok, printftoklen);
|
||||
if (tok) {
|
||||
tok += printftoklen;
|
||||
uint8_t *dest = ape;
|
||||
PTF_PARSE state = PTF_ANY;
|
||||
uint8_t value = 0;
|
||||
for (; tok < (const char *)(dest + apesize); tok++) {
|
||||
if ((state & (PTF_NUM | PTF_NUM2 | PTF_NUM3)) &&
|
||||
(*tok >= '0' && *tok <= '7')) {
|
||||
value = (value << 3) | (*tok - '0');
|
||||
state <<= 1;
|
||||
if (state & PTF_ANY) {
|
||||
*dest++ = value;
|
||||
}
|
||||
} else if (state & PTF_NUM) {
|
||||
break;
|
||||
} else {
|
||||
if (state & (PTF_NUM2 | PTF_NUM3)) {
|
||||
*dest++ = value;
|
||||
}
|
||||
if (*tok == '\\') {
|
||||
state = PTF_NUM;
|
||||
value = 0;
|
||||
} else if (*tok == '\'') {
|
||||
return true;
|
||||
} else {
|
||||
*dest++ = *tok;
|
||||
state = PTF_ANY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
errno = ENOEXEC;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a memfd and copies fd to it.
|
||||
*
|
||||
* This does an inplace conversion of APE to ELF when detected!!!!
|
||||
*/
|
||||
static int fd_to_mem_fd(const int infd, char *path) {
|
||||
if ((!IsLinux() && !IsFreebsd()) || !_weaken(mmap) || !_weaken(munmap)) {
|
||||
return enosys();
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(infd, &st) == -1) {
|
||||
return -1;
|
||||
}
|
||||
int fd;
|
||||
if (IsLinux()) {
|
||||
fd = sys_memfd_create(__func__, MFD_CLOEXEC);
|
||||
} else if (IsFreebsd()) {
|
||||
fd = sys_shm_open(SHM_ANON, O_CREAT | O_RDWR, 0);
|
||||
} else {
|
||||
return enosys();
|
||||
}
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
void *space;
|
||||
if ((sys_ftruncate(fd, st.st_size, st.st_size) != -1) &&
|
||||
((space = _weaken(mmap)(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
fd, 0)) != MAP_FAILED)) {
|
||||
ssize_t readRc;
|
||||
readRc = pread(infd, space, st.st_size, 0);
|
||||
bool success = readRc != -1;
|
||||
if (success && (st.st_size > 8) && IsApeLoadable(space)) {
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if ((success = (flags != -1) &&
|
||||
(fcntl(fd, F_SETFD, flags & (~FD_CLOEXEC)) != -1) &&
|
||||
ape_to_elf(space, st.st_size))) {
|
||||
const int newfd = fcntl(fd, F_DUPFD, 9001);
|
||||
if (newfd != -1) {
|
||||
close(fd);
|
||||
fd = newfd;
|
||||
}
|
||||
}
|
||||
}
|
||||
const int e = errno;
|
||||
if ((_weaken(munmap)(space, st.st_size) != -1) && success) {
|
||||
if (path) {
|
||||
FormatInt32(stpcpy(path, "COSMOPOLITAN_INIT_ZIPOS="), fd);
|
||||
}
|
||||
unassert(readRc == st.st_size);
|
||||
return fd;
|
||||
} else if (!success) {
|
||||
errno = e;
|
||||
}
|
||||
}
|
||||
const int e = errno;
|
||||
close(fd);
|
||||
errno = e;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes binary executable at file descriptor.
|
||||
*
|
||||
* This is only supported on Linux and FreeBSD. APE binaries are
|
||||
* supported. Zipos is supported. Zipos fds or FD_CLOEXEC APE fds or
|
||||
* fds that fail fexecve with ENOEXEC are copied to a new memfd (with
|
||||
* in-memory APE to ELF conversion) and fexecve is (re)attempted.
|
||||
*
|
||||
* @param fd is opened executable and current file position is ignored
|
||||
* @return doesn't return on success, otherwise -1 w/ errno
|
||||
* @raise ENOEXEC if file at `fd` isn't an assimilated ELF executable
|
||||
* @raise ENOSYS on Windows, XNU, OpenBSD, NetBSD, and Metal
|
||||
*/
|
||||
int fexecve(int fd, char *const argv[], char *const envp[]) {
|
||||
int rc = 0;
|
||||
if (!argv || !envp ||
|
||||
(IsAsan() &&
|
||||
(!__asan_is_valid_strlist(argv) || !__asan_is_valid_strlist(envp)))) {
|
||||
rc = efault();
|
||||
} else {
|
||||
STRACE("fexecve(%d, %s, %s) → ...", fd, DescribeStringList(argv),
|
||||
DescribeStringList(envp));
|
||||
int savedErr = 0;
|
||||
do {
|
||||
if (!IsLinux() && !IsFreebsd()) {
|
||||
rc = enosys();
|
||||
break;
|
||||
}
|
||||
if (!__isfdkind(fd, kFdZip)) {
|
||||
bool memfdReq;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BLOCK_SIGNALS;
|
||||
strace_enabled(-1);
|
||||
memfdReq = ((rc = fcntl(fd, F_GETFD)) != -1) && (rc & FD_CLOEXEC) &&
|
||||
IsAPEFd(fd);
|
||||
strace_enabled(+1);
|
||||
ALLOW_SIGNALS;
|
||||
END_CANCELLATION_POINT;
|
||||
if (rc == -1) {
|
||||
break;
|
||||
} else if (!memfdReq) {
|
||||
fexecve_impl(fd, argv, envp);
|
||||
if (errno != ENOEXEC) {
|
||||
break;
|
||||
}
|
||||
savedErr = ENOEXEC;
|
||||
}
|
||||
}
|
||||
int newfd;
|
||||
char *path = alloca(PATH_MAX);
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BLOCK_SIGNALS;
|
||||
strace_enabled(-1);
|
||||
newfd = fd_to_mem_fd(fd, path);
|
||||
strace_enabled(+1);
|
||||
ALLOW_SIGNALS;
|
||||
END_CANCELLATION_POINT;
|
||||
if (newfd == -1) {
|
||||
break;
|
||||
}
|
||||
size_t numenvs;
|
||||
for (numenvs = 0; envp[numenvs];) ++numenvs;
|
||||
// const size_t desenvs = min(500, max(numenvs + 1, 2));
|
||||
static _Thread_local char *envs[500];
|
||||
memcpy(envs, envp, numenvs * sizeof(char *));
|
||||
envs[numenvs] = path;
|
||||
envs[numenvs + 1] = NULL;
|
||||
fexecve_impl(newfd, argv, envs);
|
||||
if (!savedErr) {
|
||||
savedErr = errno;
|
||||
}
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BLOCK_SIGNALS;
|
||||
strace_enabled(-1);
|
||||
close(newfd);
|
||||
strace_enabled(+1);
|
||||
ALLOW_SIGNALS;
|
||||
END_CANCELLATION_POINT;
|
||||
} while (0);
|
||||
if (savedErr) {
|
||||
errno = savedErr;
|
||||
}
|
||||
rc = -1;
|
||||
}
|
||||
STRACE("fexecve(%d) failed %d% m", fd, rc);
|
||||
return rc;
|
||||
}
|
|
@ -199,10 +199,7 @@ textwindows void WinMainForked(void) {
|
|||
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
|
||||
|
@ -282,7 +279,6 @@ textwindows void WinMainForked(void) {
|
|||
|
||||
// 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);
|
||||
|
|
|
@ -18,11 +18,14 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.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"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
static textwindows int sys_kill_nt_impl(int pid, int sig) {
|
||||
|
@ -50,7 +53,6 @@ static textwindows int sys_kill_nt_impl(int pid, int sig) {
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -66,9 +68,14 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
|||
return raise(sig);
|
||||
}
|
||||
|
||||
int rc;
|
||||
uint64_t m;
|
||||
m = atomic_exchange(&__get_tls()->tib_sigmask, -1);
|
||||
__proc_lock();
|
||||
pthread_cleanup_push((void *)__proc_unlock, 0);
|
||||
rc = sys_kill_nt_impl(pid, sig);
|
||||
__proc_unlock();
|
||||
pthread_cleanup_pop(true);
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
146
libc/proc/mkntcmdline.c
Normal file
146
libc/proc/mkntcmdline.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/thompike.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define APPEND(c) \
|
||||
do { \
|
||||
cmdline[k++] = c; \
|
||||
if (k == ARG_MAX / 2) { \
|
||||
return e2big(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static bool NeedsQuotes(const char *s) {
|
||||
if (!*s) return true;
|
||||
do {
|
||||
switch (*s) {
|
||||
case '"':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\n':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (*s++);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
// Converts System V argv to Windows-style command line.
|
||||
//
|
||||
// Escaping is performed and it's designed to round-trip with
|
||||
// GetDosArgv() or GetDosArgv(). This function does NOT escape
|
||||
// command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd).
|
||||
//
|
||||
// TODO(jart): this needs fuzzing and security review
|
||||
//
|
||||
// @param cmdline is output buffer
|
||||
// @param argv is an a NULL-terminated array of UTF-8 strings
|
||||
// @return 0 on success, or -1 w/ errno
|
||||
// @raise E2BIG if everything is too huge
|
||||
// @see "Everyone quotes command line arguments the wrong way" MSDN
|
||||
// @see libc/runtime/getdosargv.c
|
||||
textwindows int mkntcmdline(char16_t cmdline[ARG_MAX / 2], char *const argv[]) {
|
||||
uint64_t w;
|
||||
wint_t x, y;
|
||||
int slashes, n;
|
||||
bool needsquote;
|
||||
char *ansiargv[2];
|
||||
size_t i, j, k, s;
|
||||
if (!argv[0]) {
|
||||
bzero(ansiargv, sizeof(ansiargv));
|
||||
argv = ansiargv;
|
||||
}
|
||||
for (k = i = 0; argv[i]; ++i) {
|
||||
if (i) APPEND(u' ');
|
||||
if ((needsquote = NeedsQuotes(argv[i]))) APPEND(u'"');
|
||||
for (slashes = j = 0;;) {
|
||||
x = argv[i][j++] & 255;
|
||||
if (x >= 0300) {
|
||||
n = ThomPikeLen(x);
|
||||
x = ThomPikeByte(x);
|
||||
while (--n) {
|
||||
if ((y = argv[i][j++] & 255)) {
|
||||
x = ThomPikeMerge(x, y);
|
||||
} else {
|
||||
x = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!x) break;
|
||||
if (x == '/' || x == '\\') {
|
||||
if (!i) {
|
||||
// turn / into \ for first argv[i]
|
||||
x = '\\';
|
||||
// turn \c\... into c:\ for first argv[i]
|
||||
if (k == 2 && IsAlpha(cmdline[1]) && cmdline[0] == '\\') {
|
||||
cmdline[0] = cmdline[1];
|
||||
cmdline[1] = ':';
|
||||
}
|
||||
} else {
|
||||
// turn stuff like `less /c/...`
|
||||
// into `less c:/...`
|
||||
// turn stuff like `more <"/c/..."`
|
||||
// into `more <"c:/..."`
|
||||
if (k > 3 && IsAlpha(cmdline[k - 1]) &&
|
||||
(cmdline[k - 2] == '/' || cmdline[k - 2] == '\\') &&
|
||||
(cmdline[k - 3] == '"' || cmdline[k - 3] == ' ')) {
|
||||
cmdline[k - 2] = cmdline[k - 1];
|
||||
cmdline[k - 1] = ':';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x == '\\') {
|
||||
++slashes;
|
||||
} else if (x == '"') {
|
||||
APPEND(u'"');
|
||||
APPEND(u'"');
|
||||
APPEND(u'"');
|
||||
} else {
|
||||
for (s = 0; s < slashes; ++s) {
|
||||
APPEND(u'\\');
|
||||
}
|
||||
slashes = 0;
|
||||
w = EncodeUtf16(x);
|
||||
do {
|
||||
APPEND(w);
|
||||
} while ((w >>= 16));
|
||||
}
|
||||
}
|
||||
for (s = 0; s < (slashes << needsquote); ++s) {
|
||||
APPEND(u'\\');
|
||||
}
|
||||
if (needsquote) {
|
||||
APPEND(u'"');
|
||||
}
|
||||
}
|
||||
cmdline[k] = u'\0';
|
||||
return 0;
|
||||
}
|
202
libc/proc/mkntenvblock.c
Normal file
202
libc/proc/mkntenvblock.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/getenv.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/mem/arraylist2.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/thompike.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
static inline char *StrChr(const char *s, int c) {
|
||||
for (;; ++s) {
|
||||
if ((*s & 255) == (c & 255)) return (char *)s;
|
||||
if (!*s) return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows inline int CompareStrings(const char *l, const char *r) {
|
||||
int a, b;
|
||||
size_t i = 0;
|
||||
while ((a = ToUpper(l[i] & 255)) == (b = ToUpper(r[i] & 255)) && r[i]) ++i;
|
||||
return a - b;
|
||||
}
|
||||
|
||||
static textwindows void FixPath(char *path) {
|
||||
char *p;
|
||||
|
||||
// skip over variable name
|
||||
while (*path++) {
|
||||
if (path[-1] == '=') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// turn colon into semicolon
|
||||
// unless it already looks like a dos path
|
||||
for (p = path; *p; ++p) {
|
||||
if (p[0] == ':' && p[1] != '\\') {
|
||||
p[0] = ';';
|
||||
}
|
||||
}
|
||||
|
||||
// turn \c\... into c:\...
|
||||
p = path;
|
||||
if ((p[0] == '/' || p[0] == '\\') && IsAlpha(p[1]) &&
|
||||
(p[2] == '/' || p[2] == '\\')) {
|
||||
p[0] = p[1];
|
||||
p[1] = ':';
|
||||
}
|
||||
for (; *p; ++p) {
|
||||
if (p[0] == ';' && (p[1] == '/' || p[1] == '\\') && IsAlpha(p[2]) &&
|
||||
(p[3] == '/' || p[3] == '\\')) {
|
||||
p[1] = p[2];
|
||||
p[2] = ':';
|
||||
}
|
||||
}
|
||||
|
||||
// turn slash into backslash
|
||||
for (p = path; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\\';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows void InsertString(char **a, size_t i, const char *s,
|
||||
char buf[ARG_MAX], size_t *bufi,
|
||||
bool *have_systemroot) {
|
||||
char *v;
|
||||
size_t j, k;
|
||||
|
||||
v = StrChr(s, '=');
|
||||
|
||||
// apply fixups to var=/c/...
|
||||
if (v && v[1] == '/' && IsAlpha(v[2]) && v[3] == '/') {
|
||||
v = buf + *bufi;
|
||||
for (k = 0; s[k]; ++k) {
|
||||
if (*bufi + 1 < ARG_MAX) {
|
||||
buf[(*bufi)++] = s[k];
|
||||
}
|
||||
}
|
||||
if (*bufi < ARG_MAX) {
|
||||
buf[(*bufi)++] = 0;
|
||||
FixPath(v);
|
||||
s = v;
|
||||
}
|
||||
}
|
||||
|
||||
// append to sorted list
|
||||
for (j = i; j > 0 && CompareStrings(s, a[j - 1]) < 0; --j) {
|
||||
a[j] = a[j - 1];
|
||||
}
|
||||
a[j] = (char *)s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies sorted environment variable block for Windows.
|
||||
*
|
||||
* This is designed to meet the requirements of CreateProcess().
|
||||
*
|
||||
* @param envvars receives sorted double-NUL terminated string list
|
||||
* @param envp is an a NULL-terminated array of UTF-8 strings
|
||||
* @param extravar is a VAR=val string we consider part of envp or NULL
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @error E2BIG if total number of shorts exceeded ARG_MAX/2 (32767)
|
||||
*/
|
||||
textwindows int mkntenvblock(char16_t envvars[ARG_MAX / 2], char *const envp[],
|
||||
const char *extravar, char buf[ARG_MAX]) {
|
||||
bool v;
|
||||
uint64_t w;
|
||||
char **vars;
|
||||
wint_t x, y;
|
||||
bool have_systemroot = false;
|
||||
size_t i, j, k, n, m, bufi = 0;
|
||||
for (n = 0; envp[n];) n++;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
int nbytes = (n + 1) * sizeof(char *);
|
||||
vars = alloca(nbytes);
|
||||
CheckLargeStackAllocation(vars, nbytes);
|
||||
#pragma GCC pop_options
|
||||
for (i = 0; i < n; ++i) {
|
||||
InsertString(vars, i, envp[i], buf, &bufi, &have_systemroot);
|
||||
}
|
||||
if (extravar) {
|
||||
InsertString(vars, n++, extravar, buf, &bufi, &have_systemroot);
|
||||
}
|
||||
if (!have_systemroot && environ) {
|
||||
// https://jpassing.com/2009/12/28/the-hidden-danger-of-forgetting-to-specify-systemroot-in-a-custom-environment-block/
|
||||
struct Env systemroot;
|
||||
systemroot = __getenv(environ, "SYSTEMROOT");
|
||||
if (systemroot.s) {
|
||||
InsertString(vars, n++, environ[systemroot.i], buf, &bufi,
|
||||
&have_systemroot);
|
||||
}
|
||||
}
|
||||
for (k = i = 0; i < n; ++i) {
|
||||
j = 0;
|
||||
v = false;
|
||||
do {
|
||||
x = vars[i][j++] & 0xff;
|
||||
if (x >= 0200) {
|
||||
if (x < 0300) continue;
|
||||
m = ThomPikeLen(x);
|
||||
x = ThomPikeByte(x);
|
||||
while (--m) {
|
||||
if ((y = vars[i][j++] & 0xff)) {
|
||||
x = ThomPikeMerge(x, y);
|
||||
} else {
|
||||
x = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!v) {
|
||||
if (x != '=') {
|
||||
x = ToUpper(x);
|
||||
} else {
|
||||
v = true;
|
||||
}
|
||||
}
|
||||
w = EncodeUtf16(x);
|
||||
do {
|
||||
envvars[k++] = w & 0xffff;
|
||||
if (k == ARG_MAX / 2) {
|
||||
return e2big();
|
||||
}
|
||||
} while ((w >>= 16));
|
||||
} while (x);
|
||||
}
|
||||
envvars[k] = u'\0';
|
||||
return 0;
|
||||
}
|
|
@ -144,7 +144,7 @@ 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 &&
|
||||
if (atomic_load_explicit(&__proc.allocated, memory_order_acquire) < n &&
|
||||
(i = atomic_fetch_add(&__proc.allocated, 1)) < n) {
|
||||
proc = __proc.pool + i;
|
||||
} else {
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "libc/cosmo.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -38,6 +39,7 @@
|
|||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/w.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
@ -98,7 +100,7 @@ static textwindows int CheckZombies(int pid, int *wstatus,
|
|||
}
|
||||
|
||||
static textwindows int WaitForProcess(int pid, int *wstatus, int options,
|
||||
struct rusage *opt_out_rusage) {
|
||||
struct rusage *rusage, uint64_t *m) {
|
||||
int rc, *wv;
|
||||
nsync_cv *cv;
|
||||
struct Dll *e;
|
||||
|
@ -106,7 +108,7 @@ static textwindows int WaitForProcess(int pid, int *wstatus, int options,
|
|||
struct timespec deadline = timespec_zero;
|
||||
|
||||
// check list of processes that've already exited
|
||||
if ((rc = CheckZombies(pid, wstatus, opt_out_rusage))) {
|
||||
if ((rc = CheckZombies(pid, wstatus, rusage))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -139,16 +141,16 @@ CheckForInterrupt:
|
|||
if (_check_interrupts(kSigOpRestartable) == -1) return -1;
|
||||
deadline = GetNextDeadline(deadline);
|
||||
SpuriousWakeup:
|
||||
BEGIN_BLOCKING_OPERATION;
|
||||
++*wv;
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, *m, memory_order_release);
|
||||
rc = nsync_cv_wait_with_deadline(cv, &__proc.lock, deadline, 0);
|
||||
*m = atomic_exchange(&__get_tls()->tib_sigmask, -1);
|
||||
--*wv;
|
||||
END_BLOCKING_OPERATION;
|
||||
if (rc == ECANCELED) return ecanceled();
|
||||
if (pr && pr->iszombie) return ReapZombie(pr, wstatus, opt_out_rusage);
|
||||
if (pr && pr->iszombie) return ReapZombie(pr, wstatus, rusage);
|
||||
if (rc == ETIMEDOUT) goto CheckForInterrupt;
|
||||
unassert(!rc);
|
||||
if (!pr && (rc = CheckZombies(pid, wstatus, opt_out_rusage))) return rc;
|
||||
if (!pr && (rc = CheckZombies(pid, wstatus, rusage))) return rc;
|
||||
goto SpuriousWakeup;
|
||||
}
|
||||
|
||||
|
@ -163,9 +165,12 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
|
|||
// just does an "ignore ctrl-c" internally.
|
||||
if (pid == 0) pid = -1;
|
||||
if (pid < -1) pid = -pid;
|
||||
uint64_t m = atomic_exchange(&__get_tls()->tib_sigmask, -1);
|
||||
__proc_lock();
|
||||
rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage);
|
||||
__proc_unlock();
|
||||
pthread_cleanup_push((void *)__proc_unlock, 0);
|
||||
rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage, &m);
|
||||
pthread_cleanup_pop(true);
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue