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

@ -61,7 +61,7 @@
# build/config.mk
SHELL = /bin/sh
HOSTS ?= freebsd openbsd alpine
HOSTS ?= freebsd openbsd rhel7 rhel5 xnu win10
SANITY := $(shell build/sanitycheck $$PPID)
GNUMAKEFLAGS += --output-sync

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -153,6 +153,7 @@ MATHEMATICAL = \
DEFAULT_CPPFLAGS = \
-DIMAGE_BASE_VIRTUAL=$(IMAGE_BASE_VIRTUAL) \
-nostdinc \
-iquote - \
-iquote .
DEFAULT_CFLAGS = \

View file

@ -1,3 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# OVERVIEW
#
# Run Length Decoder Scratch Pad

View file

@ -1,102 +0,0 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/calls/hefty/copyfile.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/struct/stat.h"
#include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/madv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
/**
* @fileoverview Overcommit tutorial.
* You can allocate memory like a central banker prints money.
*/
/* #define kHugeAmount (10LU * 1024LU * 1024LU * 1024LU * 1024LU) */
#define kHugeAmount (1LU * 1024LU * 1024LU * 1024LU)
int copyfile2(const char *frompath, const char *topath, bool dontoverwrite) {
struct stat st;
ssize_t transferred;
int rc, fromfd, tofd;
int64_t inoffset, outoffset;
rc = -1;
if ((fromfd = open(frompath, O_RDONLY | O_DIRECT, 0)) != -1) {
if (fstat(fromfd, &st) != -1 &&
(tofd =
open(topath,
O_WRONLY | O_CREAT | O_DIRECT | (dontoverwrite ? O_EXCL : 0),
st.st_mode & 0777)) != -1) {
inoffset = 0;
outoffset = 0;
while (st.st_size &&
(transferred = copy_file_range(fromfd, &inoffset, tofd, &outoffset,
st.st_size, 0)) != -1) {
st.st_size -= transferred;
}
if (!st.st_size) rc = 0;
rc |= close(tofd);
}
rc |= close(fromfd);
}
return rc;
}
int main(int argc, char *argv[]) {
int fd, pid;
size_t size;
long double t1, t2;
const char *core, *core2, *core3;
volatile unsigned char *mem;
size = ROUNDUP(kHugeAmount, PAGESIZE);
core = gc(xasprintf("%s.%s", getauxval(AT_EXECFN), "core"));
core2 = gc(xasprintf("%s.%s", getauxval(AT_EXECFN), "core2"));
core3 = gc(xasprintf("%s.%s", getauxval(AT_EXECFN), "core3"));
CHECK_NE(-1, (fd = open(core, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0600)));
CHECK_NE(-1, ftruncate(fd, size));
CHECK_NE(MAP_FAILED,
(mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)));
strcpy(&mem[0], "hello\n\n\n\n\n\n\n\n\n\n");
strcpy(&mem[kHugeAmount / 2], "hello\n\n\n\n\n\n\n\n\n\n");
CHECK_NE(-1, munmap(mem, size));
CHECK_NE(-1,
(pid = spawnve(
0, (int[3]){STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO},
"o/examples/stat.com",
(char *const[]){"o/examples/stat.com", core, NULL}, environ)));
CHECK_NE(-1, waitpid(pid, NULL, 0));
CHECK_NE(-1, close(fd));
t1 = dtime(CLOCK_REALTIME);
CHECK_NE(-1, copyfile(core, core2, 0));
t2 = dtime(CLOCK_REALTIME);
printf("%.6Lf\n", t2 - t1);
t1 = dtime(CLOCK_REALTIME);
CHECK_NE(-1, copyfile2(core, core3, false));
t2 = dtime(CLOCK_REALTIME);
printf("%.6Lf\n", t2 - t1);
/* CHECK_NE(-1, unlink(core)); */
return 0;
}

View file

