mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-02 23:18:44 +00:00
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:
parent
c20dad3534
commit
45b72485ad
1032 changed files with 6083 additions and 2348 deletions
2
Makefile
2
Makefile
|
@ -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.
|
@ -153,6 +153,7 @@ MATHEMATICAL = \
|
|||
DEFAULT_CPPFLAGS = \
|
||||
-DIMAGE_BASE_VIRTUAL=$(IMAGE_BASE_VIRTUAL) \
|
||||
-nostdinc \
|
||||
-iquote - \
|
||||
-iquote .
|
||||
|
||||
DEFAULT_CFLAGS = \
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# OVERVIEW
|
||||
#
|
||||
# Run Length Decoder Scratch Pad
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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,48 +43,52 @@ int main(int argc, char *argv[]) {
|
|||
showcrashreports();
|
||||
res = 0;
|
||||
|
||||
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"
|
||||
"mov\t$0x9999999999999999,%r9d\n\t"
|
||||
"mov\t$0xaaaaaaaaaaaaaaaa,%r10d\n\t"
|
||||
"mov\t$0xbbbbbbbbbbbbbbbb,%r11d\n\t"
|
||||
"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"
|
||||
"movd\t%rax,%xmm2\n\t"
|
||||
"mov\t$0x3333333333333333,%rax\n\t"
|
||||
"movd\t%rax,%xmm3\n\t"
|
||||
"mov\t$0x4444444444444444,%rax\n\t"
|
||||
"movd\t%rax,%xmm4\n\t"
|
||||
"mov\t$0x5555555555555555,%rax\n\t"
|
||||
"movd\t%rax,%xmm5\n\t"
|
||||
"mov\t$0x6666666666666666,%rax\n\t"
|
||||
"movd\t%rax,%xmm6\n\t"
|
||||
"mov\t$0x7777777777777777,%rax\n\t"
|
||||
"movd\t%rax,%xmm7\n\t"
|
||||
"mov\t$0x8888888888888888,%rax\n\t"
|
||||
"movd\t%rax,%xmm8\n\t"
|
||||
"mov\t$0x9999999999999999,%rax\n\t"
|
||||
"movd\t%rax,%xmm9\n\t"
|
||||
"mov\t$0xaaaaaaaaaaaaaaaa,%rax\n\t"
|
||||
"movd\t%rax,%xmm10\n\t"
|
||||
"mov\t$0xbbbbbbbbbbbbbbbb,%rax\n\t"
|
||||
"movd\t%rax,%xmm11\n\t"
|
||||
"mov\t$0xcccccccccccccccc,%rax\n\t"
|
||||
"movd\t%rax,%xmm12\n\t"
|
||||
"mov\t$0xdddddddddddddddd,%rax\n\t"
|
||||
"movd\t%rax,%xmm13\n\t"
|
||||
"mov\t$0xeeeeeeeeeeeeeeee,%rax\n\t"
|
||||
"movd\t%rax,%xmm14\n\t"
|
||||
"mov\t$0xffffffffffffffff,%rax\n\t"
|
||||
"movd\t%rax,%xmm15\n\t"
|
||||
"fldlg2\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"
|
||||
"mov\t$0x9999999999999999,%r9d\n\t"
|
||||
"mov\t$0xaaaaaaaaaaaaaaaa,%r10d\n\t"
|
||||
"mov\t$0xbbbbbbbbbbbbbbbb,%r11d\n\t"
|
||||
"mov\t$0x0000000000000000,%rax\n\t"
|
||||
"movd\t%rax,%xmm0\n\t"
|
||||
"mov\t$0x1111111111111111,%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"
|
||||
"mov\t$0x4444444444444444,%rax\n\t"
|
||||
"movd\t%rax,%xmm4\n\t"
|
||||
"mov\t$0x5555555555555555,%rax\n\t"
|
||||
"movd\t%rax,%xmm5\n\t"
|
||||
"mov\t$0x6666666666666666,%rax\n\t"
|
||||
"movd\t%rax,%xmm6\n\t"
|
||||
"mov\t$0x7777777777777777,%rax\n\t"
|
||||
"movd\t%rax,%xmm7\n\t"
|
||||
"mov\t$0x8888888888888888,%rax\n\t"
|
||||
"movd\t%rax,%xmm8\n\t"
|
||||
"mov\t$0x9999999999999999,%rax\n\t"
|
||||
"movd\t%rax,%xmm9\n\t"
|
||||
"mov\t$0xaaaaaaaaaaaaaaaa,%rax\n\t"
|
||||
"movd\t%rax,%xmm10\n\t"
|
||||
"mov\t$0xbbbbbbbbbbbbbbbb,%rax\n\t"
|
||||
"movd\t%rax,%xmm11\n\t"
|
||||
"mov\t$0xcccccccccccccccc,%rax\n\t"
|
||||
"movd\t%rax,%xmm12\n\t"
|
||||
"mov\t$0xdddddddddddddddd,%rax\n\t"
|
||||
"movd\t%rax,%xmm13\n\t"
|
||||
"mov\t$0xeeeeeeeeeeeeeeee,%rax\n\t"
|
||||
"movd\t%rax,%xmm14\n\t"
|
||||
"mov\t$0xffffffffffffffff,%rax\n\t"
|
||||
"movd\t%rax,%xmm15\n\t"
|
||||
"fldpi\n\t");
|
||||
|
||||
res = *(int *)(intptr_t)boo / boo;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
29
examples/echo.c
Normal file
29
examples/echo.c
Normal 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
17
examples/exec.c
Normal 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
18
examples/fork.c
Normal 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
19
examples/hostname.c
Normal 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;
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
TrySpeaker(ffplay_, (char* const*)args);
|
||||
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,
|
||||
};
|
||||
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\
|
||||
|
|
|
@ -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, "");
|
||||
return WEXITSTATUS(wstatus);
|
||||
if (WIFEXITED(wstatus)) {
|
||||
return WEXITSTATUS(wstatus);
|
||||
} else {
|
||||
return 128 + WTERMSIG(wstatus);
|
||||
}
|
||||
}
|
||||
|
|
23
examples/spawn.c
Normal file
23
examples/spawn.c
Normal 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);
|
||||
}
|
|
@ -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
15
examples/system.c
Normal 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;
|
||||
}
|
|
@ -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 *)
|
||||
|
|
|
@ -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
9
libc/bits/likely.h
Normal 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_ */
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() \
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
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();
|
||||
}
|
||||
pathtok = ep;
|
||||
while ((path = strsep(&pathtok, IsWindows() ? ";" : ":"))) {
|
||||
if (strchr(path, '=')) continue;
|
||||
if ((rc = accesscmd(pathname, path, name, namelen)) != -1) {
|
||||
break;
|
||||
static bool SearchPath(char path[hasatleast PATH_MAX], const char *name,
|
||||
size_t namelen) {
|
||||
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];
|
||||
}
|
||||
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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
return fd;
|
||||
}
|
||||
} else if (demodernize) {
|
||||
goto OldSkool;
|
||||
} else {
|
||||
fd = __dup3$sysv(oldfd, newfd, flags);
|
||||
} else if (!demodernize) {
|
||||
return __dup3$sysv(oldfd, newfd, flags);
|
||||
}
|
||||
return fd;
|
||||
OldSkool:
|
||||
fd = dup2$sysv(oldfd, newfd);
|
||||
if (flags) fd = fixupnewfd$sysv(fd, flags);
|
||||
return fd;
|
||||
return fixupnewfd$sysv(dup2$sysv(oldfd, newfd), flags);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
va_start(va, arg);
|
||||
if ((argv = mkvarargv(arg, va))) {
|
||||
execve(exe, argv, environ);
|
||||
free(argv);
|
||||
}
|
||||
va_end(va);
|
||||
if (!(exe = commandv(prog, pathbuf))) return -1;
|
||||
va_copy(vb, va);
|
||||
va_start(va, arg);
|
||||
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);
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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()) {
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -25,27 +25,30 @@
|
|||
|
||||
textwindows int fcntl$nt(int fd, int cmd, unsigned arg) {
|
||||
uint32_t flags;
|
||||
if (!__isfdkind(fd, kFdFile)) return ebadf();
|
||||
switch (cmd) {
|
||||
case F_GETFL:
|
||||
return g_fds.p[fd].flags;
|
||||
case F_SETFL:
|
||||
return (g_fds.p[fd].flags = arg);
|
||||
case F_GETFD:
|
||||
if (g_fds.p[fd].flags & O_CLOEXEC) {
|
||||
return FD_CLOEXEC;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
case F_SETFD:
|
||||
if (arg & O_CLOEXEC) {
|
||||
g_fds.p[fd].flags |= O_CLOEXEC;
|
||||
return FD_CLOEXEC;
|
||||
} else {
|
||||
g_fds.p[fd].flags &= ~O_CLOEXEC;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return 0; /* TODO(jart): Implement me. */
|
||||
if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdSocket)) {
|
||||
switch (cmd) {
|
||||
case F_GETFL:
|
||||
return g_fds.p[fd].flags;
|
||||
case F_SETFL:
|
||||
return (g_fds.p[fd].flags = arg);
|
||||
case F_GETFD:
|
||||
if (g_fds.p[fd].flags & O_CLOEXEC) {
|
||||
return FD_CLOEXEC;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
case F_SETFD:
|
||||
if (arg & O_CLOEXEC) {
|
||||
g_fds.p[fd].flags |= O_CLOEXEC;
|
||||
return FD_CLOEXEC;
|
||||
} else {
|
||||
g_fds.p[fd].flags &= ~O_CLOEXEC;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return 0; /* TODO(jart): Implement me. */
|
||||
}
|
||||
} else {
|
||||
return ebadf();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
uint32_t nSize;
|
||||
char16_t name16[256];
|
||||
nSize = ARRAYLEN(name16);
|
||||
if (GetComputerNameEx(kNtComputerNameDnsHostname, name16, &nSize)) {
|
||||
tprecode16to8(name, len, name16);
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
* @asyncsignalsafe
|
||||
*/
|
||||
uint32_t gettid(void) {
|
||||
uint32_t res;
|
||||
if (!IsWindows()) {
|
||||
uint32_t res;
|
||||
res = gettid$sysv();
|
||||
if (res <= 0) {
|
||||
res = getpid();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -17,18 +17,24 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/hefty/ntspawn.h"
|
||||
#include "libc/calls/hefty/spawn.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/ntspawn.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/nexgen32e/nt2sysv.h"
|
||||
#include "libc/nt/enum/filemapflags.h"
|
||||
#include "libc/nt/enum/pageflags.h"
|
||||
#include "libc/nt/enum/startf.h"
|
||||
#include "libc/nt/enum/wt.h"
|
||||
#include "libc/nt/ipc.h"
|
||||
#include "libc/nt/memory.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/signals.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/runtime/directmap.h"
|
||||
#include "libc/runtime/memtrack.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
|
@ -71,9 +77,11 @@ textwindows void WinMainForked(void) {
|
|||
jmp_buf jb;
|
||||
char16_t *p;
|
||||
uint64_t size;
|
||||
uint32_t i, varlen;
|
||||
struct DirectMap dm;
|
||||
char16_t var[21 + 1 + 21 + 1];
|
||||
uint32_t i, varlen, protect, access, oldprot;
|
||||
varlen = GetEnvironmentVariable(u"_FORK", var, ARRAYLEN(var));
|
||||
SetEnvironmentVariable(u"_FORK", NULL);
|
||||
if (!varlen || varlen >= ARRAYLEN(var)) return;
|
||||
p = var;
|
||||
h = ParseInt(&p);
|
||||
|
@ -84,39 +92,27 @@ textwindows void WinMainForked(void) {
|
|||
ReadAll(h, &_mmi.p[i], sizeof(_mmi.p[i]));
|
||||
addr = (void *)((uint64_t)_mmi.p[i].x << 16);
|
||||
size = ((uint64_t)(_mmi.p[i].y - _mmi.p[i].x) << 16) + FRAMESIZE;
|
||||
switch (_mmi.p[i].prot & (PROT_READ | PROT_WRITE | PROT_EXEC)) {
|
||||
case PROT_READ | PROT_WRITE | PROT_EXEC:
|
||||
protect = kNtPageExecuteReadwrite;
|
||||
access = kNtFileMapRead | kNtFileMapWrite | kNtFileMapExecute;
|
||||
break;
|
||||
case PROT_READ | PROT_WRITE:
|
||||
protect = kNtPageReadwrite;
|
||||
access = kNtFileMapRead | kNtFileMapWrite;
|
||||
break;
|
||||
case PROT_READ:
|
||||
protect = kNtPageReadonly;
|
||||
access = kNtFileMapRead;
|
||||
break;
|
||||
default:
|
||||
protect = kNtPageNoaccess;
|
||||
access = 0;
|
||||
break;
|
||||
}
|
||||
if (_mmi.p[i].flags & MAP_PRIVATE) {
|
||||
MapViewOfFileExNuma((_mmi.p[i].h = CreateFileMappingNuma(
|
||||
-1, NULL, kNtPageExecuteReadwrite, 0, size, NULL,
|
||||
kNtNumaNoPreferredNode)),
|
||||
kNtFileMapRead | kNtFileMapWrite | kNtFileMapExecute,
|
||||
0, 0, size, addr, kNtNumaNoPreferredNode);
|
||||
CloseHandle(_mmi.p[i].h);
|
||||
_mmi.p[i].h =
|
||||
__mmap$nt(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC, -1, 0)
|
||||
.maphandle;
|
||||
ReadAll(h, addr, size);
|
||||
VirtualProtect(addr, size, protect, &oldprot);
|
||||
} else {
|
||||
MapViewOfFileExNuma(_mmi.p[i].h, access, 0, 0, size, addr,
|
||||
kNtNumaNoPreferredNode);
|
||||
MapViewOfFileExNuma(
|
||||
_mmi.p[i].h,
|
||||
(_mmi.p[i].prot & PROT_WRITE)
|
||||
? kNtFileMapWrite | kNtFileMapExecute | kNtFileMapRead
|
||||
: kNtFileMapExecute | kNtFileMapRead,
|
||||
0, 0, size, addr, kNtNumaNoPreferredNode);
|
||||
}
|
||||
}
|
||||
ReadAll(h, _edata, _end - _edata);
|
||||
CloseHandle(h);
|
||||
unsetenv("_FORK");
|
||||
if (weaken(__wincrash$nt)) {
|
||||
AddVectoredExceptionHandler(1, (void *)weaken(__wincrash$nt));
|
||||
}
|
||||
longjmp(jb, 1);
|
||||
}
|
||||
|
||||
|
@ -141,13 +137,18 @@ textwindows int fork$nt(void) {
|
|||
startinfo.hStdInput = g_fds.p[0].handle;
|
||||
startinfo.hStdOutput = g_fds.p[1].handle;
|
||||
startinfo.hStdError = g_fds.p[2].handle;
|
||||
if (ntspawn(g_argv[0], g_argv, environ, &kNtIsInheritable, NULL, true, 0,
|
||||
NULL, &startinfo, &procinfo) != -1) {
|
||||
if (ntspawn(g_argv, environ, &kNtIsInheritable, NULL, true, 0, NULL,
|
||||
&startinfo, &procinfo) != -1) {
|
||||
CloseHandle(reader);
|
||||
CloseHandle(procinfo.hThread);
|
||||
g_fds.p[pid].kind = kFdProcess;
|
||||
g_fds.p[pid].handle = procinfo.hProcess;
|
||||
g_fds.p[pid].flags = O_CLOEXEC;
|
||||
if (weaken(g_sighandrvas) &&
|
||||
weaken(g_sighandrvas)[SIGCHLD] == SIG_IGN) {
|
||||
CloseHandle(procinfo.hProcess);
|
||||
} else {
|
||||
g_fds.p[pid].kind = kFdProcess;
|
||||
g_fds.p[pid].handle = procinfo.hProcess;
|
||||
g_fds.p[pid].flags = O_CLOEXEC;
|
||||
}
|
||||
WriteAll(writer, jb, sizeof(jb));
|
||||
WriteAll(writer, &_mmi.i, sizeof(_mmi.i));
|
||||
for (i = 0; i < _mmi.i; ++i) {
|
||||
|
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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 ─╬─│┼
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 (j = 0;;) {
|
||||
if (needsquote) {
|
||||
int slashes = 0;
|
||||
while (argv[i][j] && argv[i][j] == u'\\') slashes++, j++;
|
||||
slashes <<= 1;
|
||||
if (argv[i][j] == u'"') slashes++;
|
||||
while (slashes--) APPENDCHAR(u'\\');
|
||||
}
|
||||
if (!argv[i][j]) break;
|
||||
j += abs(tpdecode(&argv[i][j], &wc));
|
||||
if (CONCAT(&cmdline_p, &cmdline_i, &cmdline_n, cbuf,
|
||||
abs(pututf16(cbuf, ARRAYLEN(cbuf), wc, false))) == -1) {
|
||||
goto error;
|
||||
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) {
|
||||
slashes = 0;
|
||||
while (argv[i][j] && argv[i][j] == '\\') slashes++, j++;
|
||||
slashes <<= 1;
|
||||
if (argv[i][j] == '"') slashes++;
|
||||
while (slashes--) {
|
||||
cmdline[k++] = u'\\';
|
||||
if (k == ARG_MAX) return e2big();
|
||||
}
|
||||
}
|
||||
if (needsquote) APPENDCHAR(u'"');
|
||||
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 {
|
||||
x = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!x) break;
|
||||
if (!i && x == '/') x = '\\';
|
||||
w = EncodeUtf16(x);
|
||||
do {
|
||||
cmdline[k++] = w;
|
||||
if (k == ARG_MAX) return e2big();
|
||||
} while ((w >>= 16));
|
||||
}
|
||||
APPENDCHAR(u'\0');
|
||||
if (cmdline_i > CMD_MAX) {
|
||||
e2big();
|
||||
goto error;
|
||||
if (needsquote) {
|
||||
cmdline[k++] = u'"';
|
||||
if (k == ARG_MAX) return e2big();
|
||||
}
|
||||
} else {
|
||||
einval();
|
||||
}
|
||||
return cmdline_p;
|
||||
error:
|
||||
free(cmdline_p);
|
||||
return NULL;
|
||||
cmdline[k] = u'\0';
|
||||
return 0;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
opt_lpThreadAttributes, bInheritHandles,
|
||||
dwCreationFlags | kNtCreateUnicodeEnvironment,
|
||||
lpEnvironment, opt_lpCurrentDirectory, lpStartupInfo,
|
||||
opt_out_lpProcessInformation)) {
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
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,
|
||||
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
|
||||
opt_out_lpProcessInformation)) {
|
||||
rc = 0;
|
||||
} else {
|
||||
__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
17
libc/calls/ntspawn.h
Normal 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_ */
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
nt is choosy about open() access */
|
||||
kNtGenericExecute | kNtFileGenericWrite),
|
||||
(flags & 0xf000000f) |
|
||||
(/* this is needed if we mmap(rwx+cow)
|
||||
nt is choosy about open() access */
|
||||
(flags & O_ACCMODE) == O_RDONLY
|
||||
? kNtGenericExecute | kNtFileGenericRead
|
||||
: kNtGenericExecute | kNtFileGenericRead |
|
||||
kNtFileGenericWrite),
|
||||
(flags & O_EXCL)
|
||||
? kNtFileShareExclusive
|
||||
: kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
||||
textwindows int pipe$nt(int pipefd[2], unsigned flags) {
|
||||
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)) {
|
||||
g_fds.p[reader].kind = kFdFile;
|
||||
g_fds.p[reader].flags = flags;
|
||||
g_fds.p[writer].kind = kFdFile;
|
||||
g_fds.p[writer].flags = flags;
|
||||
pipefd[0] = reader;
|
||||
pipefd[1] = writer;
|
||||
return 0;
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
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;
|
||||
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 {
|
||||
CloseHandle(hin);
|
||||
}
|
||||
}
|
||||
return __winerr();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 */
|
||||
|
|
73
libc/calls/struct/user_regs_struct.h
Normal file
73
libc/calls/struct/user_regs_struct.h
Normal 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_ */
|
41
libc/calls/thunks/fork-sysv.S
Normal file
41
libc/calls/thunks/fork-sysv.S
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,4 +27,4 @@ mmap$sysv:
|
|||
call __mmap$sysv
|
||||
leave
|
||||
ret
|
||||
.endfn mmap$sysv,globl
|
||||
.endfn mmap$sysv,globl,hidden
|
||||
|
|
|
@ -23,4 +23,4 @@
|
|||
__wincrash$nt:
|
||||
ezlea __wincrash,ax
|
||||
jmp __nt2sysv
|
||||
.endfn __wincrash$nt,globl
|
||||
.endfn __wincrash$nt,globl,hidden
|
||||
|
|
|
@ -23,4 +23,4 @@
|
|||
pread$sysv:
|
||||
mov %rcx,%r8 # openbsd:pad
|
||||
jmp __pread$sysv
|
||||
.endfn pread$sysv,globl
|
||||
.endfn pread$sysv,globl,hidden
|
||||
|
|
|
@ -23,4 +23,4 @@
|
|||
preadv$sysv:
|
||||
mov %rcx,%r8 # openbsd:pad
|
||||
jmp __preadv$sysv
|
||||
.endfn preadv$sysv,globl
|
||||
.endfn preadv$sysv,globl,hidden
|
||||
|
|
|
@ -23,4 +23,4 @@
|
|||
pwrite$sysv:
|
||||
mov %rcx,%r8 # openbsd:pad
|
||||
jmp __pwrite$sysv
|
||||
.endfn pwrite$sysv,globl
|
||||
.endfn pwrite$sysv,globl,hidden
|
||||
|
|
|
@ -23,4 +23,4 @@
|
|||
pwritev$sysv:
|
||||
mov %rcx,%r8 # openbsd:pad
|
||||
jmp __pwritev$sysv
|
||||
.endfn pwritev$sysv,globl
|
||||
.endfn pwritev$sysv,globl,hidden
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue