Work towards improving signals and processes

This commit is contained in:
Justine Tunney 2021-01-27 19:34:02 -08:00
parent de703b182c
commit d7ac16a9ed
96 changed files with 1474 additions and 427 deletions

View file

@ -3,8 +3,8 @@
Fast portable static native textmode executable containers.
For an introduction to this project, please read the <a
href="https://justine.storage.googleapis.com/ape.html">αcτµαlly pδrταblε
εxεcµταblε</a> blog post and <a
href="https://justine.lol/ape.html">αcτµαlly pδrταblε εxεcµταblε</a>
blog post and <a
href="https://justine.lol/cosmopolitan/index.html">cosmopolitan libc</a>
website. API documentation is available <a
href="https://justine.lol/cosmopolitan/documentation.html">here</a>.

View file

@ -425,6 +425,7 @@ SECTIONS {
*(.xlm)
. = ALIGN(0x1000);
. = ALIGN(0x10000); /* for brk()/sbrk() allocation */
HIDDEN(_end = .);
PROVIDE_HIDDEN(end = .);
} AT>SmallCode :Ram

Binary file not shown.

View file

@ -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)"=

View file

@ -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)

View file

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

View file

@ -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 \

61
examples/sleep.c Normal file
View file

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

View file

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

View file

@ -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;

View file

@ -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 {

View file

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

View file

@ -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

View file

@ -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;

56
libc/calls/kill-nt.c Normal file
View file

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

View file

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

View file

@ -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;

View file

@ -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) {

View file

@ -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

View file

@ -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,

View file

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

View file

@ -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;

View file

@ -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! */
}
}

View file

@ -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! */
}

View file

@ -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

View file

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

View file

@ -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 {

View file

@ -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) {

View file

@ -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]);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,

32
libc/runtime/abort-nt.c Normal file
View file

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

View file

@ -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

77
libc/runtime/brk.c Normal file
View file

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

40
libc/runtime/ldso.c Normal file
View file

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

50
libc/runtime/pthread.c Normal file
View file

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

View file

@ -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

View file

@ -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) {

View file

@ -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;

29
libc/sock/shutdown-nt.c Normal file
View file

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

View file

@ -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 {

27
libc/stdio/fbufsize.c Normal file
View file

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

View file

@ -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.
*

View file

@ -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_ */

27
libc/stdio/flbf.c Normal file
View file

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

33
libc/stdio/flushlbf.c Normal file
View file

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

26
libc/stdio/fpending.c Normal file
View file

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

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
@ -16,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;
}

28
libc/stdio/freadable.c Normal file
View file

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

27
libc/stdio/freading.c Normal file
View file

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

View file

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

26
libc/stdio/fsetlocking.c Normal file
View file

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

28
libc/stdio/fwritable.c Normal file
View file

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

27
libc/stdio/fwriting.c Normal file
View file

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

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
@ -16,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;

View file

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

43
libc/stdio/pclose.c Normal file
View file

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

57
libc/stdio/popen.c Normal file
View file

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

View file

@ -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 *);
/*───────────────────────────────────────────────────────────────────────────│─╗

25
libc/stdio/stdio_ext.h Normal file
View file

@ -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_ */

View file

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

38
libc/stdio/systemexec.c Normal file
View file

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

33
libc/str/iconv.c Normal file
View file

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

View file

@ -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;

71
libc/str/strverscmp.c Normal file
View file

@ -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];
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

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

View file

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

View file

@ -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)],

View file

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

View file

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

View file

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

View file

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

View file

@ -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

View file

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

View file

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

View file

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