diff --git a/README.md b/README.md
index 11c43c0ac..7e7ae21e7 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,8 @@
Fast portable static native textmode executable containers.
For an introduction to this project, please read the αcτµαlly pδrταblε
-εxεcµταblε blog post and αcτµαlly pδrταblε εxεcµταblε
+blog post and cosmopolitan libc
website. API documentation is available here.
diff --git a/ape/ape.lds b/ape/ape.lds
index 6c6e25b93..077ae3f91 100644
--- a/ape/ape.lds
+++ b/ape/ape.lds
@@ -425,6 +425,7 @@ SECTIONS {
*(.xlm)
. = ALIGN(0x1000);
+ . = ALIGN(0x10000); /* for brk()/sbrk() allocation */
HIDDEN(_end = .);
PROVIDE_HIDDEN(end = .);
} AT>SmallCode :Ram
diff --git a/build/bootstrap/zipobj.com b/build/bootstrap/zipobj.com
index ca951ab21..f822251ef 100755
Binary files a/build/bootstrap/zipobj.com and b/build/bootstrap/zipobj.com differ
diff --git a/build/definitions.mk b/build/definitions.mk
index b715438ac..556dcd883 100644
--- a/build/definitions.mk
+++ b/build/definitions.mk
@@ -185,6 +185,9 @@ DEFAULT_LDFLAGS = \
-z max-page-size=0x1000 \
-Ttext-segment=$(IMAGE_BASE_VIRTUAL)
+ZIPOBJ_FLAGS = \
+ -b$(IMAGE_BASE_VIRTUAL)
+
ASONLYFLAGS = \
-g \
--debug-prefix-map="$(PWD)"=
diff --git a/build/rules.mk b/build/rules.mk
index 9e91caa63..991e95721 100644
--- a/build/rules.mk
+++ b/build/rules.mk
@@ -34,7 +34,7 @@ o/%.pkg:; @build/package $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(f
o/%.h.ok: %.h; @ACTION=CHECK.h build/compile $(COMPILE.c) -x c -g0 -o $@ $<
o/%.h.okk: %.h; @ACTION=CHECK.h build/compile $(COMPILE.cxx) -x c++ -g0 -o $@ $<
o/%.greg.o: %.greg.c; @ACTION=OBJECTIFY.greg build/compile $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $<
-o/%.zip.o: o/%; @build/zipobj $(OUTPUT_OPTION) $<
+o/%.zip.o: o/%; @build/zipobj $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
o/$(MODE)/%.a:; @$(ARCHIVE) $@ $^
o/$(MODE)/%: o/$(MODE)/%.dbg; @ACTION=OBJCOPY TARGET=$@ build/do $(OBJCOPY) -SO binary $< $@
@@ -75,7 +75,7 @@ o/$(MODE)/%.ncabi.o: %.ncabi.c; @ACTION=OBJECTIFY.nc build/compile $(OBJECTIFY.n
o/$(MODE)/%.real.o: %.c; @ACTION=OBJECTIFY.real build/compile $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.runs: o/$(MODE)/%; @ACTION=CHECK.runs TARGET=$< build/runcom $< $(TESTARGS) && touch $@
o/$(MODE)/%.pkg:; @build/package $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^)
-o/$(MODE)/%.zip.o: %; @build/zipobj $(OUTPUT_OPTION) $<
+o/$(MODE)/%.zip.o: %; @build/zipobj $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
o/$(MODE)/%-gcc.asm: %.c; @ACTION=OBJECTIFY.c build/compile $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
o/$(MODE)/%-clang.asm: CC = $(CLANG)
diff --git a/examples/ctrlc.c b/examples/ctrlc.c
index fa3e79cb8..3e2d1404a 100644
--- a/examples/ctrlc.c
+++ b/examples/ctrlc.c
@@ -8,10 +8,12 @@
╚─────────────────────────────────────────────────────────────────*/
#endif
#include "libc/calls/calls.h"
+#include "libc/errno.h"
#include "libc/log/check.h"
#include "libc/log/color.internal.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
+#include "libc/nt/thread.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
@@ -20,48 +22,41 @@
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/sig.h"
-#define kTutorialMessage "This echos stdio until Ctrl+C is pressed.\n"
-#define kVictoryMessage "\rGot Ctrl+C and longjmp() ran dtors (>'.')>\n"
-
-jmp_buf jb;
+volatile bool gotctrlc;
void GotCtrlC(int sig) {
- gclongjmp(jb, 1);
- unreachable;
-}
-
-size_t HowManyBytesOfHeapMemoryAreAllocated(void) {
- return mallinfo().uordblks;
-}
-
-void ReadAndPrintLinesLoop(void) {
- ssize_t got;
- unsigned char *buf;
- buf = gc(malloc(BUFSIZ));
- CHECK_NE(0, HowManyBytesOfHeapMemoryAreAllocated());
- for (;;) {
- CHECK_NE(-1, (got = read(STDIN_FILENO, buf, BUFSIZ)));
- CHECK_EQ(got, write(STDOUT_FILENO, buf, got));
- }
+ gotctrlc = true;
}
int main(int argc, char *argv[]) {
- int rc;
- rc = 0;
- showcrashreports();
- if (cancolor()) {
- CHECK_EQ(0 /* cosmo runtime doesn't link malloc */,
- HowManyBytesOfHeapMemoryAreAllocated());
- puts("This echos stdio until Ctrl+C is pressed.");
- if (!(rc = setjmp(jb))) {
- CHECK_NE(SIG_ERR, signal(SIGINT, GotCtrlC));
- ReadAndPrintLinesLoop();
- unreachable;
+ ssize_t rc;
+ size_t got, wrote;
+ unsigned char buf[512];
+ fprintf(stderr, "This echos stdio until Ctrl+C is pressed.\n");
+ CHECK_NE(
+ -1, sigaction(SIGINT, &(struct sigaction){.sa_handler = GotCtrlC}, NULL));
+ for (;;) {
+ rc = read(0, buf, BUFSIZ);
+ if (rc != -1) {
+ got = rc;
} else {
- --rc;
+ CHECK_EQ(EINTR, errno);
+ break;
}
- CHECK_EQ(0, HowManyBytesOfHeapMemoryAreAllocated());
- puts("\rGot Ctrl+C and longjmp() ran dtors (>'.')>");
+ if (!got) break;
+ rc = write(1, buf, got);
+ if (rc != -1) {
+ wrote = rc;
+ } else {
+ CHECK_EQ(EINTR, errno);
+ break;
+ }
+ CHECK_EQ(got, wrote);
}
- return rc;
+ if (gotctrlc) {
+ fprintf(stderr, "Got Ctrl+C\n");
+ } else {
+ fprintf(stderr, "Got EOF\n");
+ }
+ return 0;
}
diff --git a/examples/examples.mk b/examples/examples.mk
index 1dfe4fe18..58d4554ed 100644
--- a/examples/examples.mk
+++ b/examples/examples.mk
@@ -51,6 +51,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_LOG_ASAN \
LIBC_MEM \
LIBC_NEXGEN32E \
+ LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_USER32 \
LIBC_NT_WS2_32 \
diff --git a/examples/sleep.c b/examples/sleep.c
new file mode 100644
index 000000000..565ab8e68
--- /dev/null
+++ b/examples/sleep.c
@@ -0,0 +1,61 @@
+#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"
+#include "libc/stdio/stdio.h"
+#include "libc/str/str.h"
+#include "libc/time/time.h"
+#include "libc/x/x.h"
+
+/**
+ * 16kb sleep executable that runs on all operating systems.
+ * https://justine.lol/cosmopolitan/demos/sleep.c
+ * https://justine.lol/cosmopolitan/demos/sleep.com
+ * https://justine.lol/cosmopolitan/index.html
+ */
+
+#define WRITE(s) write(2, s, strlen(s))
+
+int main(int argc, char *argv[]) {
+ char *p;
+ long double x, y;
+ if (argc != 2) {
+ WRITE("Usage: ");
+ WRITE(argv[0]);
+ WRITE(" SECONDS\n");
+ exit(1);
+ }
+ x = 0;
+ for (p = argv[1]; isdigit(*p); ++p) {
+ x *= 10;
+ x += *p - '0';
+ }
+ if (*p == '.') {
+ y = 1;
+ for (++p; isdigit(*p); ++p) {
+ x += (*p - '0') * (y /= 10);
+ }
+ }
+ switch (*p) {
+ case 'm':
+ x *= 60;
+ break;
+ case 'h':
+ x *= 60 * 60;
+ break;
+ case 'd':
+ x *= 60 * 60 * 24;
+ break;
+ default:
+ break;
+ }
+ dsleep(x);
+ return 0;
+}
diff --git a/examples/unbourne.c b/examples/unbourne.c
index 61cc71c0b..2bc18f370 100644
--- a/examples/unbourne.c
+++ b/examples/unbourne.c
@@ -617,10 +617,10 @@
#define setlocate(l, s) 0
#define equal(s1, s2) (strcmp(s1, s2) == 0)
-#define getenv(p) bltinlookup((p), 0)
-#define isodigit(c) ((c) >= '0' && (c) <= '7')
-#define octtobin(c) ((c) - '0')
-#define scopy(s1, s2) ((void)strcpy(s2, s1))
+/* #define getenv(p) bltinlookup((p), 0) */
+#define isodigit(c) ((c) >= '0' && (c) <= '7')
+#define octtobin(c) ((c) - '0')
+#define scopy(s1, s2) ((void)strcpy(s2, s1))
#define TRACE(param)
/* #define TRACE(param) \ */
diff --git a/libc/calls/atfork.c b/libc/calls/atfork.c
index 3e047554a..528e2745c 100644
--- a/libc/calls/atfork.c
+++ b/libc/calls/atfork.c
@@ -16,12 +16,13 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/bits/bits.h"
#include "libc/macros.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/errfuns.h"
static struct AtFork {
- size_t i;
+ volatile size_t i;
struct AtForkCallback {
void (*fn)(void *);
void *arg;
@@ -33,17 +34,25 @@ static struct AtFork {
*
* @return 0 on success, or -1 w/ errno
* @note vfork() won't invoke callbacks
+ * @asyncsignalsafe
*/
int atfork(void *fn, void *arg) {
- if (g_atfork.i == ARRAYLEN(g_atfork.p)) return enomem();
- g_atfork.p[g_atfork.i++] = (struct AtForkCallback){.fn = fn, .arg = arg};
- return 0;
+ size_t i;
+ for (;;) {
+ i = g_atfork.i;
+ if (i == ARRAYLEN(g_atfork.p)) return enomem();
+ if (cmpxchg(&g_atfork.i, i, i + 1)) {
+ g_atfork.p[i] = (struct AtForkCallback){.fn = fn, .arg = arg};
+ return 0;
+ }
+ }
}
/**
* Triggers callbacks registered by atfork().
*
* @note only fork() should call this
+ * @asyncsignalsafe
*/
void __onfork(void) {
size_t i;
diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c
index cd45e4b76..05f34bd70 100644
--- a/libc/calls/fcntl-nt.c
+++ b/libc/calls/fcntl-nt.c
@@ -17,6 +17,9 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
+#include "libc/nt/enum/accessmask.h"
+#include "libc/nt/enum/fileflagandattributes.h"
+#include "libc/nt/enum/filesharemode.h"
#include "libc/nt/files.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
@@ -30,7 +33,22 @@ textwindows int fcntl$nt(int fd, int cmd, unsigned arg) {
case F_GETFL:
return g_fds.p[fd].flags;
case F_SETFL:
- return (g_fds.p[fd].flags = arg);
+ if (ReOpenFile(
+ g_fds.p[fd].handle,
+ (arg & O_APPEND)
+ ? kNtFileAppendData
+ : (arg & O_ACCMODE) == O_RDONLY
+ ? kNtGenericExecute | kNtFileGenericRead
+ : kNtGenericExecute | kNtFileGenericRead |
+ kNtFileGenericWrite,
+ (arg & O_EXCL) == O_EXCL
+ ? kNtFileShareExclusive
+ : kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
+ kNtFileAttributeNotContentIndexed | kNtFileAttributeNormal)) {
+ return (g_fds.p[fd].flags = arg);
+ } else {
+ return __winerr();
+ }
case F_GETFD:
if (g_fds.p[fd].flags & O_CLOEXEC) {
return FD_CLOEXEC;
@@ -38,7 +56,7 @@ textwindows int fcntl$nt(int fd, int cmd, unsigned arg) {
return 0;
}
case F_SETFD:
- if (arg & O_CLOEXEC) {
+ if (arg & FD_CLOEXEC) {
g_fds.p[fd].flags |= O_CLOEXEC;
return FD_CLOEXEC;
} else {
diff --git a/libc/calls/getpid.c b/libc/calls/getpid.c
index f75be2c35..ce9ad3689 100644
--- a/libc/calls/getpid.c
+++ b/libc/calls/getpid.c
@@ -17,15 +17,16 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
+#include "libc/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/nt/process.h"
#include "libc/runtime/runtime.h"
-static int g_pid;
+static int __pid;
-static int __get_pid(void) {
+static int __getpid(void) {
if (!IsWindows()) {
return getpid$sysv();
} else {
@@ -33,20 +34,25 @@ static int __get_pid(void) {
}
}
-static void __update_pid(void) {
- g_pid = __get_pid();
+static void __updatepid(void) {
+ __pid = __getpid();
}
/**
* Returns process id.
* @asyncsignalsafe
+ * @vforksafe
*/
int getpid(void) {
static bool once;
- if (!once) {
- __update_pid();
- atfork(__update_pid, NULL);
- once = true;
+ if (__vforked) {
+ return getpid$sysv();
}
- return g_pid;
+ if (!once) {
+ __updatepid();
+ if (cmpxchg(&once, false, true)) {
+ atfork(__updatepid, NULL);
+ }
+ }
+ return __pid;
}
diff --git a/libc/calls/hefty/vfork.S b/libc/calls/hefty/vfork.S
index 40f6ecfda..4821a69fb 100644
--- a/libc/calls/hefty/vfork.S
+++ b/libc/calls/hefty/vfork.S
@@ -27,6 +27,9 @@
/ call read() safely but you can call pread(). Call _exit() but
/ don't call exit(). Look for the vforksafe function annotation
/
+/ Do not make the assumption that the parent is suspended until
+/ the child terminates since this impl calls fork() on Windows.
+/
/ @return pid of child process or 0 if forked process
/ @returnstwice
/ @vforksafe
diff --git a/libc/calls/internal.h b/libc/calls/internal.h
index f1db931a1..d7189eb01 100644
--- a/libc/calls/internal.h
+++ b/libc/calls/internal.h
@@ -70,8 +70,14 @@ hidden extern const struct NtSecurityAttributes kNtIsInheritable;
ssize_t __getemptyfd(void) hidden;
int __ensurefds(int) hidden;
void __removefd(int) hidden;
-bool __isfdopen(int) hidden nosideeffect;
-bool __isfdkind(int, enum FdKind) hidden nosideeffect;
+
+forceinline bool __isfdopen(int fd) {
+ return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind != kFdEmpty;
+}
+
+forceinline bool __isfdkind(int fd, enum FdKind kind) {
+ return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind == kind;
+}
forceinline size_t clampio(size_t size) {
if (!IsTrustworthy()) {
@@ -214,46 +220,46 @@ void xnutrampoline(void *, i32, i32, const struct __darwin_siginfo *,
│ cosmopolitan § syscalls » windows nt » veneers ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/
-int gettimeofday$nt(struct timeval *, struct timezone *) hidden;
bool32 isatty$nt(int) hidden;
char *getcwd$nt(char *, size_t) hidden;
-int fork$nt(void) hidden;
+i64 lseek$nt(int, i64, int) hidden;
int chdir$nt(const char *) hidden;
int close$nt(int) hidden;
int dup$nt(int, int, int) hidden;
+int execve$nt(const char *, char *const[], char *const[]) hidden;
+int faccessat$nt(int, const char *, int, uint32_t) hidden;
int fadvise$nt(int, u64, u64, int) hidden;
int fcntl$nt(int, int, unsigned) hidden;
-int getpriority$nt(int) hidden;
-int setpriority$nt(int) hidden;
int fdatasync$nt(int) hidden;
int flock$nt(int, int) hidden;
+int fork$nt(void) hidden;
int fstat$nt(i64, struct stat *) hidden;
int ftruncate$nt(int, u64) hidden;
-int kill$nt(i64, int) hidden;
+int getpriority$nt(int) hidden;
+int getrusage$nt(int, struct rusage *) hidden;
+int gettimeofday$nt(struct timeval *, struct timezone *) hidden;
+int kill$nt(int, int) hidden;
int link$nt(const char *, const char *) hidden;
int lstat$nt(const char *, struct stat *) hidden;
int madvise$nt(void *, size_t, int) hidden;
int msync$nt(void *, size_t, int) hidden;
-ssize_t open$nt(const char *, u32, i32) nodiscard hidden;
+int nanosleep$nt(const struct timespec *, struct timespec *) hidden;
int pipe$nt(int[hasatleast 2], unsigned) hidden;
int rename$nt(const char *, const char *) hidden;
int rmdir$nt(const char *) hidden;
int sched_yield$nt(void) hidden;
+int setitimer$nt(int, const struct itimerval *, struct itimerval *) hidden;
+int setpriority$nt(int) hidden;
int stat$nt(const char *, struct stat *) hidden;
-int sync$nt(void) hidden;
int symlink$nt(const char *, const char *) hidden;
+int sync$nt(void) hidden;
int sysinfo$nt(struct sysinfo *) hidden;
int truncate$nt(const char *, u64) hidden;
int unlink$nt(const char *) hidden;
-i64 lseek$nt(int, i64, int) hidden;
+int utimensat$nt(int, const char *, const struct timespec *, int) hidden;
+ssize_t open$nt(const char *, u32, i32) nodiscard hidden;
ssize_t read$nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden;
ssize_t write$nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden;
-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,7 +272,6 @@ int getsetpriority$nt(int, unsigned, int, int (*)(int));
void ntcontext2linux(struct ucontext *, const struct NtContext *) hidden;
struct NtOverlapped *offset2overlap(int64_t, struct NtOverlapped *) hidden;
bool32 ntsetprivilege(i64, const char16_t *, u32) hidden;
-bool32 onntconsoleevent$nt(u32) hidden;
void __winalarm(void *, uint32_t, uint32_t) hidden;
int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden;
int64_t __winerr(void) nocallback privileged;
diff --git a/libc/calls/kill-nt.c b/libc/calls/kill-nt.c
new file mode 100644
index 000000000..d09f908cb
--- /dev/null
+++ b/libc/calls/kill-nt.c
@@ -0,0 +1,56 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/calls/internal.h"
+#include "libc/dce.h"
+#include "libc/macros.h"
+#include "libc/nt/console.h"
+#include "libc/nt/enum/ctrlevent.h"
+#include "libc/nt/process.h"
+#include "libc/sysv/errfuns.h"
+
+textwindows int kill$nt(int pid, int sig) {
+ int target;
+ uint32_t event;
+ if (!pid) return raise(sig);
+ if ((pid > 0 && __isfdkind(pid, kFdProcess)) ||
+ (pid < 0 && __isfdkind(-pid, kFdProcess))) {
+ target = GetProcessId(g_fds.p[ABS(pid)].handle);
+ } else {
+ target = pid;
+ }
+ if (target == GetCurrentProcessId()) {
+ return raise(sig);
+ } else {
+ switch (sig) {
+ case SIGINT:
+ event = kNtCtrlCEvent;
+ case SIGHUP:
+ event = kNtCtrlCloseEvent;
+ case SIGQUIT:
+ event = kNtCtrlBreakEvent;
+ default:
+ return einval();
+ }
+ if (GenerateConsoleCtrlEvent(event, target)) {
+ return 0;
+ } else {
+ return __winerr();
+ }
+ }
+}
diff --git a/libc/calls/kill.c b/libc/calls/kill.c
index aafab4075..60fbd1627 100644
--- a/libc/calls/kill.c
+++ b/libc/calls/kill.c
@@ -19,7 +19,6 @@
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
-#include "libc/sysv/errfuns.h"
/**
* Sends signal to process.
@@ -39,15 +38,9 @@
* @asyncsignalsafe
*/
int kill(int pid, int sig) {
- int me;
if (!IsWindows()) {
return kill$sysv(pid, sig, 1);
} else {
- me = getpid();
- if (!pid || pid == me || pid == -me) {
- return raise(sig);
- } else {
- return enosys();
- }
+ return kill$nt(pid, sig);
}
}
diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c
index cbb2a7995..ae6a51abd 100644
--- a/libc/calls/ntspawn.c
+++ b/libc/calls/ntspawn.c
@@ -84,7 +84,8 @@ textwindows int ntspawn(
mkntenvblock(block->envvars, envp) != -1) {
if (CreateProcess(NULL, block->cmdline, opt_lpProcessAttributes,
opt_lpThreadAttributes, bInheritHandles,
- dwCreationFlags | kNtCreateUnicodeEnvironment,
+ dwCreationFlags | kNtCreateNewProcessGroup |
+ kNtCreateUnicodeEnvironment,
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
opt_out_lpProcessInformation)) {
rc = 0;
diff --git a/libc/calls/onntconsoleevent.c b/libc/calls/onntconsoleevent.c
index 481a3b3c5..b03347aee 100644
--- a/libc/calls/onntconsoleevent.c
+++ b/libc/calls/onntconsoleevent.c
@@ -23,7 +23,7 @@
#include "libc/str/str.h"
#include "libc/sysv/consts/sig.h"
-textwindows bool32 onntconsoleevent(uint32_t CtrlType) {
+textwindows bool32 __onntconsoleevent(uint32_t CtrlType) {
int sig;
siginfo_t info;
switch (CtrlType) {
diff --git a/libc/calls/onntconsoleevent_init.S b/libc/calls/onntconsoleevent_init.S
index 68dc806d3..024c04b5a 100644
--- a/libc/calls/onntconsoleevent_init.S
+++ b/libc/calls/onntconsoleevent_init.S
@@ -20,7 +20,7 @@
.source __FILE__
.init.start 300,_init_onntconsoleevent
- ezlea onntconsoleevent$nt,cx
+ ezlea __onntconsoleevent$nt,cx
pushpop 1,%rdx
ntcall __imp_SetConsoleCtrlHandler
.init.end 300,_init_onntconsoleevent,globl,hidden
diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c
index a13db563d..f405adb68 100644
--- a/libc/calls/open-nt.c
+++ b/libc/calls/open-nt.c
@@ -45,10 +45,12 @@ static textwindows int64_t open$nt$impl(const char *file, uint32_t flags,
(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_APPEND)
+ ? kNtFileAppendData
+ : (flags & O_ACCMODE) == O_RDONLY
+ ? kNtGenericExecute | kNtFileGenericRead
+ : kNtGenericExecute | kNtFileGenericRead |
+ kNtFileGenericWrite),
(flags & O_EXCL)
? kNtFileShareExclusive
: kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
diff --git a/libc/calls/pause.c b/libc/calls/pause.c
index 06ad55495..0a721dad1 100644
--- a/libc/calls/pause.c
+++ b/libc/calls/pause.c
@@ -25,15 +25,18 @@
/**
* Waits for signal.
*
- * @return should be -1 w/ EINTR
+ * This suspends execution until an unmasked signal is delivered
+ * and its callback function has been called. It's a better idea
+ * to use sigsuspend() w/ sigprocmask() to avoid race conditions
+ *
+ * @return should always be -1 w/ EINTR
* @see sigsuspend()
*/
int pause(void) {
int rc, olderr;
sigset_t oldmask;
olderr = errno;
- rc = pause$sysv();
- if (rc == -1 && errno == ENOSYS) {
+ if ((rc = pause$sysv()) == -1 && errno == ENOSYS) {
errno = olderr;
if (sigprocmask(SIG_BLOCK, NULL, &oldmask) == -1) return -1;
rc = sigsuspend(&oldmask);
diff --git a/libc/calls/raise.c b/libc/calls/raise.c
index a20e9dc05..2236518da 100644
--- a/libc/calls/raise.c
+++ b/libc/calls/raise.c
@@ -23,7 +23,7 @@
#include "libc/nt/runtime.h"
#include "libc/sysv/consts/sig.h"
-static uint32_t GetCtrlEvent(int sig) {
+static textwindows uint32_t GetCtrlEvent(int sig) {
switch (sig) {
case SIGINT:
return kNtCtrlCEvent;
diff --git a/libc/calls/sigprocmask.c b/libc/calls/sigprocmask.c
index 7d0b35a0b..02185798f 100644
--- a/libc/calls/sigprocmask.c
+++ b/libc/calls/sigprocmask.c
@@ -34,11 +34,12 @@
* @param oldset will receive the old mask (optional) and can't overlap
* @return 0 on success, or -1 w/ errno
* @asyncsignalsafe
+ * @vforksafe
*/
int sigprocmask(int how, const sigset_t *opt_set, sigset_t *opt_out_oldset) {
if (!IsWindows()) {
return sigprocmask$sysv(how, opt_set, opt_out_oldset, 8);
} else {
- return enosys(); /* TODO(jart): Implement me! */
+ return 0; /* TODO(jart): Implement me! */
}
}
diff --git a/libc/calls/sigsuspend.c b/libc/calls/sigsuspend.c
index cee0a3995..96b526f29 100644
--- a/libc/calls/sigsuspend.c
+++ b/libc/calls/sigsuspend.c
@@ -25,14 +25,14 @@
/**
* Blocks until SIG ∉ MASK is delivered to process.
*
- * @param mask is a bitset of signals to block temporarily
+ * @param ignore is a bitset of signals to block temporarily
* @return -1 w/ EINTR
* @asyncsignalsafe
*/
-int sigsuspend(const sigset_t *mask) {
- if (!mask) return efault();
+int sigsuspend(const sigset_t *ignore) {
+ if (!ignore) return efault();
if (!IsWindows()) {
- return sigsuspend$sysv(mask, 8);
+ return sigsuspend$sysv(ignore, 8);
} else {
return enosys(); /* TODO(jart): Implement me! */
}
diff --git a/libc/calls/thunks/onntconsoleevent.S b/libc/calls/thunks/onntconsoleevent.S
index 92c4caf6f..1126da796 100644
--- a/libc/calls/thunks/onntconsoleevent.S
+++ b/libc/calls/thunks/onntconsoleevent.S
@@ -20,7 +20,7 @@
.text.windows
.source __FILE__
-onntconsoleevent$nt:
- ezlea onntconsoleevent,ax
+__onntconsoleevent$nt:
+ ezlea __onntconsoleevent,ax
jmp __nt2sysv
- .endfn onntconsoleevent$nt,globl,hidden
+ .endfn __onntconsoleevent$nt,globl,hidden
diff --git a/libc/calls/waitpid.c b/libc/calls/waitpid.c
index e3d25d65f..a5fb7d70e 100644
--- a/libc/calls/waitpid.c
+++ b/libc/calls/waitpid.c
@@ -17,9 +17,6 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
-#include "libc/calls/internal.h"
-#include "libc/calls/wait4.h"
-#include "libc/dce.h"
/**
* Waits for status to change on process.
@@ -33,9 +30,5 @@
* @asyncsignalsafe
*/
int waitpid(int pid, int *opt_out_wstatus, int options) {
- if (!IsWindows()) {
- return wait4$sysv(pid, opt_out_wstatus, options, NULL);
- } else {
- return wait4$nt(pid, opt_out_wstatus, options, NULL);
- }
+ return wait4(pid, opt_out_wstatus, options, NULL);
}
diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c
index aab0c4efc..a7e1d0818 100644
--- a/libc/calls/write-nt.c
+++ b/libc/calls/write-nt.c
@@ -41,7 +41,7 @@ textwindows ssize_t write$nt(struct Fd *fd, const struct iovec *iov,
if (WriteFile(fd->handle, iovlen ? iov[0].iov_base : NULL,
iovlen ? clampio(iov[0].iov_len) : 0, &wrote,
offset2overlap(opt_offset, &overlap))) {
- if (!wrote) assert(SumIovecLen(iov, iovlen) > 0);
+ if (!wrote) assert(!SumIovecLen(iov, iovlen));
FlushFileBuffers(fd->handle);
return wrote;
} else {
diff --git a/libc/log/backtrace2.c b/libc/log/backtrace2.c
index b45c4fb23..5ad1775d7 100644
--- a/libc/log/backtrace2.c
+++ b/libc/log/backtrace2.c
@@ -22,6 +22,7 @@
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
+#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/log/backtrace.internal.h"
@@ -40,7 +41,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
ssize_t got;
intptr_t addr;
size_t i, j, gi;
- int rc, pid, pipefds[2];
+ int ws, pid, pipefds[2];
struct Garbages *garbage;
const struct StackFrame *frame;
const char *debugbin, *p1, *p2, *p3, *addr2line;
@@ -73,7 +74,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
close(pipefds[0]);
close(pipefds[1]);
execvp(addr2line, argv);
- abort();
+ _exit(127);
}
close(pipefds[1]);
while ((got = read(pipefds[0], buf, kBacktraceBufSize)) > 0) {
@@ -99,9 +100,15 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
}
}
close(pipefds[0]);
- if (waitpid(pid, &rc, 0) == -1) return -1;
- if (WEXITSTATUS(rc) != 0) return -1;
- return 0;
+ while (waitpid(pid, &ws, 0) == -1) {
+ if (errno == EINTR) continue;
+ return -1;
+ }
+ if (WIFEXITED(ws) && !WEXITSTATUS(ws)) {
+ return 0;
+ } else {
+ return -1;
+ }
}
static int PrintBacktrace(int fd, const struct StackFrame *bp) {
diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.c
index dd239e144..17f5eabd2 100644
--- a/libc/log/backtrace3.c
+++ b/libc/log/backtrace3.c
@@ -50,7 +50,6 @@ int PrintBacktraceUsingSymbols(int fd, const struct StackFrame *bp,
char buf[256], ibuf[21];
const struct Symbol *symbol;
const struct StackFrame *frame;
- if (!st) return -1;
if (!bp) bp = __builtin_frame_address(0);
garbage = weaken(__garbage);
gi = garbage ? garbage->i : 0;
@@ -66,8 +65,9 @@ int PrintBacktraceUsingSymbols(int fd, const struct StackFrame *bp,
*p++ = ' ';
p = mempcpy(p, ibuf, uint64toarray_fixed16(addr, ibuf, 48));
*p++ = ' ';
- if (st->count && ((intptr_t)addr >= (intptr_t)&_base &&
- (intptr_t)addr <= (intptr_t)&_end)) {
+ if (st && st->count &&
+ ((intptr_t)addr >= (intptr_t)&_base &&
+ (intptr_t)addr <= (intptr_t)&_end)) {
symbol = &st->symbols[bisectcarleft((const int32_t(*)[2])st->symbols,
st->count, addr - st->addr_base - 1)];
p = stpcpy(p, &st->name_base[symbol->name_rva]);
diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c
index 8e5a010ed..37caa6e63 100644
--- a/libc/log/checkfail.c
+++ b/libc/log/checkfail.c
@@ -56,10 +56,10 @@ relegated void __check_fail(const char *suffix, const char *opstr,
gethostname(hostname, sizeof(hostname));
(dprintf)(STDERR_FILENO,
- "check failed on %s pid %d\r\n"
- "\tCHECK_%s(%s, %s);\r\n"
- "\t\t → %#lx (%s)\r\n"
- "\t\t%s %#lx (%s)\r\n",
+ "check failed on %s pid %d\n"
+ "\tCHECK_%s(%s, %s);\n"
+ "\t\t → %#lx (%s)\n"
+ "\t\t%s %#lx (%s)\n",
hostname, getpid(), sufbuf, wantstr, gotstr, want, wantstr, opstr,
got, gotstr);
@@ -68,19 +68,19 @@ relegated void __check_fail(const char *suffix, const char *opstr,
va_start(va, fmt);
(vdprintf)(STDERR_FILENO, fmt, va);
va_end(va);
- (dprintf)(STDERR_FILENO, "\r\n");
+ (dprintf)(STDERR_FILENO, "\n");
}
- (dprintf)(STDERR_FILENO, "\t%s\r\n\t%s%s%s%s\r\n", strerror(lasterr), SUBTLE,
+ (dprintf)(STDERR_FILENO, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE,
getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET);
for (i = 1; i < g_argc; ++i) {
- (dprintf)(STDERR_FILENO, "\t\t%s%s\r\n", g_argv[i],
+ (dprintf)(STDERR_FILENO, "\t\t%s%s\n", g_argv[i],
i < g_argc - 1 ? " \\" : "");
}
if (!IsTiny() && lasterr == ENOMEM) {
- (dprintf)(STDERR_FILENO, "\r\n");
+ (dprintf)(STDERR_FILENO, "\n");
PrintMemoryIntervals(STDERR_FILENO, &_mmi);
}
diff --git a/libc/log/checkfail_ndebug.c b/libc/log/checkfail_ndebug.c
index b232a73e0..a3a1a18c6 100644
--- a/libc/log/checkfail_ndebug.c
+++ b/libc/log/checkfail_ndebug.c
@@ -46,5 +46,5 @@ relegated void ___check_fail_ndebug(uint64_t want, uint64_t got,
__print(bx, uint64toarray_radix16(got, bx));
__print_string(" (");
__print(bx, int64toarray_radix10(lasterr, bx));
- __print_string(")\r\n");
+ __print_string(")\n");
}
diff --git a/libc/log/perror.c b/libc/log/perror.c
index dba0e01f8..7523a2cda 100644
--- a/libc/log/perror.c
+++ b/libc/log/perror.c
@@ -18,13 +18,21 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
-#include "libc/log/color.internal.h"
#include "libc/log/internal.h"
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
+/**
+ * Writes error messages to standard error.
+ */
void perror(const char *message) {
- fprintf(stderr, "%s%s%s: %s: %s: %s\r\n", RED2, "error", RESET,
- program_invocation_name, strerror(errno), message);
+ int err;
+ err = errno;
+ if (message && *message) {
+ fputs(message, stderr);
+ fputs(": ", stderr);
+ }
+ fputs(strerror(err), stderr);
+ fputc('\n', stderr);
}
diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c
index b9653ae51..2809abf53 100644
--- a/libc/log/vflogf.c
+++ b/libc/log/vflogf.c
@@ -41,7 +41,9 @@ STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
STATIC_YOINK("ftoa");
-static int loglevel2char(unsigned level) {
+static struct timespec vflogf_ts;
+
+static int vflogf_loglevel2char(unsigned level) {
switch (level) {
case kLogInfo:
return 'I';
@@ -78,34 +80,46 @@ void vflogf_onfail(FILE *f) {
/**
* Writes formatted message w/ timestamp to log.
+ *
+ * Timestamps are hyphenated out when multiple events happen within the
+ * same second in the same process. When timestamps are crossed out, it
+ * will display microseconsd as a delta elapsed time. This is useful if
+ * you do something like:
+ *
+ * LOGF("connecting to foo");
+ * connect(...)
+ * LOGF("connected to foo");
+ *
+ * In that case, the second log entry will always display the amount of
+ * time that it took to connect. This is great in forking applications.
*/
void(vflogf)(unsigned level, const char *file, int line, FILE *f,
const char *fmt, va_list va) {
- static struct timespec ts;
struct tm tm;
long double t2;
const char *prog;
- int64_t secs, nsec, dots;
+ bool issamesecond;
char buf32[32], *buf32p;
+ int64_t secs, nsec, dots;
if (!f) f = g_logfile;
if (fileno(f) == -1) return;
t2 = nowl();
secs = t2;
- nsec = rem1000000000int64(t2 * 1e9L);
- if (secs > ts.tv_sec) {
+ nsec = (t2 - secs) * 1e9L;
+ issamesecond = secs == vflogf_ts.tv_sec;
+ dots = issamesecond ? nsec - vflogf_ts.tv_nsec : nsec;
+ vflogf_ts.tv_sec = secs;
+ vflogf_ts.tv_nsec = nsec;
+ if (!issamesecond) {
localtime_r(&secs, &tm);
strftime(buf32, sizeof(buf32), "%Y-%m-%dT%H:%M:%S.", &tm);
buf32p = buf32;
- dots = nsec;
} else {
buf32p = "--------------------";
- dots = nsec - ts.tv_nsec;
}
- ts.tv_sec = secs;
- ts.tv_nsec = nsec;
prog = basename(program_invocation_name);
- if ((fprintf)(f, "%c%s%06ld:%s:%d:%.*s:%d] ", loglevel2char(level), buf32p,
- rem1000000int64(div1000int64(dots)), file, line,
+ if ((fprintf)(f, "%c%s%06ld:%s:%d:%.*s:%d] ", vflogf_loglevel2char(level),
+ buf32p, rem1000000int64(div1000int64(dots)), file, line,
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
vflogf_onfail(f);
}
diff --git a/libc/nt/privilege.h b/libc/nt/privilege.h
index 043c2e3d1..0f49ba915 100644
--- a/libc/nt/privilege.h
+++ b/libc/nt/privilege.h
@@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_NT_PRIVILEGE_H_
#define COSMOPOLITAN_LIBC_NT_PRIVILEGE_H_
#include "libc/nt/struct/luid.h"
+#include "libc/nt/struct/tokenprivileges.h"
/* ░░░░
▒▒▒░░░▒▒▒▒▒▒▒▓▓▓░
▒▒▒▒░░░▒▒▒▒▒▒▓▓▓▓▓▓░
@@ -34,9 +35,6 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
-struct NtLuid;
-struct NtTokenPrivileges;
-
bool32 LookupPrivilegeValue(const char16_t *opt_lpSystemName,
const char16_t *lpName, struct NtLuid *out_lpLuid);
diff --git a/libc/nt/process.h b/libc/nt/process.h
index 6fe89c151..02ff6d4b1 100644
--- a/libc/nt/process.h
+++ b/libc/nt/process.h
@@ -43,8 +43,8 @@ bool32 CreateProcess(const char16_t *opt_lpApplicationName,
struct NtProcessInformation *opt_out_lpProcessInformation)
paramsnonnull((2, 9));
-uint32_t GetThreadId(int64_t Thread); /* cf. NT_TID */
-uint32_t GetProcessId(int64_t Process); /* cf. NT_PID */
+uint32_t GetThreadId(int64_t hThread); /* cf. NT_TID */
+uint32_t GetProcessId(int64_t hProcess); /* cf. NT_PID */
void SetLastError(uint32_t dwErrCode);
uint32_t FormatMessage(uint32_t dwFlags, const void *lpSource,
uint32_t dwMessageId, uint32_t dwLanguageId,
diff --git a/libc/runtime/abort-nt.c b/libc/runtime/abort-nt.c
new file mode 100644
index 000000000..4491d2bb5
--- /dev/null
+++ b/libc/runtime/abort-nt.c
@@ -0,0 +1,32 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 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/internal.h"
+#include "libc/calls/struct/siginfo.h"
+#include "libc/nt/enum/ctrlevent.h"
+#include "libc/str/str.h"
+#include "libc/sysv/consts/sig.h"
+
+textwindows wontreturn void abort$nt(void) {
+ siginfo_t info;
+ memset(&info, 0, sizeof(info));
+ info.si_signo = SIGABRT;
+ __sigenter(SIGABRT, &info, NULL);
+ _Exit(128 + SIGABRT);
+}
diff --git a/libc/runtime/abort.S b/libc/runtime/abort.S
index de1bc818a..36164db63 100644
--- a/libc/runtime/abort.S
+++ b/libc/runtime/abort.S
@@ -56,6 +56,5 @@ abort: push %rbp
mov SIGABRT,%esi
mov __NR_kill,%eax
syscall # avoid hook and less bt noise
-2: mov $134,%edi # exit(128+SIGABRT) [bash-ism]
- call _Exit
+2: call abort$nt
.endfn abort,globl,protected
diff --git a/libc/runtime/brk.c b/libc/runtime/brk.c
new file mode 100644
index 000000000..6e95b6841
--- /dev/null
+++ b/libc/runtime/brk.c
@@ -0,0 +1,77 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/assert.h"
+#include "libc/calls/calls.h"
+#include "libc/macros.h"
+#include "libc/runtime/runtime.h"
+#include "libc/sysv/consts/map.h"
+#include "libc/sysv/consts/prot.h"
+#include "libc/sysv/errfuns.h"
+
+uintptr_t __break;
+
+/**
+ * Sets end of data section.
+ *
+ * This can be used to allocate and deallocate memory. It won't
+ * conflict with malloc() and mmap(NULL, ...) allocations since
+ * APE binaries load the image at 0x400000 and does allocations
+ * starting at 0x100080000000. You should consult _end, or call
+ * sbrk(NULL), to figure out where the existing break is first.
+ *
+ * @return 0 on success or -1 w/ errno
+ * @see mmap(), sbrk(), _end
+ */
+int brk(void *end) {
+ int rc;
+ uintptr_t x;
+ if (!__break) __break = (uintptr_t)_end;
+ x = (uintptr_t)end;
+ if (x < (uintptr_t)_end) x = (uintptr_t)_end;
+ x = ROUNDUP(x, FRAMESIZE);
+ if (x == __break) return 0;
+ /* allocate one frame at a time due to nt pickiness */
+ for (; x > __break; __break += FRAMESIZE) {
+ if (mmap((void *)__break, FRAMESIZE, PROT_READ | PROT_WRITE,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) {
+ return -1;
+ }
+ }
+ for (rc = 0; x < __break; __break -= FRAMESIZE) {
+ rc |= munmap((void *)(__break - FRAMESIZE), FRAMESIZE);
+ }
+ return 0;
+}
+
+/**
+ * Adjusts end of data section.
+ *
+ * This shrinks or increases the program break by delta bytes. On
+ * success, the previous program break is returned. It's possible
+ * to pass zero to this function to get the current program break
+ *
+ * @return old break on success or -1 w/ errno
+ * @see mmap(), brk(), _end
+ */
+void *sbrk(intptr_t delta) {
+ uintptr_t oldbreak;
+ if (!__break) __break = (uintptr_t)_end;
+ oldbreak = __break;
+ return (void *)(brk((void *)(__break + delta)) != -1 ? oldbreak : -1);
+}
diff --git a/libc/runtime/ldso.c b/libc/runtime/ldso.c
new file mode 100644
index 000000000..4034dfb34
--- /dev/null
+++ b/libc/runtime/ldso.c
@@ -0,0 +1,40 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/runtime/runtime.h"
+
+char *dlerror(void) {
+ return "cosmopolitan doesn't support dsos";
+}
+
+void *dlopen(const char *file, int mode) {
+ return NULL;
+}
+
+void *dlsym(void *handle, const char *name) {
+ return NULL;
+}
+
+int dlclose(void *handle) {
+ return -1;
+}
+
+int dl_iterate_phdr(int callback(void *info, size_t size, void *data),
+ void *data) {
+ return -1;
+}
diff --git a/libc/runtime/pthread.c b/libc/runtime/pthread.c
new file mode 100644
index 000000000..62fda4684
--- /dev/null
+++ b/libc/runtime/pthread.c
@@ -0,0 +1,50 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/bits/bits.h"
+#include "libc/errno.h"
+#include "libc/runtime/runtime.h"
+
+typedef void *pthread_t;
+typedef bool pthread_once_t;
+typedef int pthread_mutex_t;
+
+int pthread_once(pthread_once_t *once, void init(void)) {
+ if (lockcmpxchg(once, 0, 1)) init();
+ return 0;
+}
+
+int pthread_mutex_lock(pthread_mutex_t *mutex) {
+ return EINVAL;
+}
+
+int pthread_mutex_trylock(pthread_mutex_t *mutex) {
+ return EINVAL;
+}
+
+int pthread_mutex_unlock(pthread_mutex_t *mutex) {
+ return EPERM;
+}
+
+int pthread_cancel(pthread_t thread) {
+ return ESRCH;
+}
+
+void *__tls_get_addr(size_t v[2]) {
+ return NULL;
+}
diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h
index 423a3e325..015b02592 100644
--- a/libc/runtime/runtime.h
+++ b/libc/runtime/runtime.h
@@ -23,7 +23,7 @@ extern unsigned char _base[] forcealign(PAGESIZE); /* αpε */
extern unsigned char _ehead[] forcealign(PAGESIZE); /* αpε */
extern unsigned char _etext[] forcealign(PAGESIZE); /* αpε */
extern unsigned char _edata[] forcealign(PAGESIZE); /* αpε */
-extern unsigned char _end[] forcealign(PAGESIZE); /* αpε */
+extern unsigned char _end[] forcealign(FRAMESIZE); /* αpε */
extern unsigned char _ereal; /* αpε */
extern unsigned char __privileged_start; /* αpε */
extern unsigned char __test_start; /* αpε */
@@ -70,6 +70,8 @@ int msync(void *, size_t, int);
void __print(const void *, size_t);
void __print_string(const char *);
void __fast_math(void);
+void *sbrk(intptr_t);
+int brk(void *);
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § runtime » optimizations ─╬─│┼
diff --git a/libc/sock/bind-nt.c b/libc/sock/bind-nt.c
index 846ef449d..75c6ea5e8 100644
--- a/libc/sock/bind-nt.c
+++ b/libc/sock/bind-nt.c
@@ -23,16 +23,6 @@
#include "libc/sock/yoink.inc"
#include "libc/sysv/errfuns.h"
-/**
- * Assigns local address and port number to socket.
- *
- * @param fd is the file descriptor returned by socket()
- * @param addr is usually the binary-encoded ip:port on which to listen
- * @param addrsize is the byte-length of addr's true polymorphic form
- * @return socket file descriptor or -1 w/ errno
- * @error ENETDOWN, EPFNOSUPPORT, etc.
- * @asyncsignalsafe
- */
textwindows int bind$nt(struct Fd *fd, const void *addr, uint32_t addrsize) {
assert(fd->kind == kFdSocket);
if (__bind$nt(fd->handle, addr, addrsize) != -1) {
diff --git a/libc/sock/internal.h b/libc/sock/internal.h
index c951c5d4b..6aaeefd58 100644
--- a/libc/sock/internal.h
+++ b/libc/sock/internal.h
@@ -58,8 +58,6 @@ int32_t __socket$sysv(int32_t, int32_t, int32_t) hidden;
int32_t __getsockname$sysv(int32_t, void *, uint32_t *) hidden;
int32_t __getpeername$sysv(int32_t, void *, uint32_t *) hidden;
-int32_t setsockopt$sysv(int32_t, int32_t, int32_t, const void *,
- uint32_t) hidden;
int32_t accept4$sysv(int32_t, void *, uint32_t *, int) nodiscard hidden;
int32_t accept$sysv(int32_t, void *, uint32_t *) hidden;
int32_t bind$sysv(int32_t, const void *, uint32_t) hidden;
@@ -78,6 +76,7 @@ ssize_t sendto$sysv(int, const void *, size_t, int, const void *,
uint32_t) hidden;
int32_t select$sysv(int32_t, fd_set *, fd_set *, fd_set *,
struct timeval *) hidden;
+int setsockopt$sysv(int, int, int, const void *, uint32_t) hidden;
int32_t epoll_create$sysv(int32_t) hidden;
int32_t epoll_ctl$sysv(int32_t, int32_t, int32_t, void *) hidden;
int32_t epoll_wait$sysv(int32_t, void *, int32_t, int32_t) hidden;
@@ -93,6 +92,7 @@ int accept$nt(struct Fd *, void *, uint32_t *, int) hidden;
int closesocket$nt(int) hidden;
int socket$nt(int, int, int) hidden;
int select$nt(int, fd_set *, fd_set *, fd_set *, struct timeval *) hidden;
+int shutdown$nt(struct Fd *, int) hidden;
size_t iovec2nt(struct NtIovec[hasatleast 16], const struct iovec *,
size_t) hidden;
diff --git a/libc/sock/shutdown-nt.c b/libc/sock/shutdown-nt.c
new file mode 100644
index 000000000..3dde5fb15
--- /dev/null
+++ b/libc/sock/shutdown-nt.c
@@ -0,0 +1,29 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/calls/internal.h"
+#include "libc/nt/winsock.h"
+#include "libc/sock/internal.h"
+
+textwindows int shutdown$nt(struct Fd *fd, int how) {
+ if (__shutdown$nt(fd->handle, how) != -1) {
+ return 0;
+ } else {
+ return __winsockerr();
+ }
+}
diff --git a/libc/sock/shutdown.c b/libc/sock/shutdown.c
index fa47c9528..c0f2ca868 100644
--- a/libc/sock/shutdown.c
+++ b/libc/sock/shutdown.c
@@ -18,19 +18,10 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
#include "libc/dce.h"
-#include "libc/nt/winsock.h"
#include "libc/sock/internal.h"
#include "libc/sock/sock.h"
#include "libc/sysv/errfuns.h"
-static int shutdown$nt(struct Fd *fd, int how) {
- if (__shutdown$nt(fd->handle, how) != -1) {
- return 0;
- } else {
- return __winsockerr();
- }
-}
-
/**
* Disables sends or receives on a socket, without closing.
*
@@ -41,12 +32,7 @@ static int shutdown$nt(struct Fd *fd, int how) {
*/
int shutdown(int fd, int how) {
if (!IsWindows()) {
- if (!IsXnu()) {
- return shutdown$sysv(fd, how);
- } else {
- /* TODO(jart): What's wrong with XNU shutdown()? */
- return 0;
- }
+ return shutdown$sysv(fd, how);
} else if (__isfdkind(fd, kFdSocket)) {
return shutdown$nt(&g_fds.p[fd], how);
} else {
diff --git a/libc/stdio/fbufsize.c b/libc/stdio/fbufsize.c
new file mode 100644
index 000000000..254276b64
--- /dev/null
+++ b/libc/stdio/fbufsize.c
@@ -0,0 +1,27 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/assert.h"
+#include "libc/stdio/stdio_ext.h"
+
+/**
+ * Returns capacity of stdio stream buffer.
+ */
+size_t __fbufsize(FILE *f) {
+ return f->size;
+}
diff --git a/libc/stdio/fflush.c b/libc/stdio/fflush.c
index 69c491f69..cfbce4f56 100644
--- a/libc/stdio/fflush.c
+++ b/libc/stdio/fflush.c
@@ -23,22 +23,11 @@
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
+#include "libc/stdio/fflush.internal.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/o.h"
-struct StdioFlushHandles {
- size_t i, n;
- FILE **p;
-};
-
-struct StdioFlush {
- struct StdioFlushHandles handles;
- FILE *handles_initmem[8];
-};
-
-static struct StdioFlush g_fflush;
-
/**
* Blocks until data from stream buffer is written out.
*
diff --git a/libc/stdio/fflush.internal.h b/libc/stdio/fflush.internal.h
new file mode 100644
index 000000000..e5c8bd710
--- /dev/null
+++ b/libc/stdio/fflush.internal.h
@@ -0,0 +1,21 @@
+#ifndef COSMOPOLITAN_LIBC_STDIO_FFLUSH_H_
+#define COSMOPOLITAN_LIBC_STDIO_FFLUSH_H_
+#include "libc/stdio/stdio.h"
+#if !(__ASSEMBLER__ + __LINKER__ + 0)
+COSMOPOLITAN_C_START_
+
+struct StdioFlushHandles {
+ size_t i, n;
+ FILE **p;
+};
+
+struct StdioFlush {
+ struct StdioFlushHandles handles;
+ FILE *handles_initmem[8];
+};
+
+extern struct StdioFlush g_fflush;
+
+COSMOPOLITAN_C_END_
+#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
+#endif /* COSMOPOLITAN_LIBC_STDIO_FFLUSH_H_ */
diff --git a/libc/stdio/flbf.c b/libc/stdio/flbf.c
new file mode 100644
index 000000000..661e09a73
--- /dev/null
+++ b/libc/stdio/flbf.c
@@ -0,0 +1,27 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/calls/calls.h"
+#include "libc/stdio/stdio_ext.h"
+
+/**
+ * Returns nonzero if stream is line buffered.
+ */
+int __flbf(FILE *f) {
+ return f->bufmode == _IOLBF;
+}
diff --git a/libc/stdio/flushlbf.c b/libc/stdio/flushlbf.c
new file mode 100644
index 000000000..01166e0b2
--- /dev/null
+++ b/libc/stdio/flushlbf.c
@@ -0,0 +1,33 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/calls/calls.h"
+#include "libc/stdio/fflush.internal.h"
+#include "libc/stdio/stdio_ext.h"
+
+/**
+ * Flushes all line-buffered streams.
+ */
+void _flushlbf(void) {
+ int i;
+ for (i = 0; i < g_fflush.handles.i; ++i) {
+ if (g_fflush.handles.p[i]->bufmode == _IOLBF) {
+ fflush(g_fflush.handles.p[i]);
+ }
+ }
+}
diff --git a/libc/stdio/fpending.c b/libc/stdio/fpending.c
new file mode 100644
index 000000000..1d540711a
--- /dev/null
+++ b/libc/stdio/fpending.c
@@ -0,0 +1,26 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/stdio/stdio_ext.h"
+
+/**
+ * Returns number of pending output bytes.
+ */
+size_t __fpending(FILE *f) {
+ return f->end - f->beg;
+}
diff --git a/libc/calls/isfdkind.c b/libc/stdio/fpurge.c
similarity index 89%
rename from libc/calls/isfdkind.c
rename to libc/stdio/fpurge.c
index 6b2a2312a..ae87dd1ed 100644
--- a/libc/calls/isfdkind.c
+++ b/libc/stdio/fpurge.c
@@ -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,8 +16,11 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
-#include "libc/calls/internal.h"
+#include "libc/stdio/stdio_ext.h"
-bool __isfdkind(int fd, enum FdKind kind) {
- return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind == kind;
+/**
+ * Discards contents of stream buffer.
+ */
+void __fpurge(FILE *f) {
+ f->beg = f->end = 0;
}
diff --git a/libc/stdio/freadable.c b/libc/stdio/freadable.c
new file mode 100644
index 000000000..23281cb61
--- /dev/null
+++ b/libc/stdio/freadable.c
@@ -0,0 +1,28 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/stdio/stdio_ext.h"
+#include "libc/sysv/consts/o.h"
+
+/**
+ * Returns nonzero if stream allows reading.
+ */
+int __freadable(FILE *f) {
+ return (f->iomode & O_ACCMODE) == O_RDONLY ||
+ (f->iomode & O_ACCMODE) == O_RDWR;
+}
diff --git a/libc/stdio/freading.c b/libc/stdio/freading.c
new file mode 100644
index 000000000..07f2f2284
--- /dev/null
+++ b/libc/stdio/freading.c
@@ -0,0 +1,27 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/stdio/stdio_ext.h"
+#include "libc/sysv/consts/o.h"
+
+/**
+ * Returns nonzero if stream is read only.
+ */
+int __freading(FILE *f) {
+ return (f->iomode & O_ACCMODE) == O_RDONLY;
+}
diff --git a/libc/stdio/freopen.c b/libc/stdio/freopen.c
index 048b45510..275f65de4 100644
--- a/libc/stdio/freopen.c
+++ b/libc/stdio/freopen.c
@@ -17,18 +17,9 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
-#include "libc/calls/internal.h"
-#include "libc/dce.h"
-#include "libc/errno.h"
-#include "libc/nt/enum/accessmask.h"
-#include "libc/nt/enum/fileflagandattributes.h"
-#include "libc/nt/enum/filesharemode.h"
-#include "libc/nt/files.h"
-#include "libc/nt/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
-#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h"
/**
@@ -52,42 +43,16 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
if (pathname) {
/* open new stream, overwriting existing alloc */
if ((fd = open(pathname, flags, 0666)) != -1) {
- if (!IsWindows()) {
- dup3(fd, stream->fd, (flags & O_CLOEXEC));
- close(fd);
- } else {
- g_fds.p[stream->fd].handle = g_fds.p[fd].handle;
- g_fds.p[fd].kind = kFdEmpty;
- }
+ dup3(fd, stream->fd, flags & O_CLOEXEC);
+ close(fd);
stream->iomode = flags;
return stream;
} else {
return NULL;
}
} else {
- /* change mode of open file */
- if (!IsWindows()) {
- if (flags & O_CLOEXEC) {
- if (fcntl$sysv(stream->fd, F_SETFD, FD_CLOEXEC) == -1) return NULL;
- flags &= ~O_CLOEXEC;
- }
- if (flags) {
- if (fcntl$sysv(stream->fd, F_SETFL, flags) == -1) return NULL;
- }
- return stream;
- } else {
- if (ReOpenFile(
- stream->fd,
- (flags & O_RDWR) == O_RDWR ? kNtGenericWrite : kNtGenericRead,
- (flags & O_EXCL) == O_EXCL
- ? kNtFileShareExclusive
- : kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
- kNtFileAttributeNormal)) {
- return stream;
- } else {
- __winerr();
- return NULL;
- }
- }
+ fcntl(stream->fd, F_SETFD, !!(flags & O_CLOEXEC));
+ fcntl(stream->fd, F_SETFL, flags & ~O_CLOEXEC);
+ return stream;
}
}
diff --git a/libc/stdio/fsetlocking.c b/libc/stdio/fsetlocking.c
new file mode 100644
index 000000000..2dd880e79
--- /dev/null
+++ b/libc/stdio/fsetlocking.c
@@ -0,0 +1,26 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/stdio/stdio_ext.h"
+
+/**
+ * Does nothing and returns `FSETLOCKING_BYCALLER`.
+ */
+int __fsetlocking(FILE *f, int type) {
+ return FSETLOCKING_BYCALLER;
+}
diff --git a/libc/stdio/fwritable.c b/libc/stdio/fwritable.c
new file mode 100644
index 000000000..04562f467
--- /dev/null
+++ b/libc/stdio/fwritable.c
@@ -0,0 +1,28 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/stdio/stdio_ext.h"
+#include "libc/sysv/consts/o.h"
+
+/**
+ * Returns nonzero if stream allows reading.
+ */
+int __fwritable(FILE *f) {
+ return (f->iomode & O_ACCMODE) == O_WRONLY ||
+ (f->iomode & O_ACCMODE) == O_RDWR;
+}
diff --git a/libc/stdio/fwriting.c b/libc/stdio/fwriting.c
new file mode 100644
index 000000000..18209496d
--- /dev/null
+++ b/libc/stdio/fwriting.c
@@ -0,0 +1,27 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/stdio/stdio_ext.h"
+#include "libc/sysv/consts/o.h"
+
+/**
+ * Returns nonzero if stream is write only.
+ */
+int __fwriting(FILE *f) {
+ return (f->iomode & O_ACCMODE) == O_WRONLY;
+}
diff --git a/libc/calls/isfdopen.c b/libc/stdio/g_fflush.c
similarity index 90%
rename from libc/calls/isfdopen.c
rename to libc/stdio/g_fflush.c
index b1d134723..80139bd9b 100644
--- a/libc/calls/isfdopen.c
+++ b/libc/stdio/g_fflush.c
@@ -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,8 +16,6 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
-#include "libc/calls/internal.h"
+#include "libc/stdio/fflush.internal.h"
-bool __isfdopen(int fd) {
- return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind != kFdEmpty;
-}
+struct StdioFlush g_fflush;
diff --git a/libc/stdio/internal.h b/libc/stdio/internal.h
index 56b28dab3..745b94a0a 100644
--- a/libc/stdio/internal.h
+++ b/libc/stdio/internal.h
@@ -17,6 +17,7 @@ int __fwritebuf(FILE *) hidden;
long __fseteof(FILE *) hidden;
long __fseterrno(FILE *) hidden;
long __fseterr(FILE *, int) hidden;
+void __fclosepid(FILE *) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
diff --git a/libc/stdio/pclose.c b/libc/stdio/pclose.c
new file mode 100644
index 000000000..911855ad1
--- /dev/null
+++ b/libc/stdio/pclose.c
@@ -0,0 +1,43 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 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/assert.h"
+#include "libc/calls/calls.h"
+#include "libc/errno.h"
+#include "libc/stdio/stdio.h"
+#include "libc/sysv/errfuns.h"
+
+/**
+ * Closes stream created by popen().
+ * @return termination status of subprocess, or -1 w/ ECHILD
+ */
+int pclose(FILE *f) {
+ int ws, pid;
+ pid = f->pid;
+ fclose(f);
+ assert(pid);
+ if (!pid) return 0;
+TryAgain:
+ if (wait4(pid, &ws, 0, 0) != -1) {
+ return ws;
+ } else if (errno == EINTR) {
+ goto TryAgain;
+ } else {
+ return echild();
+ }
+}
diff --git a/libc/stdio/popen.c b/libc/stdio/popen.c
new file mode 100644
index 000000000..50abc39c5
--- /dev/null
+++ b/libc/stdio/popen.c
@@ -0,0 +1,57 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/calls/calls.h"
+#include "libc/errno.h"
+#include "libc/paths.h"
+#include "libc/runtime/runtime.h"
+#include "libc/stdio/internal.h"
+#include "libc/stdio/stdio.h"
+#include "libc/sysv/consts/f.h"
+#include "libc/sysv/consts/fd.h"
+#include "libc/sysv/consts/o.h"
+#include "libc/sysv/errfuns.h"
+
+/**
+ * Spawns subprocess and returns pipe stream.
+ * @see pclose()
+ */
+FILE *popen(const char *cmdline, const char *mode) {
+ FILE *f;
+ int dir, flags, pipefds[2];
+ flags = fopenflags(mode);
+ if ((flags & O_ACCMODE) == O_RDONLY) {
+ dir = 0;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ dir = 1;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (pipe(pipefds) == -1) return NULL;
+ fcntl(pipefds[dir], F_SETFD, FD_CLOEXEC);
+ if (!(f = fdopen(pipefds[dir], mode))) abort();
+ if ((f->pid = vfork()) == -1) abort();
+ if (!f->pid) {
+ dup2(pipefds[!dir], !dir);
+ systemexec(cmdline);
+ _exit(127);
+ }
+ close(pipefds[!dir]);
+ return f;
+}
diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h
index 0bc2b42d8..a0f4f1f18 100644
--- a/libc/stdio/stdio.h
+++ b/libc/stdio/stdio.h
@@ -22,6 +22,7 @@ typedef struct FILE {
uint32_t nofree; // 0x24
int (*reader)(struct FILE *); // 0x28
int (*writer)(struct FILE *); // 0x30
+ int pid; // 0x34
} FILE;
extern FILE *stdin;
@@ -69,6 +70,8 @@ unsigned favail(FILE *);
void setbuf(FILE *, char *);
void setbuffer(FILE *, char *, size_t);
int setvbuf(FILE *, char *, int, size_t);
+FILE *popen(const char *, const char *);
+int pclose(FILE *);
typedef uint64_t fpos_t;
compatfn char *gets(char *) paramsnonnull();
@@ -78,6 +81,7 @@ compatfn int64_t fseeko(FILE *, long, int) paramsnonnull();
compatfn int64_t ftello(FILE *) paramsnonnull();
int system(const char *);
+int systemexec(const char *);
int systemecho(const char *);
/*───────────────────────────────────────────────────────────────────────────│─╗
diff --git a/libc/stdio/stdio_ext.h b/libc/stdio/stdio_ext.h
new file mode 100644
index 000000000..ea8b67136
--- /dev/null
+++ b/libc/stdio/stdio_ext.h
@@ -0,0 +1,25 @@
+#ifndef COSMOPOLITAN_LIBC_STDIO_STDIO_EXT_H_
+#define COSMOPOLITAN_LIBC_STDIO_STDIO_EXT_H_
+#include "libc/stdio/stdio.h"
+
+#define FSETLOCKING_QUERY 0
+#define FSETLOCKING_INTERNAL 1
+#define FSETLOCKING_BYCALLER 2
+
+#if !(__ASSEMBLER__ + __LINKER__ + 0)
+COSMOPOLITAN_C_START_
+
+size_t __fbufsize(FILE *);
+size_t __fpending(FILE *);
+int __flbf(FILE *);
+int __freadable(FILE *);
+int __fwritable(FILE *);
+int __freading(FILE *);
+int __fwriting(FILE *);
+int __fsetlocking(FILE *, int);
+void _flushlbf(void);
+void __fpurge(FILE *);
+
+COSMOPOLITAN_C_END_
+#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
+#endif /* COSMOPOLITAN_LIBC_STDIO_STDIO_EXT_H_ */
diff --git a/libc/stdio/system.c b/libc/stdio/system.c
index a01c7fdb4..ea69be8e6 100644
--- a/libc/stdio/system.c
+++ b/libc/stdio/system.c
@@ -18,12 +18,15 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
+#include "libc/calls/sigbits.h"
+#include "libc/calls/struct/sigaction.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/paths.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
+#include "libc/sysv/consts/sig.h"
/**
* Launches program with system command interpreter.
@@ -34,26 +37,36 @@
*/
int system(const char *cmdline) {
int pid, wstatus;
- char comspec[128];
- const char *prog, *arg;
- if (weaken(fflush)) weaken(fflush)(NULL);
- if (cmdline) {
- if ((pid = vfork()) == -1) return -1;
- if (!pid) {
- strcpy(comspec, kNtSystemDirectory);
- strcat(comspec, "cmd.exe");
- prog = !IsWindows() ? _PATH_BSHELL : comspec;
- arg = !IsWindows() ? "-c" : "/C";
- execv(prog, (char *const[]){prog, arg, cmdline, NULL});
- _exit(errno);
- } else if (wait4(pid, &wstatus, 0, NULL) != -1) {
- return wstatus;
- } else {
- return -1;
+ sigset_t chldmask, savemask;
+ struct sigaction ignore, saveint, savequit;
+ if (!cmdline) return 1;
+ ignore.sa_flags = 0;
+ ignore.sa_handler = SIG_IGN;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGINT, &ignore, &saveint);
+ sigaction(SIGQUIT, &ignore, &savequit);
+ sigemptyset(&chldmask);
+ sigaddset(&chldmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &chldmask, &savemask);
+ pid = fork();
+ if (!pid) {
+ sigaction(SIGINT, &saveint, NULL);
+ sigaction(SIGQUIT, &savequit, NULL);
+ sigprocmask(SIG_SETMASK, &savemask, NULL);
+ systemexec(cmdline);
+ _exit(127);
+ } else if (pid != -1) {
+ while (wait4(pid, &wstatus, 0, NULL) == -1) {
+ if (errno != EINTR) {
+ wstatus = -1;
+ break;
+ }
}
- } else if (IsWindows()) {
- return true;
} else {
- return fileexists(_PATH_BSHELL);
+ wstatus = -1;
}
+ sigaction(SIGINT, &saveint, NULL);
+ sigaction(SIGQUIT, &savequit, NULL);
+ sigprocmask(SIG_SETMASK, &savemask, NULL);
+ return wstatus;
}
diff --git a/libc/stdio/systemexec.c b/libc/stdio/systemexec.c
new file mode 100644
index 000000000..cb9b3fd76
--- /dev/null
+++ b/libc/stdio/systemexec.c
@@ -0,0 +1,38 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/calls/calls.h"
+#include "libc/dce.h"
+#include "libc/paths.h"
+#include "libc/runtime/runtime.h"
+#include "libc/stdio/stdio.h"
+#include "libc/str/str.h"
+
+/**
+ * Executes system command replacing current process.
+ * @vforksafe
+ */
+int systemexec(const char *cmdline) {
+ char comspec[128];
+ const char *prog, *arg;
+ strcpy(comspec, kNtSystemDirectory);
+ strcat(comspec, "cmd.exe");
+ prog = !IsWindows() ? _PATH_BSHELL : comspec;
+ arg = !IsWindows() ? "-c" : "/C";
+ return execv(prog, (char *const[]){prog, arg, cmdline, NULL});
+}
diff --git a/libc/str/iconv.c b/libc/str/iconv.c
new file mode 100644
index 000000000..f490fd365
--- /dev/null
+++ b/libc/str/iconv.c
@@ -0,0 +1,33 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 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/str/str.h"
+
+typedef void *iconv_t;
+
+iconv_t iconv_open(const char *to, const char *from) {
+ return NULL;
+}
+
+int iconv_close(iconv_t cd) {
+ return -1;
+}
+
+size_t iconv(iconv_t cd, char **in, size_t *inb, char **out, size_t *outb) {
+ return -1;
+}
diff --git a/libc/str/str.h b/libc/str/str.h
index 8f814355a..9684dd26b 100644
--- a/libc/str/str.h
+++ b/libc/str/str.h
@@ -169,6 +169,7 @@ bool endswith(const char *, const char *) strlenesque;
bool endswith16(const char16_t *, const char16_t *) strlenesque;
bool wcsendswith(const wchar_t *, const wchar_t *) strlenesque;
const char *IndexDoubleNulString(const char *, unsigned) strlenesque;
+int strverscmp(const char *, const char *);
wchar_t *wmemset(wchar_t *, wchar_t, size_t) memcpyesque;
char16_t *memset16(char16_t *, char16_t, size_t) memcpyesque;
compatfn wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t) memcpyesque;
diff --git a/libc/str/strverscmp.c b/libc/str/strverscmp.c
new file mode 100644
index 000000000..153eeca83
--- /dev/null
+++ b/libc/str/strverscmp.c
@@ -0,0 +1,71 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╚──────────────────────────────────────────────────────────────────────────────╝
+│ │
+│ Musl Libc │
+│ Copyright © 2005-2014 Rich Felker, et al. │
+│ │
+│ Permission is hereby granted, free of charge, to any person obtaining │
+│ a copy of this software and associated documentation files (the │
+│ "Software"), to deal in the Software without restriction, including │
+│ without limitation the rights to use, copy, modify, merge, publish, │
+│ distribute, sublicense, and/or sell copies of the Software, and to │
+│ permit persons to whom the Software is furnished to do so, subject to │
+│ the following conditions: │
+│ │
+│ The above copyright notice and this permission notice shall be │
+│ included in all copies or substantial portions of the Software. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
+│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
+│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
+│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
+│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
+│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
+│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
+│ │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/str/str.h"
+
+asm(".ident\t\"\\n\\n\
+Musl libc (MIT License)\\n\
+Copyright 2005-2014 Rich Felker, et. al.\"");
+asm(".include \"libc/disclaimer.inc\"");
+
+/**
+ * Compares two version strings.
+ */
+int strverscmp(const char *l0, const char *r0) {
+ const unsigned char *l = (const void *)l0;
+ const unsigned char *r = (const void *)r0;
+ size_t i, dp, j;
+ int z = 1;
+ /* Find maximal matching prefix and track its maximal digit
+ * suffix and whether those digits are all zeros. */
+ for (dp = i = 0; l[i] == r[i]; i++) {
+ int c = l[i];
+ if (!c) return 0;
+ if (!isdigit(c)) {
+ dp = i + 1, z = 1;
+ } else if (c != '0') {
+ z = 0;
+ }
+ }
+ if (l[dp] != '0' && r[dp] != '0') {
+ /* If we're not looking at a digit sequence that began
+ * with a zero, longest digit string is greater. */
+ for (j = i; isdigit(l[j]); j++) {
+ if (!isdigit(r[j])) {
+ return 1;
+ }
+ }
+ if (isdigit(r[j])) {
+ return -1;
+ }
+ } else if (z && dp < i && (isdigit(l[i]) || isdigit(r[i]))) {
+ /* Otherwise, if common prefix of digit sequence is
+ * all zeros, digits order less than non-digits. */
+ return (unsigned char)(l[i] - '0') - (unsigned char)(r[i] - '0');
+ }
+ return l[i] - r[i];
+}
diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh
index 0278edbe6..04b7d49c5 100755
--- a/libc/sysv/consts.sh
+++ b/libc/sysv/consts.sh
@@ -180,21 +180,21 @@ syscon sig SIGXFSZ 25 25 25 25 25 # unix consensus & faked on nt
syscon sig SIGVTALRM 26 26 26 26 26 # unix consensus & faked on nt
syscon sig SIGPROF 27 27 27 27 27 # unix consensus & faked on nt
syscon sig SIGWINCH 28 28 28 28 28 # unix consensus & faked on nt
-syscon sig SIGBUS 7 10 10 10 0 # bsd consensus
-syscon sig SIGUSR1 10 30 30 30 0 # bsd consensus
-syscon sig SIGCHLD 17 20 20 20 0 # bsd consensus
-syscon sig SIGCONT 18 19 19 19 0 # bsd consensus
-syscon sig SIGIO 29 23 23 23 0 # bsd consensus
-syscon sig SIGSTOP 19 17 17 17 0 # bsd consensus
-syscon sig SIGSYS 31 12 12 12 0 # bsd consensus
-syscon sig SIGTSTP 20 18 18 18 0 # bsd consensus
-syscon sig SIGURG 23 0x10 0x10 0x10 0 # bsd consensus
-syscon sig SIGUSR2 12 31 31 31 0 # bsd consensus
-syscon sig SIGSTKSZ 0x2000 0x020000 0x8800 0x7000 0
-syscon sig SIGPOLL 29 0 0 0 0
-syscon sig SIGPWR 30 0 0 0 0
-syscon sig SIGSTKFLT 0x10 0 0 0 0
-syscon sig SIGUNUSED 31 0 0 0 0
+syscon sig SIGBUS 7 10 10 10 7 # bsd consensus
+syscon sig SIGUSR1 10 30 30 30 10 # bsd consensus
+syscon sig SIGCHLD 17 20 20 20 17 # bsd consensus
+syscon sig SIGCONT 18 19 19 19 18 # bsd consensus
+syscon sig SIGIO 29 23 23 23 29 # bsd consensus
+syscon sig SIGSTOP 19 17 17 17 19 # bsd consensus
+syscon sig SIGSYS 31 12 12 12 31 # bsd consensus
+syscon sig SIGTSTP 20 18 18 18 20 # bsd consensus
+syscon sig SIGURG 23 0x10 0x10 0x10 23 # bsd consensus
+syscon sig SIGUSR2 12 31 31 31 12 # bsd consensus
+syscon sig SIGSTKSZ 0x2000 0x020000 0x8800 0x7000 0x2000
+syscon sig SIGPOLL 29 0 0 0 29
+syscon sig SIGPWR 30 0 0 0 30
+syscon sig SIGSTKFLT 0x10 0 0 0 0x10
+syscon sig SIGUNUSED 31 0 0 0 31
syscon sig SIGRTMAX 0 0 126 0 0
syscon sig SIGRTMIN 0 0 65 0 0
diff --git a/libc/sysv/consts/SIGBUS.s b/libc/sysv/consts/SIGBUS.s
index b34442fcd..b0331dfab 100644
--- a/libc/sysv/consts/SIGBUS.s
+++ b/libc/sysv/consts/SIGBUS.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGBUS 7 10 10 10 0
+.syscon sig SIGBUS 7 10 10 10 7
diff --git a/libc/sysv/consts/SIGCHLD.s b/libc/sysv/consts/SIGCHLD.s
index 3a25f5ccf..4fd02e67d 100644
--- a/libc/sysv/consts/SIGCHLD.s
+++ b/libc/sysv/consts/SIGCHLD.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGCHLD 17 20 20 20 0
+.syscon sig SIGCHLD 17 20 20 20 17
diff --git a/libc/sysv/consts/SIGCONT.s b/libc/sysv/consts/SIGCONT.s
index 004525779..4cc74b898 100644
--- a/libc/sysv/consts/SIGCONT.s
+++ b/libc/sysv/consts/SIGCONT.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGCONT 18 19 19 19 0
+.syscon sig SIGCONT 18 19 19 19 18
diff --git a/libc/sysv/consts/SIGIO.s b/libc/sysv/consts/SIGIO.s
index 8a64d2bc4..ded429b3c 100644
--- a/libc/sysv/consts/SIGIO.s
+++ b/libc/sysv/consts/SIGIO.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGIO 29 23 23 23 0
+.syscon sig SIGIO 29 23 23 23 29
diff --git a/libc/sysv/consts/SIGPOLL.s b/libc/sysv/consts/SIGPOLL.s
index 7f88e1a27..36c6e42c3 100644
--- a/libc/sysv/consts/SIGPOLL.s
+++ b/libc/sysv/consts/SIGPOLL.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGPOLL 29 0 0 0 0
+.syscon sig SIGPOLL 29 0 0 0 29
diff --git a/libc/sysv/consts/SIGPWR.s b/libc/sysv/consts/SIGPWR.s
index 9fcfab49d..eb7f8ed98 100644
--- a/libc/sysv/consts/SIGPWR.s
+++ b/libc/sysv/consts/SIGPWR.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGPWR 30 0 0 0 0
+.syscon sig SIGPWR 30 0 0 0 30
diff --git a/libc/sysv/consts/SIGSTKFLT.s b/libc/sysv/consts/SIGSTKFLT.s
index fbc256189..e986d404a 100644
--- a/libc/sysv/consts/SIGSTKFLT.s
+++ b/libc/sysv/consts/SIGSTKFLT.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGSTKFLT 0x10 0 0 0 0
+.syscon sig SIGSTKFLT 0x10 0 0 0 0x10
diff --git a/libc/sysv/consts/SIGSTKSZ.s b/libc/sysv/consts/SIGSTKSZ.s
index c0410eae5..e02067c62 100644
--- a/libc/sysv/consts/SIGSTKSZ.s
+++ b/libc/sysv/consts/SIGSTKSZ.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGSTKSZ 0x2000 0x020000 0x8800 0x7000 0
+.syscon sig SIGSTKSZ 0x2000 0x020000 0x8800 0x7000 0x2000
diff --git a/libc/sysv/consts/SIGSTOP.s b/libc/sysv/consts/SIGSTOP.s
index 239996633..1438f22c2 100644
--- a/libc/sysv/consts/SIGSTOP.s
+++ b/libc/sysv/consts/SIGSTOP.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGSTOP 19 17 17 17 0
+.syscon sig SIGSTOP 19 17 17 17 19
diff --git a/libc/sysv/consts/SIGSYS.s b/libc/sysv/consts/SIGSYS.s
index 67768e78a..d13c13909 100644
--- a/libc/sysv/consts/SIGSYS.s
+++ b/libc/sysv/consts/SIGSYS.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGSYS 31 12 12 12 0
+.syscon sig SIGSYS 31 12 12 12 31
diff --git a/libc/sysv/consts/SIGTSTP.s b/libc/sysv/consts/SIGTSTP.s
index c70d3b33a..cfa45fbd2 100644
--- a/libc/sysv/consts/SIGTSTP.s
+++ b/libc/sysv/consts/SIGTSTP.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGTSTP 20 18 18 18 0
+.syscon sig SIGTSTP 20 18 18 18 20
diff --git a/libc/sysv/consts/SIGUNUSED.s b/libc/sysv/consts/SIGUNUSED.s
index 2a5c092c5..ba71f6857 100644
--- a/libc/sysv/consts/SIGUNUSED.s
+++ b/libc/sysv/consts/SIGUNUSED.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGUNUSED 31 0 0 0 0
+.syscon sig SIGUNUSED 31 0 0 0 31
diff --git a/libc/sysv/consts/SIGURG.s b/libc/sysv/consts/SIGURG.s
index f07d8610e..d57f6f772 100644
--- a/libc/sysv/consts/SIGURG.s
+++ b/libc/sysv/consts/SIGURG.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGURG 23 0x10 0x10 0x10 0
+.syscon sig SIGURG 23 0x10 0x10 0x10 23
diff --git a/libc/sysv/consts/SIGUSR1.s b/libc/sysv/consts/SIGUSR1.s
index bf64e7336..07faf27a0 100644
--- a/libc/sysv/consts/SIGUSR1.s
+++ b/libc/sysv/consts/SIGUSR1.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGUSR1 10 30 30 30 0
+.syscon sig SIGUSR1 10 30 30 30 10
diff --git a/libc/sysv/consts/SIGUSR2.s b/libc/sysv/consts/SIGUSR2.s
index 16792d049..e4bacc8c8 100644
--- a/libc/sysv/consts/SIGUSR2.s
+++ b/libc/sysv/consts/SIGUSR2.s
@@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
-.syscon sig SIGUSR2 12 31 31 31 0
+.syscon sig SIGUSR2 12 31 31 31 12
diff --git a/libc/testlib/showerror_.c b/libc/testlib/showerror_.c
index f0d61c09a..3fce21950 100644
--- a/libc/testlib/showerror_.c
+++ b/libc/testlib/showerror_.c
@@ -30,8 +30,6 @@
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
-STATIC_YOINK("__isfdkind");
-
const char *testlib_showerror_errno;
const char *testlib_showerror_file;
const char *testlib_showerror_func;
@@ -85,9 +83,10 @@ testonly void testlib_showerror_(int line, const char *wantcode,
strcpy(hostname, "unknown");
gethostname(hostname, sizeof(hostname));
fprintf(stderr,
- "\t%s%s\n"
- "\t%s @ %s%s\n",
- SUBTLE, strerror(err), program_invocation_name, hostname, RESET);
+ "\t%s%s%s\n"
+ "\t%s%s @ %s%s\n",
+ SUBTLE, strerror(err), RESET, SUBTLE, program_invocation_name,
+ hostname, RESET);
free_s(&FREED_want);
free_s(&FREED_got);
diff --git a/libc/time/asctime.c b/libc/time/asctime.c
index 0e8ebb9eb..b17b44e6f 100644
--- a/libc/time/asctime.c
+++ b/libc/time/asctime.c
@@ -20,4 +20,12 @@
static char g_asctime_buf[64];
-char *asctime(const struct tm *date) { return asctime_r(date, g_asctime_buf); }
+/**
+ * Converts date time to string.
+ *
+ * @return date time string in statically allocated buffer
+ * @see asctime_r for reentrant version
+ */
+char *asctime(const struct tm *date) {
+ return asctime_r(date, g_asctime_buf);
+}
diff --git a/libc/time/asctime_r.c b/libc/time/asctime_r.c
index 1dfe8cc70..e3e71d0cc 100644
--- a/libc/time/asctime_r.c
+++ b/libc/time/asctime_r.c
@@ -27,6 +27,13 @@ static unsigned clip(unsigned index, unsigned count) {
return index < count ? index : 0;
}
+/**
+ * Converts date time to string.
+ *
+ * @param buf needs to have 64 bytes
+ * @return pointer to buf
+ * @see asctime_r for reentrant version
+ */
char *asctime_r(const struct tm *date, char buf[hasatleast 64]) {
(snprintf)(buf, 64, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
kWeekdayNameShort[clip(date->tm_wday, 7)],
diff --git a/test/libc/calls/hefty/vfork_test.c b/test/libc/calls/hefty/vfork_test.c
index 1ce18ab5f..1c5bc6155 100644
--- a/test/libc/calls/hefty/vfork_test.c
+++ b/test/libc/calls/hefty/vfork_test.c
@@ -33,14 +33,18 @@ TEST(vfork, test) {
ASSERT_NE(-1, lseek(fd, 0, SEEK_SET));
if (!vfork()) {
EXPECT_EQ(5, pread(fd, buf, 5, 0));
+ /*
+ * TODO(jart): DOES PREAD IN CHILD REALLY CHANGE PARENT HANDLE POSITION?
+ */
+ ASSERT_NE(-1, lseek(fd, 0, SEEK_SET));
EXPECT_STREQ("hello", buf);
EXPECT_NE(-1, close(fd));
_exit(0);
}
EXPECT_EQ(0, __vforked);
+ EXPECT_NE(-1, wait(0));
EXPECT_EQ(5, read(fd, buf, 5));
EXPECT_STREQ("hello", buf);
EXPECT_NE(-1, close(fd));
- EXPECT_NE(-1, wait(0));
unlink(PATH);
}
diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c
new file mode 100644
index 000000000..44c58b051
--- /dev/null
+++ b/test/libc/calls/sigaction_test.c
@@ -0,0 +1,63 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/calls/calls.h"
+#include "libc/calls/sigbits.h"
+#include "libc/calls/struct/sigaction.h"
+#include "libc/dce.h"
+#include "libc/errno.h"
+#include "libc/runtime/runtime.h"
+#include "libc/sysv/consts/sa.h"
+#include "libc/sysv/consts/sig.h"
+#include "libc/testlib/testlib.h"
+
+bool gotsigint;
+
+void OnSigInt(int sig) {
+ gotsigint = true;
+}
+
+TEST(sigaction, test) {
+ /* TODO(jart): Why does RHEL5 behave differently? */
+ /* TODO(jart): Windows needs huge signal overhaul */
+ if (IsWindows()) return;
+ int pid, status;
+ sigset_t block, ignore, oldmask;
+ struct sigaction saint = {.sa_handler = OnSigInt};
+ sigemptyset(&block);
+ sigaddset(&block, SIGINT);
+ EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &block, &oldmask));
+ sigfillset(&ignore);
+ sigdelset(&ignore, SIGINT);
+ EXPECT_NE(-1, sigaction(SIGINT, &saint, NULL));
+ ASSERT_NE(-1, (pid = fork()));
+ if (!pid) {
+ EXPECT_NE(-1, kill(getppid(), SIGINT));
+ EXPECT_EQ(-1, sigsuspend(&ignore));
+ EXPECT_EQ(EINTR, errno);
+ EXPECT_TRUE(gotsigint);
+ _exit(0);
+ }
+ EXPECT_EQ(-1, sigsuspend(&ignore));
+ EXPECT_NE(-1, kill(pid, SIGINT));
+ EXPECT_NE(-1, waitpid(pid, &status, 0));
+ EXPECT_EQ(1, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+ EXPECT_EQ(0, WTERMSIG(status));
+ EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &oldmask, NULL));
+}
diff --git a/test/libc/stdio/popen_test.c b/test/libc/stdio/popen_test.c
new file mode 100644
index 000000000..c7d37b8ea
--- /dev/null
+++ b/test/libc/stdio/popen_test.c
@@ -0,0 +1,34 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 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/log/check.h"
+#include "libc/stdio/stdio.h"
+#include "libc/testlib/testlib.h"
+
+TEST(popen, test) {
+ int ws;
+ FILE *f;
+ f = popen("echo hi", "r");
+ ASSERT_NE(NULL, f);
+ EXPECT_EQ('h', fgetc(f));
+ EXPECT_EQ('i', fgetc(f));
+ ws = pclose(f);
+ EXPECT_NE(-1, ws);
+ EXPECT_TRUE(WIFEXITED(ws));
+ EXPECT_EQ(0, WEXITSTATUS(ws));
+}
diff --git a/third_party/chibicc/chibicc.c b/third_party/chibicc/chibicc.c
index d430b091a..cf4d69f10 100644
--- a/third_party/chibicc/chibicc.c
+++ b/third_party/chibicc/chibicc.c
@@ -1,5 +1,6 @@
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/ucontext.h"
+#include "libc/x/x.h"
#include "third_party/chibicc/chibicc.h"
asm(".ident\t\"\\n\\n\
@@ -606,7 +607,7 @@ static void run_linker(StringArray *inputs, char *output) {
strarray_push(&arr, "--gc-sections");
strarray_push(&arr, "--build-id=none");
strarray_push(&arr, "--no-dynamic-linker");
- strarray_push(&arr, "-Ttext-segment=0x400000");
+ strarray_push(&arr, xasprintf("-Ttext-segment=%#x", IMAGE_BASE_VIRTUAL));
strarray_push(&arr, "-T");
strarray_push(&arr, LDS);
strarray_push(&arr, APE);
diff --git a/third_party/chibicc/chibicc.mk b/third_party/chibicc/chibicc.mk
index c7cb18fee..2dad14334 100644
--- a/third_party/chibicc/chibicc.mk
+++ b/third_party/chibicc/chibicc.mk
@@ -14,7 +14,8 @@ CHIBICC = o/$(MODE)/third_party/chibicc/chibicc.com.dbg
CHIBICC2 = o/$(MODE)/third_party/chibicc/chibicc2.com.dbg
CHIBICC_FLAGS = \
-fno-common \
- -include libc/integral/normalize.inc
+ -include libc/integral/normalize.inc \
+ -DIMAGE_BASE_VIRTUAL=$(IMAGE_BASE_VIRTUAL)
PKGS += THIRD_PARTY_CHIBICC
THIRD_PARTY_CHIBICC_ARTIFACTS += THIRD_PARTY_CHIBICC_A
diff --git a/tool/build/runit.c b/tool/build/runit.c
index 3650a1ce9..6cd5fcffc 100644
--- a/tool/build/runit.c
+++ b/tool/build/runit.c
@@ -20,6 +20,7 @@
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
+#include "libc/calls/sigbits.h"
#include "libc/calls/struct/flock.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/sigaction.h"
@@ -119,7 +120,7 @@ char g_hostname[128];
uint16_t g_runitdport;
volatile bool alarmed;
-static void OnAlarm(void) {
+static void OnAlarm(int sig) {
alarmed = true;
}
@@ -170,7 +171,9 @@ void DeployEphemeralRunItDaemonRemotelyViaSsh(struct addrinfo *ai) {
struct stat st;
char linebuf[32];
struct timeval now, then;
+ sigset_t chldmask, savemask;
int sshpid, wstatus, binfd, pipefds[2][2];
+ struct sigaction ignore, saveint, savequit;
mkdir("o", 0755);
CHECK_NE(-1, (lock = open(gc(xasprintf("o/lock.%s", g_hostname)),
O_RDWR | O_CREAT, 0644)));
@@ -179,7 +182,7 @@ void DeployEphemeralRunItDaemonRemotelyViaSsh(struct addrinfo *ai) {
if (!read(lock, &then, 16) || ((now.tv_sec * 1000 + now.tv_usec / 1000) -
(then.tv_sec * 1000 + then.tv_usec / 1000)) >=
(RUNITD_TIMEOUT_MS >> 1)) {
- DEBUGF("spawning %s on %s:%hu", g_runitd, g_hostname, g_runitdport);
+ DEBUGF("ssh %s:%hu to spawn %s", g_hostname, g_runitdport, g_runitd);
CHECK_NE(-1, (binfd = open(g_runitd, O_RDONLY | O_CLOEXEC)));
CHECK_NE(-1, fstat(binfd, &st));
args[0] = "ssh";
@@ -189,16 +192,28 @@ void DeployEphemeralRunItDaemonRemotelyViaSsh(struct addrinfo *ai) {
args[4] = g_hostname;
args[5] = gc(MakeDeployScript(ai, st.st_size));
args[6] = NULL;
+ ignore.sa_flags = 0;
+ ignore.sa_handler = SIG_IGN;
+ LOGIFNEG1(sigemptyset(&ignore.sa_mask));
+ LOGIFNEG1(sigaction(SIGINT, &ignore, &saveint));
+ LOGIFNEG1(sigaction(SIGQUIT, &ignore, &savequit));
+ LOGIFNEG1(sigemptyset(&chldmask));
+ LOGIFNEG1(sigaddset(&chldmask, SIGCHLD));
+ LOGIFNEG1(sigprocmask(SIG_BLOCK, &chldmask, &savemask));
CHECK_NE(-1, pipe2(pipefds[0], O_CLOEXEC));
CHECK_NE(-1, pipe2(pipefds[1], O_CLOEXEC));
- if (!(sshpid = vfork())) {
+ CHECK_NE(-1, (sshpid = fork()));
+ if (!sshpid) {
+ sigaction(SIGINT, &saveint, NULL);
+ sigaction(SIGQUIT, &savequit, NULL);
+ sigprocmask(SIG_SETMASK, &savemask, NULL);
dup2(pipefds[0][0], 0);
dup2(pipefds[1][1], 1);
execv(g_ssh, args);
- abort();
+ _exit(127);
}
- close(pipefds[0][0]);
- close(pipefds[1][1]);
+ LOGIFNEG1(close(pipefds[0][0]));
+ LOGIFNEG1(close(pipefds[1][1]));
Upload(pipefds[0][1], binfd, &st);
LOGIFNEG1(close(pipefds[0][1]));
CHECK_NE(-1, (got = read(pipefds[1][0], linebuf, sizeof(linebuf))));
@@ -212,7 +227,16 @@ void DeployEphemeralRunItDaemonRemotelyViaSsh(struct addrinfo *ai) {
g_runitdport = (uint16_t)atoi(&linebuf[6]);
LOGIFNEG1(close(pipefds[1][0]));
CHECK_NE(-1, waitpid(sshpid, &wstatus, 0));
- CHECK_EQ(0, WEXITSTATUS(wstatus));
+ LOGIFNEG1(sigaction(SIGINT, &saveint, NULL));
+ LOGIFNEG1(sigaction(SIGQUIT, &savequit, NULL));
+ LOGIFNEG1(sigprocmask(SIG_SETMASK, &savemask, NULL));
+ if (WIFEXITED(wstatus)) {
+ DEBUGF("ssh %s exited with %d", g_hostname, WEXITSTATUS(wstatus));
+ } else {
+ DEBUGF("ssh %s terminated with %s", g_hostname,
+ strsignal(WTERMSIG(wstatus)));
+ }
+ CHECK(WIFEXITED(wstatus) && !WEXITSTATUS(wstatus), "wstatus=%#x", wstatus);
CHECK_NE(-1, gettimeofday(&now, 0));
CHECK_NE(-1, lseek(lock, 0, SEEK_SET));
CHECK_NE(-1, write(lock, &now, 16));
@@ -223,7 +247,11 @@ void DeployEphemeralRunItDaemonRemotelyViaSsh(struct addrinfo *ai) {
}
void SetDeadline(int micros) {
- setitimer(ITIMER_REAL, &(const struct itimerval){{0, 0}, {0, micros}}, NULL);
+ alarmed = false;
+ LOGIFNEG1(
+ sigaction(SIGALRM, &(struct sigaction){.sa_handler = OnAlarm}, NULL));
+ LOGIFNEG1(setitimer(ITIMER_REAL,
+ &(const struct itimerval){{0, 0}, {0, micros}}, NULL));
}
void Connect(void) {
@@ -242,28 +270,32 @@ void Connect(void) {
g_hostname, ip4[0], ip4[1], ip4[2], ip4[3]);
unreachable;
}
- DEBUGF("connecting to %s (%hhu.%hhu.%hhu.%hhu) to run %s", g_hostname, ip4[0],
- ip4[1], ip4[2], ip4[3], g_prog);
CHECK_NE(-1,
(g_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)));
expo = 1;
-TryAgain:
- alarmed = false;
+Reconnect:
+ DEBUGF("connecting to %s (%hhu.%hhu.%hhu.%hhu) to run %s", g_hostname, ip4[0],
+ ip4[1], ip4[2], ip4[3], g_prog);
SetDeadline(100000);
+TryAgain:
rc = connect(g_sock, ai->ai_addr, ai->ai_addrlen);
err = errno;
SetDeadline(0);
if (rc == -1) {
- if ((err == ECONNREFUSED || err == EHOSTUNREACH || err == ECONNRESET ||
- err == EINTR)) {
+ if (err == EINTR) goto TryAgain;
+ if (err == ECONNREFUSED || err == EHOSTUNREACH || err == ECONNRESET) {
+ DEBUGF("got %s from %s (%hhu.%hhu.%hhu.%hhu)", strerror(err), g_hostname,
+ ip4[0], ip4[1], ip4[2], ip4[3]);
usleep((expo *= 2));
DeployEphemeralRunItDaemonRemotelyViaSsh(ai);
- goto TryAgain;
+ goto Reconnect;
} else {
FATALF("%s(%s:%hu): %s", "connect", g_hostname, g_runitdport,
strerror(err));
unreachable;
}
+ } else {
+ DEBUGF("connected to %s", g_hostname);
}
freeaddrinfo(ai);
}
@@ -275,6 +307,7 @@ void SendRequest(void) {
const char *name;
unsigned char *hdr;
size_t progsize, namesize, hdrsize;
+ DEBUGF("running %s on %s", g_prog, g_hostname);
CHECK_NE(-1, (fd = open(g_prog, O_RDONLY)));
CHECK_NE(-1, fstat(fd, &st));
CHECK_LE((namesize = strlen((name = basename(g_prog)))), PATH_MAX);
@@ -370,8 +403,7 @@ int RunOnHost(char *spec) {
do {
Connect();
SendRequest();
- rc = ReadResponse();
- } while (rc == -1);
+ } while ((rc = ReadResponse()) == -1);
return rc;
}
@@ -384,59 +416,56 @@ bool ShouldRunInParralel(void) {
return !IsWindows() && IsParallelBuild();
}
-int RunRemoteTestsInSerial(char *hosts[], int count) {
- int i, exitcode;
- for (i = 0; i < count; ++i) {
- if ((exitcode = RunOnHost(hosts[i]))) {
- return exitcode;
- }
- }
- return 0;
-}
-
-void OnInterrupt(int sig) {
- static bool once;
- if (!once) {
- once = true;
- gclongjmp(g_jmpbuf, 128 + sig);
- } else {
- abort();
- }
-}
-
int RunRemoteTestsInParallel(char *hosts[], int count) {
- const struct sigaction onsigterm = {.sa_handler = (void *)OnInterrupt};
- struct sigaction onsigint = {.sa_handler = (void *)OnInterrupt};
- int i, rc, exitcode;
- int64_t leader, *pids;
- leader = getpid();
- pids = gc(xcalloc(count, sizeof(char *)));
- if (!(exitcode = setjmp(g_jmpbuf))) {
- sigaction(SIGINT, &onsigint, NULL);
- sigaction(SIGTERM, &onsigterm, NULL);
- for (i = 0; i < count; ++i) {
- CHECK_NE(-1, (pids[i] = fork()));
- if (!pids[i]) {
- return RunOnHost(hosts[i]);
- }
+ sigset_t chldmask, savemask;
+ int i, rc, ws, pid, *pids, exitcode;
+ struct sigaction ignore, saveint, savequit;
+ pids = calloc(count, sizeof(char *));
+ ignore.sa_flags = 0;
+ ignore.sa_handler = SIG_IGN;
+ LOGIFNEG1(sigemptyset(&ignore.sa_mask));
+ LOGIFNEG1(sigaction(SIGINT, &ignore, &saveint));
+ LOGIFNEG1(sigaction(SIGQUIT, &ignore, &savequit));
+ LOGIFNEG1(sigemptyset(&chldmask));
+ LOGIFNEG1(sigaddset(&chldmask, SIGCHLD));
+ LOGIFNEG1(sigprocmask(SIG_BLOCK, &chldmask, &savemask));
+ for (i = 0; i < count; ++i) {
+ CHECK_NE(-1, (pids[i] = fork()));
+ if (!pids[i]) {
+ sigaction(SIGINT, &saveint, NULL);
+ sigaction(SIGQUIT, &savequit, NULL);
+ sigprocmask(SIG_SETMASK, &savemask, NULL);
+ _exit(RunOnHost(hosts[i]));
}
- for (i = 0; i < count; ++i) {
- CHECK_NE(-1, waitpid(pids[i], &rc, 0));
- exitcode |= WEXITSTATUS(rc);
- }
- } else if (getpid() == leader) {
- onsigint.sa_handler = SIG_IGN;
- sigaction(SIGINT, &onsigint, NULL);
- kill(0, SIGINT);
- while (waitpid(-1, NULL, 0) > 0) donothing;
}
+ for (exitcode = 0;;) {
+ if ((pid = wait(&ws)) == -1) {
+ if (errno == EINTR) continue;
+ if (errno == ECHILD) break;
+ FATALF("wait failed");
+ }
+ for (i = 0; i < count; ++i) {
+ if (pids[i] != pid) continue;
+ if (WIFEXITED(ws)) {
+ DEBUGF("%s exited with %d", hosts[i], WEXITSTATUS(ws));
+ if (!exitcode) exitcode = WEXITSTATUS(ws);
+ } else {
+ DEBUGF("%s terminated with %s", hosts[i], strsignal(WTERMSIG(ws)));
+ if (!exitcode) exitcode = 128 + WTERMSIG(ws);
+ }
+ break;
+ }
+ }
+ LOGIFNEG1(sigaction(SIGINT, &saveint, NULL));
+ LOGIFNEG1(sigaction(SIGQUIT, &savequit, NULL));
+ LOGIFNEG1(sigprocmask(SIG_SETMASK, &savemask, NULL));
+ free(pids);
return exitcode;
}
int main(int argc, char *argv[]) {
showcrashreports();
/* g_loglevel = kLogDebug; */
- const struct sigaction onsigalrm = {.sa_handler = (void *)OnAlarm};
if (argc > 1 &&
(strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
ShowUsage(stdout, 0);
@@ -447,9 +476,7 @@ int main(int argc, char *argv[]) {
CheckExists((g_runitd = argv[1]));
CheckExists((g_prog = argv[2]));
if (argc == 1 + 2) return 0; /* hosts list empty */
- sigaction(SIGALRM, &onsigalrm, NULL);
g_sshport = 22;
g_runitdport = RUNITD_PORT;
- return (ShouldRunInParralel() ? RunRemoteTestsInParallel
- : RunRemoteTestsInSerial)(&argv[3], argc - 3);
+ return RunRemoteTestsInParallel(&argv[3], argc - 3);
}
diff --git a/tool/build/runitd.c b/tool/build/runitd.c
index 949ae13b7..b5dca0fc2 100644
--- a/tool/build/runitd.c
+++ b/tool/build/runitd.c
@@ -19,6 +19,7 @@
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
+#include "libc/calls/sigbits.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
@@ -96,32 +97,30 @@
#define kLogFile "o/runitd.log"
#define kLogMaxBytes (2 * 1000 * 1000)
-jmp_buf g_jb;
char *g_exepath;
-volatile bool g_childterm;
-volatile int g_childstatus;
struct sockaddr_in g_servaddr;
unsigned char g_buf[PAGESIZE];
bool g_daemonize, g_sendready;
int g_timeout, g_devnullfd, g_servfd, g_clifd, g_exefd;
-void OnInterrupt(int sig) {
- static bool once;
- if (once) abort();
- once = true;
- kill(0, sig);
- for (;;) {
- if (waitpid(-1, NULL, 0) == -1) {
- break;
- }
- }
- gclongjmp(g_jb, sig);
- unreachable;
-}
-
void OnChildTerminated(int sig) {
- while (waitpid(-1, &g_childstatus, WNOHANG) > 0) {
- g_childterm = true;
+ int ws, pid;
+ for (;;) {
+ if ((pid = waitpid(-1, &ws, WNOHANG)) != -1) {
+ if (pid) {
+ if (WIFEXITED(ws)) {
+ DEBUGF("worker %d exited with %d", pid, WEXITSTATUS(ws));
+ } else {
+ DEBUGF("worker %d terminated with %s", pid, strsignal(WTERMSIG(ws)));
+ }
+ } else {
+ break;
+ }
+ } else {
+ if (errno == EINTR) continue;
+ if (errno == ECHILD) break;
+ FATALF("waitpid failed in sigchld");
+ }
}
}
@@ -190,14 +189,14 @@ void StartTcpServer(void) {
CHECK_NE(-1, listen(g_servfd, 10));
asize = sizeof(g_servaddr);
CHECK_NE(-1, getsockname(g_servfd, &g_servaddr, &asize));
+ CHECK_NE(-1, fcntl(g_servfd, F_SETFD, FD_CLOEXEC));
+ LOGF("%s:%s", "listening on tcp", gc(DescribeAddress(&g_servaddr)));
if (g_sendready) {
printf("ready %hu\n", ntohs(g_servaddr.sin_port));
fflush(stdout);
fclose(stdout);
- stdout->fd = g_devnullfd;
+ dup2(g_devnullfd, stdout->fd);
}
- CHECK_NE(-1, fcntl(g_servfd, F_SETFD, FD_CLOEXEC));
- LOGF("%s:%s", "listening on tcp", gc(DescribeAddress(&g_servaddr)));
}
void SendExitMessage(int sock, int rc) {
@@ -242,7 +241,9 @@ void HandleClient(void) {
ssize_t got, wrote;
struct sockaddr_in addr;
char *addrstr, *exename;
- int rc, wstatus, child, pipefds[2];
+ sigset_t chldmask, savemask;
+ int exitcode, wstatus, child, pipefds[2];
+ struct sigaction ignore, saveint, savequit;
uint32_t addrsize, namesize, filesize, remaining;
/* read request to run program */
@@ -252,7 +253,6 @@ void HandleClient(void) {
close(g_clifd);
return;
}
- g_childterm = false;
addrstr = gc(DescribeAddress(&addr));
DEBUGF("%s %s %s", gc(DescribeAddress(&g_servaddr)), "accepted", addrstr);
got = recv(g_clifd, (p = &g_buf[0]), sizeof(g_buf), 0);
@@ -303,13 +303,25 @@ void HandleClient(void) {
/* run program, tee'ing stderr to both log and client */
DEBUGF("spawning %s", exename);
+ ignore.sa_flags = 0;
+ ignore.sa_handler = SIG_IGN;
+ LOGIFNEG1(sigemptyset(&ignore.sa_mask));
+ LOGIFNEG1(sigaction(SIGINT, &ignore, &saveint));
+ LOGIFNEG1(sigaction(SIGQUIT, &ignore, &savequit));
+ LOGIFNEG1(sigemptyset(&chldmask));
+ LOGIFNEG1(sigaddset(&chldmask, SIGCHLD));
+ LOGIFNEG1(sigprocmask(SIG_BLOCK, &chldmask, &savemask));
CHECK_NE(-1, pipe2(pipefds, O_CLOEXEC));
- if (!(child = vfork())) {
+ CHECK_NE(-1, (child = fork()));
+ if (!child) {
+ sigaction(SIGINT, &saveint, NULL);
+ sigaction(SIGQUIT, &savequit, NULL);
+ sigprocmask(SIG_SETMASK, &savemask, NULL);
dup2(pipefds[1], 2);
execv(g_exepath, (char *const[]){g_exepath, NULL});
- abort();
+ _exit(127);
}
- close(pipefds[1]);
+ LOGIFNEG1(close(pipefds[1]));
DEBUGF("communicating %s[%d]", exename, child);
for (;;) {
CHECK_NE(-1, (got = read(pipefds[0], g_buf, sizeof(g_buf))));
@@ -320,23 +332,24 @@ void HandleClient(void) {
fwrite(g_buf, got, 1, stderr);
SendOutputFragmentMessage(g_clifd, kRunitStderr, g_buf, got);
}
-
- if ((rc = waitpid(child, &wstatus, 0)) != -1) {
- g_childstatus = wstatus;
- } else {
- CHECK_EQ(ECHILD, errno);
- CHECK(g_childterm);
+ while (waitpid(child, &wstatus, 0) == -1) {
+ if (errno == EINTR) continue;
+ FATALF("waitpid failed");
}
- if (WIFSIGNALED(g_childstatus)) {
- rc = 128 + WTERMSIG(g_childstatus);
+ if (WIFEXITED(wstatus)) {
+ DEBUGF("%s exited with %d", exename, WEXITSTATUS(wstatus));
+ exitcode = WEXITSTATUS(wstatus);
} else {
- rc = WEXITSTATUS(g_childstatus);
+ DEBUGF("%s terminated with %s", exename, strsignal(WTERMSIG(wstatus)));
+ exitcode = 128 + WTERMSIG(wstatus);
}
- DEBUGF("exited %s[%d] -> %d", exename, child, rc);
+ LOGIFNEG1(sigaction(SIGINT, &saveint, NULL));
+ LOGIFNEG1(sigaction(SIGQUIT, &savequit, NULL));
+ LOGIFNEG1(sigprocmask(SIG_SETMASK, &savemask, NULL));
/* let client know how it went */
LOGIFNEG1(unlink(g_exepath));
- SendExitMessage(g_clifd, rc);
+ SendExitMessage(g_clifd, exitcode);
LOGIFNEG1(shutdown(g_clifd, SHUT_RDWR));
LOGIFNEG1(close(g_clifd));
_exit(0);
@@ -363,29 +376,17 @@ TryAgain:
}
int Serve(void) {
- int rc;
- const struct sigaction onsigint = {.sa_handler = (void *)OnInterrupt,
- .sa_flags = SA_NODEFER};
- const struct sigaction onsigterm = {.sa_handler = (void *)OnInterrupt,
- .sa_flags = SA_NODEFER};
- const struct sigaction onsigchld = {.sa_handler = (void *)OnChildTerminated,
- .sa_flags = SA_RESTART};
StartTcpServer();
- defer(close_s, &g_servfd);
- if (!(rc = setjmp(g_jb))) {
- sigaction(SIGINT, &onsigint, NULL);
- sigaction(SIGTERM, &onsigterm, NULL);
- sigaction(SIGCHLD, &onsigchld, NULL);
- for (;;) {
- if (!Poll() && !g_timeout) break;
- }
- LOGF("timeout expired, shutting down");
- } else {
- if (isatty(fileno(stderr))) fputc('\r', stderr);
- LOGF("got %s, shutting down", strsignal(rc));
- rc += 128;
+ sigaction(SIGCHLD,
+ (&(struct sigaction){.sa_handler = (void *)OnChildTerminated,
+ .sa_flags = SA_RESTART}),
+ NULL);
+ for (;;) {
+ if (!Poll() && !g_timeout) break;
}
- return rc;
+ close(g_servfd);
+ LOGF("timeout expired, shutting down");
+ return 0;
}
void Daemonize(void) {
@@ -393,15 +394,17 @@ void Daemonize(void) {
if (fork() > 0) _exit(0);
setsid();
if (fork() > 0) _exit(0);
- stdin->fd = g_devnullfd;
- if (!g_sendready) stdout->fd = g_devnullfd;
- if (stat(kLogFile, &st) != -1 && st.st_size > kLogMaxBytes) unlink(kLogFile);
- freopen(kLogFile, "a", stderr);
+ dup2(g_devnullfd, stdin->fd);
+ if (!g_sendready) dup2(g_devnullfd, stdout->fd);
+ freopen(kLogFile, "ae", stderr);
+ if (fstat(fileno(stderr), &st) != -1 && st.st_size > kLogMaxBytes) {
+ ftruncate(fileno(stderr), 0);
+ }
}
int main(int argc, char *argv[]) {
showcrashreports();
- g_loglevel = kLogDebug;
+ /* g_loglevel = kLogDebug; */
GetOpts(argc, argv);
CHECK_NE(-1, (g_devnullfd = open("/dev/null", O_RDWR)));
defer(close_s, &g_devnullfd);
diff --git a/tool/build/zipobj.c b/tool/build/zipobj.c
index df28b9a88..30584bc1d 100644
--- a/tool/build/zipobj.c
+++ b/tool/build/zipobj.c
@@ -23,6 +23,7 @@
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/timespec.h"
#include "libc/elf/def.h"
+#include "libc/fmt/conv.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
@@ -68,6 +69,7 @@
char *symbol_;
char *outpath_;
char *yoink_;
+int64_t image_base_;
const size_t kMinCompressSize = 32;
const char kNoCompressExts[][8] = {".gz", ".xz", ".jpg", ".png",
@@ -83,7 +85,8 @@ wontreturn void PrintUsage(int rc, FILE *f) {
void GetOpts(int *argc, char ***argv) {
int opt;
yoink_ = "__zip_start";
- while ((opt = getopt(*argc, *argv, "?ho:s:y:")) != -1) {
+ image_base_ = IMAGE_BASE_VIRTUAL;
+ while ((opt = getopt(*argc, *argv, "?ho:s:y:b:")) != -1) {
switch (opt) {
case 'o':
outpath_ = optarg;
@@ -94,6 +97,9 @@ void GetOpts(int *argc, char ***argv) {
case 'y':
yoink_ = optarg;
break;
+ case 'b':
+ image_base_ = strtol(optarg, NULL, 0);
+ break;
case '?':
case 'h':
PrintUsage(EXIT_SUCCESS, stdout);
@@ -261,7 +267,7 @@ void EmitZip(struct ElfWriter *elf, const char *name, size_t namesize,
ELF64_ST_INFO(STB_LOCAL, STT_OBJECT), STV_DEFAULT, 0,
kZipCdirHdrLinkableSize);
elfwriter_appendrela(elf, kZipCfileOffsetOffset, lfilesym, R_X86_64_32,
- -IMAGE_BASE_VIRTUAL);
+ -image_base_);
elfwriter_commit(elf, kZipCdirHdrLinkableSize);
elfwriter_finishsection(elf);
}