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;
}
return rc;
CHECK_EQ(got, wrote);
}
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,7 +617,7 @@
#define setlocate(l, s) 0
#define equal(s1, s2) (strcmp(s1, s2) == 0)
#define getenv(p) bltinlookup((p), 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))

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};
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:
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,7 +45,9 @@ 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
(flags & O_APPEND)
? kNtFileAppendData
: (flags & O_ACCMODE) == O_RDONLY
? kNtGenericExecute | kNtFileGenericRead
: kNtGenericExecute | kNtFileGenericRead |
kNtFileGenericWrite),

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;
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,7 +65,8 @@ 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 &&
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)];

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;
}
} 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));
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;
}
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;
}
fcntl(stream->fd, F_SETFD, !!(flags & O_CLOEXEC));
fcntl(stream->fd, F_SETFL, flags & ~O_CLOEXEC);
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;
}
}
}
}

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;
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) {
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) {
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 {
wstatus = -1;
}
sigaction(SIGINT, &saveint, NULL);
sigaction(SIGQUIT, &savequit, NULL);
sigprocmask(SIG_SETMASK, &savemask, NULL);
return wstatus;
} else {
return -1;
}
} else if (IsWindows()) {
return true;
} else {
return fileexists(_PATH_BSHELL);
}
}

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);
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]) {
return RunOnHost(hosts[i]);
sigaction(SIGINT, &saveint, NULL);
sigaction(SIGQUIT, &savequit, NULL);
sigprocmask(SIG_SETMASK, &savemask, NULL);
_exit(RunOnHost(hosts[i]));
}
}
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) {
CHECK_NE(-1, waitpid(pids[i], &rc, 0));
exitcode |= WEXITSTATUS(rc);
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);
}
} else if (getpid() == leader) {
onsigint.sa_handler = SIG_IGN;
sigaction(SIGINT, &onsigint, NULL);
kill(0, SIGINT);
while (waitpid(-1, NULL, 0) > 0) donothing;
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);
void OnChildTerminated(int sig) {
int ws, pid;
for (;;) {
if (waitpid(-1, NULL, 0) == -1) {
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");
}
gclongjmp(g_jb, sig);
unreachable;
}
void OnChildTerminated(int sig) {
while (waitpid(-1, &g_childstatus, WNOHANG) > 0) {
g_childterm = true;
}
}
@ -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);
sigaction(SIGCHLD,
(&(struct sigaction){.sa_handler = (void *)OnChildTerminated,
.sa_flags = SA_RESTART}),
NULL);
for (;;) {
if (!Poll() && !g_timeout) break;
}
close(g_servfd);
LOGF("timeout expired, shutting down");
} else {
if (isatty(fileno(stderr))) fputc('\r', stderr);
LOGF("got %s, shutting down", strsignal(rc));
rc += 128;
}
return rc;
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);
}