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); }