@ -8,6 +8,7 @@
*/
#endif
#include "libc/bits/pushpop.h"
#include "libc/calls/calls.h"
#include "libc/calls/ucontext.h"
#include "libc/log/log.h"
#include "libc/math.h"
@ -42,8 +43,7 @@ int main(int argc, char *argv[]) {
showcrashreports();
res = 0;
asm volatile(
"mov\t$0x1111111111111111,%rax\n\t"
asm volatile("mov\t$0x1111111111111111,%rax\n\t"
"mov\t$0x3333333333333333,%rcx\n\t"
"mov\t$0x4444444444444444,%rdx\n\t"
"mov\t$0x8888888888888888,%r8\n\t"
@ -53,8 +53,12 @@ int main(int argc, char *argv[]) {
"mov\t$0x0000000000000000,%rax\n\t"
"movd\t%rax,%xmm0\n\t"
"mov\t$0x1111111111111111,%rax\n\t"
"movd\t%rax,%xmm1\n\t"
"mov\t$0x2222222222222222,%rax\n\t"
"push\t%rax\n\t"
"push\t%rax\n\t"
"movdqu\t(%rsp),%xmm1\n\t"
"pop\t%rax\n\t"
"pop\t%rax\n\t"
"mov\t$0x2222222222220022,%rax\n\t"
"movd\t%rax,%xmm2\n\t"
"mov\t$0x3333333333333333,%rax\n\t"
"movd\t%rax,%xmm3\n\t"
@ -82,8 +86,9 @@ int main(int argc, char *argv[]) {
"movd\t%rax,%xmm14\n\t"
"mov\t$0xffffffffffffffff,%rax\n\t"
"movd\t%rax,%xmm15\n\t"
"fldlg2\n\t");
"fldpi\n\t");
res = *(int *)(intptr_t)boo / boo;
return res;
}

29
examples/echo.c Normal file
View file

@ -0,0 +1,29 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
int main(int argc, char *argv[]) {
int i, j;
bool wantnewline;
if (argc > 1 && !strcmp(argv[1], "-n")) {
i = 2;
wantnewline = false;
} else {
i = 1;
wantnewline = true;
}
for (j = 0; i + j < argc; ++j) {
if (j) fputc(' ', stdout);
fputs(argv[i + j], stdout);
}
if (wantnewline) fputc('\n', stdout);
return 0;
}

17
examples/exec.c Normal file
View file

@ -0,0 +1,17 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/runtime/runtime.h"
int main(int argc, char *argv[]) {
if (argc < 2) return 1;
execv(argv[1], argv + 1);
abort();
}

18
examples/fork.c Normal file
View file

@ -0,0 +1,18 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/stdio/stdio.h"
int main(int argc, char *argv[]) {
int pid;
pid = fork();
fprintf(stderr, "fork returned %d\n", pid);
return 0;
}

19
examples/hostname.c Normal file
View file

@ -0,0 +1,19 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/log/check.h"
#include "libc/stdio/stdio.h"
int main(int argc, char *argv[]) {
char hostname[254];
CHECK_NE(-1, gethostname(hostname, sizeof(hostname)));
puts(hostname);
return 0;
}

View file

@ -15,7 +15,6 @@
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/winsize.h"
#include "libc/errno.h"
@ -148,7 +147,6 @@ struct ZipGames {
static int frame_;
static int drain_;
static int playfd_;
static int devnull_;
static int playpid_;
static bool exited_;
static bool timeout_;
@ -308,21 +306,6 @@ void GetTermSize(void) {
WriteStringNow("\e[0m\e[H\e[J");
}
bool TrySpeaker(const char* prog, char* const* args) {
int rc;
int fds[3];
fds[0] = -1;
fds[1] = devnull_;
fds[2] = devnull_;
if ((rc = spawnve(0, fds, prog, args, environ)) != -1) {
playpid_ = rc;
playfd_ = fds[0];
return true;
} else {
return false;
}
}
void IoInit(void) {
GetTermSize();
xsigaction(SIGINT, (void*)OnCtrlC, 0, 0, NULL);
@ -1700,6 +1683,8 @@ char* GetLine(void) {
int PlayGame(const char* romfile, const char* opt_tasfile) {
FILE* fp;
int devnull;
int pipefds[2];
inputfn_ = opt_tasfile;
if (!(fp = fopen(romfile, "rb"))) {
@ -1716,13 +1701,23 @@ int PlayGame(const char* romfile, const char* opt_tasfile) {
// open speaker
// todo: this needs plenty of work
devnull_ = open("/dev/null", O_WRONLY);
if ((ffplay_ = commandvenv("FFPLAY", "ffplay"))) {
const char* args[] = {
"ffplay", "-nodisp", "-loglevel", "quiet", "-fflags", "nobuffer", "-ac",
"1", "-ar", "1789773", "-f", "s16le", "pipe:", NULL,
devnull = open("/dev/null", O_WRONLY | O_CLOEXEC);
pipe2(pipefds, O_CLOEXEC);
if (!(playpid_ = vfork())) {
const char* const args[] = {
"ffplay", "-nodisp", "-loglevel", "quiet", "-fflags",
"nobuffer", "-ac", "1", "-ar", "1789773",
"-f", "s16le", "pipe:", NULL,
};
TrySpeaker(ffplay_, (char* const*)args);
dup2(pipefds[0], 0);
dup2(devnull, 1);
dup2(devnull, 2);
execv(ffplay_, (char* const*)args);
abort();
}
close(pipefds[0]);
playfd_ = pipefds[1];
} else {
fputs("\nWARNING\n\
\n\

View file

@ -8,7 +8,6 @@
*/
#endif
#include "libc/calls/calls.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/struct/rusage.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
@ -23,26 +22,30 @@ void Show(const char *name, long measurement, const char *unit) {
fprintf(stderr, "%-*s%,*d %s\n", 32, name, 32, measurement, unit);
}
long TvToUs(struct timeval tv) {
return 1000000l * tv.tv_usec + tv.tv_sec;
long TvToNs(struct timeval tv) {
return tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
}
int main(int argc, char *argv[]) {
const char *exe;
int pid, wstatus;
long double ts1, ts2;
struct rusage rusage;
char pathbuf[PATH_MAX];
if (argc < 2) {
fprintf(stderr, "Usage: %s PROG [ARGS...]\n", argv[0]);
return 1;
}
memset(&rusage, -1, sizeof(rusage));
CHECK_GT(argc, 1);
CHECK_NOTNULL((exe = commandv(argv[1], pathbuf)));
ts1 = nowl();
CHECK_NE(-1, (pid = spawnve(0, NULL, exe, &argv[1], environ)));
if (!(pid = vfork())) {
execvp(argv[1], argv + 1);
abort();
}
CHECK_NE(-1, wait4(pid, &wstatus, 0, &rusage));
ts2 = nowl();
Show("wall time", lroundl((ts2 - ts1) * 1e9l), "ns");
Show("user time", TvToUs(rusage.ru_utime), "µs");
Show("sys time", TvToUs(rusage.ru_stime), "µs");
Show("user time", TvToNs(rusage.ru_utime), "ns");
Show("sys time", TvToNs(rusage.ru_stime), "ns");
Show("maximum resident set size", rusage.ru_maxrss, "");
Show("integral shared memory size", rusage.ru_ixrss, "");
Show("integral unshared data size", rusage.ru_idrss, "");
@ -57,5 +60,9 @@ int main(int argc, char *argv[]) {
Show("signals received", rusage.ru_nsignals, "");
Show("voluntary context switches", rusage.ru_nvcsw, "");
Show("involuntary context switches", rusage.ru_nivcsw, "");
if (WIFEXITED(wstatus)) {
return WEXITSTATUS(wstatus);
} else {
return 128 + WTERMSIG(wstatus);
}
}

23
examples/spawn.c Normal file
View file

@ -0,0 +1,23 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/runtime/runtime.h"
int main(int argc, char *argv[]) {
int pid, wstatus;
if (argc < 2) return 1;
pid = fork();
if (!pid) {
execv(argv[1], argv + 1);
abort();
}
waitpid(pid, &wstatus, 0);
return WEXITSTATUS(wstatus);
}

View file

@ -1,60 +0,0 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/alg/alg.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/fmt/conv.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/x/x.h"
const char kProgram[] = "o/default/examples/hello.com";
int main(int argc, char *argv[]) {
/**
* Runs make if hello.com doesn't exist.
*
* 1. gc() automates calling free() on return.
* 2. xasprintf("foo %s", "bar") is our version of "foo %s" % ("bar")
* 3. Demonstrates correct escaping for bourne shell
*/
if (!fileexists(kProgram)) {
system(gc(xasprintf("%s '%s'", "make -j4",
gc(replacestr(kProgram, "'", "'\"'\"'")))));
}
/**
* Our version of subprocess.Popen
* 1. Doesn't require fork() so pain-free on NT
* 2. Google checks are like assert() but better
*/
ssize_t transferred;
int child, wstatus, procfds[3] = {STDIN_FILENO, -1, STDERR_FILENO};
CHECK_NE(-1,
(child = spawnve(0, procfds, /* run w/o shell */ kProgram,
(char *const[]){/* argv[0] */ basename(kProgram),
/* argv[1] */ "boop",
/* sentinel */ NULL},
environ)));
printf("%s %s: ", kProgram, "says");
fflush(stdout);
for (;;) {
transferred = copyfd(procfds[1], NULL, fileno(stdout), NULL, INT_MAX, 0);
CHECK_NE(-1, transferred);
if (!transferred) break;
}
CHECK_NE(-1, waitpid(child, &wstatus, 0));
CHECK_EQ(0, WEXITSTATUS(wstatus));
return 0;
}

15
examples/system.c Normal file
View file

@ -0,0 +1,15 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/stdio/stdio.h"
int main(int argc, char *argv[]) {
system("notepad");
return 0;
}

View file

@ -21,13 +21,6 @@ void qsort_r(void *, size_t, size_t,
int tarjan(int, const int (*)[2], int, int[], int[], int *)
paramsnonnull((2, 4)) nocallback nothrow;
void *memmem(const void *, size_t, const void *, size_t)
paramsnonnull() nothrow nocallback nosideeffect;
void *memmem16(const void *, size_t, const void *, size_t)
paramsnonnull() nothrow nocallback nosideeffect;
void *wmemmem(const void *, size_t, const void *, size_t)
paramsnonnull() nothrow nocallback nosideeffect;
#define __algalloc returnspointerwithnoaliases nothrow nocallback nodiscard
char *replacestr(const char *, const char *, const char *)

View file

@ -29,7 +29,7 @@
* @return newly allocated memory that must be free()'d or NULL w/ errno
* @error ENOMEM, EINVAL
*/
char *(replacestr)(const char *s, const char *needle, const char *replacement) {
char *replacestr(const char *s, const char *needle, const char *replacement) {
char *p1, *p2, *res_p;
size_t left, nlen, rlen, res_i, res_n;
if (*needle) {

9
libc/bits/likely.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef COSMOPOLITAN_LIBC_BITS_LIKELY_H_
#define COSMOPOLITAN_LIBC_BITS_LIKELY_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#define likely(expr) __builtin_expect(!!(expr), 1)
#define unlikely(expr) __builtin_expect(!!(expr), 0)
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_BITS_LIKELY_H_ */

View file

@ -29,7 +29,6 @@
* @param mode can be R_OK, W_OK, X_OK, F_OK
* @return 0 if ok, or -1 and sets errno
* @asyncsignalsafe
* @syscall
*/
int access(const char *path, int mode) {
char16_t path16[PATH_MAX];

View file

@ -29,9 +29,10 @@ static struct AtFork {
} g_atfork;
/**
* Registers function to be called when PID changes.
* Registers function to be called by fork() in child.
*
* @return 0 on success, or -1 w/ errno
* @note vfork() won't invoke callbacks
*/
int atfork(void *fn, void *arg) {
if (g_atfork.i == ARRAYLEN(g_atfork.p)) return enomem();
@ -40,9 +41,9 @@ int atfork(void *fn, void *arg) {
}
/**
* Triggers atfork() callbacks.
* Triggers callbacks registered by atfork().
*
* Only fork() should call this.
* @note only fork() should call this
*/
void __onfork(void) {
size_t i;

View file

@ -43,7 +43,7 @@
#define WEXITSTATUS(s) (((s)&0xff00) >> 8)
#define WIFCONTINUED(s) ((s) == 0xffff)
#define WIFEXITED(s) (!WTERMSIG(s))
#define WIFSIGNALED(s) (((s)&0xffff) - 1U < 0xffu)
#define WIFSIGNALED(s) (((s)&0xffff) - 1u < 0xffu)
#define WIFSTOPPED(s) ((short)((((s)&0xffff) * 0x10001) >> 8) > 0x7f00)
#define WSTOPSIG(s) WEXITSTATUS(s)
#define WTERMSIG(s) ((s)&0x7f)
@ -148,8 +148,8 @@ int openanon(char *, unsigned) nodiscard;
int openat(int, const char *, int, ...);
int pause(void);
int personality(uint64_t);
int pipe(int[hasatleast 2]) nodiscard;
int pipe2(int[hasatleast 2], int) nodiscard;
int pipe(int[hasatleast 2]);
int pipe2(int[hasatleast 2], int);
int posix_fadvise(int, uint64_t, uint64_t, int);
int posix_fallocate(int, int64_t, int64_t);
int posix_madvise(void *, uint64_t, int);
@ -226,6 +226,10 @@ uint32_t getsid(int) nosideeffect;
uint32_t gettid(void) nosideeffect;
uint32_t getuid(void) nosideeffect;
uint32_t umask(int32_t);
long ptrace(int, int, void *, void *);
int chroot(const char *);
int prctl();
int sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
#define getcwd(BUF, SIZE) \
(__builtin_constant_p(BUF) && (&(BUF)[0] == NULL) ? get_current_dir_name() \

View file

@ -74,6 +74,15 @@ o/$(MODE)/libc/calls/ntcontext2linux.o: \
OVERRIDE_COPTS += \
-O3
o/$(MODE)/libc/calls/execl.o \
o/$(MODE)/libc/calls/execle.o \
o/$(MODE)/libc/calls/execlp.o \
o/$(MODE)/libc/calls/execve-nt.o \
o/$(MODE)/libc/calls/execve-sysv.o \
o/$(MODE)/libc/calls/mkntenvblock.o: \
OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED
LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)))
LIBC_CALLS_SRCS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_SRCS))
LIBC_CALLS_HDRS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_HDRS))

View file

@ -32,7 +32,7 @@
*
* @return 0 on success, or -1 w/ errno
* @asyncsignalsafe
* @syscall
* @vforksafe
*/
int close(int fd) {
int rc;
@ -52,7 +52,7 @@ int close(int fd) {
} else {
rc = ebadf();
}
if (fd < g_fds.n) {
if (!__vforked && fd < g_fds.n) {
g_fds.p[fd].kind = kFdEmpty;
g_fds.f = MIN(g_fds.f, fd);
}

View file

@ -19,79 +19,41 @@
#include "libc/bits/progn.internal.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/nt/ntdll.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/errfuns.h"
static int accessexe(char pathname[hasatleast PATH_MAX], size_t len,
const char *ext) {
len = stpcpy(&pathname[len], ext) - &pathname[0];
if (isexecutable(pathname)) {
return len;
} else {
return -1;
}
}
static int accesscmd(char pathname[hasatleast PATH_MAX], const char *path,
const char *name, size_t namelen) { /* cf. %PATHEXT% */
int rc;
char *p;
bool hasdot;
size_t pathlen, len;
pathlen = strlen(path);
static bool AccessCommand(char path[hasatleast PATH_MAX], const char *name,
size_t namelen, size_t pathlen) {
if (pathlen + 1 + namelen + 1 + 4 + 1 > PATH_MAX) return -1;
p = mempcpy(pathname, path, pathlen);
if (pathlen && pathname[pathlen - 1] != '/') *p++ = '/';
p = mempcpy(p, name, namelen);
len = p - &pathname[0];
hasdot = !!memchr(basename(name), '.', namelen);
if ((hasdot && (rc = accessexe(pathname, len, "")) != -1) ||
(!hasdot &&
((rc = accessexe(pathname, len, ".com")) != -1 ||
(IsWindows() && (rc = accessexe(pathname, len, ".exe")) != -1) ||
(!IsWindows() && (rc = accessexe(pathname, len, "")) != -1)))) {
return rc;
} else {
return -1;
if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) {
path[pathlen++] = '/';
}
memcpy(path + pathlen, name, namelen + 1);
if (isexecutable(path)) return true;
memcpy(path + pathlen + namelen, ".com", 5);
if (isexecutable(path)) return true;
memcpy(path + pathlen + namelen, ".exe", 5);
if (isexecutable(path)) return true;
return false;
}
static int searchcmdpath(char pathname[hasatleast PATH_MAX], const char *name,
static bool SearchPath(char path[hasatleast PATH_MAX], const char *name,
size_t namelen) {
int rc;
char *path, *pathtok, ep[PATH_MAX];
rc = -1;
if (!memccpy(ep,
firstnonnull(emptytonull(getenv("PATH")),
"/bin:/usr/local/bin:/usr/bin"),
'\0', sizeof(ep))) {
return enomem();
size_t i;
const char *p;
p = firstnonnull(emptytonull(getenv("PATH")), "/bin:/usr/local/bin:/usr/bin");
for (;; p += i) {
while (*p == ':' || *p == ';') ++p;
if (!*p) break;
for (i = 0; i < PATH_MAX && p[i] && p[i] != ':' && p[i] != ';'; ++i) {
path[i] = p[i];
}
pathtok = ep;
while ((path = strsep(&pathtok, IsWindows() ? ";" : ":"))) {
if (strchr(path, '=')) continue;
if ((rc = accesscmd(pathname, path, name, namelen)) != -1) {
break;
if (AccessCommand(path, name, namelen, i)) {
return true;
}
}
return rc;
}
static char *mkcmdquery(const char *name, size_t namelen,
char scratch[hasatleast PATH_MAX]) {
char *p;
if (namelen + 1 + 1 > PATH_MAX) return NULL;
p = mempcpy(scratch, name, namelen);
*p++ = '=';
*p++ = '\0';
if (IsWindows() || IsXnu()) strntolower(scratch, namelen);
return &scratch[0];
return false;
}
/**
@ -100,20 +62,27 @@ static char *mkcmdquery(const char *name, size_t namelen,
* @return execve()'able path, or NULL w/ errno
* @errno ENOENT, EACCES, ENOMEM
* @see free(), execvpe()
* @asyncsignalsafe
* @vforksafe
*/
char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) {
char *p;
size_t len;
size_t namelen;
int rc, olderr;
if (!(namelen = strlen(name))) return PROGN(enoent(), NULL);
if (namelen + 1 > PATH_MAX) return PROGN(enametoolong(), NULL);
if (name[0] == '/' || name[0] == '\\') {
memcpy(pathbuf, name, namelen + 1);
return pathbuf;
}
olderr = errno;
if (!(len = strlen(name))) return PROGN(enoent(), NULL);
if (memchr(name, '=', len)) return PROGN(einval(), NULL);
if ((IsWindows() &&
((rc = accesscmd(pathbuf, kNtSystemDirectory, name, len)) != -1 ||
(rc = accesscmd(pathbuf, kNtWindowsDirectory, name, len)) != -1)) ||
(rc = accesscmd(pathbuf, "", name, len)) != -1 ||
(!strpbrk(name, "/\\") &&
(rc = searchcmdpath(pathbuf, name, len)) != -1)) {
(AccessCommand(pathbuf, name, namelen,
stpcpy(pathbuf, kNtSystemDirectory) - pathbuf) ||
AccessCommand(pathbuf, name, namelen,
stpcpy(pathbuf, kNtWindowsDirectory) - pathbuf))) ||
AccessCommand(strcpy(pathbuf, ""), name, namelen, 0) ||
SearchPath(pathbuf, name, namelen)) {
errno = olderr;
return pathbuf;
} else {

View file

@ -29,6 +29,7 @@
* Implements dup(), dup2(), and dup3() for Windows NT.
*/
textwindows int dup$nt(int oldfd, int newfd, int flags) {
int64_t proc;
if (!__isfdkind(oldfd, kFdFile)) return ebadf();
if (newfd == -1) {
if ((newfd = __getemptyfd()) == -1) {
@ -41,9 +42,9 @@ textwindows int dup$nt(int oldfd, int newfd, int flags) {
} else {
return -1;
}
if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle,
GetCurrentProcess(), &g_fds.p[newfd].handle, 0, true,
kNtDuplicateSameAccess)) {
proc = GetCurrentProcess();
if (DuplicateHandle(proc, g_fds.p[oldfd].handle, proc, &g_fds.p[newfd].handle,
0, true, kNtDuplicateSameAccess)) {
g_fds.p[newfd].kind = g_fds.p[oldfd].kind;
g_fds.p[newfd].flags = flags;
return newfd;

View file

@ -26,7 +26,7 @@
* @param fd remains open afterwards
* @return some arbitrary new number for fd
* @asyncsignalsafe
* @syscall
* @vforksafe
*/
nodiscard int dup(int fd) {
if (!IsWindows()) {

View file

@ -28,7 +28,7 @@
* unless it's equal to oldfd, in which case dup2() is a no-op
* @return new file descriptor, or -1 w/ errno
* @asyncsignalsafe
* @syscall
* @vforksafe
*/
int dup2(int oldfd, int newfd) {
if (oldfd == newfd) return newfd;

View file

@ -31,16 +31,11 @@ int32_t dup3$sysv(int32_t oldfd, int32_t newfd, int flags) {
if ((fd == -1 && errno == ENOSYS) || fd == __NR_dup3_linux) {
demodernize = true;
errno = olderr;
goto OldSkool;
}
} else if (demodernize) {
goto OldSkool;
} else {
fd = __dup3$sysv(oldfd, newfd, flags);
return fd;
}
return fd;
OldSkool:
fd = dup2$sysv(oldfd, newfd);
if (flags) fd = fixupnewfd$sysv(fd, flags);
return fd;
} else if (!demodernize) {
return __dup3$sysv(oldfd, newfd, flags);
}
return fixupnewfd$sysv(dup2$sysv(oldfd, newfd), flags);
}

View file

@ -16,10 +16,9 @@
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"
#include "libc/mem/alloca.h"
#include "libc/runtime/runtime.h"
/**
* Executes program, with current environment.
@ -31,16 +30,23 @@
* @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)
* @asyncsignalsafe
* @vforksafe
*/
int execl(const char *exe, const char *arg, ... /*, NULL*/) {
va_list va;
void *argv;
int i;
char **argv;
va_list va, vb;
va_copy(vb, va);
va_start(va, arg);
if ((argv = mkvarargv(arg, va))) {
execve(exe, argv, environ);
free(argv);
}
for (i = 0; va_arg(va, const char *); ++i) donothing;
va_end(va);
return -1;
argv = alloca((i + 2) * sizeof(char *));
va_start(vb, arg);
argv[0] = arg;
for (i = 1;; ++i) {
if (!(argv[i] = va_arg(vb, const char *))) break;
}
va_end(vb);
return execv(exe, argv);
}

View file

@ -17,8 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/hefty/mkvarargv.h"
#include "libc/mem/mem.h"
#include "libc/mem/alloca.h"
/**
* Executes program, with custom environment.
@ -31,17 +30,25 @@
* @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)
* @asyncsignalsafe
* @vforksafe
*/
int execle(const char *exe, const char *arg,
... /*, NULL, char *const envp[] */) {
va_list va;
void *argv;
int i;
va_list va, vb;
char **argv, **envp;
va_copy(vb, va);
va_start(va, arg);
if ((argv = mkvarargv(arg, va))) {
execve(exe, argv, va_arg(va, char *const *));
free(argv);
}
for (i = 0; va_arg(va, const char *); ++i) donothing;
envp = va_arg(va, char **);
va_end(va);
return -1;
argv = alloca((i + 2) * sizeof(char *));
va_start(vb, arg);
argv[0] = arg;
for (i = 1;; ++i) {
if (!(argv[i] = va_arg(vb, const char *))) break;
}
va_end(vb);
return execve(exe, argv, envp);
}

View file

@ -17,8 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/hefty/mkvarargv.h"
#include "libc/mem/mem.h"
#include "libc/mem/alloca.h"
#include "libc/runtime/runtime.h"
/**
@ -31,20 +30,26 @@
* @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
* @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];
if ((exe = commandv(prog, pathbuf))) {
va_list va;
void *argv;
if (!(exe = commandv(prog, pathbuf))) return -1;
va_copy(vb, va);
va_start(va, arg);
if ((argv = mkvarargv(arg, va))) {
execve(exe, argv, environ);
free(argv);
}
for (i = 0; va_arg(va, const char *); ++i) donothing;
va_end(va);
argv = alloca((i + 2) * sizeof(char *));
va_start(vb, arg);
argv[0] = arg;
for (i = 1;; ++i) {
if (!(argv[i] = va_arg(vb, const char *))) break;
}
return -1;
va_end(vb);
return execv(exe, argv);
}

View file

@ -16,12 +16,13 @@
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"
#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);

View file

@ -16,9 +16,9 @@
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/calls/ntspawn.h"
#include "libc/mem/alloca.h"
#include "libc/nt/accounting.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/enum/status.h"
@ -27,13 +27,12 @@
#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;
int rc;
size_t i;
uint32_t dwExitCode;
struct NtStartupInfo startinfo;
struct NtProcessInformation procinfo;
@ -48,20 +47,18 @@ textwindows int execve$nt(const char *program, char *const argv[],
close(i);
}
}
if (ntspawn(program, argv, envp, NULL, NULL, true, 0, NULL, &startinfo,
&procinfo) == -1) {
return -1;
}
for (i = 0; argv[i];) ++i;
i = (i + 1) * sizeof(char *);
argv = memcpy(alloca(i), argv, i);
memcpy(argv, &program, sizeof(program));
rc = ntspawn(argv, envp, NULL, NULL, true, 0, NULL, &startinfo, &procinfo);
if (rc == -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);
CloseHandle(procinfo.hProcess);
ExitProcess(dwExitCode);
}

View file

@ -1,7 +1,7 @@
/*-*- 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
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
@ -16,17 +16,24 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/runtime/gc.h"
#include "libc/calls/hefty/ntspawn.h"
#include "libc/testlib/testlib.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/errno.h"
#include "libc/mem/alloca.h"
#include "libc/paths.h"
#include "libc/str/str.h"
TEST(sortenvp, test) {
char *envp[] = {"u=b", "c=d", "韩=非", "uh=d", "hduc=d", NULL};
char **sortedenvp = gc(sortenvp(envp));
EXPECT_STREQ("c=d", sortedenvp[0]);
EXPECT_STREQ("hduc=d", sortedenvp[1]);
EXPECT_STREQ("u=b", sortedenvp[2]);
EXPECT_STREQ("uh=d", sortedenvp[3]);
EXPECT_STREQ("韩=非", sortedenvp[4]);
EXPECT_EQ(NULL, sortedenvp[5]);
int execve$sysv(const char *prog, char *const argv[], char *const envp[]) {
size_t i, n;
char **shargs, bash[PATH_MAX];
if (__execve$sysv(prog, argv, envp) != -1) return 0;
if (errno != ENOEXEC) return -1;
for (i = 0; argv[i];) ++i;
shargs = alloca((i + 2) * sizeof(char *));
memcpy(shargs + 2, argv + 1, i * sizeof(char *));
shargs[0] = !IsFreebsd() ? _PATH_BSHELL
: firstnonnull(commandv("bash", bash), _PATH_BSHELL);
shargs[1] = prog;
return __execve$sysv(shargs[0], shargs, envp);
}

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/hefty/internal.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
@ -32,6 +31,7 @@
* @param envp[n-1] is NULL
* @return doesn't return, or -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
int execve(const char *program, char *const argv[], char *const envp[]) {
if (!IsWindows()) {

View file

@ -16,12 +16,13 @@
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"
#include "libc/runtime/runtime.h"
/**
* Replaces process, with path search, using default environment.
* @notasyncsignalsafe
* @asyncsignalsafe
* @vforksafe
*/
int execvp(const char *file, char *const argv[]) {
return execvpe(file, argv, environ);

View file

@ -28,13 +28,12 @@
* @param argv is [file,argv..argvₙ,NULL]
* @param envp is ["key=val",...,NULL]
* @return doesn't return on success, otherwise -1 w/ errno
* @notasyncsignalsafe
* @asyncsignalsafe
* @vforksafe
*/
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;
if (!(exe = commandv(prog, pathbuf))) return -1;
return execve(exe, argv, envp);
}

View file

@ -16,7 +16,6 @@
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/internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/hefty/internal.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/sysv/consts/at.h"

View file

@ -36,6 +36,7 @@
* @param mode can be 0, FALLOC_xxx
* @param length is how much physical space to reserve / commit
* @return 0 on success, or -1 w/ errno
* @note limited availability on rhel5 and openbsd
* @see ftruncate()
* @syscall
*/
@ -68,7 +69,7 @@ int fallocate(int fd, int32_t mode, int64_t offset, int64_t length) {
*/
return ftruncate$nt(fd, length);
} else {
return eopnotsupp();
return enosys();
}
} else {
return enosys();

View file

@ -25,7 +25,7 @@
textwindows int fcntl$nt(int fd, int cmd, unsigned arg) {
uint32_t flags;
if (!__isfdkind(fd, kFdFile)) return ebadf();
if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdSocket)) {
switch (cmd) {
case F_GETFL:
return g_fds.p[fd].flags;
@ -48,4 +48,7 @@ textwindows int fcntl$nt(int fd, int cmd, unsigned arg) {
default:
return 0; /* TODO(jart): Implement me. */
}
} else {
return ebadf();
}
}

View file

@ -27,9 +27,9 @@
*/
int fixupnewfd$sysv(int fd, int flags) {
if (fd != -1) {
if (flags & O_CLOEXEC) fcntl$sysv(fd, F_SETFD, FD_CLOEXEC);
fcntl$sysv(fd, F_SETFL,
(fcntl$sysv(fd, F_GETFL, 0) ^ (flags & O_NONBLOCK)));
if (flags & O_CLOEXEC) {
fcntl$sysv(fd, F_SETFD, FD_CLOEXEC);
}
}
return fd;
}

View file

@ -46,7 +46,6 @@ textwindows int fstat$nt(int64_t handle, struct stat *st) {
break;
case kNtFileTypeDisk:
if (GetFileInformationByHandle(handle, &wst)) {
dprintf(1, "handle = %ld\n", handle);
st->st_mode =
(S_IRUSR | S_IXUSR |
(!(wst.dwFileAttributes & kNtFileAttributeReadonly) ? S_IWUSR

View file

@ -20,6 +20,7 @@
#include "libc/calls/internal.h"
#include "libc/calls/struct/utsname.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/macros.h"
#include "libc/nt/enum/computernameformat.h"
#include "libc/nt/errors.h"
@ -28,6 +29,9 @@
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#define CTL_KERN 1
#define KERN_HOSTNAME 10
/**
* Returns name of host system, e.g.
*
@ -35,18 +39,35 @@
* ^^^^^^^^^^^^
*/
int gethostname(char *name, size_t len) {
uint32_t nSize;
char16_t name16[256];
struct utsname u;
if (len < 1) return einval();
if (!name) return efault();
if (!IsWindows()) {
if (uname(&u) == -1) return -1;
if (!memccpy(name, u.nodename, '\0', len)) {
name[len - 1] = '\0';
if (IsBsd()) {
char *p;
int cmd[2];
char buf[254];
size_t buflen;
cmd[0] = CTL_KERN;
cmd[1] = KERN_HOSTNAME;
buflen = sizeof(buf);
if (sysctl(cmd, 2, buf, &buflen, NULL, 0) == -1) {
if (errno == ENOMEM) errno = ENAMETOOLONG;
return -1;
}
strncpy(name, buf, len);
name[len - 1] = '\0';
if ((p = strchr(name, '.'))) *p = '\0';
return 0;
} else {
struct utsname u;
if (uname(&u) == -1) return -1;
memccpy(name, u.nodename, '\0', len);
name[len - 1] = '\0';
return 0;
}
} else {
uint32_t nSize;
char16_t name16[256];
nSize = ARRAYLEN(name16);
if (GetComputerNameEx(kNtComputerNameDnsHostname, name16, &nSize)) {
tprecode16to8(name, len, name16);

View file

@ -26,8 +26,8 @@
* @asyncsignalsafe
*/
uint32_t gettid(void) {
if (!IsWindows()) {
uint32_t res;
if (!IsWindows()) {
res = gettid$sysv();
if (res <= 0) {
res = getpid();

View file

@ -20,19 +20,21 @@
#include "libc/bits/weaken.h"
#include "libc/calls/internal.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
int __ensurefds(int fd) {
size_t i, n;
size_t n;
struct Fd *p;
if (fd < g_fds.n) return fd;
if (weaken(realloc)) {
if ((p = weaken(realloc)(
g_fds.p != g_fds.__init_p ? g_fds.p : NULL,
(n = MAX(fd + 1, (i = g_fds.n) << 1)) * sizeof(*p)))) {
do {
p[i++].kind = kFdEmpty;
} while (i < n);
if (weaken(malloc)) {
n = MAX(fd + 1, g_fds.n + (g_fds.n << 1));
if ((p = weaken(malloc)(n * sizeof(*p)))) {
memcpy(p, g_fds.p, g_fds.n * sizeof(*p));
memset(p + g_fds.n, 0, (n - g_fds.n) * sizeof(*p));
if (g_fds.p != g_fds.__init_p && weaken(free)) {
weaken(free)(g_fds.p);
}
g_fds.p = p;
g_fds.n = n;
return fd;

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

@ -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);
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,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,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,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 */

View file

@ -9,6 +9,7 @@
#include "libc/dce.h"
#include "libc/limits.h"
#include "libc/macros.h"
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/nt/struct/securityattributes.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/nt/struct/systeminfo.h"
@ -37,7 +38,7 @@ struct IoctlPtmGet {
};
struct Fds {
size_t f; // arbitrary free slot start search index
size_t f; // lowest free slot
size_t n; // monotonic capacity
struct Fd {
int64_t handle;
@ -59,6 +60,7 @@ struct Fds {
extern const struct Fd kEmptyFd;
hidden extern int __vforked;
hidden extern int g_sighandrvas[NSIG];
hidden extern struct Fds g_fds;
hidden extern struct NtSystemInfo g_ntsysteminfo;
@ -91,6 +93,7 @@ forceinline size_t clampio(size_t size) {
char *getcwd$sysv(char *, u64) hidden;
i32 __dup3$sysv(i32, i32, i32) hidden;
i32 __execve$sysv(const char *, char *const[], char *const[]) hidden;
i32 __fstat$sysv(i32, struct stat *) hidden;
i32 __fstatat$sysv(i32, const char *, struct stat *, i32) hidden;
i32 __pipe2$sysv(i32[hasatleast 2], u32) hidden;
@ -119,7 +122,8 @@ i32 fsync$sysv(i32) hidden;
i32 ftruncate$sysv(i32, i64) hidden;
i32 futimes$sysv(i32, const struct timeval *) hidden;
i32 futimesat$sysv(i32, const char *, const struct timeval *) hidden;
i32 getdents(i32, char *, u32) hidden;
i32 getdents(i32, char *, u32, i64 *) hidden;
i32 getitimer$sysv(i32, struct itimerval *) hidden;
i32 getppid$sysv(void) hidden;
i32 getpriority$sysv(i32, u32) hidden;
i32 getrlimit$sysv(i32, struct rlimit *) hidden;
@ -150,7 +154,10 @@ i32 sched_setaffinity$sysv(i32, u64, const void *) hidden;
i32 sched_yield$sysv(void) hidden;
i32 setitimer$sysv(i32, const struct itimerval *, struct itimerval *) hidden;
i32 setpriority$sysv(i32, u32, i32) hidden;
i32 setresgid$sysv(uint32_t, uint32_t, uint32_t) hidden;
i32 setresuid$sysv(uint32_t, uint32_t, uint32_t) hidden;
i32 setrlimit$sysv(i32, const struct rlimit *) hidden;
i32 setsid$sysv(void) hidden;
i32 sigaction$sysv(i32, const void *, void *, i64) hidden;
i32 sigprocmask$sysv(i32, const sigset *, sigset *, u64) hidden;
i32 sigsuspend$sysv(const sigset *, u64) hidden;
@ -169,6 +176,7 @@ i64 copy_file_range$sysv(i32, long *, i32, long *, u64, u32) hidden;
i64 getrandom$sysv(void *, u64, u32) hidden;
i64 pread$sysv(i32, void *, u64, i64) hidden;
i64 preadv$sysv(i32, struct iovec *, i32, i64) hidden;
i64 ptrace$sysv(int, i32, void *, void *) hidden;
i64 pwrite$sysv(i32, const void *, u64, i64) hidden;
i64 pwritev$sysv(i32, const struct iovec *, i32, i64) hidden;
i64 read$sysv(i32, void *, u64) hidden;
@ -176,10 +184,6 @@ i64 sendfile$sysv(i32, i32, i64 *, u64) hidden;
i64 splice$sysv(i32, i64 *, i32, i64 *, u64, u32) hidden;
i64 vmsplice$sysv(i32, const struct iovec *, i64, u32) hidden;
i64 write$sysv(i32, const void *, u64) hidden;
int getitimer$sysv(i32, struct itimerval *) hidden;
int setresgid$sysv(uint32_t, uint32_t, uint32_t) hidden;
int setresuid$sysv(uint32_t, uint32_t, uint32_t) hidden;
int setsid$sysv(void) hidden;
u32 getgid$sysv(void) hidden;
u32 getpid$sysv(void) hidden;
u32 getsid$sysv(int) hidden;
@ -248,6 +252,8 @@ int utimensat$nt(int, const char *, const struct timespec *, int) hidden;
int getrusage$nt(int, struct rusage *) hidden;
int setitimer$nt(int, const struct itimerval *, struct itimerval *) hidden;
int nanosleep$nt(const struct timespec *, struct timespec *) hidden;
int faccessat$nt(int, const char *, int, uint32_t) hidden;
int execve$nt(const char *, char *const[], char *const[]) hidden;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » windows nt » support
@ -266,6 +272,7 @@ int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden;
int64_t __winerr(void) nocallback privileged;
int __mkntpath(const char *, char16_t[hasatleast PATH_MAX - 16]) hidden;
int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX - 16], int) hidden;
unsigned __wincrash$nt(struct NtExceptionPointers *);
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » metal

View file

@ -17,12 +17,8 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/enum/filetype.h"
#include "libc/nt/files.h"
/**
* Returns true if file descriptor is backed by character i/o.

View file

@ -16,13 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/stat.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/sysv/consts/s.h"
/**
* Returns true if file exists and is executable.
*
* @see access(exe, X_OK) which is more accurate on NT
* @asyncsignalsafe
*/
bool isexecutable(const char *path) {
struct stat st;

View file

@ -16,31 +16,13 @@
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/calls/ntspawn.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/str/thompike.h"
#include "libc/str/utf16.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.
*
@ -53,45 +35,60 @@ static int mkntcmdline_append(char16_t **p, size_t *i, size_t *n, char16_t c) {
* @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;
textwindows int mkntcmdline(char16_t cmdline[ARG_MAX], char *const argv[]) {
uint64_t w;
wint_t x, y;
int slashes, n;
size_t i, j, k;
bool needsquote;
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 (k = i = 0; argv[i]; ++i) {
if (i) {
cmdline[k++] = u' ';
if (k == ARG_MAX) return e2big();
}
needsquote = !*argv[i] || argv[i][strcspn(argv[i], " \t\n\v\"")];
if (needsquote) {
cmdline[k++] = u'"';
if (k == ARG_MAX) return e2big();
}
for (j = 0;;) {
if (needsquote) {
int slashes = 0;
while (argv[i][j] && argv[i][j] == u'\\') slashes++, j++;
slashes = 0;
while (argv[i][j] && argv[i][j] == '\\') 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 (argv[i][j] == '"') slashes++;
while (slashes--) {
cmdline[k++] = u'\\';
if (k == ARG_MAX) return e2big();
}
}
if (needsquote) APPENDCHAR(u'"');
}
APPENDCHAR(u'\0');
if (cmdline_i > CMD_MAX) {
e2big();
goto error;
}
x = argv[i][j++] & 0xff;
if (x >= 0300) {
n = ThomPikeLen(x);
x = ThomPikeByte(x);
while (--n) {
if ((y = argv[i][j++] & 0xff)) {
x = ThomPikeMerge(x, y);
} else {
einval();
x = 0;
break;
}
return cmdline_p;
error:
free(cmdline_p);
return NULL;
}
}
if (!x) break;
if (!i && x == '/') x = '\\';
w = EncodeUtf16(x);
do {
cmdline[k++] = w;
if (k == ARG_MAX) return e2big();
} while ((w >>= 16));
}
if (needsquote) {
cmdline[k++] = u'"';
if (k == ARG_MAX) return e2big();
}
}
cmdline[k] = u'\0';
return 0;
}

View file

@ -17,15 +17,35 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/alg/arraylist2.internal.h"
#include "libc/calls/hefty/ntspawn.h"
#include "libc/calls/ntspawn.h"
#include "libc/fmt/conv.h"
#include "libc/macros.h"
#include "libc/mem/alloca.h"
#include "libc/mem/mem.h"
#include "libc/str/oldutf16.internal.h"
#include "libc/str/str.h"
#include "libc/str/thompike.h"
#include "libc/str/tpdecode.internal.h"
#include "libc/str/utf16.h"
#include "libc/sysv/errfuns.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 sorted environment variable block for Windows.
*
@ -34,33 +54,40 @@
* @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;
textwindows int mkntenvblock(char16_t envvars[ARG_MAX], char *const envp[]) {
axdx_t rc;
uint64_t w;
char **vars;
wint_t x, y;
size_t i, j, k, n, m;
for (n = 0; envp[n];) n++;
vars = alloca(n * sizeof(char *));
memcpy(vars, envp, n * sizeof(char *));
SortStrings(vars, n);
for (k = i = 0; i < n; ++i) {
j = 0;
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;
}
}
++bi;
if (bi > ENV_MAX) {
e2big();
goto error;
}
free(envp);
return bp;
w = EncodeUtf16(x);
do {
envvars[k++] = w & 0xffff;
if (k == ARG_MAX) return e2big();
} while ((w >>= 16));
} while (x);
}
error:
free(envp);
free(bp);
return NULL;
envvars[k] = u'\0';
return 0;
}

View file

@ -36,16 +36,6 @@ textwindows void ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) {
ctx->uc_mcontext.cs = cr->SegCs;
ctx->uc_mcontext.gs = cr->SegGs;
ctx->uc_mcontext.fs = cr->SegFs;
ctx->uc_mcontext.fpregs = &ctx->fpustate;
ctx->fpustate.cwd = cr->FltSave.ControlWord;
ctx->fpustate.swd = cr->FltSave.StatusWord;
ctx->fpustate.mxcsr = cr->FltSave.MxCsr;
ctx->fpustate.mxcr_mask = cr->FltSave.MxCsr_Mask;
/* copy r8,r9,r10,r11,r12,r13,r15 */
memcpy(&ctx->uc_mcontext.r8, &cr->R8, 8 * sizeof(int64_t));
/* copy st0-st7 as well as xmm0-xmm15 */
memcpy(ctx->fpustate.st, &cr->FltSave.FloatRegisters,
sizeof(ctx->fpustate.st) + sizeof(ctx->fpustate.xmm));
memcpy(ctx->fpustate.st, &cr->FltSave.FloatRegisters,
sizeof(ctx->fpustate.st) + sizeof(ctx->fpustate.xmm));
ctx->uc_mcontext.fpregs = &ctx->__fpustate;
memcpy(&ctx->__fpustate, &cr->FltSave, sizeof(ctx->__fpustate));
}

View file

@ -1,7 +1,7 @@
/*-*- 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
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
@ -16,21 +16,24 @@
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/bits/pushpop.h"
#include "libc/calls/internal.h"
#include "libc/fmt/conv.h"
#include "libc/calls/ntspawn.h"
#include "libc/macros.h"
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/memory.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"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/securityattributes.h"
#include "libc/nt/struct/startupinfo.h"
struct SpawnBlock {
char16_t cmdline[ARG_MAX];
char16_t envvars[ARG_MAX];
};
/**
* Spawns process on Windows NT.
@ -39,10 +42,8 @@
* 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 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
@ -57,32 +58,44 @@
* @see spawnve() which abstracts this function
*/
textwindows int ntspawn(
const char *program, char *const argv[], char *const envp[],
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,
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,
int64_t handle;
size_t blocksize;
struct SpawnBlock *block;
rc = -1;
block = NULL;
blocksize = ROUNDUP(sizeof(*block), FRAMESIZE);
if ((handle = CreateFileMappingNuma(
-1,
&(struct NtSecurityAttributes){sizeof(struct NtSecurityAttributes),
NULL, false},
pushpop(kNtPageReadwrite), 0, blocksize, NULL,
kNtNumaNoPreferredNode)) &&
(block =
MapViewOfFileExNuma(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0,
blocksize, NULL, kNtNumaNoPreferredNode))) {
if (mkntcmdline(block->cmdline, argv) != -1 &&
mkntenvblock(block->envvars, envp) != -1) {
if (CreateProcess(NULL, block->cmdline, opt_lpProcessAttributes,
opt_lpThreadAttributes, bInheritHandles,
dwCreationFlags | kNtCreateUnicodeEnvironment,
lpEnvironment, opt_lpCurrentDirectory, lpStartupInfo,
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
opt_out_lpProcessInformation)) {
rc = 0;
} else {
rc = __winerr();
__winerr();
}
}
} else {
rc = -1;
__winerr();
}
free(lpCommandLine);
free(lpEnvironment);
if (block) UnmapViewOfFile(block);
if (handle) CloseHandle(handle);
return rc;
}

17
libc/calls/ntspawn.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_HEFTY_NTSPAWN_H_
#define COSMOPOLITAN_LIBC_CALLS_HEFTY_NTSPAWN_H_
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/securityattributes.h"
#include "libc/nt/struct/startupinfo.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int mkntcmdline(char16_t[ARG_MAX], char *const[]) hidden;
int mkntenvblock(char16_t[ARG_MAX], char *const[]) hidden;
int ntspawn(char *const[], char *const[], struct NtSecurityAttributes *,
struct NtSecurityAttributes *, bool32, uint32_t, const char16_t *,
const struct NtStartupInfo *, struct NtProcessInformation *) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_HEFTY_NTSPAWN_H_ */

View file

@ -45,5 +45,6 @@ textwindows bool32 onntconsoleevent(uint32_t CtrlType) {
}
memset(&info, 0, sizeof(info));
info.si_signo = sig;
return __sigenter(sig, &info, NULL);
__sigenter(sig, &info, NULL);
return true;
}

View file

@ -42,9 +42,13 @@ static textwindows int64_t open$nt$impl(const char *file, uint32_t flags,
if (__mkntpath2(file, file16, flags) == -1) return -1;
if ((handle = CreateFile(
file16,
(flags & 0xf000000f) | (/* this is needed if we mmap(rwx+cow)
(flags & 0xf000000f) |
(/* this is needed if we mmap(rwx+cow)
nt is choosy about open() access */
kNtGenericExecute | kNtFileGenericWrite),
(flags & O_ACCMODE) == O_RDONLY
? kNtGenericExecute | kNtFileGenericRead
: kNtGenericExecute | kNtFileGenericRead |
kNtFileGenericWrite),
(flags & O_EXCL)
? kNtFileShareExclusive
: kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,

View file

@ -35,6 +35,8 @@
* ignored if O_CREAT or O_TMPFILE weren't passed
* @return number needing close(), or -1 w/ errno
* @note don't call open() from signal handlers
* @asyncsignalsafe
* @vforksafe
*/
nodiscard int open(const char *file, int flags, ...) {
va_list va;

View file

@ -16,25 +16,67 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/alg/reverse.h"
#include "libc/calls/internal.h"
#include "libc/nt/createfile.h"
#include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/creationdisposition.h"
#include "libc/nt/enum/filesharemode.h"
#include "libc/nt/ipc.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/sysv/consts/o.h"
static const char kPipeNamePrefix[] = "\\\\?\\pipe\\cosmo\\";
static size_t UintToChar16Array(char16_t *a, uint64_t i) {
size_t j = 0;
do {
a[j++] = i % 10 + '0';
i /= 10;
} while (i > 0);
a[j] = 0;
reverse(a, j);
return j;
}
static char16_t *CreatePipeName(char16_t *a) {
static long x;
unsigned i;
for (i = 0; kPipeNamePrefix[i]; ++i) a[i] = kPipeNamePrefix[i];
i += UintToChar16Array(a + i, GetCurrentProcessId());
a[i++] = u'-';
i += UintToChar16Array(a + i, GetCurrentProcessId());
a[i++] = u'-';
i += UintToChar16Array(a + i, x++);
a[i] = u'\0';
return a;
}
textwindows int pipe$nt(int pipefd[2], unsigned flags) {
int64_t hin, hout;
int reader, writer;
if ((reader = __getemptyfd()) == -1) return -1;
if ((writer = __getemptyfd()) == -1) return -1;
if (CreatePipe(&g_fds.p[reader].handle, &g_fds.p[writer].handle,
&kNtIsInheritable, 0)) {
char16_t pipename[64];
CreatePipeName(pipename);
if ((hin = CreateNamedPipe(pipename, kNtPipeAccessInbound,
kNtPipeWait | kNtPipeReadmodeByte, 1, 65536, 65536,
0, &kNtIsInheritable)) != -1) {
if ((hout = CreateFile(pipename, kNtGenericWrite, kNtFileShareWrite,
&kNtIsInheritable, kNtOpenExisting, 0, 0)) != -1) {
reader = __getemptyfd();
g_fds.p[reader].kind = kFdFile;
g_fds.p[reader].flags = flags;
g_fds.p[reader].handle = hin;
writer = __getemptyfd();
g_fds.p[writer].kind = kFdFile;
g_fds.p[writer].flags = flags;
g_fds.p[writer].handle = hout;
pipefd[0] = reader;
pipefd[1] = writer;
return 0;
} else {
return __winerr();
CloseHandle(hin);
}
}
return __winerr();
}

View file

@ -30,12 +30,14 @@
pipe$sysv:
push %rbp
mov %rsp,%rbp
xor %esi,%esi # FreeBSD is pipe2()
#if SupportsFreebsd()
xor %esi,%esi
#endif
call __pipe$sysv
#if SupportsXnu()
testb IsXnu() # XNU has special needs
testb IsXnu()
jz 1f
cmp $-1,%eax
cmp $-1,%rax
je 1f
mov %eax,(%rdi)
mov %edx,4(%rdi)

View file

@ -22,7 +22,7 @@
#include "libc/sysv/errfuns.h"
/**
* Creates file-less file descriptors for inter-process communication.
* Creates file-less file descriptors for interprocess communication.
*
* @param fd is (reader, writer)
* @return 0 on success or -1 w/ errno

View file

@ -37,6 +37,8 @@
* @return [1..size] bytes on success, 0 on EOF, or -1 w/ errno; with
* exception of size==0, in which case return zero means no error
* @see pwrite(), write()
* @asyncsignalsafe
* @vforksafe
*/
ssize_t pread(int fd, void *buf, size_t size, int64_t offset) {
ssize_t rc;

View file

@ -34,6 +34,8 @@
* @param count is recommended to be 16 or fewer; if it exceeds IOV_MAX
* then the extra buffers are simply ignored
* @return number of bytes actually read, or -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
ssize_t preadv(int fd, struct iovec *iovec, int count, int64_t off) {
static bool once, demodernize;

View file

@ -1,7 +1,7 @@
/*-*- 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
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
@ -16,16 +16,17 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/alg/alg.h"
#include "libc/alg/arraylist2.internal.h"
#include "libc/bits/safemacros.h"
#include "libc/str/str.h"
#include "libc/calls/internal.h"
#include "libc/sysv/errfuns.h"
#undef strlen
#undef replacestr
#define replacestr replacestr16
#define memmem memmem16
#define char char16_t
#define strlen strlen16
#include "libc/alg/replacestr.c"
/**
* Traces process.
*
* @param request can be PTRACE_xxx
* @note de facto linux only atm
*/
long ptrace(int request, int pid, void *addr, void *data) {
/* TODO(jart): FreeBSD addr and data args are different */
if (request == -1) return einval(); /* see consts.sh */
return ptrace$sysv(request, pid, addr, data);
}

View file

@ -34,6 +34,8 @@
* @return [1..size] bytes on success, or -1 w/ errno; noting zero is
* impossible unless size was passed as zero to do an error check
* @see pread(), write()
* @asyncsignalsafe
* @vforksafe
*/
ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) {
ssize_t rc;

View file

@ -38,6 +38,8 @@
* @param count is recommended to be 16 or fewer; if it exceeds IOV_MAX
* then the extra buffers are simply ignored
* @return number of bytes actually sent, or -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
ssize_t pwritev(int fd, const struct iovec *iovec, int count, int64_t off) {
static bool once, demodernize;

View file

@ -28,6 +28,7 @@
* Reads data to multiple buffers.
*
* @return number of bytes actually read, or -1 w/ errno
* @asyncsignalsafe
*/
ssize_t readv(int fd, const struct iovec *iov, int iovlen) {
if (fd < 0) return einval();

View file

@ -2,7 +2,7 @@
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_DIRENT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
struct dirent { /* linux getdents64 abi */
struct dirent { /* openbsd and linux getdents64 abi */
uint64_t d_ino; /* inode number */
int64_t d_off; /* implementation-dependent location number */
uint16_t d_reclen; /* byte length of this whole struct and string */

View file

@ -0,0 +1,73 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_USER_REGS_STRUCT_H_
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_USER_REGS_STRUCT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* Linux Kernel user registers.
*
* @note superset of struct pt_regs
* @see ptrace() w/ PTRACE_SYSCALL
*/
struct user_regs_struct {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t rbp;
uint64_t rbx;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rax;
uint64_t rcx;
uint64_t rdx;
uint64_t rsi;
uint64_t rdi;
uint64_t orig_rax;
uint64_t rip;
uint64_t cs;
uint64_t eflags;
uint64_t rsp;
uint64_t ss;
uint64_t fs_base;
uint64_t gs_base;
uint64_t ds;
uint64_t es;
uint64_t fs;
uint64_t gs;
};
struct useregs_struct$freebsd {
int64_t r15;
int64_t r14;
int64_t r13;
int64_t r12;
int64_t r11;
int64_t r10;
int64_t r9;
int64_t r8;
int64_t rdi;
int64_t rsi;
int64_t rbp;
int64_t rbx;
int64_t rdx;
int64_t rcx;
int64_t rax;
uint32_t trapno;
uint16_t fs;
uint16_t gs;
uint32_t err;
uint16_t es;
uint16_t ds;
int64_t rip;
int64_t cs;
int64_t rflags;
int64_t rsp;
int64_t ss;
};
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_USER_REGS_STRUCT_H_ */

View file

@ -0,0 +1,41 @@
/*-*- 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 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/dce.h"
#include "libc/macros.h"
/ Makes fork() kernel ABI consistent across UNIX systems.
/
/ @return 0 if parent, pid if child, or -1 on error
fork$sysv:
push %rbp
mov %rsp,%rbp
.profilable
call __fork$sysv
#if SupportsXnu()
testb IsXnu()
jz 1f
cmp $-1,%rax
je 1f
neg %edx # edx is 0 for parent and 1 for child
not %edx # eax always returned with childs pid
and %edx,%eax
#endif
1: pop %rbp
ret
.endfn fork$sysv,globl,hidden

View file

@ -21,6 +21,6 @@
/ Directly calls ftruncate() impl on host o/s if available.
ftruncate$sysv:
mov %rsp,%rdx # openbsd:pad
mov %rsi,%rdx # openbsd:pad
jmp __ftruncate$sysv
.endfn ftruncate$sysv,globl
.endfn ftruncate$sysv,globl,hidden

View file

@ -29,7 +29,7 @@ lseek$sysv:
cmovnz .Lzero(%rip),%rsi
#endif
jmp __lseek$sysv
.endfn lseek$sysv,globl
.endfn lseek$sysv,globl,hidden
.rodata.cst8
.Lzero: .quad 0

View file

@ -27,4 +27,4 @@ mmap$sysv:
call __mmap$sysv
leave
ret
.endfn mmap$sysv,globl
.endfn mmap$sysv,globl,hidden

View file

@ -23,4 +23,4 @@
__wincrash$nt:
ezlea __wincrash,ax
jmp __nt2sysv
.endfn __wincrash$nt,globl
.endfn __wincrash$nt,globl,hidden

View file

@ -23,4 +23,4 @@
pread$sysv:
mov %rcx,%r8 # openbsd:pad
jmp __pread$sysv
.endfn pread$sysv,globl
.endfn pread$sysv,globl,hidden

View file

@ -23,4 +23,4 @@
preadv$sysv:
mov %rcx,%r8 # openbsd:pad
jmp __preadv$sysv
.endfn preadv$sysv,globl
.endfn preadv$sysv,globl,hidden

View file

@ -23,4 +23,4 @@
pwrite$sysv:
mov %rcx,%r8 # openbsd:pad
jmp __pwrite$sysv
.endfn pwrite$sysv,globl
.endfn pwrite$sysv,globl,hidden

View file

@ -23,4 +23,4 @@
pwritev$sysv:
mov %rcx,%r8 # openbsd:pad
jmp __pwritev$sysv
.endfn pwritev$sysv,globl
.endfn pwritev$sysv,globl,hidden

View file

@ -21,6 +21,6 @@
/ Directly calls truncate() impl on host o/s if available.
truncate$sysv:
mov %rsp,%rdx # openbsd:pad
mov %rsi,%rdx # openbsd:pad
jmp __truncate$sysv
.endfn truncate$sysv,globl
.endfn truncate$sysv,globl,hidden

View file

@ -144,9 +144,9 @@ struct ucontext {
};
struct ucontext *uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
uint8_t uc_sigmask[128 + 16]; /* ?!? wut */
struct FpuState fpustate;
mcontext_t uc_mcontext; /* use this */
sigset_t uc_sigmask;
struct FpuState __fpustate; /* for cosmo on non-linux */
};
typedef struct ucontext ucontext_t;

View file

@ -51,6 +51,8 @@ static int vdprintfputchar(int c, struct VdprintfState *df) {
/**
* Formats string directly to system i/o device.
* @asyncsignalsafe
* @vforksafe
*/
int(vdprintf)(int fd, const char *fmt, va_list va) {
struct VdprintfState df;

View file

@ -1,7 +1,7 @@
/*-*- 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
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
@ -16,14 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/alg/alg.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/calls/internal.h"
#undef memmem
#undef memchr
#define char char16_t
#define memmem memmem16
#define memchr memchr16
#include "libc/alg/memmem.c"
int __vforked;

View file

@ -25,6 +25,7 @@
* may be inspected using WEEXITSTATUS(), etc.
* @return process id of terminated child or -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
int wait(int *opt_out_wstatus) {
return wait4(-1, opt_out_wstatus, 0, NULL);

View file

@ -439,6 +439,7 @@ wontreturn void xnutrampoline(void *fn, int infostyle, int sig,
g.uc.uc_stack.ss_sp = xnuctx->uc_stack.ss_sp;
g.uc.uc_stack.ss_flags = xnuctx->uc_stack.ss_flags;
g.uc.uc_stack.ss_size = xnuctx->uc_stack.ss_size;
g.uc.uc_mcontext.fpregs = &g.uc.__fpustate;
if (xnuctx->uc_mcontext) {
if (xnuctx->uc_mcsize >= sizeof(struct __darwin_x86_exception_state64)) {
xnuexceptionstate2linux(&g.uc.uc_mcontext, &xnuctx->uc_mcontext->__es);
@ -449,7 +450,7 @@ wontreturn void xnutrampoline(void *fn, int infostyle, int sig,
&xnuctx->uc_mcontext->__ss);
}
if (xnuctx->uc_mcsize >= sizeof(struct __darwin_mcontext64)) {
xnussefpustate2linux(&g.uc.fpustate, &xnuctx->uc_mcontext->__fs);
xnussefpustate2linux(&g.uc.__fpustate, &xnuctx->uc_mcontext->__fs);
}
}
}

View file

@ -24,7 +24,7 @@
#define METAL 2
#define WINDOWS 4
#define XNU 8
#define OPENBSD 16 /* 2019-12-11: new openbsd drm might thwart syscall */
#define OPENBSD 16
#define FREEBSD 32
/* #define YOUR_CLOUD_PLATFORM_HERE 64 /\* jtunney@gmail.com *\/ */
/* #define YOUR_CLOUD_PLATFORM_HERE 128 /\* jtunney@gmail.com *\/ */
@ -89,6 +89,7 @@
#define SupportsXnu() ((SUPPORT_VECTOR & XNU) == XNU)
#define SupportsFreebsd() ((SUPPORT_VECTOR & FREEBSD) == FREEBSD)
#define SupportsOpenbsd() ((SUPPORT_VECTOR & OPENBSD) == OPENBSD)
#define SupportsBsd() (!!(SUPPORT_VECTOR & (XNU | FREEBSD | OPENBSD)))
#define SupportsSystemv() \
((SUPPORT_VECTOR & (LINUX | METAL | XNU | OPENBSD | FREEBSD)) != 0)

Some files were not shown because too many files have changed in this diff Show more