diff --git a/Makefile b/Makefile index fb4832a8..d147fc6e 100644 --- a/Makefile +++ b/Makefile @@ -316,7 +316,7 @@ COSMOPOLITAN_HEADERS = \ o/$(MODE)/cosmopolitan.a: $(filter-out o/libc/stubs/exit11.o,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_OBJS))) o/cosmopolitan.h: \ - o/$(MODE)/tool/build/rollup.com.dbg \ + o/$(MODE)/tool/build/rollup.com \ libc/integral/normalize.inc \ $(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_HDRS)) @ACTION=ROLLUP TARGET=$@ build/do $^ >$@ diff --git a/ape/ape.S b/ape/ape.S index 38968aef..e24e7b3c 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -34,7 +34,6 @@ │ αcτµαlly pδrταblε εxεcµταblε § program header │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/config.h" -#include "ape/lib/apm.h" #include "ape/lib/pc.h" #include "ape/macros.h" #include "ape/notice.inc" @@ -49,7 +48,7 @@ #define USE_SYMBOL_HACK 0 - .source "NOTICE" + .source "LICENSE" .source "ape/ape.S" .source "ape/ape.lds" .section .text,"ax",@progbits @@ -360,85 +359,9 @@ pcread: push %ax xor %ax,%ax # try disk reset on error int $0x13 pop %ax - jmp 1b + jmp pcread .endfn pcread -/ Waits for serial lines to become idle. -/ -/ @param di short array of serial ports (0 means not present) -/ @param si number of items in array -/ @mode long,legacy,real -sflush: mov %si,%cx - mov %di,%si - xor %dx,%dx -0: lodsb - mov %al,%dl - lodsb - mov %al,%dh - test %ax,%ax - jz 2f - add $UART_LSR,%dx - mov $UART_TTYIDL,%ah -1: in %dx,%al - and %ah,%al - rep - nop - jz 1b - loop 0b -2: ret - .endfn sflush,globl - -/ Transmits byte over serial line. -/ -/ This is both blocking and asynchronous. -/ -/ @param di character to send -/ @param si serial port -/ @mode long,legacy,real -/ @see ttytxr -sputc: push %ax - push %cx - push %dx - mov %si,%dx - add $UART_LSR,%dx - mov $UART_TTYTXR,%ah -1: in %dx,%al - and %ah,%al - jnz 2f - rep - nop - jmp 1b -2: mov %di,%ax - mov %si,%dx - out %al,%dx - pop %dx - pop %cx - pop %ax - ret - .endfn sputc,globl - -/ Shuts down personal computer. -/ -/ @mode real -/ @noreturn -apmoff: mov $0x5300,%ax # apm installation check - xor %bx,%bx # for the apm bios itself - int $APM_SERVICE - jc 1f - cmp $'P<<8|'M,%bx # did apm bios service interrupt? - jne 1f - mov $0x5301,%ax # real mode interface connect - xor %bx,%bx # to apm bios device - int $APM_SERVICE # ignore errors e.g. already connected - xor %bx,%bx - xor %cx,%cx - mov $0x5307,%ax # set power state - mov $1,%bl # for all devices within my dominion - mov $3,%cl # to off - int $APM_SERVICE -1: call panic - .endfn apmoff,globl - /*───────────────────────────────────────────────────────────────────────────│─╗ │ αcτµαlly pδrταblε εxεcµταblε § partition table ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ @@ -1156,115 +1079,41 @@ sinit: mov %di,%dx 2: ret .endfn sinit,global,hidden -/ Abnormally exits program. -/ -/ @param di message -/ @mode real -/ @noreturn -rldie: call rlpute - call rloff - .endfn rldie,globl,hidden - -/ Shuts down machine. -/ -/ @mode real -/ @noreturn -rloff: mov $kBiosDataAreaXlm+COM1,%di - mov $4,%si - call sflush - call apmoff - .endfn rloff,globl,hidden - -/ Prints error message. -/ -/ @param di message -/ @mode real -rlpute: mov kBiosDataAreaXlm+METAL_STDERR(%bx),%si - test %si,%si - jnz 1f - mov kBiosDataAreaXlm+METAL_STDOUT,%si -1: xor %ax,%ax - push %ax - push %si - mov $REAL(.Lstr.crlf),%ax - push %ax - push %si - push %di - push %si - mov $REAL(.Lstr.error),%ax - push %ax -1: pop %di - test %di,%di - jz 2f - pop %si - call rlput2 - jmp 1b -2: ret - .endfn rlpute,globl,hidden - -/ Prints string to both video and serial. -/ -/ @param di NUL-terminated string -/ @param si serial port -/ @mode real -rlput2: push %di - push %si - call rvputs - pop %si - pop %di - test %si,%si - jz 1f - call sputs -1: ret - .endfn rlput2,globl,hidden - -/ Writes string to serial line. -/ -/ @param di NUL-terminated string -/ @param si serial port -/ @mode long,legacy,real -sputs: push %bx - mov %di,%bx -1: xchg %bx,%si - lodsb - xchg %bx,%si - test %al,%al - jz 2f - mov %ax,%di - push %si - rlcall sputc - pop %si - jmp 1b -2: pop %bx - ret - .endfn sputs,globl - / Video put string. / / @param di is the string / @mode real -rvputs: mov %di,%si +rvputs: push %bp + push %bx + mov %di,%si 0: lodsb test %al,%al je 1f - rlcall rvputc - jmp 0b -1: ret - .endfn rvputs,globl,hidden - -/ Video put char. -/ -/ @param al is the char -/ @mode real -rvputc: push %bx # don't clobber bp,bx,di,si,cx - push %bp # original ibm pc scroll up bug mov $7,%bx # normal mda/cga style page zero mov $0x0e,%ah # teletype output al cp437 int $0x10 # vidya service - pop %bp # preserves al - pop %bx + jmp 0b +1: pop %bx + pop %bp ret - .endfn rvputc + .endfn rvputs,globl,hidden + +/ Abnormally halts startup. +/ +/ @param di message +/ @mode real +/ @noreturn +rldie: push %di + mov $REAL(.Lstr.error),%di + call rvputs + pop %di + call rvputs + mov $REAL(.Lstr.crlf),%di + call rvputs + xor %ax,%ax # get keystroke + int $0x16 # keyboard service + jmp triplf + .endfn rldie,globl,hidden /* █ █▒ █ █ @@ -1321,6 +1170,7 @@ longmodeloader: jc 9f call unreal / call hiload + call pinit jmp golong 9: mov $REAL(.Lstr.e820),%ax call rldie @@ -1568,7 +1418,6 @@ pinit: push %ds / @see Intel Manual V3A §4.1.2 golong: cli lidt XLM(BADIDT) - call pinit mov %cr4,%eax or $CR4_PAE|CR4_PGE|CR4_OSFXSR,%eax mov %eax,%cr4 diff --git a/ape/lib/apelib.mk b/ape/lib/apelib.mk index 85f4ddf6..4e730666 100644 --- a/ape/lib/apelib.mk +++ b/ape/lib/apelib.mk @@ -21,7 +21,7 @@ APE_LIB_A_OBJS = \ $(APE_LIB_A_SRCS:%=o/$(MODE)/%.zip.o) \ o/$(MODE)/ape/ape.lds.zip.o \ o/$(MODE)/ape/ape.S.zip.o \ - o/$(MODE)/NOTICE.zip.o + o/$(MODE)/LICENSE.zip.o APE_LIB_A_CHECKS = $(APE_LIB_A_HDRS:%=o/$(MODE)/%.ok) APE_LIB_A_DIRECTDEPS = LIBC_STR LIBC_STUBS diff --git a/examples/hangman.c b/examples/hangman.c new file mode 100644 index 00000000..f3b508dd --- /dev/null +++ b/examples/hangman.c @@ -0,0 +1,197 @@ +/* UNIX v7 usr/src/games/hangman.c + * + * Copyright 2002 Caldera International Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code and documentation must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed or owned by Caldera + * International, Inc. + * + * Neither the name of Caldera International, Inc. nor the names of + * other contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENCE BY CALDERA + * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" +#include "libc/rand/rand.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/time/time.h" +#include "third_party/zlib/zlib.h" + +/* clang-format off */ +#define DICT "usr/share/dict/hangman" +#define MAXERR 7 +#define MINSCORE 0 +#define MINLEN 7 + +char *dictfile; +int alive,lost; +FILE *dict; +long int dictlen; +float errors=0, words=0; +char word[26],alph[26],realword[26]; + +void fatal(s) + char *s; +{ + fprintf(stderr,"%s\n",s); + exit(1); +} + +void setup() +{ + long tvec; + struct stat statb; + time(&tvec); + srand(tvec); + if((dict=fopen(dictfile,"r"))==NULL) fatal("no dictionary"); + if(stat(dictfile,&statb)<0) fatal("can't stat"); + dictlen=statb.st_size; +} + +double frand() +{ + return(rand()/32768.); +} + +void pscore() +{ + if(words!=0) printf("(%4.2f/%.0f) ",errors/words,words); +} + +void getword() +{ char wbuf[128],c; + int i,j; +loop: + if(fscanf(dict,"%s\n",wbuf)==EOF) + { fseek(dict,0L,0); + goto loop; + } + if((c=wbuf[0])>'z' || c<'a') goto loop; + for(i=j=0;wbuf[j]!=0;i++,j++) + { if(wbuf[j]=='-') j++; + wbuf[i]=wbuf[j]; + } + wbuf[i]=0; + if(i'z') goto loop; + pscore(); + strcpy(realword,wbuf); + for(j=0;j'z') + { printf("lower case\n"); + goto loop; + } + if(alph[c-'a']!=0) + { printf("you guessed that\n"); + goto loop; + } + else alph[c-'a']=c; + for(i=0;realword[i]!=0;i++) + if(realword[i]==c) + { word[i]=c; + ok=1; + } + if(ok==0) + { alive--; + errors=errors+1; + if(alive<=0) lost=1; + return; + } + for(i=0;word[i]!=0;i++) + if(word[i]=='.') return; + alive=0; + lost=0; + return; +} + +void wordout() +{ + errors=errors+2; + printf("the answer was %s, you blew it\n",realword); +} + +void youwon() +{ + printf("you win, the word is %s\n",realword); +} + +main(argc,argv) + char **argv; +{ + if(argc==1) dictfile=DICT; + else dictfile=argv[1]; + setup(); + for(;;) + { startnew(); + while(alive>0) + { stateout(); + getguess(); + } + words=words+1; + if(lost) wordout(); + else youwon(); + } +} diff --git a/examples/nesemu1.cc b/examples/nesemu1.cc index 5de68076..84d02c23 100644 --- a/examples/nesemu1.cc +++ b/examples/nesemu1.cc @@ -43,7 +43,7 @@ #include "libc/time/time.h" #include "libc/x/x.h" #include "libc/zip.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" #include "third_party/getopt/getopt.h" #include "tool/viz/lib/knobs.h" diff --git a/libc/bits/bits.h b/libc/bits/bits.h index 6f631bfe..979c6862 100644 --- a/libc/bits/bits.h +++ b/libc/bits/bits.h @@ -277,27 +277,17 @@ unsigned long hamming(unsigned long, unsigned long) pureconst; * @return true if value was exchanged, otherwise false * @see lockcmpxchg() */ -#define cmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ - ({ \ - bool DidIt; \ - asm(ZFLAG_ASM("cmpxchg\t%3,%1") \ - : ZFLAG_CONSTRAINT(DidIt), "+m"(*(IFTHING)), "+a"(*(ISEQUALTOME)) \ - : "r"((typeof(*(IFTHING)))(REPLACEITWITHME)) \ - : "cc"); \ - DidIt; \ - }) - -#define ezcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ - ({ \ - bool DidIt; \ - autotype(IFTHING) IfThing = (IFTHING); \ - typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \ - typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \ - asm(ZFLAG_ASM("cmpxchg\t%3,%1") \ - : ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \ - : "r"(ReplaceItWithMe) \ - : "cc"); \ - DidIt; \ +#define cmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ + ({ \ + bool DidIt; \ + autotype(IFTHING) IfThing = (IFTHING); \ + typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \ + typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \ + asm volatile(ZFLAG_ASM("cmpxchg\t%3,%1") \ + : ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \ + : "r"(ReplaceItWithMe) \ + : "cc"); \ + DidIt; \ }) /** @@ -307,14 +297,17 @@ unsigned long hamming(unsigned long, unsigned long) pureconst; * @return true if value was exchanged, otherwise false * @see lockcmpxchg() */ -#define lockcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ - ({ \ - bool DidIt; \ - asm(ZFLAG_ASM("lock cmpxchg\t%3,%1") \ - : ZFLAG_CONSTRAINT(DidIt), "+m"(*(IFTHING)), "+a"(*(ISEQUALTOME)) \ - : "r"((typeof(*(IFTHING)))(REPLACEITWITHME)) \ - : "cc"); \ - DidIt; \ +#define lockcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ + ({ \ + bool DidIt; \ + autotype(IFTHING) IfThing = (IFTHING); \ + typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \ + typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \ + asm volatile(ZFLAG_ASM("lock cmpxchg\t%3,%1") \ + : ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \ + : "r"(ReplaceItWithMe) \ + : "cc"); \ + DidIt; \ }) /** diff --git a/libc/calls/chdir-nt.c b/libc/calls/chdir-nt.c index 98d19e37..d3d4bfc2 100644 --- a/libc/calls/chdir-nt.c +++ b/libc/calls/chdir-nt.c @@ -33,6 +33,6 @@ textwindows int chdir$nt(const char *path) { if (SetCurrentDirectory(path16)) { return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/close-nt.c b/libc/calls/close-nt.c index f50f8e29..0f7a333d 100644 --- a/libc/calls/close-nt.c +++ b/libc/calls/close-nt.c @@ -24,7 +24,7 @@ textwindows int close$nt(int fd) { bool32 ok; - if (isfdopen(fd)) { + if (__isfdopen(fd)) { if (g_fds.p[fd].kind == kFdFile) { /* * Like Linux, closing a file on Windows doesn't guarantee it's @@ -37,8 +37,8 @@ textwindows int close$nt(int fd) { if (g_fds.p[fd].kind == kFdConsole) { ok &= CloseHandle(g_fds.p[fd].extra); } - removefd(fd); - return ok ? 0 : winerr(); + __removefd(fd); + return ok ? 0 : __winerr(); } else { return ebadf(); } diff --git a/libc/calls/close.c b/libc/calls/close.c index 14bcd0d5..1f8eec35 100644 --- a/libc/calls/close.c +++ b/libc/calls/close.c @@ -20,9 +20,10 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/macros.h" #include "libc/sock/internal.h" #include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Closes file descriptor. @@ -32,17 +33,24 @@ */ int close(int fd) { int rc; - if (fd == -1) return einval(); - if (isfdkind(fd, kFdZip)) { - rc = weaken(__zipos_close)( - (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle); + if (fd < 0) return einval(); + if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { + rc = weaken(__zipos_close)(fd); + } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdEpoll) { + rc = weaken(close$epoll)(fd); } else if (!IsWindows()) { rc = close$sysv(fd); - } else if (isfdkind(fd, kFdSocket)) { + } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdSocket) { rc = weaken(closesocket$nt)(fd); - } else { + } else if (fd < g_fds.n && + (g_fds.p[fd].kind == kFdFile || g_fds.p[fd].kind == kFdConsole)) { rc = close$nt(fd); + } else { + rc = ebadf(); + } + if (fd < g_fds.n) { + g_fds.p[fd].kind = kFdEmpty; + g_fds.f = MIN(g_fds.f, fd); } - removefd(fd); return rc; } diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index 14f7fe42..e21b425b 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -30,17 +30,17 @@ * Implements dup(), dup2(), and dup3() for Windows NT. */ textwindows int dup$nt(int oldfd, int newfd, int flags) { - if (!isfdkind(oldfd, kFdFile)) return ebadf(); + if (!__isfdkind(oldfd, kFdFile)) return ebadf(); if (newfd == -1) { - if ((newfd = createfd()) == -1) return -1; - } else if (isfdindex(newfd)) { + if ((newfd = __getemptyfd()) == -1) { + return -1; + } + } else if (__ensurefds(newfd) != -1) { if (g_fds.p[newfd].kind != kFdEmpty) { close(newfd); } } else { - do { - if (growfds() == -1) return -1; - } while (newfd >= g_fds.n); + return -1; } if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle, GetCurrentProcess(), &g_fds.p[newfd].handle, 0, @@ -49,6 +49,6 @@ textwindows int dup$nt(int oldfd, int newfd, int flags) { g_fds.p[newfd].flags = flags; return newfd; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/fadvise-nt.c b/libc/calls/fadvise-nt.c index f96afaab..c2c75922 100644 --- a/libc/calls/fadvise-nt.c +++ b/libc/calls/fadvise-nt.c @@ -38,7 +38,7 @@ textwindows int fadvise$nt(int fd, uint64_t offset, uint64_t len, int advice) { struct NtIoStatusBlock iostatus; struct NtFileBasicInformation basicinfo; struct NtFileAccessInformation accessinfo; - if (!isfdkind(fd, kFdFile)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return ebadf(); sharemode = /* xxx: no clue how to query this */ kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete; /* TODO(jart): can we do it in one call w/ NtQueryObject? */ @@ -56,7 +56,7 @@ textwindows int fadvise$nt(int fd, uint64_t offset, uint64_t len, int advice) { } return 0; } - return winerr(); + return __winerr(); } else if (status == kNtStatusDllNotFound) { return enosys(); } else { diff --git a/libc/calls/fallocate.c b/libc/calls/fallocate.c index d2ee5657..dcd54b9d 100644 --- a/libc/calls/fallocate.c +++ b/libc/calls/fallocate.c @@ -51,7 +51,7 @@ int fallocate(int fd, int32_t mode, int64_t offset, int64_t length) { } else if (!IsWindows()) { return posix_fallocate$sysv(fd, offset, length); } else if (IsWindows()) { - if (!isfdkind(fd, kFdFile)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return ebadf(); if (mode == FALLOC_FL_ZERO_RANGE) { if (DeviceIoControl( g_fds.p[fd].handle, kNtFsctlSetZeroData, @@ -59,7 +59,7 @@ int fallocate(int fd, int32_t mode, int64_t offset, int64_t length) { sizeof(struct NtFileZeroDataInformation), NULL, 0, &br, NULL)) { return 0; } else { - return winerr(); + return __winerr(); } } else if (!mode && !offset) { /* diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index d8ea9889..91e5e82c 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -21,20 +21,31 @@ #include "libc/nt/files.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" +#include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" textwindows int fcntl$nt(int fd, int cmd, unsigned arg) { uint32_t flags; - if (!isfdkind(fd, kFdFile)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return ebadf(); switch (cmd) { + case F_GETFL: + return g_fds.p[fd].flags; + case F_SETFL: + return (g_fds.p[fd].flags = arg); case F_GETFD: - if (!GetHandleInformation(g_fds.p[fd].handle, &flags)) return -1; - arg = (flags & FD_CLOEXEC) ^ FD_CLOEXEC; - return arg; + if (g_fds.p[fd].flags & O_CLOEXEC) { + return FD_CLOEXEC; + } else { + return 0; + } case F_SETFD: - arg ^= FD_CLOEXEC; - if (!SetHandleInformation(g_fds.p[fd].handle, FD_CLOEXEC, arg)) return -1; - return 0; + if (arg & O_CLOEXEC) { + g_fds.p[fd].flags |= O_CLOEXEC; + return FD_CLOEXEC; + } else { + g_fds.p[fd].flags &= ~O_CLOEXEC; + return 0; + } default: return 0; /* TODO(jart): Implement me. */ } diff --git a/libc/calls/fdatasync-nt.c b/libc/calls/fdatasync-nt.c index 1f781a71..48b21dcf 100644 --- a/libc/calls/fdatasync-nt.c +++ b/libc/calls/fdatasync-nt.c @@ -22,11 +22,11 @@ #include "libc/sysv/errfuns.h" textwindows int fdatasync$nt(int fd) { - if (!isfdkind(fd, kFdFile)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return ebadf(); /* * XXX: On Windows NT this might be more analagous to fflush() and * Microsoft docs say to do manual block i/o for database-ish * guarantees on disk persistence. Consider: Really good UPS. */ - return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : winerr(); + return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : __winerr(); } diff --git a/libc/calls/fdkind.c b/libc/calls/fdkind.c index 5587c12b..8433323b 100644 --- a/libc/calls/fdkind.c +++ b/libc/calls/fdkind.c @@ -20,7 +20,7 @@ #include "libc/calls/internal.h" enum FdKind fdkind(int fd) { - if (isfdindex(fd)) { + if (0 <= fd && fd <= g_fds.n) { return g_fds.p[fd].kind; } else { return kFdEmpty; diff --git a/libc/calls/flock-nt.c b/libc/calls/flock-nt.c index c9418e3e..e6c41033 100644 --- a/libc/calls/flock-nt.c +++ b/libc/calls/flock-nt.c @@ -29,7 +29,7 @@ textwindows int flock$nt(int fd, int op) { struct NtOverlapped ov; struct NtByHandleFileInformation info; - if (!isfdkind(fd, kFdFile)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return ebadf(); memset(&ov, 0, sizeof(ov)); if (GetFileInformationByHandle(g_fds.p[fd].handle, &info) && ((!(op & LOCK_UN) && @@ -39,6 +39,6 @@ textwindows int flock$nt(int fd, int op) { info.nFileSizeHigh, &ov)))) { return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/fstat-nt.c b/libc/calls/fstat-nt.c index 2363cc7f..2773ad47 100644 --- a/libc/calls/fstat-nt.c +++ b/libc/calls/fstat-nt.c @@ -66,6 +66,6 @@ textwindows int fstat$nt(int64_t handle, struct stat *st) { st->st_blocks = roundup(actualsize, PAGESIZE) / 512; return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/fstat.c b/libc/calls/fstat.c index df181231..29eb5d61 100644 --- a/libc/calls/fstat.c +++ b/libc/calls/fstat.c @@ -22,20 +22,20 @@ #include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Returns information about file, via open()'d descriptor. * @asyncsignalsafe */ int fstat(int fd, struct stat *st) { - if (isfdkind(fd, kFdZip)) { + if (__isfdkind(fd, kFdZip)) { return weaken(__zipos_fstat)( (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, st); } else if (!IsWindows()) { return fstat$sysv(fd, st); } else { - if (!isfdkind(fd, kFdFile)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return ebadf(); return fstat$nt(g_fds.p[fd].handle, st); } } diff --git a/libc/calls/ftruncate-nt.c b/libc/calls/ftruncate-nt.c index e0dc335a..ef01ade7 100644 --- a/libc/calls/ftruncate-nt.c +++ b/libc/calls/ftruncate-nt.c @@ -25,14 +25,14 @@ textwindows int ftruncate$nt(int fd, uint64_t length) { bool32 ok; int64_t tell; - if (!isfdkind(fd, kFdFile)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return ebadf(); tell = -1; if (SetFilePointerEx(g_fds.p[fd].handle, 0, &tell, kNtFileCurrent)) { ok = SetFilePointerEx(g_fds.p[fd].handle, length, NULL, kNtFileBegin) && SetEndOfFile(g_fds.p[fd].handle); SetFilePointerEx(g_fds.p[fd].handle, tell, NULL, kNtFileBegin); - return ok ? 0 : winerr(); + return ok ? 0 : __winerr(); } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/g_fds.c b/libc/calls/g_fds.c index b7685d69..350390b4 100644 --- a/libc/calls/g_fds.c +++ b/libc/calls/g_fds.c @@ -33,12 +33,22 @@ void InitializeFileDescriptors(void) { pushmov(&fds->f, 3ul); pushmov(&fds->n, ARRAYLEN(fds->__init_p)); fds->p = fds->__init_p; - fds->__init_p[STDIN_FILENO].kind = pushpop(kFdFile); - fds->__init_p[STDOUT_FILENO].kind = pushpop(kFdFile); - fds->__init_p[STDERR_FILENO].kind = pushpop(kFdFile); - fds->__init_p[STDIN_FILENO].handle = GetStdHandle(pushpop(kNtStdInputHandle)); - fds->__init_p[STDOUT_FILENO].handle = - GetStdHandle(pushpop(kNtStdOutputHandle)); - fds->__init_p[STDERR_FILENO].handle = - GetStdHandle(pushpop(kNtStdErrorHandle)); + if (!IsMetal()) { + fds->__init_p[STDIN_FILENO].kind = pushpop(kFdFile); + fds->__init_p[STDOUT_FILENO].kind = pushpop(kFdFile); + fds->__init_p[STDERR_FILENO].kind = pushpop(kFdFile); + fds->__init_p[STDIN_FILENO].handle = + GetStdHandle(pushpop(kNtStdInputHandle)); + fds->__init_p[STDOUT_FILENO].handle = + GetStdHandle(pushpop(kNtStdOutputHandle)); + fds->__init_p[STDERR_FILENO].handle = + GetStdHandle(pushpop(kNtStdErrorHandle)); + } else { + fds->__init_p[STDIN_FILENO].kind = pushpop(kFdSerial); + fds->__init_p[STDOUT_FILENO].kind = pushpop(kFdSerial); + fds->__init_p[STDERR_FILENO].kind = pushpop(kFdSerial); + fds->__init_p[STDIN_FILENO].handle = 0x3F8; + fds->__init_p[STDOUT_FILENO].handle = 0x3F8; + fds->__init_p[STDERR_FILENO].handle = 0x3F8; + } } diff --git a/libc/calls/getcwd-nt.c b/libc/calls/getcwd-nt.c index 59a24b8a..2835e173 100644 --- a/libc/calls/getcwd-nt.c +++ b/libc/calls/getcwd-nt.c @@ -31,7 +31,7 @@ textwindows char *getcwd$nt(char *buf, size_t size) { erange(); } } else { - winerr(); + __winerr(); } return NULL; } diff --git a/libc/calls/getdomainname.c b/libc/calls/getdomainname.c index 626bbc0a..3d96355d 100644 --- a/libc/calls/getdomainname.c +++ b/libc/calls/getdomainname.c @@ -45,7 +45,7 @@ int getdomainname(char *name, size_t len) { } else { nSize = ARRAYLEN(name16); if (!GetComputerNameEx(kNtComputerNameDnsFullyQualified, name16, &nSize)) { - return winerr(); + return __winerr(); } tprecode16to8(name, MIN(MIN(ARRAYLEN(name16), nSize + 1), len), name16); return 0; diff --git a/libc/calls/createfd.c b/libc/calls/getemptyfd.c similarity index 91% rename from libc/calls/createfd.c rename to libc/calls/getemptyfd.c index c89f1fb7..71b8ce1b 100644 --- a/libc/calls/createfd.c +++ b/libc/calls/getemptyfd.c @@ -25,14 +25,11 @@ /** * Finds open file descriptor slot. */ -ssize_t createfd(void) { - size_t fd; - for (;;) { - while (g_fds.f < g_fds.n) { - if (g_fds.p[(fd = g_fds.f++)].kind == kFdEmpty) { - return fd; - } +ssize_t __getemptyfd(void) { + for (; g_fds.f < g_fds.n; ++g_fds.f) { + if (g_fds.p[g_fds.f].kind == kFdEmpty) { + return g_fds.f; } - if (growfds() == -1) return -1; } + return __ensurefds(g_fds.f); } diff --git a/libc/calls/gethostname.c b/libc/calls/gethostname.c index c3fc401e..6e301aab 100644 --- a/libc/calls/gethostname.c +++ b/libc/calls/gethostname.c @@ -50,7 +50,7 @@ int gethostname(char *name, size_t len) { } else { nSize = ARRAYLEN(name16); if (!GetComputerNameEx(kNtComputerNameDnsHostname, name16, &nSize)) { - return winerr(); + return __winerr(); } tprecode16to8(name, MIN(MIN(ARRAYLEN(name16), nSize + 1), len), name16); return 0; diff --git a/libc/calls/getpriority-nt.c b/libc/calls/getpriority-nt.c index d383e52d..8e1d1ca3 100644 --- a/libc/calls/getpriority-nt.c +++ b/libc/calls/getpriority-nt.c @@ -46,6 +46,6 @@ textwindows int getpriority$nt(int ignored) { } abort(); } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/getrusage-nt.c b/libc/calls/getrusage-nt.c index 794f65f6..5e136b88 100644 --- a/libc/calls/getrusage-nt.c +++ b/libc/calls/getrusage-nt.c @@ -40,6 +40,6 @@ textwindows int getrusage$nt(int who, struct rusage *usage) { FileTimeToTimeVal(&usage->ru_stime, KernelFileTime); return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/growfds.c b/libc/calls/growfds.c index 57ae2d1b..767dba47 100644 --- a/libc/calls/growfds.c +++ b/libc/calls/growfds.c @@ -23,18 +23,20 @@ #include "libc/mem/mem.h" #include "libc/sysv/errfuns.h" -int growfds(void) { +int __ensurefds(int fd) { size_t i, n; struct Fd *p; + if (fd < g_fds.n) return fd; if (weaken(realloc)) { - if ((p = weaken(realloc)(g_fds.p != g_fds.__init_p ? g_fds.p : NULL, - (n = (i = g_fds.n) << 1) * sizeof(*p)))) { + if ((p = weaken(realloc)( + g_fds.p != g_fds.__init_p ? g_fds.p : NULL, + (n = MAX(fd + 1, (i = g_fds.n) << 1)) * sizeof(*p)))) { do { p[i++].kind = kFdEmpty; } while (i < n); g_fds.p = p; g_fds.n = n; - return 0; + return fd; } else { return enomem(); } diff --git a/libc/calls/hefty/copyfile.c b/libc/calls/hefty/copyfile.c index 0619748e..6ad973e6 100644 --- a/libc/calls/hefty/copyfile.c +++ b/libc/calls/hefty/copyfile.c @@ -55,7 +55,7 @@ static textwindows int copyfile$nt(const char *src, const char *dst, } return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/hefty/dirstream.c b/libc/calls/hefty/dirstream.c index 28b26fbb..67552d75 100644 --- a/libc/calls/hefty/dirstream.c +++ b/libc/calls/hefty/dirstream.c @@ -74,7 +74,7 @@ static textwindows noinline DIR *opendir$nt(const char *name) { if ((res->fd = FindFirstFile(name16, &res->windata)) != -1) { return res; } else { - winerr(); + __winerr(); free(res); return NULL; } @@ -220,7 +220,7 @@ int closedir(DIR *dir) { if (!IsWindows()) { rc = close(dir->fd); } else { - rc = FindClose(dir->fd) ? 0 : winerr(); + rc = FindClose(dir->fd) ? 0 : __winerr(); } free(dir); } else { diff --git a/libc/calls/hefty/fork-nt.c b/libc/calls/hefty/fork-nt.c index 4c7e87fd..b6a6a7e4 100644 --- a/libc/calls/hefty/fork-nt.c +++ b/libc/calls/hefty/fork-nt.c @@ -67,7 +67,7 @@ textwindows void WinMainForked(void) { char16_t *p; uint64_t size; char16_t var[21 + 1 + 21 + 1]; - uint32_t i, varlen, protect, access; + uint32_t i, varlen, protect, access, oldprot; varlen = GetEnvironmentVariable(u"_FORK", var, ARRAYLEN(var)); if (!varlen || varlen >= ARRAYLEN(var)) return; p = var; @@ -98,11 +98,13 @@ textwindows void WinMainForked(void) { break; } if (_mmi.p[i].flags & MAP_PRIVATE) { - MapViewOfFileExNuma( - (_mmi.p[i].h = CreateFileMappingNuma(-1, NULL, protect, 0, size, NULL, - kNtNumaNoPreferredNode)), - access, 0, 0, size, addr, kNtNumaNoPreferredNode); + MapViewOfFileExNuma((_mmi.p[i].h = CreateFileMappingNuma( + -1, NULL, kNtPageExecuteReadwrite, 0, size, NULL, + kNtNumaNoPreferredNode)), + kNtFileMapRead | kNtFileMapWrite | kNtFileMapExecute, + 0, 0, size, addr, kNtNumaNoPreferredNode); ReadAll(h, addr, size); + VirtualProtect(addr, size, protect, &oldprot); } else { MapViewOfFileExNuma(_mmi.p[i].h, access, 0, 0, size, addr, kNtNumaNoPreferredNode); @@ -148,7 +150,7 @@ textwindows int fork$nt(void) { } unsetenv("_FORK"); } else { - rc = winerr(); + rc = __winerr(); } } else { rc = 0; diff --git a/libc/calls/hefty/ntspawn.c b/libc/calls/hefty/ntspawn.c index 4ddf1703..6e8d4fa7 100644 --- a/libc/calls/hefty/ntspawn.c +++ b/libc/calls/hefty/ntspawn.c @@ -78,7 +78,7 @@ textwindows int ntspawn( opt_out_lpProcessInformation)) { rc = 0; } else { - rc = winerr(); + rc = __winerr(); } } else { rc = -1; diff --git a/libc/calls/hefty/spawnve-nt.c b/libc/calls/hefty/spawnve-nt.c index 357b6e7e..0c14c4ba 100644 --- a/libc/calls/hefty/spawnve-nt.c +++ b/libc/calls/hefty/spawnve-nt.c @@ -49,14 +49,14 @@ textwindows int spawnve$nt(unsigned flags, int stdiofds[3], const char *program, sti.cb = sizeof(sti); sti.dwFlags = kNtStartfUsestdhandles; - if ((pid = createfd()) == -1) return -1; + if ((pid = __getemptyfd()) == -1) return -1; for (i = 0; i < 3; ++i) { if (stdiofds[i] == -1) { x = &h; y = &sti.stdiofds[i]; if (kIoMotion[i]) xchg(&x, &y); - if ((tubes[i] = createfd()) != -1 && + if ((tubes[i] = __getemptyfd()) != -1 && CreatePipe(x, y, &kNtIsInheritable, 0)) { g_fds.p[tubes[i]].handle = h; } else { diff --git a/libc/calls/hefty/ttyname_r.c b/libc/calls/hefty/ttyname_r.c index ba4c4838..8e9d8e61 100644 --- a/libc/calls/hefty/ttyname_r.c +++ b/libc/calls/hefty/ttyname_r.c @@ -76,7 +76,7 @@ int ttyname_r(int fd, char *buf, size_t size) { } else if (IsFreebsd()) { return ttyname$freebsd(fd, buf, size); } else if (IsWindows()) { - if (isfdkind(fd, kFdFile)) { + if (__isfdkind(fd, kFdFile)) { return ttyname$nt(fd, buf, size); } else { return ebadf(); diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 0326ceb2..790f3472 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -2,6 +2,7 @@ #define COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_ #ifndef __STRICT_ANSI__ #include "libc/calls/calls.h" +#include "libc/calls/struct/iovec.h" #include "libc/calls/struct/itimerval.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timeval.h" @@ -37,7 +38,7 @@ struct IoctlPtmGet { struct Fds { size_t f; // arbitrary free slot start search index - size_t n; // capacity + size_t n; // monotonic capacity struct Fd { int64_t handle; int64_t extra; @@ -47,7 +48,9 @@ struct Fds { kFdSocket, kFdProcess, kFdConsole, + kFdSerial, kFdZip, + kFdEpoll, } kind; unsigned flags; } * p; @@ -62,20 +65,12 @@ hidden extern struct NtSystemInfo g_ntsysteminfo; hidden extern struct NtStartupInfo g_ntstartupinfo; hidden extern const struct NtSecurityAttributes kNtIsInheritable; -ssize_t createfd(void) hidden; -int growfds(void) hidden; -void removefd(int) hidden; +ssize_t __getemptyfd(void) hidden; +int __ensurefds(int) hidden; +void __removefd(int) hidden; enum FdKind fdkind(int) hidden nosideeffect; -bool isfdopen(int) hidden nosideeffect; -bool isfdkind(int, enum FdKind) hidden nosideeffect; - -forceinline bool isfdindex(int fd) { - if (!IsTrustworthy()) { - return (0 <= fd && fd < g_fds.n); - } else { - return true; - } -} +bool __isfdopen(int) hidden nosideeffect; +bool __isfdkind(int, enum FdKind) hidden nosideeffect; forceinline size_t clampio(size_t size) { if (!IsTrustworthy()) { @@ -267,7 +262,7 @@ bool32 onntconsoleevent$nt(u32) hidden; void __winalarm(void *, uint32_t, uint32_t) hidden; int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden; i64 ntreturn(u32); -i64 winerr(void) nocallback privileged; +i64 __winerr(void) nocallback privileged; #define mkntpath(PATH, PATH16) mkntpath2(PATH, -1u, PATH16) #define mkntpath2(PATH, FLAGS, PATH16) \ @@ -280,6 +275,13 @@ i64 winerr(void) nocallback privileged; Count; \ }) +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ cosmopolitan § syscalls » drivers ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +ssize_t readv$serial(struct Fd *, const struct iovec *, int) hidden; +ssize_t writev$serial(struct Fd *, const struct iovec *, int) hidden; + #undef sigset #undef i32 #undef i64 diff --git a/libc/calls/ioctl-default.c b/libc/calls/ioctl-default.c index 312f19fb..94852c8d 100644 --- a/libc/calls/ioctl-default.c +++ b/libc/calls/ioctl-default.c @@ -29,13 +29,13 @@ int ioctl$default(int fd, uint64_t request, void *memory) { int64_t handle; if (!IsWindows()) { return ioctl$sysv(fd, request, memory); - } else if (isfdopen(fd)) { + } else if (__isfdopen(fd)) { if (g_fds.p[fd].kind == kFdSocket) { handle = g_fds.p[fd].handle; if ((rc = weaken(__ioctlsocket$nt)(handle, request, memory)) != -1) { return rc; } else { - return weaken(winsockerr)(); + return weaken(__winsockerr)(); } } else { return eopnotsupp(); diff --git a/libc/calls/ioctl-tiocgwinsz-nt.c b/libc/calls/ioctl-tiocgwinsz-nt.c index 129cfd4b..f1b95923 100644 --- a/libc/calls/ioctl-tiocgwinsz-nt.c +++ b/libc/calls/ioctl-tiocgwinsz-nt.c @@ -33,7 +33,7 @@ textwindows int ioctl$tiocgwinsz$nt(int fd, struct winsize *ws) { struct NtConsoleScreenBufferInfoEx sbinfo; fds[0] = fd, fds[1] = 1, fds[2] = 0; for (i = 0; i < ARRAYLEN(fds); ++i) { - if (isfdkind(fds[i], kFdFile) || isfdkind(fds[i], kFdConsole)) { + if (__isfdkind(fds[i], kFdFile) || __isfdkind(fds[i], kFdConsole)) { if (GetConsoleMode(g_fds.p[fds[i]].handle, &mode)) { memset(&sbinfo, 0, sizeof(sbinfo)); sbinfo.cbSize = sizeof(sbinfo); @@ -50,7 +50,7 @@ textwindows int ioctl$tiocgwinsz$nt(int fd, struct winsize *ws) { ws->ws_ypixel = 0; return 0; } else { - winerr(); + __winerr(); } } else { enotty(); diff --git a/libc/calls/ioctl-tiocswinsz-nt.c b/libc/calls/ioctl-tiocswinsz-nt.c index 9a9abdf8..0fdacb25 100644 --- a/libc/calls/ioctl-tiocswinsz-nt.c +++ b/libc/calls/ioctl-tiocswinsz-nt.c @@ -29,10 +29,10 @@ textwindows int ioctl$tiocswinsz$nt(int fd, const struct winsize *ws) { uint32_t mode; struct NtCoord coord; if (!ws) return efault(); - if (!isfdkind(fd, kFdFile)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return ebadf(); if (!GetConsoleMode(g_fds.p[fd].handle, &mode)) return enotty(); coord.X = ws->ws_col; coord.Y = ws->ws_row; - if (!SetConsoleScreenBufferSize(g_fds.p[fd].handle, coord)) return winerr(); + if (!SetConsoleScreenBufferSize(g_fds.p[fd].handle, coord)) return __winerr(); return 0; } diff --git a/libc/calls/isatty-nt.c b/libc/calls/isatty-nt.c index 49f05234..632c1259 100644 --- a/libc/calls/isatty-nt.c +++ b/libc/calls/isatty-nt.c @@ -23,6 +23,6 @@ #include "libc/sysv/errfuns.h" textwindows bool32 isatty$nt(int fd) { - if (!isfdkind(fd, kFdFile)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return ebadf(); return GetFileType(g_fds.p[fd].handle) == kNtFileTypeChar; } diff --git a/libc/calls/ischardev.c b/libc/calls/ischardev.c index a65b8d52..9e13a720 100644 --- a/libc/calls/ischardev.c +++ b/libc/calls/ischardev.c @@ -40,7 +40,7 @@ textstartup bool32 ischardev(int fd) { return false; } } else { - return isfdkind(fd, kFdFile) && + return __isfdkind(fd, kFdFile) && GetFileType(g_fds.p[fd].handle) == kNtFileTypeChar; } } diff --git a/libc/calls/isfdkind.c b/libc/calls/isfdkind.c index 6c21e017..deed5092 100644 --- a/libc/calls/isfdkind.c +++ b/libc/calls/isfdkind.c @@ -19,6 +19,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" -bool isfdkind(int fd, enum FdKind kind) { - return isfdindex(fd) && g_fds.p[fd].kind == kind; +bool __isfdkind(int fd, enum FdKind kind) { + return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind == kind; } diff --git a/libc/calls/isfdopen.c b/libc/calls/isfdopen.c index d4047085..3a3fc84a 100644 --- a/libc/calls/isfdopen.c +++ b/libc/calls/isfdopen.c @@ -19,6 +19,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" -bool isfdopen(int fd) { - return isfdindex(fd) && g_fds.p[fd].kind != kFdEmpty; +bool __isfdopen(int fd) { + return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind != kFdEmpty; } diff --git a/libc/calls/link-nt.c b/libc/calls/link-nt.c index b19e40f6..118fe195 100644 --- a/libc/calls/link-nt.c +++ b/libc/calls/link-nt.c @@ -17,10 +17,10 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" -#include "libc/calls/internal.h" -#include "libc/calls/calls.h" textwindows int link$nt(const char *existingpath, const char *newpath) { char16_t newpath16[PATH_MAX]; @@ -30,7 +30,7 @@ textwindows int link$nt(const char *existingpath, const char *newpath) { if (CreateHardLink(newpath16, existingpath16, NULL)) { return 0; } else { - return winerr(); + return __winerr(); } } else { return -1; diff --git a/libc/calls/lseek-nt.c b/libc/calls/lseek-nt.c index d6b2e98c..65f5ee93 100644 --- a/libc/calls/lseek-nt.c +++ b/libc/calls/lseek-nt.c @@ -23,10 +23,10 @@ textwindows int64_t lseek$nt(int fd, int64_t offset, int whence) { int64_t res; - if (!isfdkind(fd, kFdFile)) return ebadf(); + if (!__isfdkind(fd, kFdFile)) return ebadf(); if (SetFilePointerEx(g_fds.p[fd].handle, offset, &res, whence)) { return res; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/madvise-nt.c b/libc/calls/madvise-nt.c index ed0a16e7..5c17dbb4 100644 --- a/libc/calls/madvise-nt.c +++ b/libc/calls/madvise-nt.c @@ -61,7 +61,7 @@ textwindows int madvise$nt(void *addr, size_t length, int advice) { if (fn(GetCurrentProcess(), &rangecount, ranges, 0)) { return 0; } else { - return winerr(); + return __winerr(); } } else { return enosys(); @@ -72,7 +72,7 @@ textwindows int madvise$nt(void *addr, size_t length, int advice) { if (fn(addr, length, kNtVmOfferPriorityNormal)) { return 0; } else { - return winerr(); + return __winerr(); } } else { return enosys(); diff --git a/libc/calls/mkdir.c b/libc/calls/mkdir.c index 581a6ed3..83994bf9 100644 --- a/libc/calls/mkdir.c +++ b/libc/calls/mkdir.c @@ -31,7 +31,7 @@ static textwindows noinline int mkdir$nt(const char *path, uint32_t mode) { if (CreateDirectory(path16, NULL)) { return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/mprotect.greg.c b/libc/calls/mprotect.greg.c index d28b3e43..332817fb 100644 --- a/libc/calls/mprotect.greg.c +++ b/libc/calls/mprotect.greg.c @@ -55,7 +55,7 @@ int mprotect(void *addr, uint64_t len, int prot) { if (__imp_VirtualProtect(addr, len, prot2nt(prot, 0), &oldprot)) { return 0; } else { - return winerr(); + return __winerr(); } } } diff --git a/libc/calls/nanosleep-xnu.c b/libc/calls/nanosleep-xnu.c index de6c5695..587d4eb0 100644 --- a/libc/calls/nanosleep-xnu.c +++ b/libc/calls/nanosleep-xnu.c @@ -22,7 +22,6 @@ #include "libc/macros.h" #include "libc/nexgen32e/nexgen32e.h" #include "libc/sock/internal.h" -#include "libc/sock/select.internal.h" int nanosleep$xnu(const struct timespec *req, struct timespec *rem) { long millis; diff --git a/libc/calls/ntaccesscheck.c b/libc/calls/ntaccesscheck.c index 49d31286..0b5f9c15 100644 --- a/libc/calls/ntaccesscheck.c +++ b/libc/calls/ntaccesscheck.c @@ -81,7 +81,7 @@ textwindows int ntaccesscheck(const char16_t *pathname, uint32_t flags) { (result || flags == F_OK)) { rc = 0; } else { - rc = winerr(); + rc = __winerr(); } close(hImpersonatedToken); close(hToken); diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 6ef2b0d4..1ab9d375 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -49,7 +49,7 @@ static textwindows int64_t open$nt$impl(const char *file, uint32_t flags, (flags & O_EXCL) ? kNtFileShareExclusive : kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, - (flags & O_CLOEXEC) ? &kNtIsInheritable : NULL, + &kNtIsInheritable, (flags & O_CREAT) && (flags & O_EXCL) ? kNtCreateNew : (flags & O_CREAT) && (flags & O_TRUNC) @@ -94,7 +94,7 @@ static textwindows ssize_t open$nt$console(const struct NtMagicPaths *mp, open$nt$impl(mp->conout, (flags & ~O_ACCMODE) | O_WRONLY, mode); assert(g_fds.p[fd].extra != -1); } else { - return winerr(); + return __winerr(); } g_fds.p[fd].kind = kFdConsole; g_fds.p[fd].flags = flags; @@ -111,13 +111,13 @@ static textwindows ssize_t open$nt$file(const char *file, uint32_t flags, ((flags & O_CREAT) && (flags & O_TRUNC))) { return eisdir(); } else { - return winerr(); + return __winerr(); } } textwindows ssize_t open$nt(const char *file, uint32_t flags, int32_t mode) { size_t fd; - if ((fd = createfd()) == -1) return -1; + if ((fd = __getemptyfd()) == -1) return -1; if ((flags & O_ACCMODE) == O_RDWR && tinystrcmp(file, kNtMagicPaths.devtty) == 0) { return open$nt$console(&kNtMagicPaths, flags, mode, fd); diff --git a/libc/calls/open.c b/libc/calls/open.c index 150e93b1..77e1631b 100644 --- a/libc/calls/open.c +++ b/libc/calls/open.c @@ -24,7 +24,7 @@ #include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Opens file. diff --git a/libc/calls/openanon.c b/libc/calls/openanon.c index 64017619..1d3dd007 100644 --- a/libc/calls/openanon.c +++ b/libc/calls/openanon.c @@ -61,24 +61,25 @@ static int openanon$impl(const char *name, unsigned flags, char pathbuf[hasatleast PATH_MAX]) { int fd; openanon$genpath(name, state, pathbuf); + flags |= O_RDWR | O_CREAT | O_EXCL | O_TRUNC; if (!IsWindows()) { - flags |= O_RDWR | O_CREAT | O_EXCL | O_TRUNC; if ((fd = openat$sysv(AT_FDCWD, pathbuf, flags, 0600)) != -1) { unlink(pathbuf); } return fd; } else { - if ((fd = createfd()) != -1 && + if ((fd = __getemptyfd()) != -1 && (g_fds.p[fd].handle = CreateFileA( pathbuf, kNtGenericRead | kNtGenericWrite, kNtFileShareExclusive, - (flags & O_CLOEXEC) ? &kNtIsInheritable : NULL, kNtCreateAlways, + &kNtIsInheritable, kNtCreateAlways, (kNtFileAttributeNotContentIndexed | kNtFileAttributeNormal | kNtFileAttributeTemporary | kNtFileFlagDeleteOnClose), 0)) != -1) { g_fds.p[fd].kind = kFdFile; + g_fds.p[fd].flags = flags; return fd; } else { - return winerr(); + return __winerr(); } } } diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index 3c5d9da0..1454c05d 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -24,11 +24,10 @@ textwindows int pipe$nt(int pipefd[2], unsigned flags) { int reader, writer; - if ((reader = createfd()) == -1) return -1; - if ((writer = createfd()) == -1) return -1; + if ((reader = __getemptyfd()) == -1) return -1; + if ((writer = __getemptyfd()) == -1) return -1; if (CreatePipe(&g_fds.p[reader].handle, &g_fds.p[writer].handle, - (flags & O_CLOEXEC) ? NULL : &kNtIsInheritable, - 0 /* let NT pick buffer size */)) { + &kNtIsInheritable, 0)) { g_fds.p[reader].kind = kFdFile; g_fds.p[reader].flags = flags; g_fds.p[writer].kind = kFdFile; @@ -37,6 +36,6 @@ textwindows int pipe$nt(int pipefd[2], unsigned flags) { pipefd[1] = writer; return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/pread.c b/libc/calls/pread.c index a9101e07..69533053 100644 --- a/libc/calls/pread.c +++ b/libc/calls/pread.c @@ -26,7 +26,7 @@ #include "libc/macros.h" #include "libc/runtime/runtime.h" #include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Reads from file at offset, thus avoiding superfluous lseek(). @@ -42,12 +42,12 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) { ssize_t rc; if (fd == -1 || offset < 0) return einval(); - if (isfdkind(fd, kFdZip)) { + if (__isfdkind(fd, kFdZip)) { rc = weaken(__zipos_read)( (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, buf, size, offset); } else if (!IsWindows()) { rc = pread$sysv(fd, buf, size, offset); - } else if (isfdkind(fd, kFdFile)) { + } else if (__isfdkind(fd, kFdFile)) { rc = read$nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, offset); } else { rc = ebadf(); diff --git a/libc/calls/pwrite.c b/libc/calls/pwrite.c index bb4923b7..2afd131f 100644 --- a/libc/calls/pwrite.c +++ b/libc/calls/pwrite.c @@ -43,7 +43,7 @@ ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) { size = MIN(size, 0x7ffff000); if (!IsWindows()) { rc = pwrite$sysv(fd, buf, size, offset); - } else if (isfdkind(fd, kFdFile)) { + } else if (__isfdkind(fd, kFdFile)) { rc = write$nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, offset); } else { return ebadf(); diff --git a/libc/calls/raise.c b/libc/calls/raise.c index 8d5849b0..407d76a9 100644 --- a/libc/calls/raise.c +++ b/libc/calls/raise.c @@ -60,7 +60,7 @@ int raise(int sig) { if (GenerateConsoleCtrlEvent(GetCtrlEvent(sig), 0)) { return 0; } else { - return winerr(); + return __winerr(); } } } diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index ddf9d684..c465f5f4 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -40,6 +40,6 @@ textwindows ssize_t read$nt(struct Fd *fd, const struct iovec *iov, } else if (NtGetErr() == kNtErrorBrokenPipe) { return 0; /* read() doesn't EPIPE lool */ } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/read.c b/libc/calls/read.c index eb3e0c4b..a53df463 100644 --- a/libc/calls/read.c +++ b/libc/calls/read.c @@ -17,17 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/bits/weaken.h" -#include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" -#include "libc/dce.h" -#include "libc/macros.h" -#include "libc/runtime/runtime.h" -#include "libc/sock/internal.h" -#include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.h" +#include "libc/sock/sock.h" /** * Reads data from file descriptor. @@ -41,24 +32,5 @@ * @asyncsignalsafe */ ssize_t read(int fd, void *buf, size_t size) { - ssize_t rc; - if (fd == -1) return einval(); - if (isfdkind(fd, kFdZip)) { - rc = - weaken(__zipos_read)((struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, - (struct iovec[]){{buf, size}}, 1, -1); - } else if (!IsWindows()) { - rc = read$sysv(fd, buf, size); - } else if (isfdkind(fd, kFdFile) || isfdkind(fd, kFdConsole)) { - rc = read$nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, -1); - } else if (isfdkind(fd, kFdSocket)) { - rc = weaken(recvfrom$nt)(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, 0, - NULL, NULL); - } else { - rc = ebadf(); - } - if (!IsTrustworthy() && rc != -1) { - if ((size_t)rc > size) abort(); - } - return rc; + return readv(fd, &(struct iovec){buf, size}, 1); } diff --git a/libc/calls/readv-serial.c b/libc/calls/readv-serial.c new file mode 100644 index 00000000..d6455f13 --- /dev/null +++ b/libc/calls/readv-serial.c @@ -0,0 +1,56 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "ape/lib/pc.h" +#include "libc/calls/internal.h" +#include "libc/nexgen32e/uart.internal.h" + +static bool IsDataAvailable(struct Fd *fd) { + return inb(fd->handle + UART_LSR) & UART_TTYDA; +} + +static int GetFirstIov(struct iovec *iov, int iovlen) { + int i; + for (i = 0; i < iovlen; ++i) { + if (iov[i].iov_len) { + return i; + } + } + return -1; +} + +ssize_t readv$serial(struct Fd *fd, const struct iovec *iov, int iovlen) { + size_t i, j, got = 0; + if ((i = GetFirstIov(iov, iovlen)) != -1) { + while (!IsDataAvailable(fd)) asm("pause"); + i = 0; + j = 0; + do { + ++got; + ((char *)iov[i].iov_base)[j] = inb(fd->handle); + if (++j == iov[i].iov_len) { + j = 0; + if (++i == iovlen) { + break; + } + } + } while (IsDataAvailable(fd)); + } + return got; +} diff --git a/libc/calls/readv.c b/libc/calls/readv.c index b299c3f5..1588ec26 100644 --- a/libc/calls/readv.c +++ b/libc/calls/readv.c @@ -23,7 +23,7 @@ #include "libc/calls/struct/iovec.h" #include "libc/sock/internal.h" #include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Reads data to multiple buffers. @@ -31,19 +31,19 @@ * @return number of bytes actually read, or -1 w/ errno */ ssize_t readv(int fd, const struct iovec *iov, int iovlen) { - if (!IsTrustworthy()) { - if (fd == -1) return einval(); - if (iovlen < 0) return einval(); - } - if (isfdkind(fd, kFdZip)) { + if (fd < 0) return einval(); + if (iovlen < 0) return einval(); + if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { return weaken(__zipos_read)( (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, -1); - } - if (!IsWindows()) { + } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdSerial) { + return readv$serial(&g_fds.p[fd], iov, iovlen); + } else if (!IsWindows()) { return readv$sysv(fd, iov, iovlen); - } else if (isfdkind(fd, kFdFile) || isfdkind(fd, kFdConsole)) { + } else if (fd < g_fds.n && + (g_fds.p[fd].kind == kFdFile || g_fds.p[fd].kind == kFdConsole)) { return read$nt(&g_fds.p[fd], iov, iovlen, -1); - } else if (isfdkind(fd, kFdSocket)) { + } else if (fd < g_fds.n && (g_fds.p[fd].kind == kFdSocket)) { return weaken(recvfrom$nt)(&g_fds.p[fd], iov, iovlen, 0, NULL, 0); } else { return ebadf(); diff --git a/libc/calls/removefd.c b/libc/calls/removefd.c index 4cdb450b..7a7a4aca 100644 --- a/libc/calls/removefd.c +++ b/libc/calls/removefd.c @@ -20,8 +20,8 @@ #include "libc/calls/internal.h" #include "libc/macros.h" -void removefd(int fd) { - if (isfdopen(fd)) { +void __removefd(int fd) { + if (__isfdopen(fd)) { g_fds.p[fd].kind = kFdEmpty; g_fds.f = MIN(g_fds.f, fd); } diff --git a/libc/calls/rename-nt.c b/libc/calls/rename-nt.c index 91fa3864..b2cda5b3 100644 --- a/libc/calls/rename-nt.c +++ b/libc/calls/rename-nt.c @@ -34,6 +34,6 @@ textwindows int rename$nt(const char *oldpath, const char *newpath) { if (MoveFileEx(oldpath16, newpath16, kNtMovefileReplaceExisting)) { return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/rmdir-nt.c b/libc/calls/rmdir-nt.c index 5305ecdc..aa9d06f1 100644 --- a/libc/calls/rmdir-nt.c +++ b/libc/calls/rmdir-nt.c @@ -27,6 +27,6 @@ textwindows int rmdir$nt(const char *path) { if (RemoveDirectory(path16)) { return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/sched_setaffinity.c b/libc/calls/sched_setaffinity.c index c09e4079..d6f024dc 100644 --- a/libc/calls/sched_setaffinity.c +++ b/libc/calls/sched_setaffinity.c @@ -56,7 +56,7 @@ static textwindows noinline int sched_setaffinity$nt(int pid, } } } - rc = SetAffinityMask(handle ? handle : pid, mask) ? 0 : winerr(); + rc = SetAffinityMask(handle ? handle : pid, mask) ? 0 : __winerr(); if (handle) CloseHandle(handle); return rc; } diff --git a/libc/calls/setitimer-nt.c b/libc/calls/setitimer-nt.c index 38c9bc87..6362d93c 100644 --- a/libc/calls/setitimer-nt.c +++ b/libc/calls/setitimer-nt.c @@ -69,7 +69,7 @@ textwindows int setitimer$nt(int which, const struct itimerval *newvalue, if (newvalue) { if (newvalue->it_value.tv_sec && newvalue->it_value.tv_usec) { if (!(ith = CreateWaitableTimer(NULL, false, NULL))) { - return winerr(); + return __winerr(); } duetime = -(newvalue->it_value.tv_sec * HECTONANOSECONDS + newvalue->it_value.tv_usec * 10); diff --git a/libc/calls/setpriority-nt.c b/libc/calls/setpriority-nt.c index 79980d55..f74b125c 100644 --- a/libc/calls/setpriority-nt.c +++ b/libc/calls/setpriority-nt.c @@ -48,6 +48,6 @@ textwindows int setpriority$nt(int nice) { SetThreadPriority(GetCurrentThread(), p.wut)) { return p.nice; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index fbdca6d7..1b2ffe46 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -81,7 +81,7 @@ static void sigaction$cosmo2native(union metasigaction *sa) { sa_mask); break; default: - abort(); + break; } } @@ -105,7 +105,7 @@ static void sigaction$native2cosmo(union metasigaction *sa) { sa_mask); break; default: - abort(); + break; } } diff --git a/libc/calls/stat-nt.c b/libc/calls/stat-nt.c index 3f085404..ddc631e6 100644 --- a/libc/calls/stat-nt.c +++ b/libc/calls/stat-nt.c @@ -41,6 +41,6 @@ textwindows int stat$nt(const char *path, struct stat *st) { CloseHandle(fh); return rc; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/stat.c b/libc/calls/stat.c index a05987c2..e974599f 100644 --- a/libc/calls/stat.c +++ b/libc/calls/stat.c @@ -24,7 +24,7 @@ #include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Returns information about thing. diff --git a/libc/calls/symlink-nt.c b/libc/calls/symlink-nt.c index 164eabc2..2aa6eaa7 100644 --- a/libc/calls/symlink-nt.c +++ b/libc/calls/symlink-nt.c @@ -29,6 +29,6 @@ textwindows int symlink$nt(const char *target, const char *linkpath) { if (CreateSymbolicLink(linkpath16, target16, flags)) { return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/sysinfo-nt.c b/libc/calls/sysinfo-nt.c index f8fbc3e9..894edfec 100644 --- a/libc/calls/sysinfo-nt.c +++ b/libc/calls/sysinfo-nt.c @@ -34,6 +34,6 @@ textwindows int sysinfo$nt(struct sysinfo *info) { info->mem_unit = 1; return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/truncate-nt.c b/libc/calls/truncate-nt.c index cb140f0c..4b8c82c9 100644 --- a/libc/calls/truncate-nt.c +++ b/libc/calls/truncate-nt.c @@ -36,5 +36,5 @@ textwindows int truncate$nt(const char *path, uint64_t length) { CloseHandle(fh); if (ok) return 0; } - return winerr(); + return __winerr(); } diff --git a/libc/calls/unlink-nt.c b/libc/calls/unlink-nt.c index b3254fdd..37db5279 100644 --- a/libc/calls/unlink-nt.c +++ b/libc/calls/unlink-nt.c @@ -27,6 +27,6 @@ textwindows int unlink$nt(const char *name) { if (DeleteFile(name16)) { return 0; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/utimensat-nt.c b/libc/calls/utimensat-nt.c index 6df29994..0cca9110 100644 --- a/libc/calls/utimensat-nt.c +++ b/libc/calls/utimensat-nt.c @@ -48,7 +48,7 @@ textwindows int utimensat$nt(int dirfd, const char *path, -1) { closeme = true; } else { - return winerr(); + return __winerr(); } } else { return einval(); @@ -77,7 +77,7 @@ textwindows int utimensat$nt(int dirfd, const char *path, if (SetFileTime(fh, NULL, ftp[0], ftp[1])) { rc = 0; } else { - rc = winerr(); + rc = __winerr(); } if (closeme) { CloseHandle(fh); diff --git a/libc/calls/wait4-nt.c b/libc/calls/wait4-nt.c index 6fff38e4..45efcb92 100644 --- a/libc/calls/wait4-nt.c +++ b/libc/calls/wait4-nt.c @@ -41,7 +41,7 @@ textwindows int wait4$nt(int pid, int *opt_out_wstatus, int options, uint32_t i, count, timeout; struct NtFileTime createfiletime, exitfiletime, kernelfiletime, userfiletime; if (pid != -1) { - if (!isfdkind(pid, kFdProcess)) { + if (!__isfdkind(pid, kFdProcess)) { return echild(); } handles[0] = g_fds.p[pid].handle; @@ -67,8 +67,8 @@ textwindows int wait4$nt(int pid, int *opt_out_wstatus, int options, } else { i = WaitForMultipleObjects(count, handles, false, -1); } - if (i == kNtWaitFailed) return winerr(); - if (!GetExitCodeProcess(handles[i], &dwExitCode)) return winerr(); + if (i == kNtWaitFailed) return __winerr(); + if (!GetExitCodeProcess(handles[i], &dwExitCode)) return __winerr(); if (dwExitCode == kNtStillActive) continue; if (opt_out_wstatus) { /* @see WEXITSTATUS() */ *opt_out_wstatus = (dwExitCode & 0xff) << 8; @@ -80,6 +80,8 @@ textwindows int wait4$nt(int pid, int *opt_out_wstatus, int options, FileTimeToTimeVal(&opt_out_rusage->ru_utime, userfiletime); FileTimeToTimeVal(&opt_out_rusage->ru_stime, kernelfiletime); } + CloseHandle(g_fds.p[pids[i]].handle); + g_fds.p[pids[i]].kind = kFdEmpty; return pids[i]; } } diff --git a/libc/calls/winerr.greg.c b/libc/calls/winerr.greg.c index 18589b71..a943b50f 100644 --- a/libc/calls/winerr.greg.c +++ b/libc/calls/winerr.greg.c @@ -29,7 +29,7 @@ * @return -1 w/ few exceptions * @note this is a code-size saving device */ -privileged int64_t winerr(void) { +privileged int64_t __winerr(void) { if (IsWindows()) { errno = GetLastError(); return -1; diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index 089fda14..378f21cf 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -46,6 +46,6 @@ textwindows ssize_t write$nt(struct Fd *fd, const struct iovec *iov, FlushFileBuffers(fd->handle); return wrote; } else { - return winerr(); + return __winerr(); } } diff --git a/libc/calls/write.c b/libc/calls/write.c index a52fc22f..ce694009 100644 --- a/libc/calls/write.c +++ b/libc/calls/write.c @@ -17,15 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/bits/weaken.h" -#include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" -#include "libc/dce.h" -#include "libc/sock/internal.h" -#include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.h" +#include "libc/sock/sock.h" /** * Writes data to file descriptor. @@ -39,34 +32,5 @@ * @asyncsignalsafe */ ssize_t write(int fd, const void *buf, size_t size) { - ssize_t rc; - size_t wrote; - if (fd == -1) return einval(); - if (isfdkind(fd, kFdZip)) { - rc = weaken(__zipos_write)( - (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, - (struct iovec[]){{buf, size}}, 1, -1); - } else if (!IsWindows()) { - rc = write$sysv(fd, buf, size); - } else if (isfdkind(fd, kFdSocket)) { - rc = weaken(sendto$nt)(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, 0, - NULL, 0); - } else if (isfdkind(fd, kFdFile) || isfdkind(fd, kFdConsole)) { - rc = write$nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, -1); - } else { - return ebadf(); - } - if (rc != -1) { - wrote = (size_t)rc; - if (wrote == 0) { - assert(size == 0); - } else { - assert(wrote <= size); - } - } - if (!IsTrustworthy() && rc != -1) { - if (!rc && size) abort(); - if ((size_t)rc > size) abort(); - } - return rc; + return writev(fd, &(struct iovec){buf, size}, 1); } diff --git a/libc/stdio/serialstdio.c b/libc/calls/writev-serial.c similarity index 73% rename from libc/stdio/serialstdio.c rename to libc/calls/writev-serial.c index a1bb536d..7a1f55aa 100644 --- a/libc/stdio/serialstdio.c +++ b/libc/calls/writev-serial.c @@ -18,39 +18,17 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/lib/pc.h" +#include "libc/calls/internal.h" #include "libc/nexgen32e/uart.internal.h" -#include "libc/stdio/internal.h" -#include "libc/stdio/stdio.h" -static void fin(FILE *f) { - f->buf[f->end] = inb(0x3F8); - f->end = (f->end + 1) & (f->size - 1); -} - -static void fout(FILE *f) { - outb(0x3F8, f->buf[f->beg]); - f->beg = (f->beg + 1) & (f->size - 1); -} - -static int serialstdio(FILE *f, unsigned char status, void action(FILE *)) { - int block = 1; - unsigned tally = 0; - while (f->end != f->beg) { - if (!(inb(0x3F8 + UART_LSR) & status)) { - if (!block) break; - asm("pause"); - } else { - action(f); - tally++; +ssize_t writev$serial(struct Fd *fd, const struct iovec *iov, int iovlen) { + size_t i, j, wrote = 0; + for (i = 0; i < iovlen; ++i) { + for (j = 0; j < iov[i].iov_len; ++j) { + while (!(inb(fd->handle + UART_LSR) & UART_TTYTXR)) asm("pause"); + outb(fd->handle, ((char *)iov[i].iov_base)[j]); + ++wrote; } } - return (int)tally; -} - -int fsreadbuf(FILE *f) { - return serialstdio(f, UART_TTYDA, fin); -} - -int fswritebuf(FILE *f) { - return serialstdio(f, UART_TTYTXR, fout); + return wrote; } diff --git a/libc/calls/writev.c b/libc/calls/writev.c index 3a0a2336..2ae05f0d 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -22,7 +22,7 @@ #include "libc/calls/internal.h" #include "libc/sock/internal.h" #include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Writes data from multiple buffers. @@ -35,19 +35,19 @@ * @return number of bytes actually handed off, or -1 w/ errno */ ssize_t writev(int fd, const struct iovec *iov, int iovlen) { - if (!IsTrustworthy()) { - if (fd == -1) return einval(); - if (iovlen < 0) return einval(); - } - if (isfdkind(fd, kFdZip)) { + if (fd < 0) return einval(); + if (iovlen < 0) return einval(); + if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { return weaken(__zipos_write)( (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, -1); - } - if (!IsWindows()) { + } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdSerial) { + return writev$serial(&g_fds.p[fd], iov, iovlen); + } else if (!IsWindows()) { return writev$sysv(fd, iov, iovlen); - } else if (isfdkind(fd, kFdFile) || isfdkind(fd, kFdConsole)) { + } else if (fd < g_fds.n && + (g_fds.p[fd].kind == kFdFile || g_fds.p[fd].kind == kFdConsole)) { return write$nt(&g_fds.p[fd], iov, iovlen, -1); - } else if (isfdkind(fd, kFdSocket)) { + } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdSocket) { return weaken(sendto$nt)(&g_fds.p[fd], iov, iovlen, 0, NULL, 0); } else { return ebadf(); diff --git a/libc/dns/getntnameservers.c b/libc/dns/getntnameservers.c index 631e6908..6ab7ab64 100644 --- a/libc/dns/getntnameservers.c +++ b/libc/dns/getntnameservers.c @@ -82,7 +82,7 @@ textwindows int getntnameservers(struct ResolvConf *resolv) { } } } else { - rc = winerr(); + rc = __winerr(); } RegCloseKey(hkInterfaces); return rc; diff --git a/libc/fmt/fmt.h b/libc/fmt/fmt.h index 57bc139f..4733e36f 100644 --- a/libc/fmt/fmt.h +++ b/libc/fmt/fmt.h @@ -24,7 +24,8 @@ int vsprintf(char *, const char *, va_list) paramsnonnull((2)) nothrow nocallback frownedupon(vsnprintf); int sscanf(const char *, const char *, ...) scanfesque(2); int vsscanf(const char *, const char *, va_list); -int vcscanf(int callback(void *), void *, const char *, va_list); +int vcscanf(int (*)(void *), int (*)(int, void *), void *, const char *, + va_list); char *strerror(int) returnsnonnull nothrow nocallback; int strerror_r(int, char *, size_t) nothrow nocallback; int palandprintf(void *, void *, const char *, va_list) hidden; diff --git a/libc/fmt/vcscanf.c b/libc/fmt/vcscanf.c index 2ca4ebcf..02be64bd 100644 --- a/libc/fmt/vcscanf.c +++ b/libc/fmt/vcscanf.c @@ -44,7 +44,8 @@ * @param va points to the variadic argument state * @see libc/fmt/pflink.h (dynamic memory is not a requirement) */ -int vcscanf(int callback(void *), void *arg, const char *fmt, va_list va) { +int vcscanf(int callback(void *), int unget(int, void *), void *arg, + const char *fmt, va_list va) { struct FreeMe { struct FreeMe *next; void *ptr; @@ -56,13 +57,18 @@ int vcscanf(int callback(void *), void *arg, const char *fmt, va_list va) { while (c != -1) { switch (p[i++]) { case '\0': - return items; + if (c != -1 && unget) { + unget(c, arg); + } + goto Done; case ' ': case '\t': case '\n': case '\r': case '\v': - while (isspace(c)) c = callback(arg); + while (isspace(c)) { + c = callback(arg); + } break; case '%': { uintmax_t number; diff --git a/libc/fmt/vsscanf.c b/libc/fmt/vsscanf.c index 1f273c55..c443a305 100644 --- a/libc/fmt/vsscanf.c +++ b/libc/fmt/vsscanf.c @@ -47,5 +47,5 @@ static int vsscanfcb(void *arg) { */ int(vsscanf)(const char *str, const char *fmt, va_list va) { struct StringScannerState state = {(const unsigned char *)str, 0}; - return vcscanf(vsscanfcb, &state, fmt, va); + return vcscanf(vsscanfcb, NULL, &state, fmt, va); } diff --git a/libc/isystem/windows.h b/libc/isystem/windows.h index 399a9c9b..68ad2772 100644 --- a/libc/isystem/windows.h +++ b/libc/isystem/windows.h @@ -100,71 +100,72 @@ #define PKAFFINITY KAFFINITY* #define KPRIORITY LONG -#define PWCHAR WCHAR* -#define LPWCH WCHAR* -#define PWCH WCHAR* -#define LPCWCH CONST WCHAR* -#define PCWCH CONST WCHAR* -#define NWPSTR WCHAR* -#define LPWSTR WCHAR* -#define PWSTR WCHAR* -#define PZPWSTR PWSTR* -#define PCZPWSTR CONST PWSTR* -#define LPUWSTR WCHAR aligned(1)* -#define PUWSTR WCHAR aligned(1)* -#define LPCWSTR CONST WCHAR* -#define PCWSTR CONST WCHAR* -#define PZPCWSTR PCWSTR* -#define LPCUWSTR CONST WCHAR aligned(1)* -#define PCUWSTR CONST WCHAR aligned(1)* -#define PCHAR CHAR* -#define LPCH CHAR* -#define PCH CHAR* -#define LPCCH CONST CHAR* -#define PCCH CONST CHAR* -#define NPSTR CHAR* -#define LPSTR CHAR* -#define PSTR CHAR* -#define PZPSTR PSTR* -#define PCZPSTR CONST PSTR* -#define LPCSTR CONST CHAR* -#define PCSTR CONST CHAR* -#define PZPCSTR PCSTR* -#define TCHAR WCHAR -#define PTCHAR WCHAR* -#define TBYTE WCHAR -#define PTBYTE WCHAR* -#define LPTCH LPWSTR -#define PTCH LPWSTR -#define PTSTR LPWSTR -#define LPTSTR LPWSTR -#define PCTSTR LPCWSTR -#define LPCTSTR LPCWSTR -#define PUTSTR LPUWSTR -#define LPUTSTR LPUWSTR -#define PCUTSTR LPCUWSTR -#define LPCUTSTR LPCUWSTR -#define LP LPWSTR -#define PSHORT int16_t* -#define PLONG int32_t* -#define HANDLE int64_t -#define PHANDLE HANDLE* -#define FCHAR BYTE -#define FSHORT WORD -#define FLONG DWORD -#define HRESULT LONG -#define CCHAR char -#define LCID DWORD -#define PLCID PDWORD -#define LANGID WORD -#define LONGLONG int64_t -#define ULONGLONG uint64_t -#define USN LONGLONG -#define PLONGLONG LONGLONG* -#define PULONGLONG ULONGLONG* -#define DWORDLONG ULONGLONG -#define PDWORDLONG DWORDLONG* -#define LARGE_INTEGER int64_t +#define PWCHAR WCHAR* +#define LPWCH WCHAR* +#define PWCH WCHAR* +#define LPCWCH CONST WCHAR* +#define PCWCH CONST WCHAR* +#define NWPSTR WCHAR* +#define LPWSTR WCHAR* +#define PWSTR WCHAR* +#define PZPWSTR PWSTR* +#define PCZPWSTR CONST PWSTR* +#define LPUWSTR WCHAR aligned(1)* +#define PUWSTR WCHAR aligned(1)* +#define LPCWSTR CONST WCHAR* +#define PCWSTR CONST WCHAR* +#define PZPCWSTR PCWSTR* +#define LPCUWSTR CONST WCHAR aligned(1)* +#define PCUWSTR CONST WCHAR aligned(1)* +#define PCHAR CHAR* +#define LPCH CHAR* +#define PCH CHAR* +#define LPCCH CONST CHAR* +#define PCCH CONST CHAR* +#define NPSTR CHAR* +#define LPSTR CHAR* +#define PSTR CHAR* +#define PZPSTR PSTR* +#define PCZPSTR CONST PSTR* +#define LPCSTR CONST CHAR* +#define PCSTR CONST CHAR* +#define PZPCSTR PCSTR* +#define TCHAR WCHAR +#define PTCHAR WCHAR* +#define TBYTE WCHAR +#define PTBYTE WCHAR* +#define LPTCH LPWSTR +#define PTCH LPWSTR +#define PTSTR LPWSTR +#define LPTSTR LPWSTR +#define PCTSTR LPCWSTR +#define LPCTSTR LPCWSTR +#define PUTSTR LPUWSTR +#define LPUTSTR LPUWSTR +#define PCUTSTR LPCUWSTR +#define LPCUTSTR LPCUWSTR +#define LP LPWSTR +#define PSHORT int16_t* +#define PLONG int32_t* +#define HANDLE int64_t +#define PHANDLE HANDLE* +#define FCHAR BYTE +#define FSHORT WORD +#define FLONG DWORD +#define HRESULT LONG +#define CCHAR char +#define LCID DWORD +#define PLCID PDWORD +#define LANGID WORD +#define LONGLONG int64_t +#define ULONGLONG uint64_t +#define USN LONGLONG +#define PLONGLONG LONGLONG* +#define PULONGLONG ULONGLONG* +#define DWORDLONG ULONGLONG +#define PDWORDLONG DWORDLONG* +#define LARGE_INTEGER int64_t +#define PLARGE_INTEGER int64_t* #define ULONG uint32_t #define PULONG ULONG* @@ -1273,9 +1274,9 @@ #define WSAPROTOCOL_INFO struct NtWsaProtocolInfo #define LPWSAPROTOCOL_INFO struct NtWsaProtocolInfo* -#define _WSABUF iovec$nt -#define WSABUF struct iovec$nt -#define LPWSABUF struct iovec$nt* +#define _WSABUF NtIovec +#define WSABUF struct NtIovec +#define LPWSABUF struct NtIovec* #define _GUID NtGuid #define GUID struct NtGuid @@ -1320,9 +1321,9 @@ #define SD_SEND SHUT_WR #define SD_BOTH SHUT_RDWR -#define WSAMSG struct msghdr$nt -#define PWSAMSG struct msghdr$nt* -#define LPWSAMSG struct msghdr$nt* +#define WSAMSG struct NtMsgHdr +#define PWSAMSG struct NtMsgHdr* +#define LPWSAMSG struct NtMsgHdr* #define _MEMORYSTATUSEX NtMemoryStatusEx #define MEMORYSTATUSEX struct NtMemoryStatusEx diff --git a/libc/libc.mk b/libc/libc.mk index fb86662a..3c3134fb 100644 --- a/libc/libc.mk +++ b/libc/libc.mk @@ -4,7 +4,7 @@ PKGS += LIBC LIBC_HDRS = $(filter %.h,$(LIBC_FILES)) -LIBC_FILES := $(wildcard libc/*) $(wildcard libc/internal/*) +LIBC_FILES := $(wildcard libc/*) LIBC_CHECKS = $(LIBC_HDRS:%=o/$(MODE)/%.ok) .PHONY: o/$(MODE)/libc diff --git a/libc/nexgen32e/uart.internal.h b/libc/nexgen32e/uart.internal.h index d8a214c4..627638fb 100644 --- a/libc/nexgen32e/uart.internal.h +++ b/libc/nexgen32e/uart.internal.h @@ -1,24 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_NEXGEN32E_UART_H_ #define COSMOPOLITAN_LIBC_NEXGEN32E_UART_H_ -/** - * @fileoverview PC Serial Line Helpers (8250 UART 16550+). - * - *

These functions provide the baseline of PC telecommunications - * support that doesn't require switching context or cpu mode. - * - *

Example use cases: - *

- * - * @see www.lammertbies.nl/comm/info/serial-uart.html - */ - -#define COM1 0x0 /* offset in pc bios data area with port number (0x400) */ +#define COM1 0x0 #define COM2 0x2 #define COM3 0x4 #define COM4 0x6 @@ -40,16 +23,4 @@ #define UART_TTYIDL (1 << 6) /* serial thr empty and line idle */ #define UART_TTYEDF (1 << 7) /* erroneous data in fifo */ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -void sinit(unsigned short *ports, unsigned count, const char *config); -void sputc(unsigned char ch, int port); -int sgetc(int port); -void sputs(const char *s, int port); -void sflush(unsigned short *ports, unsigned count); -unsigned char slsr(int port); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_NEXGEN32E_UART_H_ */ diff --git a/libc/internal/notice.h b/libc/notice.internal.h similarity index 100% rename from libc/internal/notice.h rename to libc/notice.internal.h diff --git a/libc/nt/KernelBase/AcquireSRWLockExclusive.s b/libc/nt/KernelBase/AcquireSRWLockExclusive.s new file mode 100644 index 00000000..3a6887a2 --- /dev/null +++ b/libc/nt/KernelBase/AcquireSRWLockExclusive.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_AcquireSRWLockExclusive,AcquireSRWLockExclusive,683 + + .text.windows +AcquireSRWLockExclusive: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_AcquireSRWLockExclusive(%rip) + leave + ret + .endfn AcquireSRWLockExclusive,globl + .previous diff --git a/libc/nt/KernelBase/AcquireSRWLockShared.s b/libc/nt/KernelBase/AcquireSRWLockShared.s new file mode 100644 index 00000000..8cc8f70a --- /dev/null +++ b/libc/nt/KernelBase/AcquireSRWLockShared.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_AcquireSRWLockShared,AcquireSRWLockShared,684 + + .text.windows +AcquireSRWLockShared: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_AcquireSRWLockShared(%rip) + leave + ret + .endfn AcquireSRWLockShared,globl + .previous diff --git a/libc/nt/KernelBase/DeleteCriticalSection.s b/libc/nt/KernelBase/DeleteCriticalSection.s new file mode 100644 index 00000000..65cc2446 --- /dev/null +++ b/libc/nt/KernelBase/DeleteCriticalSection.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_DeleteCriticalSection,DeleteCriticalSection,0 + + .text.windows +DeleteCriticalSection: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_DeleteCriticalSection(%rip) + leave + ret + .endfn DeleteCriticalSection,globl + .previous diff --git a/libc/nt/KernelBase/EnterCriticalSection.s b/libc/nt/KernelBase/EnterCriticalSection.s new file mode 100644 index 00000000..0aed1ec9 --- /dev/null +++ b/libc/nt/KernelBase/EnterCriticalSection.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_EnterCriticalSection,EnterCriticalSection,0 + + .text.windows +EnterCriticalSection: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_EnterCriticalSection(%rip) + leave + ret + .endfn EnterCriticalSection,globl + .previous diff --git a/libc/nt/KernelBase/GetTickCount64.s b/libc/nt/KernelBase/GetTickCount64.s index 0a90d908..7115a8ad 100644 --- a/libc/nt/KernelBase/GetTickCount64.s +++ b/libc/nt/KernelBase/GetTickCount64.s @@ -1,2 +1,14 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_GetTickCount64,GetTickCount64,780 + + .text.windows +GetTickCount64: + push %rbp + mov %rsp,%rbp + .profilable + sub $32,%rsp + call *__imp_GetTickCount64(%rip) + leave + ret + .endfn GetTickCount64,globl + .previous diff --git a/libc/nt/KernelBase/InitializeCriticalSection.s b/libc/nt/KernelBase/InitializeCriticalSection.s new file mode 100644 index 00000000..2eeac249 --- /dev/null +++ b/libc/nt/KernelBase/InitializeCriticalSection.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_InitializeCriticalSection,InitializeCriticalSection,0 + + .text.windows +InitializeCriticalSection: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_InitializeCriticalSection(%rip) + leave + ret + .endfn InitializeCriticalSection,globl + .previous diff --git a/libc/nt/KernelBase/InitializeCriticalSectionAndSpinCount.s b/libc/nt/KernelBase/InitializeCriticalSectionAndSpinCount.s index ef88ca05..3ef5a08b 100644 --- a/libc/nt/KernelBase/InitializeCriticalSectionAndSpinCount.s +++ b/libc/nt/KernelBase/InitializeCriticalSectionAndSpinCount.s @@ -1,5 +1,5 @@ .include "o/libc/nt/codegen.inc" -.imp KernelBase,__imp_InitializeCriticalSectionAndSpinCount,InitializeCriticalSectionAndSpinCount,856 +.imp KernelBase,__imp_InitializeCriticalSectionAndSpinCount,InitializeCriticalSectionAndSpinCount,0 .text.windows InitializeCriticalSectionAndSpinCount: diff --git a/libc/nt/KernelBase/InitializeSRWLock.s b/libc/nt/KernelBase/InitializeSRWLock.s new file mode 100644 index 00000000..a99e7c41 --- /dev/null +++ b/libc/nt/KernelBase/InitializeSRWLock.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_InitializeSRWLock,InitializeSRWLock,1128 + + .text.windows +InitializeSRWLock: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_InitializeSRWLock(%rip) + leave + ret + .endfn InitializeSRWLock,globl + .previous diff --git a/libc/nt/KernelBase/LeaveCriticalSection.s b/libc/nt/KernelBase/LeaveCriticalSection.s new file mode 100644 index 00000000..3220e4c4 --- /dev/null +++ b/libc/nt/KernelBase/LeaveCriticalSection.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_LeaveCriticalSection,LeaveCriticalSection,0 + + .text.windows +LeaveCriticalSection: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_LeaveCriticalSection(%rip) + leave + ret + .endfn LeaveCriticalSection,globl + .previous diff --git a/libc/nt/KernelBase/ReleaseSRWLockExclusive.s b/libc/nt/KernelBase/ReleaseSRWLockExclusive.s new file mode 100644 index 00000000..92ace636 --- /dev/null +++ b/libc/nt/KernelBase/ReleaseSRWLockExclusive.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_ReleaseSRWLockExclusive,ReleaseSRWLockExclusive,1341 + + .text.windows +ReleaseSRWLockExclusive: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_ReleaseSRWLockExclusive(%rip) + leave + ret + .endfn ReleaseSRWLockExclusive,globl + .previous diff --git a/libc/nt/KernelBase/ReleaseSRWLockShared.s b/libc/nt/KernelBase/ReleaseSRWLockShared.s new file mode 100644 index 00000000..7be264ae --- /dev/null +++ b/libc/nt/KernelBase/ReleaseSRWLockShared.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_ReleaseSRWLockShared,ReleaseSRWLockShared,1342 + + .text.windows +ReleaseSRWLockShared: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_ReleaseSRWLockShared(%rip) + leave + ret + .endfn ReleaseSRWLockShared,globl + .previous diff --git a/libc/nt/KernelBase/SetCriticalSectionSpinCount.s b/libc/nt/KernelBase/SetCriticalSectionSpinCount.s new file mode 100644 index 00000000..bcd156d5 --- /dev/null +++ b/libc/nt/KernelBase/SetCriticalSectionSpinCount.s @@ -0,0 +1,12 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_SetCriticalSectionSpinCount,SetCriticalSectionSpinCount,0 + + .text.windows +SetCriticalSectionSpinCount: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_SetCriticalSectionSpinCount(%rip),%rax + jmp __sysv2nt + .endfn SetCriticalSectionSpinCount,globl + .previous diff --git a/libc/nt/KernelBase/TryAcquireSRWLockExclusive.s b/libc/nt/KernelBase/TryAcquireSRWLockExclusive.s new file mode 100644 index 00000000..8b149b58 --- /dev/null +++ b/libc/nt/KernelBase/TryAcquireSRWLockExclusive.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_TryAcquireSRWLockExclusive,TryAcquireSRWLockExclusive,1467 + + .text.windows +TryAcquireSRWLockExclusive: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_TryAcquireSRWLockExclusive(%rip) + leave + ret + .endfn TryAcquireSRWLockExclusive,globl + .previous diff --git a/libc/nt/KernelBase/TryAcquireSRWLockShared.s b/libc/nt/KernelBase/TryAcquireSRWLockShared.s new file mode 100644 index 00000000..f776ff40 --- /dev/null +++ b/libc/nt/KernelBase/TryAcquireSRWLockShared.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_TryAcquireSRWLockShared,TryAcquireSRWLockShared,1468 + + .text.windows +TryAcquireSRWLockShared: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_TryAcquireSRWLockShared(%rip) + leave + ret + .endfn TryAcquireSRWLockShared,globl + .previous diff --git a/libc/nt/KernelBase/TryEnterCriticalSection.s b/libc/nt/KernelBase/TryEnterCriticalSection.s new file mode 100644 index 00000000..301b34cb --- /dev/null +++ b/libc/nt/KernelBase/TryEnterCriticalSection.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp KernelBase,__imp_TryEnterCriticalSection,TryEnterCriticalSection,0 + + .text.windows +TryEnterCriticalSection: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_TryEnterCriticalSection(%rip) + leave + ret + .endfn TryEnterCriticalSection,globl + .previous diff --git a/libc/nt/enum/afd.h b/libc/nt/enum/afd.h new file mode 100644 index 00000000..7a613ac3 --- /dev/null +++ b/libc/nt/enum/afd.h @@ -0,0 +1,13 @@ +#ifndef COSMOPOLITAN_LIBC_NT_ENUM_AFD_H_ +#define COSMOPOLITAN_LIBC_NT_ENUM_AFD_H_ + +#define kNtAfdPollReceive 0x0001 +#define kNtAfdPollReceiveExpedited 0x0002 +#define kNtAfdPollSend 0x0004 +#define kNtAfdPollDisconnect 0x0008 +#define kNtAfdPollAbort 0x0010 +#define kNtAfdPollLocalClose 0x0020 +#define kNtAfdPollAccept 0x0080 +#define kNtAfdPollConnectFail 0x0100 + +#endif /* COSMOPOLITAN_LIBC_NT_ENUM_AFD_H_ */ diff --git a/libc/nt/enum/filemapflags.h b/libc/nt/enum/filemapflags.h index 5da8e952..fa6ae192 100644 --- a/libc/nt/enum/filemapflags.h +++ b/libc/nt/enum/filemapflags.h @@ -1,13 +1,12 @@ #ifndef COSMOPOLITAN_LIBC_NT_ENUM_FILEMAPFLAGS_H_ #define COSMOPOLITAN_LIBC_NT_ENUM_FILEMAPFLAGS_H_ -/* Choose subset of NtPageFlags passed earlier. */ -#define kNtFileMapCopy 0x00000001u -#define kNtFileMapWrite 0x00000002u -#define kNtFileMapRead 0x00000004u -#define kNtFileMapExecute 0x00000020u -#define kNtFileMapReserve 0x80000000u +#define kNtFileMapCopy 0x00000001u +#define kNtFileMapWrite 0x00000002u +#define kNtFileMapRead 0x00000004u +#define kNtFileMapExecute 0x00000020u +#define kNtFileMapReserve 0x80000000u #define kNtFileMapTargetsInvalid 0x40000000u -#define kNtFileMapLargePages 0x20000000u +#define kNtFileMapLargePages 0x20000000u #endif /* COSMOPOLITAN_LIBC_NT_ENUM_FILEMAPFLAGS_H_ */ diff --git a/libc/nt/enum/ioctl.h b/libc/nt/enum/ioctl.h index 676d3320..4884dd64 100644 --- a/libc/nt/enum/ioctl.h +++ b/libc/nt/enum/ioctl.h @@ -2,5 +2,6 @@ #define COSMOPOLITAN_LIBC_NT_ENUM_IOCTL_H_ #define kNtIoctlDiskGetDriveGeometry 0x00070000u +#define kNtIoctlAfdPoll 0x00012024u #endif /* COSMOPOLITAN_LIBC_NT_ENUM_IOCTL_H_ */ diff --git a/libc/nt/enum/keyedevent.h b/libc/nt/enum/keyedevent.h new file mode 100644 index 00000000..49fcdc08 --- /dev/null +++ b/libc/nt/enum/keyedevent.h @@ -0,0 +1,10 @@ +#ifndef COSMOPOLITAN_LIBC_NT_ENUM_KEYEDEVENT_H_ +#define COSMOPOLITAN_LIBC_NT_ENUM_KEYEDEVENT_H_ +#include "libc/nt/enum/accessmask.h" + +#define kNtKeyedeventWait 0x00000001u +#define kNtKeyedeventWake 0x00000002u +#define kNtKeyedeventAllAccess \ + (kNtStandardRightsRequired | kNtKeyedeventWait | kNtKeyedeventWake) + +#endif /* COSMOPOLITAN_LIBC_NT_ENUM_KEYEDEVENT_H_ */ diff --git a/libc/nt/enum/sio.h b/libc/nt/enum/sio.h new file mode 100644 index 00000000..9993718b --- /dev/null +++ b/libc/nt/enum/sio.h @@ -0,0 +1,7 @@ +#ifndef COSMOPOLITAN_LIBC_NT_ENUM_SIO_H_ +#define COSMOPOLITAN_LIBC_NT_ENUM_SIO_H_ + +#define kNtSioBspHandlePoll 0x4800001D +#define kNtSioBaseHandle 0x48000022 + +#endif /* COSMOPOLITAN_LIBC_NT_ENUM_SIO_H_ */ diff --git a/libc/nt/enum/status.h b/libc/nt/enum/status.h index 5e2e65f1..eb781c86 100644 --- a/libc/nt/enum/status.h +++ b/libc/nt/enum/status.h @@ -2,79 +2,82 @@ #define COSMOPOLITAN_LIBC_NT_STATUS_H_ /* high two bits = {success,informational,warning,error} */ -#define kNtStatusWait0 0x00000000 /* success statuses */ -#define kNtStatusAbandonedWait0 0x00000080 -#define kNtStatusUserApc 0x000000C0 -#define kNtStatusTimeout 0x00000102 -#define kNtStatusPending 0x00000103 -#define kNtStatusGuardPageViolation 0x80000001 /* warning statuses */ -#define kNtStatusDatatypeMisalignment 0x80000002 -#define kNtStatusBreakpoint 0x80000003 -#define kNtStatusSingleStep 0x80000004 -#define kNtStatusLongjump 0x80000026 -#define kNtStatusUnwindConsolidate 0x80000029 -#define kNtStatusAccessViolation 0xC0000005 /* error statuses */ -#define kNtStatusInPageError 0xC0000006 -#define kNtStatusInvalidHandle 0xC0000008 -#define kNtStatusInvalidParameter 0xC000000D -#define kNtStatusNoMemory 0xC0000017 -#define kNtStatusIllegalInstruction 0xC000001D -#define kNtStatusNoncontinuableException 0xC0000025 -#define kNtStatusInvalidDisposition 0xC0000026 -#define kNtStatusArrayBoundsExceeded 0xC000008C -#define kNtStatusFloatDenormalOperand 0xC000008D -#define kNtStatusFloatDivideByZero 0xC000008E -#define kNtStatusFloatInexactResult 0xC000008F -#define kNtStatusFloatInvalidOperation 0xC0000090 -#define kNtStatusFloatOverflow 0xC0000091 -#define kNtStatusFloatStackCheck 0xC0000092 -#define kNtStatusFloatUnderflow 0xC0000093 -#define kNtStatusIntegerDivideBYZero 0xC0000094 -#define kNtStatusIntegerOverflow 0xC0000095 -#define kNtStatusPrivilegedInstruction 0xC0000096 -#define kNtStatusStackOverflow 0xC00000FD -#define kNtStatusDllNotFound 0xC0000135 -#define kNtStatusOrdinalNotFound 0xC0000138 -#define kNtStatusEntrypointNotFound 0xC0000139 -#define kNtStatusControlCExit 0xC000013A -#define kNtStatusDllInitFailed 0xC0000142 -#define kNtStatusFloatMultipleFaults 0xC00002B4 -#define kNtStatusFloatMultipleTraps 0xC00002B5 -#define kNtStatusRegNatConsumption 0xC00002C9 -#define kNtStatusHeapCorruption 0xC0000374 -#define kNtStatusStackBufferOverrun 0xC0000409 -#define kNtStatusInvalidCruntimeParameter 0xC0000417 -#define kNtStatusAssertionFailure 0xC0000420 -#define kNtStatusEnclaveViolation 0xC00004A2 -#define kNtStatusSegmentNotification 0x40000005 -#define kNtStatusFatalAppExit 0x40000015 -/* statuses for the debugger facility */ -#define kNtDbgExceptionHandled 0x00010001 -#define kNtDbgContinue 0x00010002 -#define kNtDbgReplyLater 0x40010001 -#define kNtDbgTerminateThread 0x40010003 -#define kNtDbgTerminateProcess 0x40010004 -#define kNtDbgControlC 0x40010005 -#define kNtDbgPrintexceptionC 0x40010006 -#define kNtDbgRipexception 0x40010007 -#define kNtDbgControlBreak 0x40010008 -#define kNtDbgCommandException 0x40010009 -#define kNtDbgPrintexceptionWideC 0x4001000A -#define kNtDbgExceptionNotHandled 0x80010001 -#define kNtStillActive kNtStatusPending +#define kNtStatusSuccess 0x00000000u /* success statuses */ +#define kNtStatusWait0 0x00000000u +#define kNtStatusAbandonedWait0 0x00000080u +#define kNtStatusUserApc 0x000000C0u +#define kNtStatusTimeout 0x00000102u +#define kNtStatusPending 0x00000103u +#define kNtStatusGuardPageViolation 0x80000001u /* warning statuses */ +#define kNtStatusDatatypeMisalignment 0x80000002u +#define kNtStatusBreakpoint 0x80000003u +#define kNtStatusSingleStep 0x80000004u +#define kNtStatusLongjump 0x80000026u +#define kNtStatusUnwindConsolidate 0x80000029u +#define kNtStatusAccessViolation 0xC0000005u /* error statuses */ +#define kNtStatusInPageError 0xC0000006u +#define kNtStatusInvalidHandle 0xC0000008u +#define kNtStatusInvalidParameter 0xC000000Du +#define kNtStatusNoMemory 0xC0000017u +#define kNtStatusIllegalInstruction 0xC000001Du +#define kNtStatusNoncontinuableException 0xC0000025u +#define kNtStatusInvalidDisposition 0xC0000026u +#define kNtStatusArrayBoundsExceeded 0xC000008Cu +#define kNtStatusFloatDenormalOperand 0xC000008Du +#define kNtStatusFloatDivideByZero 0xC000008Eu +#define kNtStatusFloatInexactResult 0xC000008Fu +#define kNtStatusFloatInvalidOperation 0xC0000090u +#define kNtStatusFloatOverflow 0xC0000091u +#define kNtStatusFloatStackCheck 0xC0000092u +#define kNtStatusFloatUnderflow 0xC0000093u +#define kNtStatusIntegerDivideBYZero 0xC0000094u +#define kNtStatusIntegerOverflow 0xC0000095u +#define kNtStatusPrivilegedInstruction 0xC0000096u +#define kNtStatusStackOverflow 0xC00000FDu +#define kNtStatusDllNotFound 0xC0000135u +#define kNtStatusOrdinalNotFound 0xC0000138u +#define kNtStatusEntrypointNotFound 0xC0000139u +#define kNtStatusControlCExit 0xC000013Au +#define kNtStatusDllInitFailed 0xC0000142u +#define kNtStatusFloatMultipleFaults 0xC00002B4u +#define kNtStatusFloatMultipleTraps 0xC00002B5u +#define kNtStatusRegNatConsumption 0xC00002C9u +#define kNtStatusHeapCorruption 0xC0000374u +#define kNtStatusStackBufferOverrun 0xC0000409u +#define kNtStatusInvalidCruntimeParameter 0xC0000417u +#define kNtStatusAssertionFailure 0xC0000420u +#define kNtStatusEnclaveViolation 0xC00004A2u +#define kNtStatusSegmentNotification 0x40000005u +#define kNtStatusFatalAppExit 0x40000015u +#define kNtStatusNotFound 0xC0000225u +#define kNtStatusCancelled 0xC0000120u + +#define kNtDbgExceptionHandled 0x00010001u +#define kNtDbgContinue 0x00010002u +#define kNtDbgReplyLater 0x40010001u +#define kNtDbgTerminateThread 0x40010003u +#define kNtDbgTerminateProcess 0x40010004u +#define kNtDbgControlC 0x40010005u +#define kNtDbgPrintexceptionC 0x40010006u +#define kNtDbgRipexception 0x40010007u +#define kNtDbgControlBreak 0x40010008u +#define kNtDbgCommandException 0x40010009u +#define kNtDbgPrintexceptionWideC 0x4001000Au +#define kNtDbgExceptionNotHandled 0x80010001u +#define kNtStillActive kNtStatusPending #if !(__ASSEMBLER__ + __LINKER__ + 0) typedef uint32_t NtStatus; -forceinline int NtSeverity(NtStatus s) { return (unsigned)s >> 30; } -forceinline bool32 NtSuccess(NtStatus s) { return NtSeverity(s) == 0; } -forceinline bool32 NtInformation(NtStatus s) { return NtSeverity(s) == 1; } -forceinline bool32 NtWarning(NtStatus s) { return NtSeverity(s) == 2; } -forceinline bool32 NtError(NtStatus s) { return NtSeverity(s) == 3; } -forceinline int NtCode(NtStatus s) { return s & 0xffff; } -forceinline int NtFacility(NtStatus s) { return (s >> 16) & 0xfff; } -forceinline int NtFacilityCode(NtStatus s) { return s & 0x0FFFFFFF; } +#define NtSuccess(s) ((NtStatus)(s) >= 0) +#define NtInformation(s) (NtSeverity(s) == 1) +#define NtWarning(s) (NtSeverity(s) == 2) +#define NtError(s) (NtSeverity(s) == 3) +#define NtCode(s) ((NtStatus)(s)&0xffff) +#define NtSeverity(s) ((NtStatus)(s) >> 30) +#define NtFacility(s) (((NtStatus)(s) >> 16) & 0xfff) +#define NtFacilityCode(s) ((NtStatus)(s)&0x0FFFFFFF) #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_NT_STATUS_H_ */ diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 7204b82f..f9fb6c52 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -2682,7 +2682,7 @@ imp 'GetThreadTimes' GetThreadTimes KernelBase 777 5 imp 'GetThreadUILanguage' GetThreadUILanguage KernelBase 778 imp 'GetThreadWaitChain' GetThreadWaitChain advapi32 1369 imp 'GetTickCount' GetTickCount KernelBase 779 -imp 'GetTickCount64' GetTickCount64 KernelBase 780 +imp 'GetTickCount64' GetTickCount64 KernelBase 780 0 imp 'GetTimeFormatA' GetTimeFormatA KernelBase 781 imp 'GetTimeFormatAWorker' GetTimeFormatAWorker kernel32 782 imp 'GetTimeFormatEx' GetTimeFormatEx KernelBase 782 @@ -3587,7 +3587,7 @@ imp 'NtAssociateWaitCompletionPacket' NtAssociateWaitCompletionPacket ntdll imp 'NtCallEnclave' NtCallEnclave ntdll 249 imp 'NtCallbackReturn' NtCallbackReturn ntdll 250 3 imp 'NtCancelIoFile' NtCancelIoFile ntdll 251 2 -imp 'NtCancelIoFileEx' NtCancelIoFileEx ntdll 252 +imp 'NtCancelIoFileEx' NtCancelIoFileEx ntdll 252 3 imp 'NtCancelSynchronousIoFile' NtCancelSynchronousIoFile ntdll 253 imp 'NtCancelTimer' NtCancelTimer ntdll 254 imp 'NtCancelTimer2' NtCancelTimer2 ntdll 255 @@ -3622,7 +3622,7 @@ imp 'NtCreateJobObject' NtCreateJobObject ntdll 283 imp 'NtCreateJobSet' NtCreateJobSet ntdll 284 imp 'NtCreateKey' NtCreateKey ntdll 285 7 imp 'NtCreateKeyTransacted' NtCreateKeyTransacted ntdll 286 -imp 'NtCreateKeyedEvent' NtCreateKeyedEvent ntdll 287 +imp 'NtCreateKeyedEvent' NtCreateKeyedEvent ntdll 287 4 imp 'NtCreateLowBoxToken' NtCreateLowBoxToken ntdll 288 imp 'NtCreateMailslotFile' NtCreateMailslotFile ntdll 289 imp 'NtCreateMutant' NtCreateMutant ntdll 290 @@ -3862,7 +3862,7 @@ imp 'NtRecoverResourceManager' NtRecoverResourceManager ntdll 523 imp 'NtRecoverTransactionManager' NtRecoverTransactionManager ntdll 524 imp 'NtRegisterProtocolAddressInformation' NtRegisterProtocolAddressInformation ntdll 525 imp 'NtRegisterThreadTerminatePort' NtRegisterThreadTerminatePort ntdll 526 -imp 'NtReleaseKeyedEvent' NtReleaseKeyedEvent ntdll 527 +imp 'NtReleaseKeyedEvent' NtReleaseKeyedEvent ntdll 527 4 imp 'NtReleaseMutant' NtReleaseMutant ntdll 528 imp 'NtReleaseSemaphore' NtReleaseSemaphore ntdll 529 imp 'NtReleaseWorkerFactoryWorker' NtReleaseWorkerFactoryWorker ntdll 530 @@ -3983,7 +3983,7 @@ imp 'NtVdm64CreateProcessInternal' NtVdm64CreateProcessInternalW kernel32 imp 'NtVdmControl' NtVdmControl ntdll 644 imp 'NtWaitForAlertByThreadId' NtWaitForAlertByThreadId ntdll 645 imp 'NtWaitForDebugEvent' NtWaitForDebugEvent ntdll 646 -imp 'NtWaitForKeyedEvent' NtWaitForKeyedEvent ntdll 647 +imp 'NtWaitForKeyedEvent' NtWaitForKeyedEvent ntdll 647 4 imp 'NtWaitForMultipleObjects' NtWaitForMultipleObjects ntdll 648 imp 'NtWaitForMultipleObjects32' NtWaitForMultipleObjects32 ntdll 649 imp 'NtWaitForSingleObject' NtWaitForSingleObject ntdll 650 3 @@ -5338,7 +5338,7 @@ imp 'RtlNewSecurityObjectWithMultipleInheritance' RtlNewSecurityObjectWithMulti imp 'RtlNormalizeProcessParams' RtlNormalizeProcessParams ntdll 1244 imp 'RtlNormalizeString' RtlNormalizeString ntdll 1245 imp 'RtlNtPathNameToDosPathName' RtlNtPathNameToDosPathName ntdll 1246 -imp 'RtlNtStatusToDosError' RtlNtStatusToDosError ntdll 1247 +imp 'RtlNtStatusToDosError' RtlNtStatusToDosError ntdll 1247 1 imp 'RtlNtStatusToDosErrorNoTeb' RtlNtStatusToDosErrorNoTeb ntdll 1248 imp 'RtlNtdllName' RtlNtdllName ntdll 1249 imp 'RtlNumberGenericTableElements' RtlNumberGenericTableElements ntdll 1250 @@ -7881,7 +7881,7 @@ imp 'qsort$nt' qsort ntdll 2294 imp 'qsort_s$nt' qsort_s ntdll 2295 imp 'recv$nt' recv ws2_32 16 imp '__recvfrom$nt' recvfrom ws2_32 17 -imp 'select$nt' select ws2_32 18 +imp '__select$nt' select ws2_32 18 5 imp 'semDxTrimNotification' semDxTrimNotification gdi32 1965 imp 'send$nt' send ws2_32 19 imp '__sendto$nt' sendto ws2_32 20 @@ -7970,3 +7970,19 @@ imp 'wsprintfA' wsprintfA user32 2596 imp 'wsprintf' wsprintfW user32 2601 imp 'wvsprintfA' wvsprintfA user32 2602 imp 'wvsprintf' wvsprintfW user32 2603 + +imp 'InitializeCriticalSection' InitializeCriticalSection KernelBase 0 1 +imp 'EnterCriticalSection' EnterCriticalSection KernelBase 0 1 +imp 'LeaveCriticalSection' LeaveCriticalSection KernelBase 0 1 +imp 'TryEnterCriticalSection' TryEnterCriticalSection KernelBase 0 1 +imp 'DeleteCriticalSection' DeleteCriticalSection KernelBase 0 1 +imp 'InitializeCriticalSectionAndSpinCount' InitializeCriticalSectionAndSpinCount KernelBase 0 2 +imp 'SetCriticalSectionSpinCount' SetCriticalSectionSpinCount KernelBase 0 2 + +imp 'InitializeSRWLock' InitializeSRWLock KernelBase 1128 1 +imp 'AcquireSRWLockExclusive' AcquireSRWLockExclusive KernelBase 683 1 +imp 'AcquireSRWLockShared' AcquireSRWLockShared KernelBase 684 1 +imp 'ReleaseSRWLockExclusive' ReleaseSRWLockExclusive KernelBase 1341 1 +imp 'ReleaseSRWLockShared' ReleaseSRWLockShared KernelBase 1342 1 +imp 'TryAcquireSRWLockExclusive' TryAcquireSRWLockExclusive KernelBase 1467 1 +imp 'TryAcquireSRWLockShared' TryAcquireSRWLockShared KernelBase 1468 1 diff --git a/libc/nt/nt/debug.h b/libc/nt/nt/debug.h index 35a880c0..8cf191a7 100644 --- a/libc/nt/nt/debug.h +++ b/libc/nt/nt/debug.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_NT_NT_DEBUG_H_ #define COSMOPOLITAN_LIBC_NT_NT_DEBUG_H_ #include "libc/nt/enum/status.h" +#include "libc/nt/struct/context.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ /* ░░░░ @@ -32,8 +33,6 @@ COSMOPOLITAN_C_START_ Windows to the next, and possibly even between service packs for each release.” ──Quoth MSDN */ -struct NtContext; - NtStatus NtContinue(struct NtContext *Context, int32_t TestAlert); COSMOPOLITAN_C_END_ diff --git a/libc/nt/nt/file.h b/libc/nt/nt/file.h index 3b171510..2fb214b7 100644 --- a/libc/nt/nt/file.h +++ b/libc/nt/nt/file.h @@ -2,6 +2,7 @@ #define COSMOPOLITAN_LIBC_NT_NT_FILE_H_ #include "libc/nt/enum/fileinformationclass.h" #include "libc/nt/enum/status.h" +#include "libc/nt/struct/iostatusblock.h" #include "libc/nt/thunk/msabi.h" #include "libc/nt/typedef/ioapcroutine.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) @@ -38,6 +39,8 @@ COSMOPOLITAN_C_START_ struct NtIoStatusBlock; struct NtObjectAttributes; +NtStatus NtClose(int64_t handle); + NtStatus NtCreateFile(int64_t *out_FileHandle, uint32_t DesiredAccess, struct NtObjectAttributes *ObjectAttributes, struct NtIoStatusBlock *out_IoStatusBlock, @@ -45,32 +48,48 @@ NtStatus NtCreateFile(int64_t *out_FileHandle, uint32_t DesiredAccess, uint32_t ShareAccess, uint32_t CreateDisposition, uint32_t CreateOptions, void *opt_EaBuffer, uint32_t EaLength); + NtStatus NtReadFile(int64_t FileHandle, void *opt_Event, NtIoApcRoutine opt_ApcRoutine, void *opt_ApcContext, struct NtIoStatusBlock *out_IoStatusBlock, void *out_Buffer, uint32_t Length, int64_t *opt_ByteOffset, uint32_t *opt_Key); + NtStatus NtWriteFile(int64_t FileHandle, void *opt_Event, NtIoApcRoutine opt_ApcRoutine, void *opt_ApcContext, struct NtIoStatusBlock *out_IoStatusBlock, const void *Buffer, uint32_t Length, int64_t *opt_ByteOffset, uint32_t *opt_Key); -NtStatus NtClose(int64_t handle); + NtStatus NtDuplicateObject(int64_t SourceProcessHandle, void *SourceHandle, void *TargetProcessHandle, void **opt_out_TargetHandle, uint32_t DesiredAcess, uint32_t Atrributes, uint32_t options_t); + NtStatus NtQueryInformationFile(int64_t FileHandle, struct NtIoStatusBlock *out_IoStatusBlock, void *out_FileInformation, uint32_t FileInformationLength, uint32_t FileInformationClass); + NtStatus NtSetInformationFile(int64_t FileHandle, struct NtIoStatusBlock *out_IoStatusBlock, void *FileInformation, uint32_t FileInformationLength, uint32_t FileInformationClass); +NtStatus NtDeviceIoControlFile( + int64_t FileHandle, int64_t opt_Event, NtIoApcRoutine opt_ApcRoutine, + void *opt_ApcContext, struct NtIoStatusBlock *out_IoStatusBlock, + uint32_t IoControlCode, void *opt_InputBuffer, uint32_t InputBufferLength, + void *opt_out_OutputBuffer, uint32_t OutputBufferLength); + +NtStatus NtCancelIoFileEx(int64_t FileHandle, + struct NtIoStatusBlock *IoRequestToCancel, + struct NtIoStatusBlock *IoStatusBlock); + +NtStatus RtlNtStatusToDosError(NtStatus Status); + #if ShouldUseMsabiAttribute() #include "libc/nt/thunk/ntfile.inc" #endif /* ShouldUseMsabiAttribute() */ diff --git a/libc/nt/nt/key.h b/libc/nt/nt/key.h new file mode 100644 index 00000000..795e65c4 --- /dev/null +++ b/libc/nt/nt/key.h @@ -0,0 +1,48 @@ +#ifndef COSMOPOLITAN_LIBC_NT_NT_EVENT_H_ +#define COSMOPOLITAN_LIBC_NT_NT_EVENT_H_ +#include "libc/nt/enum/status.h" +#include "libc/nt/struct/objectattributes.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ +/* ░░░░ + ▒▒▒░░░▒▒▒▒▒▒▒▓▓▓░ + ▒▒▒▒░░░▒▒▒▒▒▒▓▓▓▓▓▓░ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▒▓░ + ▒▒▒░░░░▒▒▒▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▒ ▒▒▒▓▓█ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ░▒▒▒░░░░▒▒▒▒▒▒▓▓▓▓▓▓ █▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓░ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▒▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▒ + ▒▒▒▒▓▓ ▓▒▒▓▓▓▓ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▓ ▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓ + ░░░░░░░░░░░▒▒▒▒ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▒░░░░░░░░░░▒▒▒▒▒▓▓▓ ▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ░▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓░ ░▓███▓ + ▒▒░░░░░░░░░░▒▒▒▒▒▓▓░ ▒▓▓▓▒▒▒ ░▒▒▒▓ ████████████ + ▒▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▒▓▓▓▓▒▒▒▒▒▒▒▒░░░▒▒▒▒▒░ ░███ + ▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▓ ███ + ▒▒░░░░░░░░░░▒▒▒▒▒▒▓▓ ▒▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒ ▓██ + ▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▓ ▓██ + ▒▒░░░▒▒▒░░░▒▒░▒▒▒▓▓▒ ▒▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒ ███ + ░▒▓ ░▓▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▓ ▓██ +╔────────────────────────────────────────────────────────────────▀▀▀─────────│─╗ +│ cosmopolitan § new technology » beyond the pale » files ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│─╝ + “The functions and structures in [for these APIs] are internal to + the operating system and subject to change from one release of + Windows to the next, and possibly even between service packs for + each release.” ──Quoth MSDN */ + +NtStatus NtCreateKeyedEvent(int64_t *KeyedEventHandle, uint32_t DesiredAccess, + struct NtObjectAttributes *ObjectAttributes, + uint32_t Flags); + +NtStatus NtReleaseKeyedEvent(int64_t KeyedEventHandle, void *KeyValue, + bool32 Alertable, int64_t *Timeout); + +NtStatus NtWaitForKeyedEvent(int64_t KeyedEventHandle, void *KeyValue, + bool32 Alertable, int64_t *Timeout); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_NT_EVENT_H_ */ diff --git a/libc/nt/ntdll.h b/libc/nt/ntdll.h index c550c0a7..82950063 100644 --- a/libc/nt/ntdll.h +++ b/libc/nt/ntdll.h @@ -180,11 +180,6 @@ NtStatus NtFlushInstructionCache(int64_t ProcessHandle, void *opt_BaseAddress, size_t FlushSize); NtStatus NtQueryAttributesFile(const struct NtObjectAttributes *object, struct NtFileBasicInformation *file_information); -NtStatus NtDeviceIoControlFile( - int64_t FileHandle, void *opt_Event, NtIoApcRoutine opt_ApcRoutine, - void *opt_ApcContext, struct NtIoStatusBlock *out_IoStatusBlock, - uint32_t IoControlCode, void *opt_InputBuffer, uint32_t InputBufferLength, - void *opt_out_OutputBuffer, uint32_t OutputBufferLength); NtStatus NtQueryDirectoryFile( int64_t FileHandle, void *opt_Event, NtIoApcRoutine opt_ApcRoutine, void *opt_ApcContext, struct NtIoStatusBlock *out_IoStatusBlock, diff --git a/libc/nt/ntdll/NtCancelIoFileEx.s b/libc/nt/ntdll/NtCancelIoFileEx.s index 67026e07..6b304f14 100644 --- a/libc/nt/ntdll/NtCancelIoFileEx.s +++ b/libc/nt/ntdll/NtCancelIoFileEx.s @@ -1,2 +1,12 @@ .include "o/libc/nt/ntdllimport.inc" .ntimp NtCancelIoFileEx + + .text.windows +NtCancelIoFileEx: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_NtCancelIoFileEx(%rip),%rax + jmp __sysv2nt + .endfn NtCancelIoFileEx,globl + .previous diff --git a/libc/nt/ntdll/NtCreateKeyedEvent.s b/libc/nt/ntdll/NtCreateKeyedEvent.s index 9f592845..f3091149 100644 --- a/libc/nt/ntdll/NtCreateKeyedEvent.s +++ b/libc/nt/ntdll/NtCreateKeyedEvent.s @@ -1,2 +1,12 @@ .include "o/libc/nt/ntdllimport.inc" .ntimp NtCreateKeyedEvent + + .text.windows +NtCreateKeyedEvent: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_NtCreateKeyedEvent(%rip),%rax + jmp __sysv2nt + .endfn NtCreateKeyedEvent,globl + .previous diff --git a/libc/nt/ntdll/NtReleaseKeyedEvent.s b/libc/nt/ntdll/NtReleaseKeyedEvent.s index 83210f8c..8875cbbc 100644 --- a/libc/nt/ntdll/NtReleaseKeyedEvent.s +++ b/libc/nt/ntdll/NtReleaseKeyedEvent.s @@ -1,2 +1,12 @@ .include "o/libc/nt/ntdllimport.inc" .ntimp NtReleaseKeyedEvent + + .text.windows +NtReleaseKeyedEvent: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_NtReleaseKeyedEvent(%rip),%rax + jmp __sysv2nt + .endfn NtReleaseKeyedEvent,globl + .previous diff --git a/libc/nt/ntdll/NtWaitForKeyedEvent.s b/libc/nt/ntdll/NtWaitForKeyedEvent.s index 032ab561..d432d906 100644 --- a/libc/nt/ntdll/NtWaitForKeyedEvent.s +++ b/libc/nt/ntdll/NtWaitForKeyedEvent.s @@ -1,2 +1,12 @@ .include "o/libc/nt/ntdllimport.inc" .ntimp NtWaitForKeyedEvent + + .text.windows +NtWaitForKeyedEvent: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_NtWaitForKeyedEvent(%rip),%rax + jmp __sysv2nt + .endfn NtWaitForKeyedEvent,globl + .previous diff --git a/libc/nt/ntdll/RtlNtStatusToDosError.s b/libc/nt/ntdll/RtlNtStatusToDosError.s index 3acd7a7c..615c4e95 100644 --- a/libc/nt/ntdll/RtlNtStatusToDosError.s +++ b/libc/nt/ntdll/RtlNtStatusToDosError.s @@ -1,2 +1,15 @@ .include "o/libc/nt/ntdllimport.inc" .ntimp RtlNtStatusToDosError + + .text.windows +RtlNtStatusToDosError: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_RtlNtStatusToDosError(%rip) + leave + ret + .endfn RtlNtStatusToDosError,globl + .previous diff --git a/libc/nt/struct/afd.h b/libc/nt/struct/afd.h new file mode 100644 index 00000000..94c151cc --- /dev/null +++ b/libc/nt/struct/afd.h @@ -0,0 +1,22 @@ +#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_AFD_H_ +#define COSMOPOLITAN_LIBC_NT_STRUCT_AFD_H_ +#include "libc/nt/enum/status.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct NtAfdPollHandleInfo { + int64_t Handle; + uint32_t Events; + NtStatus Status; +}; + +struct NtAfdPollInfo { + int64_t Timeout; + uint32_t NumberOfHandles; + uint32_t Exclusive; + struct NtAfdPollHandleInfo Handles[1]; +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_AFD_H_ */ diff --git a/libc/nt/struct/criticalsection.h b/libc/nt/struct/criticalsection.h index 289c86a9..350a061b 100644 --- a/libc/nt/struct/criticalsection.h +++ b/libc/nt/struct/criticalsection.h @@ -1,9 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_NT_STRUCT_CRITICALSECTION_H_ #define COSMOPOLITAN_LIBC_NT_STRUCT_CRITICALSECTION_H_ +#include "libc/nt/struct/criticalsectiondebug.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct NtCriticalSectionDebug; - struct NtCriticalSection { struct NtCriticalSectionDebug *DebugInfo; int32_t LockCount; diff --git a/libc/nt/struct/criticalsectiondebug.h b/libc/nt/struct/criticalsectiondebug.h index 6a62c315..8d5cae63 100644 --- a/libc/nt/struct/criticalsectiondebug.h +++ b/libc/nt/struct/criticalsectiondebug.h @@ -1,10 +1,9 @@ #ifndef COSMOPOLITAN_LIBC_NT_STRUCT_CRITICALSECTIONDEBUG_H_ #define COSMOPOLITAN_LIBC_NT_STRUCT_CRITICALSECTIONDEBUG_H_ +#include "libc/nt/struct/criticalsection.h" #include "libc/nt/struct/linkedlist.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct NtCriticalSection; - struct NtCriticalSectionDebug { uint16_t Type; uint16_t CreatorBackTraceIndex; diff --git a/libc/nt/struct/objectattributes.h b/libc/nt/struct/objectattributes.h index 55de4184..0dc240f8 100644 --- a/libc/nt/struct/objectattributes.h +++ b/libc/nt/struct/objectattributes.h @@ -1,13 +1,12 @@ #ifndef COSMOPOLITAN_LIBC_NT_I_OBJECTATTRIBUTES_H_ #define COSMOPOLITAN_LIBC_NT_I_OBJECTATTRIBUTES_H_ +#include "libc/nt/struct/rtluserprocessparameters.h" +#include "libc/nt/struct/securityattributes.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct NtUnicodeString; -struct NtSecurityDescriptor; - struct NtObjectAttributes { uint32_t Length; - void *RootDirectory; + int64_t RootDirectory; struct NtUnicodeString *ObjectName; uint32_t Attributes; /* OBJ_INHERIT, etc. */ struct NtSecurityDescriptor *SecurityDescriptor; diff --git a/libc/nt/struct/rtluserprocessparameters.h b/libc/nt/struct/rtluserprocessparameters.h index fd5ca717..b5d0d45b 100644 --- a/libc/nt/struct/rtluserprocessparameters.h +++ b/libc/nt/struct/rtluserprocessparameters.h @@ -1,9 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_NT_STRUCT_RTLUSERPROCESSPARAMETERS_H_ #define COSMOPOLITAN_LIBC_NT_STRUCT_RTLUSERPROCESSPARAMETERS_H_ +#include "libc/nt/struct/unicodestring.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct NtUnicodeString; - struct NtRtlUserProcessParameters { uint32_t MaximumLength; uint32_t Length; diff --git a/libc/nt/struct/securityattributes.h b/libc/nt/struct/securityattributes.h index 7e388bbf..2529aa86 100644 --- a/libc/nt/struct/securityattributes.h +++ b/libc/nt/struct/securityattributes.h @@ -1,9 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_NT_STRUCT_SECURITYATTRIBUTES_H_ #define COSMOPOLITAN_LIBC_NT_STRUCT_SECURITYATTRIBUTES_H_ +#include "libc/nt/struct/securitydescriptor.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct NtSecurityDescriptor; - struct NtSecurityAttributes { uint32_t nLength; struct NtSecurityDescriptor *lpSecurityDescriptor; diff --git a/libc/nt/struct/securitydescriptor.h b/libc/nt/struct/securitydescriptor.h index ad1e6392..2fbc853c 100644 --- a/libc/nt/struct/securitydescriptor.h +++ b/libc/nt/struct/securitydescriptor.h @@ -1,9 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_NT_STRUCT_SECURITYDESCRIPTOR_H_ #define COSMOPOLITAN_LIBC_NT_STRUCT_SECURITYDESCRIPTOR_H_ +#include "libc/nt/struct/acl.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct NtAcl; - struct NtSecurityDescriptor { uint8_t Revision; uint8_t Sbz1; diff --git a/libc/nt/synchronization.h b/libc/nt/synchronization.h index 05d3ef28..9e73eea5 100644 --- a/libc/nt/synchronization.h +++ b/libc/nt/synchronization.h @@ -32,6 +32,21 @@ COSMOPOLITAN_C_START_ │ cosmopolitan § new technology » synchronization ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ +#define InterlockedAdd(PTR, VAL) \ + ({ \ + typeof(*(PTR)) Res; \ + typeof(Res) Val = (VAL); \ + asm volatile("xadd\t%0,%1" : "=r"(Res), "+m"(*(PTR)) : "0"(Val)); \ + Res + Val; \ + }) + +#define InterlockedExchange(PTR, VAL) \ + ({ \ + typeof(*(PTR)) Res = (VAL); \ + asm volatile("xchg\t%0,%1" : "+r"(Res), "+m"(*(PTR))); \ + Res; \ + }) + typedef void (*NtTimerapcroutine)(void *lpArgToCompletionRoutine, uint32_t dwTimerLowValue, uint32_t dwTimerHighValue); @@ -78,6 +93,16 @@ int32_t InitializeCriticalSectionAndSpinCount( uint32_t SetCriticalSectionSpinCount( struct NtCriticalSection *lpCriticalSection, uint32_t dwSpinCount); +void InitializeSRWLock(intptr_t *); +void AcquireSRWLockExclusive(intptr_t *); +void AcquireSRWLockShared(intptr_t *); +void ReleaseSRWLockExclusive(intptr_t *); +void ReleaseSRWLockShared(intptr_t *); +void TryAcquireSRWLockExclusive(intptr_t *); +void TryAcquireSRWLockShared(intptr_t *); + +uint64_t GetTickCount64(void); + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_NT_SYNCHRONIZATION_H_ */ diff --git a/libc/nt/typedef/exceptionhandler.h b/libc/nt/typedef/exceptionhandler.h index d45afe2a..ac9da9d4 100644 --- a/libc/nt/typedef/exceptionhandler.h +++ b/libc/nt/typedef/exceptionhandler.h @@ -1,12 +1,11 @@ #ifndef COSMOPOLITAN_LIBC_NT_TYPEDEF_EXCEPTIONHANDLER_H_ #define COSMOPOLITAN_LIBC_NT_TYPEDEF_EXCEPTIONHANDLER_H_ +#include "libc/nt/struct/context.h" +#include "libc/nt/struct/exceptionframe.h" +#include "libc/nt/struct/ntexceptionrecord.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -struct NtExceptionFrame; -struct NtExceptionRecord; -struct NtContext; - typedef unsigned (*NtExceptionHandler)(struct NtExceptionRecord *, struct NtExceptionFrame *, struct NtContext *, void *); diff --git a/libc/nt/typedef/ioapcroutine.h b/libc/nt/typedef/ioapcroutine.h index 24436531..f74bf00a 100644 --- a/libc/nt/typedef/ioapcroutine.h +++ b/libc/nt/typedef/ioapcroutine.h @@ -1,9 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_NT_TYPEDEF_IOAPCROUTINE_H_ #define COSMOPOLITAN_LIBC_NT_TYPEDEF_IOAPCROUTINE_H_ +#include "libc/nt/struct/iostatusblock.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct NtIoStatusBlock; - typedef void (*NtIoApcRoutine)(void *ApcContext, struct NtIoStatusBlock *IoStatusBlock, uint32_t Reserved); diff --git a/libc/nt/winsock.h b/libc/nt/winsock.h index 5a01ec07..2d92713b 100644 --- a/libc/nt/winsock.h +++ b/libc/nt/winsock.h @@ -43,6 +43,9 @@ │ cosmopolitan § new technology » winsock ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ +#define kNtCompEqual 0 +#define kNtCompNotless 1 + #define kNtWsaFlagOverlapped 0x01 #define kNtWsaFlagNoHandleInherit 0x80 @@ -83,63 +86,31 @@ #define kNtSioUdpConnreset 0x9800000Cu #define kNtSioUdpNetreset 0x9800000Fu +#define kNtNspNotifyImmediately 0 +#define kNtNspNotifyHwnd 1 +#define kNtNspNotifyEvent 2 +#define kNtNspNotifyPort 3 +#define kNtNspNotifyApc 4 + #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define kNtWsaidAcceptex \ - { \ - 0xB5367DF1, 0xCBAC, 0x11CF, { \ - 0x95, 0xCA, 0x00, 0x80, 0x5F, 0x48, 0xA1, 0x92 \ - } \ - } - -#define kNtWsaidConnectex \ - { \ - 0x25A207B9, 0xDDF3, 0x4660, { \ - 0x8E, 0xE9, 0x76, 0xE5, 0x8C, 0x74, 0x06, 0x3E \ - } \ - } - -#define kNtWsaidDisconnectex \ - { \ - 0x7FDA2E11, 0x8630, 0x436F, { \ - 0xA0, 0x31, 0xF5, 0x36, 0xA6, 0xEE, 0xC1, 0x57 \ - } \ - } - -#define kNtWsaidTransmitfile \ - { \ - 0xB5367DF0, 0xCBAC, 0x11CF, { \ - 0x95, 0xCA, 0x00, 0x80, 0x5F, 0x48, 0xA1, 0x92 \ - } \ - } - -enum NtWsaEComparator { COMP_EQUAL, COMP_NOTLESS }; - -enum NtWsaCompletionType { - kNtNspNotifyImmediately = 0, - kNtNspNotifyHwnd, - kNtNspNotifyEvent, - kNtNspNotifyPort, - kNtNspNotifyApc, -}; - -struct timeval$nt { +struct NtTimeval { int32_t tv_sec; /* [sic] */ int32_t tv_usec; }; -struct iovec$nt { +struct NtIovec { uint32_t len; char *buf; }; -struct msghdr$nt { +struct NtMsgHdr { struct sockaddr *name; int32_t namelen; - struct iovec$nt *lpBuffers; + struct NtIovec *lpBuffers; uint32_t dwBufferCount; - struct iovec$nt Control; + struct NtIovec Control; uint32_t dwFlags; }; @@ -223,12 +194,12 @@ struct NtFlowSpec { struct NtQos { struct NtFlowSpec SendingFlowspec; struct NtFlowSpec ReceivingFlowspec; - struct iovec$nt ProviderSpecific; + struct NtIovec ProviderSpecific; }; struct NtWsaVersion { uint32_t dwVersion; - enum NtWsaEComparator ecHow; + int ecHow; }; struct NtAfProtocols { @@ -303,9 +274,9 @@ struct NtTransmitFileBuffers { }; typedef int (*NtConditionProc)( - const struct iovec$nt *lpCallerId, const struct iovec$nt *lpCallerData, + const struct NtIovec *lpCallerId, const struct NtIovec *lpCallerData, struct NtQos *inout_lpSQOS, struct NtQos *inout_lpGQOS, - const struct iovec$nt *lpCalleeId, const struct iovec$nt *lpCalleeData, + const struct NtIovec *lpCalleeId, const struct NtIovec *lpCalleeData, uint32_t *out_group, const uint32_t *dwCallbackData); typedef void (*NtWsaOverlappedCompletionRoutine)( @@ -313,7 +284,7 @@ typedef void (*NtWsaOverlappedCompletionRoutine)( const struct NtOverlapped *lpOverlapped, uint32_t dwFlags); struct NtWsaCompletion { - enum NtWsaCompletionType Type; + int Type; union { struct { int64_t hWnd; @@ -335,6 +306,11 @@ struct NtWsaCompletion { } Parameters; }; +struct NtFdSet { + uint32_t fd_count; + int64_t fd_array[64]; +}; + /** * Winsock2 prototypes. * @@ -359,14 +335,16 @@ int __ioctlsocket$nt(uint64_t, int32_t, uint32_t *); int __listen$nt(uint64_t, int); int __setsockopt$nt(uint64_t, int, int, const void *, int); int __shutdown$nt(uint64_t, int); +int __select$nt(int, struct NtFdSet *, struct NtFdSet *, struct NtFdSet *, + struct NtTimeval *); uint64_t WSASocket(int af, int type, int protocol, const struct NtWsaProtocolInfo *opt_lpProtocolInfo, const uint32_t opt_group, uint32_t dwFlags) nodiscard; int WSAConnect(uint64_t s, const struct sockaddr *name, const int namelen, - const struct iovec$nt *opt_lpCallerData, - struct iovec$nt *opt_out_lpCalleeData, + const struct NtIovec *opt_lpCallerData, + struct NtIovec *opt_out_lpCalleeData, const struct NtQos *opt_lpSQOS, const struct NtQos *opt_lpGQOS) paramsnonnull((2)); @@ -376,7 +354,7 @@ bool32 WSAConnectByName(uint64_t s, const char16_t *nodename, struct sockaddr *out_LocalAddress, uint32_t *opt_inout_RemoteAddressLength, struct sockaddr *out_RemoteAddress, - const struct timeval$nt *opt_timeout, + const struct NtTimeval *opt_timeout, struct NtOverlapped *__Reserved) paramsnonnull((2, 3)); bool32 WSAConnectByList(uint64_t s, @@ -385,7 +363,7 @@ bool32 WSAConnectByList(uint64_t s, struct sockaddr *out_LocalAddress, uint32_t *opt_inout_RemoteAddressLength, struct sockaddr *out_RemoteAddress, - const struct timeval$nt *opt_timeout, + const struct NtTimeval *opt_timeout, struct NtOverlapped *__Reserved) paramsnonnull((2)); int64_t WSAAccept(uint64_t s, struct sockaddr *out_addr, @@ -394,19 +372,19 @@ int64_t WSAAccept(uint64_t s, struct sockaddr *out_addr, const uint32_t *opt_dwCallbackData) paramsnonnull((2)) nodiscard; -int WSASend(uint64_t s, const struct iovec$nt *lpBuffers, - uint32_t dwBufferCount, uint32_t *opt_out_lpNumberOfBytesSent, - uint32_t dwFlags, struct NtOverlapped *opt_inout_lpOverlapped, +int WSASend(uint64_t s, const struct NtIovec *lpBuffers, uint32_t dwBufferCount, + uint32_t *opt_out_lpNumberOfBytesSent, uint32_t dwFlags, + struct NtOverlapped *opt_inout_lpOverlapped, const NtWsaOverlappedCompletionRoutine opt_lpCompletionRoutine) paramsnonnull((2)); -int WSASendMsg(int64_t Handle, const struct msghdr$nt *lpMsg, uint32_t dwFlags, +int WSASendMsg(int64_t Handle, const struct NtMsgHdr *lpMsg, uint32_t dwFlags, uint32_t *opt_out_lpNumberOfBytesSent, struct NtOverlapped *opt_inout_lpOverlapped, const NtWsaOverlappedCompletionRoutine opt_lpCompletionRoutine) paramsnonnull((2)); -int WSASendTo(uint64_t s, const struct iovec$nt *lpBuffers, +int WSASendTo(uint64_t s, const struct NtIovec *lpBuffers, uint32_t dwBufferCount, uint32_t *opt_out_lpNumberOfBytesSent /* opt if !overlapped */, uint32_t dwFlags, const void *opt_tosockaddr, @@ -418,14 +396,14 @@ int WSASendTo(uint64_t s, const struct iovec$nt *lpBuffers, int WSAPoll(struct pollfd$nt *inout_fdArray, uint32_t nfds, signed timeout_ms) paramsnonnull(); -int WSARecv(uint64_t s, const struct iovec$nt *out_lpBuffers, +int WSARecv(uint64_t s, const struct NtIovec *out_lpBuffers, uint32_t dwBufferCount, uint32_t *opt_out_lpNumberOfBytesRecvd, uint32_t *inout_lpFlags, struct NtOverlapped *opt_inout_lpOverlapped, const NtWsaOverlappedCompletionRoutine opt_lpCompletionRoutine) paramsnonnull((2, 5)); -int WSARecvFrom(uint64_t s, const struct iovec$nt *out_lpBuffers, +int WSARecvFrom(uint64_t s, const struct NtIovec *out_lpBuffers, uint32_t dwBufferCount, uint32_t *opt_out_lpNumberOfBytesRecvd, uint32_t *inout_lpFlags, void *out_fromsockaddr, uint32_t *inout_fromsockaddrlen, @@ -434,7 +412,7 @@ int WSARecvFrom(uint64_t s, const struct iovec$nt *out_lpBuffers, paramsnonnull((2, 5, 6, 7)); int WSARecvDisconnect(uint64_t s, - const struct iovec$nt *opt_lpInboundDisconnectData); + const struct NtIovec *opt_lpInboundDisconnectData); int WSADuplicateSocket(uint64_t s, uint32_t dwProcessId, struct NtWsaProtocolInfo *out_lpProtocolInfo) @@ -445,7 +423,7 @@ int WSAIoctl(uint64_t s, uint32_t dwIoControlCode, const void *lpvInBuffer, uint32_t *out_lpcbBytesReturned, struct NtOverlapped *opt_inout_lpOverlapped, const NtWsaOverlappedCompletionRoutine opt_lpCompletionRoutine) - paramsnonnull((3, 5, 7)); + paramsnonnull((5, 7)); int WSANSPIoctl(int64_t hLookup, uint32_t dwControlCode, const void *lpvInBuffer, uint32_t cbInBuffer, @@ -479,12 +457,12 @@ int WSAEnumProtocols(const int32_t *opt_lpiProtocols, struct NtWsaProtocolInfo *out_lpProtocolBuffer, uint32_t *inout_lpdwBufferLength) paramsnonnull(); -bool32 WSAGetQOSByName(uint64_t s, const struct iovec$nt *lpQOSName, +bool32 WSAGetQOSByName(uint64_t s, const struct NtIovec *lpQOSName, struct NtQos *out_lpQOS) paramsnonnull(); uint64_t WSAJoinLeaf(uint64_t s, const struct sockaddr *name, const int namelen, - const struct iovec$nt *opt_lpCallerData, - struct iovec$nt *opt_out_lpCalleeData, + const struct NtIovec *opt_lpCallerData, + struct NtIovec *opt_out_lpCalleeData, const struct NtQos *opt_lpSQOS, const struct NtQos *opt_lpGQOS, uint32_t dwFlags) paramsnonnull((2, 4)); diff --git a/libc/nt/ws2_32/select.s b/libc/nt/ws2_32/select.s index d8b67566..e38fa07f 100644 --- a/libc/nt/ws2_32/select.s +++ b/libc/nt/ws2_32/select.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp ws2_32,__imp_select,select,18 + + .text.windows +__select$nt: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_select(%rip),%rax + jmp __sysv2nt6 + .endfn __select$nt,globl + .previous diff --git a/libc/runtime/directmapnt.c b/libc/runtime/directmapnt.c index dea746d3..141ecaa1 100644 --- a/libc/runtime/directmapnt.c +++ b/libc/runtime/directmapnt.c @@ -18,6 +18,7 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/nt/enum/pageflags.h" #include "libc/nt/memory.h" #include "libc/nt/runtime.h" #include "libc/runtime/directmap.h" @@ -26,26 +27,24 @@ textwindows struct DirectMap DirectMapNt(void *addr, size_t size, unsigned prot, unsigned flags, int fd, int64_t off) { int64_t handle; struct DirectMap res; - uint32_t protect, access; if (fd != -1) { handle = g_fds.p[fd].handle; } else { handle = kNtInvalidHandleValue; } - protect = prot2nt(prot, flags); - access = fprot2nt(prot, flags); - if ((res.maphandle = - CreateFileMappingNuma(handle, NULL, protect, size >> 32, size, NULL, - kNtNumaNoPreferredNode))) { - if (!(res.addr = MapViewOfFileExNuma(res.maphandle, access, off >> 32, off, - size, addr, kNtNumaNoPreferredNode))) { + if ((res.maphandle = CreateFileMappingNuma( + handle, NULL, kNtPageExecuteReadwrite, size >> 32, size, NULL, + kNtNumaNoPreferredNode))) { + if (!(res.addr = MapViewOfFileExNuma(res.maphandle, fprot2nt(prot, flags), + off >> 32, off, size, addr, + kNtNumaNoPreferredNode))) { CloseHandle(res.maphandle); res.maphandle = kNtInvalidHandleValue; - res.addr = (void *)(intptr_t)winerr(); + res.addr = (void *)(intptr_t)__winerr(); } } else { res.maphandle = kNtInvalidHandleValue; - res.addr = (void *)(intptr_t)winerr(); + res.addr = (void *)(intptr_t)__winerr(); } return res; } diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index fb722582..8f434e15 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -114,8 +114,8 @@ static textwindows void NormalizeCmdExe(void) { static textwindows char *AllocateMemory(void *addr, size_t size, int64_t *h) { return MapViewOfFileExNuma( - (*h = CreateFileMappingNuma(-1, NULL, pushpop(kNtPageReadwrite), 0, size, - NULL, kNtNumaNoPreferredNode)), + (*h = CreateFileMappingNuma(-1, NULL, pushpop(kNtPageExecuteReadwrite), 0, + size, NULL, kNtNumaNoPreferredNode)), kNtFileMapRead | kNtFileMapWrite, 0, 0, size, addr, kNtNumaNoPreferredNode); } diff --git a/libc/sock/accept-nt.c b/libc/sock/accept-nt.c index 577daa23..52109840 100644 --- a/libc/sock/accept-nt.c +++ b/libc/sock/accept-nt.c @@ -22,6 +22,7 @@ #include "libc/nt/files.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/consts/fio.h" #include "libc/sysv/consts/sock.h" #include "libc/sysv/errfuns.h" @@ -31,20 +32,20 @@ textwindows int accept$nt(struct Fd *fd, void *addr, uint32_t *addrsize, int client; uint32_t yes; assert(fd->kind == kFdSocket); - if ((client = createfd()) == -1) return -1; + if ((client = __getemptyfd()) == -1) return -1; if ((g_fds.p[client].handle = WSAAccept(fd->handle, addr, (int32_t *)addrsize, NULL, NULL)) != -1) { if (flags & SOCK_NONBLOCK) { yes = 1; if (__ioctlsocket$nt(g_fds.p[client].handle, FIONBIO, &yes) == -1) { __closesocket$nt(g_fds.p[client].handle); - return winsockerr(); + return __winsockerr(); } } g_fds.p[client].kind = kFdSocket; g_fds.p[client].flags = flags; return client; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/accept.c b/libc/sock/accept.c index 46b6e995..2bf6b460 100644 --- a/libc/sock/accept.c +++ b/libc/sock/accept.c @@ -17,11 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" -#include "libc/dce.h" -#include "libc/sock/internal.h" #include "libc/sock/sock.h" -#include "libc/sysv/errfuns.h" /** * Creates client socket file descriptor for incoming connection. @@ -33,11 +29,5 @@ * @asyncsignalsafe */ int accept(int fd, void *out_addr, uint32_t *inout_addrsize) { - if (!IsWindows()) { - return accept$sysv(fd, out_addr, inout_addrsize); - } else if (isfdkind(fd, kFdSocket)) { - return accept$nt(&g_fds.p[fd], out_addr, inout_addrsize, 0); - } else { - return ebadf(); - } + return accept4(fd, out_addr, inout_addrsize, 0); } diff --git a/libc/sock/accept4.c b/libc/sock/accept4.c index 71a54952..526699e1 100644 --- a/libc/sock/accept4.c +++ b/libc/sock/accept4.c @@ -32,13 +32,14 @@ * @param flags can have SOCK_{CLOEXEC,NONBLOCK}, which may apply to * both the newly created socket and the server one * @return client fd which needs close(), or -1 w/ errno + * @asyncsignalsafe */ int accept4(int fd, void *out_addr, uint32_t *inout_addrsize, int flags) { if (!out_addr) return efault(); if (!inout_addrsize) return efault(); if (!IsWindows()) { return accept4$sysv(fd, out_addr, inout_addrsize, flags); - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return accept$nt(&g_fds.p[fd], out_addr, inout_addrsize, flags); } else { return ebadf(); diff --git a/libc/sock/bind-nt.c b/libc/sock/bind-nt.c index 9d1d2ee5..43c53173 100644 --- a/libc/sock/bind-nt.c +++ b/libc/sock/bind-nt.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" /** @@ -38,6 +39,6 @@ textwindows int bind$nt(struct Fd *fd, const void *addr, uint32_t addrsize) { if (__bind$nt(fd->handle, addr, addrsize) != -1) { return 0; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/bind.c b/libc/sock/bind.c index a5390433..e9a33b02 100644 --- a/libc/sock/bind.c +++ b/libc/sock/bind.c @@ -48,7 +48,7 @@ int bind(int fd, const void *addr, uint32_t addrsize) { sockaddr2bsd(&addr2); return bind$sysv(fd, &addr2, addrsize); } - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return bind$nt(&g_fds.p[fd], addr, addrsize); } else { return ebadf(); diff --git a/libc/sock/closesocket-nt.c b/libc/sock/closesocket-nt.c index eee70c8b..18d0cfea 100644 --- a/libc/sock/closesocket-nt.c +++ b/libc/sock/closesocket-nt.c @@ -21,16 +21,17 @@ #include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" textwindows int closesocket$nt(int fd) { int rc; - if (!isfdkind(fd, kFdSocket)) return ebadf(); + if (!__isfdkind(fd, kFdSocket)) return ebadf(); if (__closesocket$nt(g_fds.p[fd].handle) != -1) { rc = 0; } else { - rc = winsockerr(); + rc = __winsockerr(); } - removefd(fd); + __removefd(fd); return rc; } diff --git a/libc/sock/connect-nt.c b/libc/sock/connect-nt.c index 628d22a6..bb4dd941 100644 --- a/libc/sock/connect-nt.c +++ b/libc/sock/connect-nt.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" textwindows int connect$nt(struct Fd *fd, const void *addr, uint32_t addrsize) { diff --git a/libc/sock/connect.c b/libc/sock/connect.c index 8a4e7c88..018faf95 100644 --- a/libc/sock/connect.c +++ b/libc/sock/connect.c @@ -37,7 +37,7 @@ int connect(int fd, const void *addr, uint32_t addrsize) { if (!addr) return efault(); if (!IsWindows()) { return connect$sysv(fd, addr, addrsize); - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return connect$nt(&g_fds.p[fd], addr, addrsize); } else { return ebadf(); diff --git a/libc/sock/epoll.c b/libc/sock/epoll.c new file mode 100644 index 00000000..1964e90c --- /dev/null +++ b/libc/sock/epoll.c @@ -0,0 +1,1506 @@ +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ wepoll │ +│ https://github.com/piscisaureus/wepoll │ +│ │ +│ Copyright 2012-2020, Bert Belder │ +│ All rights reserved. │ +│ │ +│ Redistribution and use in source and binary forms, with or without │ +│ modification, are permitted provided that the following conditions are │ +│ met: │ +│ │ +│ * Redistributions of source code must retain the above copyright │ +│ notice, this list of conditions and the following disclaimer. │ +│ │ +│ * Redistributions in binary form must reproduce the above copyright │ +│ notice, this list of conditions and the following disclaimer in the │ +│ documentation and/or other materials provided with the distribution. │ +│ │ +│ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS │ +│ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT │ +│ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR │ +│ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT │ +│ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, │ +│ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT │ +│ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, │ +│ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY │ +│ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT │ +│ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE │ +│ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/calls/internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/limits.h" +#include "libc/macros.h" +#include "libc/mem/mem.h" +#include "libc/nt/enum/accessmask.h" +#include "libc/nt/enum/afd.h" +#include "libc/nt/enum/filesharemode.h" +#include "libc/nt/enum/ioctl.h" +#include "libc/nt/enum/keyedevent.h" +#include "libc/nt/enum/sio.h" +#include "libc/nt/enum/status.h" +#include "libc/nt/enum/wait.h" +#include "libc/nt/errors.h" +#include "libc/nt/files.h" +#include "libc/nt/iocp.h" +#include "libc/nt/nt/file.h" +#include "libc/nt/nt/key.h" +#include "libc/nt/ntdll.h" +#include "libc/nt/process.h" +#include "libc/nt/runtime.h" +#include "libc/nt/struct/afd.h" +#include "libc/nt/struct/criticalsection.h" +#include "libc/nt/struct/objectattributes.h" +#include "libc/nt/struct/overlappedentry.h" +#include "libc/nt/struct/unicodestring.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/winsock.h" +#include "libc/runtime/runtime.h" +#include "libc/sock/epoll.h" +#include "libc/sock/internal.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/epoll.h" +#include "libc/sysv/errfuns.h" + +/** + * @fileoverview epoll + * + * This is an alternative to poll() that's popular for event driven + * network servers that want >10,000 sockets per machine and don't do + * cpu bound computations that would otherwise block the event loop. + * + * This works on Linux and is polyfilled on Windows. It's worth noting + * that these polyfills depend on Microsoft's internal APIs. However + * these particular NTDLL APIs are also used by libuv, nodejs, etc. so + * we're reasonably certain Microsoft has compatibility policies in + * place where they've promised not to break them. + * + * TODO(jart): Polyfill kqueue for XNU/FreeBSD/OpenBSD. + */ + +asm(".ident\t\"\\n\\n\ +wepoll (BSD-2)\\n\ +Copyright 2012-2020 Bert Belder\\n\ +https://github.com/piscisaureus/wepoll\""); +asm(".include \"libc/disclaimer.inc\""); + +#define MAX_GROUP_SIZE 32 + +#define REFLOCK__REF 0x00000001 +#define REFLOCK__REF_MASK 0x0fffffff +#define REFLOCK__DESTROY 0x10000000 +#define REFLOCK__DESTROY_MASK 0xf0000000 +#define REFLOCK__POISON 0x300dead0 + +#define KNOWN_EVENTS \ + (EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | \ + EPOLLRDBAND | EPOLLWRNORM | EPOLLWRBAND | EPOLLMSG | EPOLLRDHUP) + +#define RTL_CONSTANT_STRING(s) \ + { sizeof(s) - sizeof((s)[0]), sizeof(s), s } + +#define RTL_CONSTANT_OBJECT_ATTRIBUTES(ObjectName, Attributes) \ + { sizeof(struct NtObjectAttributes), 0, ObjectName, Attributes, NULL, NULL } + +#define RETURN_MAP_ERROR(value) \ + do { \ + err_map_win_error(); \ + return value; \ + } while (0) + +#define RETURN_SET_ERROR(value, error) \ + do { \ + err_set_win_error(error); \ + return value; \ + } while (0) + +#define CONTAINOF(ptr, type, member) \ + ((type *)((uintptr_t)(ptr)-offsetof(type, member))) + +#define TREE__ROTATE(cis, trans) \ + struct TreeNode *p = node; \ + struct TreeNode *q = node->trans; \ + struct TreeNode *parent = p->parent; \ + if (parent) { \ + if (parent->left == p) \ + parent->left = q; \ + else \ + parent->right = q; \ + } else { \ + tree->root = q; \ + } \ + q->parent = parent; \ + p->parent = q; \ + p->trans = q->cis; \ + if (p->trans) p->trans->parent = p; \ + q->cis = p; + +#define TREE__INSERT_OR_DESCEND(side) \ + if (parent->side) { \ + parent = parent->side; \ + } else { \ + parent->side = node; \ + break; \ + } + +#define TREE__REBALANCE_AFTER_INSERT(cis, trans) \ + struct TreeNode *grandparent = parent->parent; \ + struct TreeNode *uncle = grandparent->trans; \ + if (uncle && uncle->red) { \ + parent->red = uncle->red = false; \ + grandparent->red = true; \ + node = grandparent; \ + } else { \ + if (node == parent->trans) { \ + tree__rotate_##cis(tree, parent); \ + node = parent; \ + parent = node->parent; \ + } \ + parent->red = false; \ + grandparent->red = true; \ + tree__rotate_##trans(tree, grandparent); \ + } + +#define TREE__REBALANCE_AFTER_REMOVE(cis, trans) \ + struct TreeNode *sibling = parent->trans; \ + if (sibling->red) { \ + sibling->red = false; \ + parent->red = true; \ + tree__rotate_##cis(tree, parent); \ + sibling = parent->trans; \ + } \ + if ((sibling->left && sibling->left->red) || \ + (sibling->right && sibling->right->red)) { \ + if (!sibling->trans || !sibling->trans->red) { \ + sibling->cis->red = false; \ + sibling->red = true; \ + tree__rotate_##trans(tree, sibling); \ + sibling = parent->trans; \ + } \ + sibling->red = parent->red; \ + parent->red = sibling->trans->red = false; \ + tree__rotate_##cis(tree, parent); \ + node = tree->root; \ + break; \ + } \ + sibling->red = true; + +#define tree_root(t) (t)->root +#define port_state_to_handle_tree_node(p) (&(p)->handle_tree_node) +#define sock_state_from_queue_node(q) CONTAINOF(q, struct SockState, queue_node) +#define sock_state_to_queue_node(s) (&(s)->queue_node) +#define sock_state_from_tree_node(t) CONTAINOF(t, struct SockState, tree_node) +#define sock_state_to_tree_node(s) (&(s)->tree_node) +#define poll_group_from_queue_node(q) CONTAINOF(q, struct PollGroup, queue_node) +#define poll_group_get_afd_device_handle(pg) (pg)->afd_device_handle + +enum PollStatus { + kPollIdle, + kPollPending, + kPollCancelled, +}; + +struct RefLock { + int state; +}; + +struct TreeNode { + struct TreeNode *left; + struct TreeNode *right; + struct TreeNode *parent; + uintptr_t key; + bool red; +}; + +struct Tree { + struct TreeNode *root; +}; + +struct TsTree { + struct Tree tree; + intptr_t lock; +}; + +struct TsTreeNode { + struct TreeNode tree_node; + struct RefLock reflock; +}; + +struct QueueNode { + struct QueueNode *prev; + struct QueueNode *next; +}; + +struct Queue { + struct QueueNode head; +}; + +struct PortState { + int64_t iocp_handle; + struct Tree sock_tree; + struct Queue sock_update_queue; + struct Queue sock_deleted_queue; + struct Queue poll_group_queue; + struct TsTreeNode handle_tree_node; + struct NtCriticalSection lock; + size_t active_poll_count; +}; + +struct PollGroup { + struct PortState *port_state; + struct QueueNode queue_node; + int64_t afd_device_handle; + size_t group_size; +}; + +struct SockState { + struct NtIoStatusBlock io_status_block; + struct NtAfdPollInfo poll_info; + struct QueueNode queue_node; + struct TreeNode tree_node; + struct PollGroup *poll_group; + int64_t base_socket; + epoll_data_t user_data; + uint32_t user_events; + uint32_t pending_events; + enum PollStatus poll_status; + bool delete_pending; +}; + +static const struct NtUnicodeString afd__device_name = + RTL_CONSTANT_STRING(u"\\Device\\Afd\\Wepoll"); + +static const struct NtObjectAttributes afd__device_attributes = + RTL_CONSTANT_OBJECT_ATTRIBUTES(&afd__device_name, 0); + +static int64_t reflock__keyed_event; +static struct TsTree epoll__handle_tree; + +static textwindows void err_map_win_error(void) { + errno = MapDosErrorToErrno(GetLastError()); +} + +static textwindows void err_set_win_error(uint32_t error) { + SetLastError(error); + errno = MapDosErrorToErrno(error); +} + +static textwindows int err_check_handle(int64_t handle) { + uint32_t flags; + /* GetHandleInformation() succeeds when passed INVALID_HANDLE_VALUE, + so check for this condition explicitly. */ + if (handle == kNtInvalidHandleValue) { + RETURN_SET_ERROR(-1, kNtErrorInvalidHandle); + } + if (!GetHandleInformation(handle, &flags)) { + RETURN_MAP_ERROR(-1); + } + return 0; +} + +static textwindows void tree_init(struct Tree *tree) { + memset(tree, 0, sizeof *tree); +} + +static textwindows void ts_tree_init(struct TsTree *ts_tree) { + tree_init(&ts_tree->tree); + InitializeSRWLock(&ts_tree->lock); +} + +static textwindows int reflock_global_init(void) { + NtStatus status; + if ((status = NtCreateKeyedEvent(&reflock__keyed_event, + kNtKeyedeventAllAccess, NULL, 0)) != + kNtStatusSuccess) { + RETURN_SET_ERROR(-1, RtlNtStatusToDosError(status)); + } + return 0; +} + +static textwindows int epoll_global_init(void) { + ts_tree_init(&epoll__handle_tree); + return 0; +} + +static textwindows int wepoll_init(void) { + static bool once; + static bool result; + if (!once) { + if (reflock_global_init() < 0 || epoll_global_init() < 0) { + result = false; + } else { + result = true; + } + once = true; + } + return result; +} + +static textwindows int afd_create_device_handle( + int64_t iocp_handle, int64_t *afd_device_handle_out) { + NtStatus status; + int64_t afd_device_handle; + struct NtIoStatusBlock iosb; + /* By opening \Device\Afd without specifying any extended attributes, + we'll get a handle that lets us talk to the AFD driver, but that + doesn't have an *associated endpoint (so it's not a socket). */ + status = NtCreateFile(&afd_device_handle, kNtSynchronize, + &afd__device_attributes, &iosb, NULL, 0, + kNtFileShareRead | kNtFileShareWrite, 1, 0, NULL, 0); + if (status != kNtStatusSuccess) { + RETURN_SET_ERROR(-1, RtlNtStatusToDosError(status)); + } + if (!CreateIoCompletionPort(afd_device_handle, iocp_handle, 0, 0)) { + goto error; + } + if (!SetFileCompletionNotificationModes(afd_device_handle, + kNtFileSkipSetEventOnHandle)) { + goto error; + } + *afd_device_handle_out = afd_device_handle; + return 0; +error: + CloseHandle(afd_device_handle); + RETURN_MAP_ERROR(-1); +} + +static textwindows int afd_poll(int64_t afd_device_handle, + struct NtAfdPollInfo *poll_info, + struct NtIoStatusBlock *io_status_block) { + NtStatus status; + /* Blocking operation is not supported.*/ + assert(io_status_block); + io_status_block->Status = kNtStatusPending; + status = + NtDeviceIoControlFile(afd_device_handle, 0, NULL, io_status_block, + io_status_block, kNtIoctlAfdPoll, poll_info, + sizeof(*poll_info), poll_info, sizeof(*poll_info)); + if (status == kNtStatusSuccess) { + return 0; + } else if (status == kNtStatusPending) { + RETURN_SET_ERROR(-1, kNtErrorIoPending); + } else { + RETURN_SET_ERROR(-1, RtlNtStatusToDosError(status)); + } +} + +static textwindows int afd_cancel_poll( + int64_t afd_device_handle, struct NtIoStatusBlock *io_status_block) { + NtStatus cancel_status; + struct NtIoStatusBlock cancel_iosb; + /* If the poll operation has already completed or has been cancelled + earlier, there's nothing left for us to do. */ + if (io_status_block->Status != kNtStatusPending) return 0; + cancel_status = + NtCancelIoFileEx(afd_device_handle, io_status_block, &cancel_iosb); + /* NtCancelIoFileEx() may return STATUS_NOT_FOUND if the operation completed + just before calling NtCancelIoFileEx(). This is not an error. */ + if (cancel_status == kNtStatusSuccess || cancel_status == kNtStatusNotFound) { + return 0; + } else { + RETURN_SET_ERROR(-1, RtlNtStatusToDosError(cancel_status)); + } +} + +static textwindows void queue_node_init(struct QueueNode *node) { + node->prev = node; + node->next = node; +} + +static textwindows void queue_init(struct Queue *queue) { + queue_node_init(&queue->head); +} + +static textwindows void queue__detach_node(struct QueueNode *node) { + node->prev->next = node->next; + node->next->prev = node->prev; +} + +static textwindows bool queue_is_enqueued(const struct QueueNode *node) { + return node->prev != node; +} + +static textwindows bool queue_is_empty(const struct Queue *queue) { + return !queue_is_enqueued(&queue->head); +} + +static textwindows struct QueueNode *queue_first(const struct Queue *queue) { + return !queue_is_empty(queue) ? queue->head.next : NULL; +} + +static textwindows struct QueueNode *queue_last(const struct Queue *queue) { + return !queue_is_empty(queue) ? queue->head.prev : NULL; +} + +static textwindows void queue_prepend(struct Queue *queue, + struct QueueNode *node) { + node->next = queue->head.next; + node->prev = &queue->head; + node->next->prev = node; + queue->head.next = node; +} + +static textwindows void queue_append(struct Queue *queue, + struct QueueNode *node) { + node->next = &queue->head; + node->prev = queue->head.prev; + node->prev->next = node; + queue->head.prev = node; +} + +static textwindows void queue_move_to_start(struct Queue *queue, + struct QueueNode *node) { + queue__detach_node(node); + queue_prepend(queue, node); +} + +static textwindows void queue_move_to_end(struct Queue *queue, + struct QueueNode *node) { + queue__detach_node(node); + queue_append(queue, node); +} + +static textwindows void queue_remove(struct QueueNode *node) { + queue__detach_node(node); + queue_node_init(node); +} + +static textwindows struct PortState *port__alloc(void) { + struct PortState *port_state = malloc(sizeof *port_state); + if (!port_state) RETURN_SET_ERROR(NULL, kNtErrorNotEnoughMemory); + return port_state; +} + +static textwindows int64_t port__create_iocp(void) { + int64_t iocp_handle = CreateIoCompletionPort(kNtInvalidHandleValue, 0, 0, 0); + if (!iocp_handle) RETURN_MAP_ERROR(0); + return iocp_handle; +} + +static textwindows int port__close_iocp(struct PortState *port_state) { + int64_t iocp_handle = port_state->iocp_handle; + port_state->iocp_handle = 0; + if (!CloseHandle(iocp_handle)) RETURN_MAP_ERROR(-1); + return 0; +} + +static textwindows void tree_node_init(struct TreeNode *node) { + memset(node, 0, sizeof *node); +} + +static textwindows void reflock_init(struct RefLock *reflock) { + reflock->state = 0; +} + +static textwindows void ts_tree_node_init(struct TsTreeNode *node) { + tree_node_init(&node->tree_node); + reflock_init(&node->reflock); +} + +static textwindows void tree__rotate_left(struct Tree *tree, + struct TreeNode *node) { + TREE__ROTATE(left, right) +} + +static textwindows void tree__rotate_right(struct Tree *tree, + struct TreeNode *node) { + TREE__ROTATE(right, left) +} + +static textwindows int tree_add(struct Tree *tree, struct TreeNode *node, + uintptr_t key) { + struct TreeNode *parent; + parent = tree->root; + if (parent) { + for (;;) { + if (key < parent->key) { + TREE__INSERT_OR_DESCEND(left) + } else if (key > parent->key) { + TREE__INSERT_OR_DESCEND(right) + } else { + return -1; + } + } + } else { + tree->root = node; + } + node->key = key; + node->left = node->right = NULL; + node->parent = parent; + node->red = true; + for (; parent && parent->red; parent = node->parent) { + if (parent == parent->parent->left) { + TREE__REBALANCE_AFTER_INSERT(left, right) + } else { + TREE__REBALANCE_AFTER_INSERT(right, left) + } + } + tree->root->red = false; + return 0; +} + +static textwindows int ts_tree_add(struct TsTree *ts_tree, + struct TsTreeNode *node, uintptr_t key) { + int r; + AcquireSRWLockExclusive(&ts_tree->lock); + r = tree_add(&ts_tree->tree, &node->tree_node, key); + ReleaseSRWLockExclusive(&ts_tree->lock); + return r; +} + +static textwindows void port__free(struct PortState *port) { + assert(port != NULL); + free(port); +} + +static textwindows struct PortState *port_new(int64_t *iocp_handle_out) { + struct PortState *port_state; + int64_t iocp_handle; + port_state = port__alloc(); + if (!port_state) goto err1; + iocp_handle = port__create_iocp(); + if (!iocp_handle) goto err2; + memset(port_state, 0, sizeof *port_state); + port_state->iocp_handle = iocp_handle; + tree_init(&port_state->sock_tree); + queue_init(&port_state->sock_update_queue); + queue_init(&port_state->sock_deleted_queue); + queue_init(&port_state->poll_group_queue); + ts_tree_node_init(&port_state->handle_tree_node); + InitializeCriticalSection(&port_state->lock); + *iocp_handle_out = iocp_handle; + return port_state; +err2: + port__free(port_state); +err1: + return NULL; +} + +static textwindows int sock__cancel_poll(struct SockState *sock_state) { + assert(sock_state->poll_status == kPollPending); + if (afd_cancel_poll(poll_group_get_afd_device_handle(sock_state->poll_group), + &sock_state->io_status_block) < 0) { + return -1; + } + sock_state->poll_status = kPollCancelled; + sock_state->pending_events = 0; + return 0; +} + +static textwindows void port_cancel_socket_update( + struct PortState *port_state, struct SockState *sock_state) { + if (!queue_is_enqueued(sock_state_to_queue_node(sock_state))) return; + queue_remove(sock_state_to_queue_node(sock_state)); +} + +static textwindows struct TreeNode *tree_find(const struct Tree *tree, + uintptr_t key) { + struct TreeNode *node = tree->root; + while (node) { + if (key < node->key) { + node = node->left; + } else if (key > node->key) { + node = node->right; + } else { + return node; + } + } + return NULL; +} + +static textwindows struct TsTreeNode *ts_tree__find_node(struct TsTree *ts_tree, + uintptr_t key) { + struct TreeNode *tree_node = tree_find(&ts_tree->tree, key); + if (!tree_node) return NULL; + return CONTAINOF(tree_node, struct TsTreeNode, tree_node); +} + +static textwindows void tree_del(struct Tree *tree, struct TreeNode *node) { + bool red; + struct TreeNode *parent, *left, *right, *next; + parent = node->parent; + left = node->left; + right = node->right; + if (!left) { + next = right; + } else if (!right) { + next = left; + } else { + next = right; + while (next->left) next = next->left; + } + if (parent) { + if (parent->left == node) { + parent->left = next; + } else { + parent->right = next; + } + } else { + tree->root = next; + } + if (left && right) { + red = next->red; + next->red = node->red; + next->left = left; + left->parent = next; + if (next != right) { + parent = next->parent; + next->parent = node->parent; + node = next->right; + parent->left = node; + next->right = right; + right->parent = next; + } else { + next->parent = parent; + parent = next; + node = next->right; + } + } else { + red = node->red; + node = next; + } + if (node) node->parent = parent; + if (red) return; + if (node && node->red) { + node->red = false; + return; + } + do { + if (node == tree->root) break; + if (node == parent->left) { + TREE__REBALANCE_AFTER_REMOVE(left, right) + } else { + TREE__REBALANCE_AFTER_REMOVE(right, left) + } + node = parent; + parent = parent->parent; + } while (!node->red); + if (node) node->red = false; +} + +static textwindows void reflock__signal_event(void *address) { + NtStatus status = + NtReleaseKeyedEvent(reflock__keyed_event, address, false, NULL); + if (status != kNtStatusSuccess) abort(); +} + +static textwindows void reflock__await_event(void *address) { + NtStatus status = + NtWaitForKeyedEvent(reflock__keyed_event, address, false, NULL); + if (status != kNtStatusSuccess) abort(); +} + +static textwindows void reflock_ref(struct RefLock *reflock) { + long state = InterlockedAdd(&reflock->state, REFLOCK__REF); + /* Verify that the counter didn 't overflow and the lock isn' t destroyed.*/ + assert((state & REFLOCK__DESTROY_MASK) == 0); +} + +static textwindows void reflock_unref(struct RefLock *reflock) { + long state = InterlockedAdd(&reflock->state, -REFLOCK__REF); + /* Verify that the lock was referenced and not already destroyed.*/ + assert((state & REFLOCK__DESTROY_MASK & ~REFLOCK__DESTROY) == 0); + if (state == REFLOCK__DESTROY) reflock__signal_event(reflock); +} + +static textwindows struct TsTreeNode *ts_tree_del_and_ref( + struct TsTree *ts_tree, uintptr_t key) { + struct TsTreeNode *ts_tree_node; + AcquireSRWLockExclusive(&ts_tree->lock); + ts_tree_node = ts_tree__find_node(ts_tree, key); + if (ts_tree_node != NULL) { + tree_del(&ts_tree->tree, &ts_tree_node->tree_node); + reflock_ref(&ts_tree_node->reflock); + } + ReleaseSRWLockExclusive(&ts_tree->lock); + return ts_tree_node; +} + +static textwindows struct TsTreeNode *ts_tree_find_and_ref( + struct TsTree *ts_tree, uintptr_t key) { + struct TsTreeNode *ts_tree_node; + AcquireSRWLockShared(&ts_tree->lock); + ts_tree_node = ts_tree__find_node(ts_tree, key); + if (ts_tree_node != NULL) reflock_ref(&ts_tree_node->reflock); + ReleaseSRWLockShared(&ts_tree->lock); + return ts_tree_node; +} + +static textwindows void ts_tree_node_unref(struct TsTreeNode *node) { + reflock_unref(&node->reflock); +} + +static textwindows void reflock_unref_and_destroy(struct RefLock *reflock) { + long state, ref_count; + state = InterlockedAdd(&reflock->state, REFLOCK__DESTROY - REFLOCK__REF); + ref_count = state & REFLOCK__REF_MASK; + /* Verify that the lock was referenced and not already destroyed. */ + assert((state & REFLOCK__DESTROY_MASK) == REFLOCK__DESTROY); + if (ref_count != 0) reflock__await_event(reflock); + state = InterlockedExchange(&reflock->state, REFLOCK__POISON); + assert(state == REFLOCK__DESTROY); +} + +static textwindows void ts_tree_node_unref_and_destroy( + struct TsTreeNode *node) { + reflock_unref_and_destroy(&node->reflock); +} + +static textwindows void port_unregister_socket(struct PortState *port_state, + struct SockState *sock_state) { + tree_del(&port_state->sock_tree, sock_state_to_tree_node(sock_state)); +} + +static textwindows void port_remove_deleted_socket( + struct PortState *port_state, struct SockState *sock_state) { + if (!queue_is_enqueued(sock_state_to_queue_node(sock_state))) return; + queue_remove(sock_state_to_queue_node(sock_state)); +} + +static textwindows struct Queue *port_get_poll_group_queue( + struct PortState *port_state) { + return &port_state->poll_group_queue; +} + +static textwindows void poll_group_release(struct PollGroup *poll_group) { + struct PortState *port_state = poll_group->port_state; + struct Queue *poll_group_queue = port_get_poll_group_queue(port_state); + poll_group->group_size--; + assert(poll_group->group_size < MAX_GROUP_SIZE); + queue_move_to_end(poll_group_queue, &poll_group->queue_node); + /* Poll groups are currently only freed when the epoll port is closed. */ +} + +static textwindows void sock__free(struct SockState *sock_state) { + assert(sock_state != NULL); + free(sock_state); +} + +static textwindows void port_add_deleted_socket(struct PortState *port_state, + struct SockState *sock_state) { + if (queue_is_enqueued(sock_state_to_queue_node(sock_state))) return; + queue_append(&port_state->sock_deleted_queue, + sock_state_to_queue_node(sock_state)); +} + +static textwindows int sock__delete(struct PortState *port_state, + struct SockState *sock_state, bool force) { + if (!sock_state->delete_pending) { + if (sock_state->poll_status == kPollPending) { + sock__cancel_poll(sock_state); + } + port_cancel_socket_update(port_state, sock_state); + port_unregister_socket(port_state, sock_state); + sock_state->delete_pending = true; + } + /* If the poll request still needs to complete, the sock_state object + can't be free'd yet. `sock_feed_event()` or `port_close()` will + take care of this later. */ + if (force || sock_state->poll_status == kPollIdle) { + port_remove_deleted_socket(port_state, sock_state); + poll_group_release(sock_state->poll_group); + sock__free(sock_state); + } else { + /* Free the socket later.*/ + port_add_deleted_socket(port_state, sock_state); + } + return 0; +} + +static textwindows void sock_delete(struct PortState *port_state, + struct SockState *sock_state) { + sock__delete(port_state, sock_state, false); +} + +static textwindows void sock_force_delete(struct PortState *port_state, + struct SockState *sock_state) { + sock__delete(port_state, sock_state, true); +} + +static textwindows void poll_group_delete(struct PollGroup *poll_group) { + assert(poll_group->group_size == 0); + CloseHandle(poll_group->afd_device_handle); + queue_remove(&poll_group->queue_node); + free(poll_group); +} + +static textwindows int port_delete(struct PortState *port_state) { + struct TreeNode *tree_node; + struct QueueNode *queue_node; + struct SockState *sock_state; + struct PollGroup *poll_group; + /* At this point the IOCP port should have been closed.*/ + assert(!port_state->iocp_handle); + while ((tree_node = tree_root(&port_state->sock_tree)) != NULL) { + sock_state = sock_state_from_tree_node(tree_node); + sock_force_delete(port_state, sock_state); + } + while ((queue_node = queue_first(&port_state->sock_deleted_queue)) != NULL) { + sock_state = sock_state_from_queue_node(queue_node); + sock_force_delete(port_state, sock_state); + } + while ((queue_node = queue_first(&port_state->poll_group_queue)) != NULL) { + poll_group = poll_group_from_queue_node(queue_node); + poll_group_delete(poll_group); + } + assert(queue_is_empty(&port_state->sock_update_queue)); + DeleteCriticalSection(&port_state->lock); + port__free(port_state); + return 0; +} + +static textwindows int64_t port_get_iocp_handle(struct PortState *port_state) { + assert(port_state->iocp_handle); + return port_state->iocp_handle; +} + +static textwindows struct PollGroup *poll_group__new( + struct PortState *port_state) { + int64_t iocp_handle = port_get_iocp_handle(port_state); + struct Queue *poll_group_queue = port_get_poll_group_queue(port_state); + struct PollGroup *poll_group = malloc(sizeof *poll_group); + if (!poll_group) RETURN_SET_ERROR(NULL, kNtErrorNotEnoughMemory); + memset(poll_group, 0, sizeof *poll_group); + queue_node_init(&poll_group->queue_node); + poll_group->port_state = port_state; + if (afd_create_device_handle(iocp_handle, &poll_group->afd_device_handle) < + 0) { + free(poll_group); + return NULL; + } + queue_append(poll_group_queue, &poll_group->queue_node); + return poll_group; +} + +static textwindows struct PollGroup *poll_group_acquire( + struct PortState *port_state) { + struct Queue *poll_group_queue = port_get_poll_group_queue(port_state); + struct PollGroup *poll_group = !queue_is_empty(poll_group_queue) + ? CONTAINOF(queue_last(poll_group_queue), + struct PollGroup, queue_node) + : NULL; + if (!poll_group || poll_group->group_size >= MAX_GROUP_SIZE) + poll_group = poll_group__new(port_state); + if (!poll_group) return NULL; + if (++poll_group->group_size == MAX_GROUP_SIZE) + queue_move_to_start(poll_group_queue, &poll_group->queue_node); + return poll_group; +} + +static textwindows int port_close(struct PortState *port_state) { + int result; + EnterCriticalSection(&port_state->lock); + result = port__close_iocp(port_state); + LeaveCriticalSection(&port_state->lock); + return result; +} + +static textwindows uint32_t sock__epoll_events_to_afd_events(uint32_t e) { + /* Always monitor for kNtAfdPollLocalClose, which is triggered when + the socket is closed with closesocket() or CloseHandle(). */ + uint32_t a = kNtAfdPollLocalClose; + if (e & (EPOLLIN | EPOLLRDNORM)) a |= kNtAfdPollReceive | kNtAfdPollAccept; + if (e & (EPOLLPRI | EPOLLRDBAND)) a |= kNtAfdPollReceiveExpedited; + if (e & (EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND)) a |= kNtAfdPollSend; + if (e & (EPOLLIN | EPOLLRDNORM | EPOLLRDHUP)) a |= kNtAfdPollDisconnect; + if (e & EPOLLHUP) a |= kNtAfdPollAbort; + if (e & EPOLLERR) a |= kNtAfdPollConnectFail; + return a; +} + +static textwindows uint32_t sock__afd_events_to_epoll_events(uint32_t a) { + uint32_t e = 0; + if (a & (kNtAfdPollReceive | kNtAfdPollAccept)) e |= EPOLLIN | EPOLLRDNORM; + if (a & kNtAfdPollReceiveExpedited) e |= EPOLLPRI | EPOLLRDBAND; + if (a & kNtAfdPollSend) e |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; + if (a & kNtAfdPollDisconnect) e |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; + if (a & kNtAfdPollAbort) e |= EPOLLHUP; + if (a & kNtAfdPollConnectFail) { + /* Linux reports all these events after connect() has failed. */ + e |= EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLRDNORM | EPOLLWRNORM | EPOLLRDHUP; + } + return e; +} + +static textwindows int sock_update(struct PortState *port_state, + struct SockState *sock_state) { + assert(!sock_state->delete_pending); + if ((sock_state->poll_status == kPollPending) && + !(sock_state->user_events & KNOWN_EVENTS & ~sock_state->pending_events)) { + /* All the events the user is interested in are already being + monitored by the pending poll operation. It might spuriously + complete because of an event that we're no longer interested in; + when that happens we'll submit a new poll operation with the + updated event mask. */ + } else if (sock_state->poll_status == kPollPending) { + /* A poll operation is already pending, but it's not monitoring for + all the *events that the user is interested in .Therefore, cancel + the pending *poll operation; when we receive it's completion + package, a new poll *operation will be submitted with the correct + event mask. */ + if (sock__cancel_poll(sock_state) < 0) return -1; + } else if (sock_state->poll_status == kPollCancelled) { + /* The poll operation has already been cancelled, we're still waiting for + it to return.For now, there' s nothing that needs to be done. */ + } else if (sock_state->poll_status == kPollIdle) { + /* No poll operation is pending; start one. */ + sock_state->poll_info.Exclusive = false; + sock_state->poll_info.NumberOfHandles = 1; + sock_state->poll_info.Timeout = INT64_MAX; + sock_state->poll_info.Handles[0].Handle = (int64_t)sock_state->base_socket; + sock_state->poll_info.Handles[0].Status = 0; + sock_state->poll_info.Handles[0].Events = + sock__epoll_events_to_afd_events(sock_state->user_events); + if (afd_poll(poll_group_get_afd_device_handle(sock_state->poll_group), + &sock_state->poll_info, &sock_state->io_status_block) < 0) { + switch (GetLastError()) { + case kNtErrorIoPending: + /* Overlapped poll operation in progress; this is expected. */ + break; + case kNtErrorInvalidHandle: + /* Socket closed; it'll be dropped from the epoll set. */ + return sock__delete(port_state, sock_state, false); + default: + /* Other errors are propagated to the caller. */ + RETURN_MAP_ERROR(-1); + } + } + /* The poll request was successfully submitted.*/ + sock_state->poll_status = kPollPending; + sock_state->pending_events = sock_state->user_events; + } else { + unreachable; + } + port_cancel_socket_update(port_state, sock_state); + return 0; +} + +static textwindows int port__update_events(struct PortState *port_state) { + struct QueueNode *queue_node; + struct SockState *sock_state; + struct Queue *sock_update_queue = &port_state->sock_update_queue; + /* Walk queue, submitting new poll requests for sockets needing it */ + while (!queue_is_empty(sock_update_queue)) { + queue_node = queue_first(sock_update_queue); + sock_state = sock_state_from_queue_node(queue_node); + if (sock_update(port_state, sock_state) < 0) return -1; + /* sock_update() removes the socket from the update queue.*/ + } + return 0; +} + +static textwindows void port__update_events_if_polling( + struct PortState *port_state) { + if (port_state->active_poll_count > 0) port__update_events(port_state); +} + +static textwindows void port_request_socket_update( + struct PortState *port_state, struct SockState *sock_state) { + if (queue_is_enqueued(sock_state_to_queue_node(sock_state))) return; + queue_append(&port_state->sock_update_queue, + sock_state_to_queue_node(sock_state)); +} + +static textwindows int sock_feed_event(struct PortState *port_state, + struct NtIoStatusBlock *io_status_block, + struct epoll_event *ev) { + uint32_t epoll_events; + struct SockState *sock_state; + struct NtAfdPollInfo *poll_info; + epoll_events = 0; + sock_state = CONTAINOF(io_status_block, struct SockState, io_status_block); + poll_info = &sock_state->poll_info; + sock_state->poll_status = kPollIdle; + sock_state->pending_events = 0; + if (sock_state->delete_pending) { + /* Socket has been deleted earlier and can now be freed.*/ + return sock__delete(port_state, sock_state, false); + } else if (io_status_block->Status == kNtStatusCancelled) { + /* The poll request was cancelled by CancelIoEx.*/ + } else if (!NtSuccess(io_status_block->Status)) { + /* The overlapped request itself failed in an unexpected way.*/ + epoll_events = EPOLLERR; + } else if (poll_info->NumberOfHandles < 1) { + /* This poll operation succeeded but didn't report any socket events. */ + } else if (poll_info->Handles[0].Events & kNtAfdPollLocalClose) { + /* The poll operation reported that the socket was closed.*/ + return sock__delete(port_state, sock_state, false); + } else { + /* Events related to our socket were reported.*/ + epoll_events = + sock__afd_events_to_epoll_events(poll_info->Handles[0].Events); + } + /* Requeue the socket so a new poll request will be submitted.*/ + port_request_socket_update(port_state, sock_state); + /* Filter out events that the user didn't ask for. */ + epoll_events &= sock_state->user_events; + /* Return if there are no epoll events to report.*/ + if (epoll_events == 0) return 0; + /* If the the socket has the EPOLLONESHOT flag set, unmonitor all + events, even EPOLLERR and EPOLLHUP. But always keep looking for + closed sockets. */ + if (sock_state->user_events & EPOLLONESHOT) { + sock_state->user_events = 0; + } + ev->data = sock_state->user_data; + ev->events = epoll_events; + return 1; +} + +static textwindows int port__feed_events(struct PortState *port_state, + struct epoll_event *epoll_events, + struct NtOverlappedEntry *iocp_events, + uint32_t iocp_event_count) { + uint32_t i; + int epoll_event_count; + struct epoll_event *ev; + struct NtIoStatusBlock *io_status_block; + epoll_event_count = 0; + for (i = 0; i < iocp_event_count; i++) { + io_status_block = (struct NtIoStatusBlock *)iocp_events[i].lpOverlapped; + ev = &epoll_events[epoll_event_count]; + epoll_event_count += sock_feed_event(port_state, io_status_block, ev); + } + return epoll_event_count; +} + +static textwindows int port__poll(struct PortState *port_state, + struct epoll_event *epoll_events, + struct NtOverlappedEntry *iocp_events, + uint32_t maxevents, uint32_t timeout) { + bool32 r; + uint32_t completion_count; + if (port__update_events(port_state) < 0) return -1; + port_state->active_poll_count++; + LeaveCriticalSection(&port_state->lock); + r = GetQueuedCompletionStatusEx(port_state->iocp_handle, iocp_events, + maxevents, &completion_count, timeout, false); + EnterCriticalSection(&port_state->lock); + port_state->active_poll_count--; + if (!r) RETURN_MAP_ERROR(-1); + return port__feed_events(port_state, epoll_events, iocp_events, + completion_count); +} + +static textwindows int port_wait(struct PortState *port_state, + struct epoll_event *events, int maxevents, + int timeout) { + int result; + uint64_t now, due = 0; + uint32_t gqcs_timeout; + struct NtOverlappedEntry *iocp_events; + struct NtOverlappedEntry stack_iocp_events[64]; + /* Check whether `maxevents` is in range.*/ + if (maxevents <= 0) RETURN_SET_ERROR(-1, kNtErrorInvalidParameter); + /* Decide whether the IOCP completion list can live on the stack, or + allocate memory for it on the heap. */ + if ((size_t)maxevents <= ARRAYLEN(stack_iocp_events)) { + iocp_events = stack_iocp_events; + } else if ((iocp_events = malloc((size_t)maxevents * sizeof(*iocp_events))) == + NULL) { + iocp_events = stack_iocp_events; + maxevents = ARRAYLEN(stack_iocp_events); + } + /* Compute the timeout for GetQueuedCompletionStatus, and the wait end + time, if the user specified a timeout other than zero or infinite. */ + if (timeout > 0) { + due = GetTickCount64() + (uint64_t)timeout; + gqcs_timeout = (uint32_t)timeout; + } else if (timeout == 0) { + gqcs_timeout = 0; + } else { + gqcs_timeout = -1; + } + EnterCriticalSection(&port_state->lock); + /* Dequeue completion packets until either at least one interesting + event has been discovered, or the timeout is reached. */ + for (;;) { + result = port__poll(port_state, events, iocp_events, (uint32_t)maxevents, + gqcs_timeout); + if (result < 0 || result > 0) break; + /* Result, error, or time - out. */ + if (timeout < 0) continue; + /* When timeout is negative, never time out. */ + /* Update time. */ + now = GetTickCount64(); + /* Do not allow the due time to be in the past. */ + if (now >= due) { + SetLastError(kNtWaitTimeout); + break; + } + /* Recompute time-out argument for GetQueuedCompletionStatus. */ + gqcs_timeout = (uint32_t)(due - now); + } + port__update_events_if_polling(port_state); + LeaveCriticalSection(&port_state->lock); + if (iocp_events != stack_iocp_events) { + free(iocp_events); + } + if (result >= 0) { + return result; + } else if (GetLastError() == kNtWaitTimeout) { + return 0; + } else { + return -1; + } +} + +static textwindows int64_t ws__ioctl_get_bsp_socket(int64_t socket, + uint32_t ioctl) { + uint32_t bytes; + int64_t bsp_socket; + if (WSAIoctl(socket, ioctl, NULL, 0, &bsp_socket, sizeof(bsp_socket), &bytes, + NULL, NULL) != -1) { + return bsp_socket; + } else { + return -1; + } +} + +static textwindows int64_t ws_get_base_socket(int64_t socket) { + uint32_t error; + int64_t base_socket; + for (;;) { + base_socket = ws__ioctl_get_bsp_socket(socket, kNtSioBaseHandle); + if (base_socket != -1) { + return base_socket; + } + error = GetLastError(); + if (error == WSAENOTSOCK) { + RETURN_SET_ERROR(-1, error); + } + /* + * Even though Microsoft documentation clearly states that Layered + * Spyware Providers must never ever intercept the SIO_BASE_HANDLE + * ioctl, Komodia LSPs (that Lenovo got sued for preinstalling) do + * so anyway in order to redirect decrypted https requests through + * some foreign proxy and inject ads which breaks high-performance + * network event io. However it doesn't handle SIO_BSP_HANDLE_POLL + * which will at least let us obtain the socket associated with the + * next winsock protocol chain entry. If this succeeds, loop around + * and call SIO_BASE_HANDLE again with the returned BSP socket, to + * make sure we unwrap all layers and retrieve the real base socket. + */ + base_socket = ws__ioctl_get_bsp_socket(socket, kNtSioBspHandlePoll); + if (base_socket != -1 && base_socket != socket) { + socket = base_socket; + } else { + RETURN_SET_ERROR(-1, error); + } + } +} + +static textwindows struct SockState *sock__alloc(void) { + struct SockState *sock_state = malloc(sizeof *sock_state); + if (!sock_state) RETURN_SET_ERROR(NULL, kNtErrorNotEnoughMemory); + return sock_state; +} + +static textwindows int port_register_socket(struct PortState *port_state, + struct SockState *sock_state, + int64_t socket) { + if (tree_add(&port_state->sock_tree, sock_state_to_tree_node(sock_state), + socket) < 0) { + RETURN_SET_ERROR(-1, kNtErrorAlreadyExists); + } + return 0; +} + +static textwindows struct SockState *sock_new(struct PortState *port_state, + int64_t socket) { + int64_t base_socket; + struct PollGroup *poll_group; + struct SockState *sock_state; + if (socket == 0 || socket == -1) RETURN_SET_ERROR(0, kNtErrorInvalidHandle); + base_socket = ws_get_base_socket(socket); + if (base_socket == -1) return NULL; + poll_group = poll_group_acquire(port_state); + if (!poll_group) return NULL; + sock_state = sock__alloc(); + if (!sock_state) goto err1; + memset(sock_state, 0, sizeof *sock_state); + sock_state->base_socket = base_socket; + sock_state->poll_group = poll_group; + tree_node_init(&sock_state->tree_node); + queue_node_init(&sock_state->queue_node); + if (port_register_socket(port_state, sock_state, socket) < 0) goto err2; + return sock_state; +err2: + sock__free(sock_state); +err1: + poll_group_release(poll_group); + return NULL; +} + +static textwindows int sock_set_event(struct PortState *port_state, + struct SockState *sock_state, + const struct epoll_event *ev) { + /* EPOLLERR and EPOLLHUP are always reported, even when not requested + by the caller. However they are disabled after a event has been + reported for a socket for which the EPOLLONESHOT flag was set. */ + uint32_t events = ev->events | EPOLLERR | EPOLLHUP; + sock_state->user_events = events; + sock_state->user_data = ev->data; + if ((events & KNOWN_EVENTS & ~sock_state->pending_events) != 0) { + port_request_socket_update(port_state, sock_state); + } + return 0; +} + +static textwindows int port__ctl_add(struct PortState *port_state, int64_t sock, + struct epoll_event *ev) { + struct SockState *sock_state = sock_new(port_state, sock); + if (!sock_state) return -1; + if (sock_set_event(port_state, sock_state, ev) < 0) { + sock_delete(port_state, sock_state); + return -1; + } + port__update_events_if_polling(port_state); + return 0; +} + +static textwindows struct SockState *port_find_socket( + struct PortState *port_state, int64_t socket) { + struct TreeNode *tree_node = tree_find(&port_state->sock_tree, socket); + if (!tree_node) RETURN_SET_ERROR(NULL, kNtErrorNotFound); + return sock_state_from_tree_node(tree_node); +} + +static textwindows int port__ctl_mod(struct PortState *port_state, int64_t sock, + struct epoll_event *ev) { + struct SockState *sock_state = port_find_socket(port_state, sock); + if (!sock_state) return -1; + if (sock_set_event(port_state, sock_state, ev) < 0) return -1; + port__update_events_if_polling(port_state); + return 0; +} + +static textwindows int port__ctl_del(struct PortState *port_state, + int64_t sock) { + struct SockState *sock_state = port_find_socket(port_state, sock); + if (!sock_state) return -1; + sock_delete(port_state, sock_state); + return 0; +} + +static textwindows int port__ctl_op(struct PortState *port_state, int op, + int64_t sock, struct epoll_event *ev) { + switch (op) { + case EPOLL_CTL_ADD: + return port__ctl_add(port_state, sock, ev); + case EPOLL_CTL_MOD: + return port__ctl_mod(port_state, sock, ev); + case EPOLL_CTL_DEL: + return port__ctl_del(port_state, sock); + default: + RETURN_SET_ERROR(-1, kNtErrorInvalidParameter); + } +} + +static textwindows int port_ctl(struct PortState *port_state, int op, + int64_t sock, struct epoll_event *ev) { + int result; + EnterCriticalSection(&port_state->lock); + result = port__ctl_op(port_state, op, sock, ev); + LeaveCriticalSection(&port_state->lock); + return result; +} + +static textwindows struct PortState *port_state_from_handle_tree_node( + struct TsTreeNode *tree_node) { + return CONTAINOF(tree_node, struct PortState, handle_tree_node); +} + +static textwindows noinline int epoll_create1$nt(uint32_t flags) { + int fd; + int64_t ephnd; + struct PortState *port_state; + struct TsTreeNode *tree_node; + if ((fd = __getemptyfd()) == -1) return -1; + if (wepoll_init() < 0) return -1; + port_state = port_new(&ephnd); + if (!port_state) return -1; + tree_node = port_state_to_handle_tree_node(port_state); + if (ts_tree_add(&epoll__handle_tree, tree_node, (uintptr_t)ephnd) < 0) { + /* This should never happen. */ + port_delete(port_state); + RETURN_SET_ERROR(-1, kNtErrorAlreadyExists); + } + g_fds.p[fd].kind = kFdEpoll; + g_fds.p[fd].handle = ephnd; + g_fds.p[fd].flags = flags; + return fd; +} + +static textwindows noinline int epoll_ctl$nt(int epfd, int op, int fd, + struct epoll_event *ev) { + int r; + struct PortState *port_state; + struct TsTreeNode *tree_node; + if (!IsWindows()) { + return epoll_ctl$sysv(epfd, op, fd, ev); + } else { + if (wepoll_init() < 0) return -1; + if (!__isfdopen(fd)) return ebadf(); + if (!__isfdkind(epfd, kFdEpoll)) return ebadf(); + tree_node = ts_tree_find_and_ref(&epoll__handle_tree, g_fds.p[epfd].handle); + if (!tree_node) { + err_set_win_error(kNtErrorInvalidParameter); + goto err; + } + port_state = port_state_from_handle_tree_node(tree_node); + r = port_ctl(port_state, op, g_fds.p[fd].handle, ev); + ts_tree_node_unref(tree_node); + if (r < 0) goto err; + return 0; + err: + /* On Linux, in the case of epoll_ctl(), EBADF takes priority over + other *errors. Wepoll mimics this behavior. */ + err_check_handle(g_fds.p[epfd].handle); + err_check_handle(g_fds.p[fd].handle); + return -1; + } +} + +static textwindows noinline int epoll_wait$nt(int epfd, + struct epoll_event *events, + int maxevents, int timeoutms) { + int num_events; + struct PortState *port_state; + struct TsTreeNode *tree_node; + if (!__isfdkind(epfd, kFdEpoll)) return ebadf(); + if (maxevents <= 0) return einval(); + if (wepoll_init() < 0) return -1; + tree_node = ts_tree_find_and_ref(&epoll__handle_tree, g_fds.p[epfd].handle); + if (!tree_node) { + err_set_win_error(kNtErrorInvalidParameter); + goto err; + } + port_state = port_state_from_handle_tree_node(tree_node); + num_events = port_wait(port_state, events, maxevents, timeoutms); + ts_tree_node_unref(tree_node); + if (num_events < 0) goto err; + return num_events; +err: + err_check_handle(g_fds.p[epfd].handle); + return -1; +} + +static textwindows noinline int close$epoll$nt(int fd) { + struct PortState *port_state; + struct TsTreeNode *tree_node; + if (wepoll_init() < 0) return -1; + tree_node = ts_tree_del_and_ref(&epoll__handle_tree, g_fds.p[fd].handle); + if (!tree_node) { + err_set_win_error(kNtErrorInvalidParameter); + goto err; + } + port_state = port_state_from_handle_tree_node(tree_node); + port_close(port_state); + ts_tree_node_unref_and_destroy(tree_node); + return port_delete(port_state); +err: + err_check_handle(g_fds.p[fd].handle); + return -1; +} + +int close$epoll(int fd) { + if (!IsWindows()) { + return close$sysv(fd); + } else { + return close$epoll$nt(fd); + } +} + +/** + * Creates new epoll instance. + * + * @param size is ignored but must be greater than zero + * @param flags must be zero as there are no supported flags + * @return epoll file descriptor, or -1 on failure + */ +int epoll_create(int size) { + if (size <= 0) return einval(); + return epoll_create1(0); +} + +/** + * Creates new epoll instance. + * + * @param size is ignored but must be greater than zero + * @param flags must be zero or can have O_CLOEXEC + * @return epoll file descriptor, or -1 on failure + */ +int epoll_create1(int flags) { + int fd; + if (flags & ~O_CLOEXEC) return einval(); + if (!IsWindows()) { + return __ensurefds(fixupnewfd$sysv(epoll_create$sysv(1337), flags)); + } else { + return epoll_create1$nt(flags); + } +} + +/** + * Controls which socket events are monitored. + * + * It is recommended to always explicitly remove a socket from its epoll + * set using EPOLL_CTL_DEL before closing it. As on Linux, your closed + * sockets are automatically removed from the epoll set, but wepoll may + * not be able to detect that a socket was closed until the next call to + * epoll_wait(). + * + * @param epfd is file descriptor created by epoll_create() + * @param op can be EPOLL_CTL_{ADD,MOD,DEL} + * @param fd is file descriptor to monitor + * @param ev is ignored if op is EPOLL_CTL_DEL + * @param ev->events can have these flags: + * - EPOLLIN: trigger on fd readable + * - EPOLLOUT: trigger on fd writeable + * - EPOLLERR: trigger on fd error (superfluous: always reported) + * - EPOLLHUP: trigger on fd remote hangup (superfluous: always reported) + * - EPOLLPRI: trigger on fd exceptional conditions, e.g. oob + * - EPOLLONESHOT: report event(s) only once + * - EPOLLEXCLUSIVE: not supported on windows + * - EPOLLWAKEUP: not supported on windows + * - EPOLLET: edge triggered mode (not supported on windows) + * - EPOLLRDNORM + * - EPOLLRDBAND + * - EPOLLWRNORM + * - EPOLLWRBAND + * - EPOLLRDHUP + * - EPOLLMSG + * @error ENOTSOCK on Windows if fd isn't a socket :( + * @return 0 on success, or -1 w/ errno + */ +int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev) { + if (!IsWindows()) { + return epoll_ctl$sysv(epfd, op, fd, ev); + } else { + return epoll_ctl$nt(epfd, op, fd, ev); + } +} + +/** + * Receives socket events. + * + * @param events will receive information about what happened + * @param maxevents is array length of events + * @param timeoutms is milliseconds, 0 to not block, or -1 for forever + * @return number of events stored, 0 on timeout, or -1 w/ errno + */ +int epoll_wait(int epfd, struct epoll_event *events, int maxevents, + int timeoutms) { + if (!IsWindows()) { + return epoll_wait$sysv(epfd, events, maxevents, timeoutms); + } else { + return epoll_wait$nt(epfd, events, maxevents, timeoutms); + } +} diff --git a/libc/sock/epoll.h b/libc/sock/epoll.h new file mode 100644 index 00000000..94acd971 --- /dev/null +++ b/libc/sock/epoll.h @@ -0,0 +1,25 @@ +#ifndef COSMOPOLITAN_LIBC_SOCK_WEPOLL_H_ +#define COSMOPOLITAN_LIBC_SOCK_WEPOLL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +typedef union epoll_data { + void *ptr; + int fd; + uint32_t u32; + uint64_t u64; +} epoll_data_t; + +struct thatispacked epoll_event { + uint32_t events; + epoll_data_t data; +}; + +int epoll_create(int); +int epoll_create1(int); +int epoll_ctl(int, int, int, struct epoll_event *); +int epoll_wait(int, struct epoll_event *, int, int); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_SOCK_WEPOLL_H_ */ diff --git a/libc/sock/fixupnewsockfd.c b/libc/sock/fixupnewsockfd.c index fe965986..b5eb9e97 100644 --- a/libc/sock/fixupnewsockfd.c +++ b/libc/sock/fixupnewsockfd.c @@ -17,10 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/sock/internal.h" -#include "libc/sock/sock.h" #include "libc/calls/internal.h" +#include "libc/sock/internal.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sock.h" diff --git a/libc/sock/getpeername-nt.c b/libc/sock/getpeername-nt.c index b481b9fb..62fa3190 100644 --- a/libc/sock/getpeername-nt.c +++ b/libc/sock/getpeername-nt.c @@ -22,6 +22,7 @@ #include "libc/dce.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" textwindows int getpeername$nt(struct Fd *fd, void *out_addr, @@ -30,6 +31,6 @@ textwindows int getpeername$nt(struct Fd *fd, void *out_addr, if (__getpeername$nt(fd->handle, out_addr, out_addrsize) != -1) { return 0; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/getpeername.c b/libc/sock/getpeername.c index 08fd89f2..085706eb 100644 --- a/libc/sock/getpeername.c +++ b/libc/sock/getpeername.c @@ -31,7 +31,7 @@ int getpeername(int fd, void *out_addr, uint32_t *out_addrsize) { if (!IsWindows()) { return getpeername$sysv(fd, out_addr, out_addrsize); - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return getpeername$nt(&g_fds.p[fd], out_addr, out_addrsize); } else { return ebadf(); diff --git a/libc/sock/getsockname-nt.c b/libc/sock/getsockname-nt.c index 3e85749e..8ac7ef41 100644 --- a/libc/sock/getsockname-nt.c +++ b/libc/sock/getsockname-nt.c @@ -22,6 +22,7 @@ #include "libc/dce.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" textwindows int getsockname$nt(struct Fd *fd, void *out_addr, @@ -30,6 +31,6 @@ textwindows int getsockname$nt(struct Fd *fd, void *out_addr, if (__getsockname$nt(fd->handle, out_addr, out_addrsize) != -1) { return 0; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/getsockname.c b/libc/sock/getsockname.c index 7b6b9e4a..31e34578 100644 --- a/libc/sock/getsockname.c +++ b/libc/sock/getsockname.c @@ -31,7 +31,7 @@ int getsockname(int fd, void *out_addr, uint32_t *out_addrsize) { if (!IsWindows()) { return getsockname$sysv(fd, out_addr, out_addrsize); - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return getsockname$nt(&g_fds.p[fd], out_addr, out_addrsize); } else { return ebadf(); diff --git a/libc/sock/getsockopt-nt.c b/libc/sock/getsockopt-nt.c index 4a9feb58..3acbe6a8 100644 --- a/libc/sock/getsockopt-nt.c +++ b/libc/sock/getsockopt-nt.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" textwindows int getsockopt$nt(struct Fd *fd, int level, int optname, @@ -31,6 +32,6 @@ textwindows int getsockopt$nt(struct Fd *fd, int level, int optname, -1) { return 0; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/getsockopt.c b/libc/sock/getsockopt.c index db9096bc..64d162e2 100644 --- a/libc/sock/getsockopt.c +++ b/libc/sock/getsockopt.c @@ -39,7 +39,7 @@ int getsockopt(int fd, int level, int optname, void *out_opt_optval, if (optname == -1) return 0; /* our sysvconsts definition */ if (!IsWindows()) { return getsockopt$sysv(fd, level, optname, out_opt_optval, out_optlen); - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return getsockopt$nt(&g_fds.p[fd], level, optname, out_opt_optval, out_optlen); } else { diff --git a/libc/sock/internal.h b/libc/sock/internal.h index 7d1f8682..8e110925 100644 --- a/libc/sock/internal.h +++ b/libc/sock/internal.h @@ -3,6 +3,7 @@ #ifndef __STRICT_ANSI__ #include "libc/bits/bits.h" #include "libc/nt/winsock.h" +#include "libc/sock/select.h" #include "libc/sock/sock.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -48,6 +49,8 @@ struct msghdr$bsd { uint32_t msg_flags; /* « different type */ }; +errno_t MapDosErrorToErrno(uint32_t); + int32_t __accept$sysv(int32_t, void *, uint32_t *) nodiscard hidden; int32_t __accept4$sysv(int32_t, void *, uint32_t *, int) nodiscard hidden; int32_t __connect$sysv(int32_t, const void *, uint32_t) hidden; @@ -73,6 +76,11 @@ int64_t writev$sysv(int32_t, const struct iovec *, int32_t) hidden; ssize_t recvfrom$sysv(int, void *, size_t, int, void *, uint32_t *) hidden; 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; +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; int poll$nt(struct pollfd *, uint64_t, int32_t) hidden; int getsockopt$nt(struct Fd *, int, int, void *, uint32_t *) hidden; @@ -84,8 +92,9 @@ int bind$nt(struct Fd *, const void *, uint32_t); 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; -size_t iovec2nt(struct iovec$nt[hasatleast 16], const struct iovec *, +size_t iovec2nt(struct NtIovec[hasatleast 16], const struct iovec *, size_t) hidden; ssize_t sendto$nt(struct Fd *, const struct iovec *, size_t, uint32_t, void *, uint32_t) hidden; @@ -93,12 +102,14 @@ ssize_t recvfrom$nt(struct Fd *, const struct iovec *, size_t, uint32_t, void *, uint32_t *) hidden; void winsockinit(void) hidden; -int64_t winsockerr(void) nocallback hidden; +int64_t __winsockerr(void) nocallback hidden; int fixupnewsockfd$sysv(int, int) hidden; ssize_t WinSendRecv(int64_t, void *, size_t, uint32_t, struct sockaddr *, uint32_t *, bool) hidden; int64_t winsockblock(int64_t, unsigned, int64_t) hidden; +int close$epoll(int) hidden; + /** * Converts sockaddr (Linux/Windows) → sockaddr$bsd (XNU/BSD). */ diff --git a/libc/sock/iovec2nt.c b/libc/sock/iovec2nt.c index 5821bfd9..2640d6be 100644 --- a/libc/sock/iovec2nt.c +++ b/libc/sock/iovec2nt.c @@ -29,7 +29,7 @@ * @return effective iovlen * @see IOV_MAX */ -textwindows size_t iovec2nt(struct iovec$nt iovnt[hasatleast 16], +textwindows size_t iovec2nt(struct NtIovec iovnt[hasatleast 16], const struct iovec *iov, size_t iovlen) { size_t i, limit; for (limit = 0x7ffff000, i = 0; i < MIN(16, iovlen); ++i) { diff --git a/libc/sock/listen-nt.c b/libc/sock/listen-nt.c index 05928c66..d677bdfd 100644 --- a/libc/sock/listen-nt.c +++ b/libc/sock/listen-nt.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/errfuns.h" textwindows int listen$nt(struct Fd *fd, int backlog) { @@ -28,6 +29,6 @@ textwindows int listen$nt(struct Fd *fd, int backlog) { if (__listen$nt(fd->handle, backlog) != -1) { return 0; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/listen.c b/libc/sock/listen.c index 9d0af994..a6625bb6 100644 --- a/libc/sock/listen.c +++ b/libc/sock/listen.c @@ -37,7 +37,7 @@ int listen(int fd, int backlog) { if (!IsWindows()) { return listen$sysv(fd, backlog); - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return listen$nt(&g_fds.p[fd], backlog); } else { return ebadf(); diff --git a/libc/sock/mapdoserrortoerrno.c b/libc/sock/mapdoserrortoerrno.c new file mode 100644 index 00000000..284b916c --- /dev/null +++ b/libc/sock/mapdoserrortoerrno.c @@ -0,0 +1,176 @@ +/*-*- 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 │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" +#include "libc/nt/errors.h" +#include "libc/sock/sock.h" + +/** + * Translates Windows error using superset of consts.sh. + */ +textwindows errno_t MapDosErrorToErrno(uint32_t error) { + switch (error) { + case kNtErrorModNotFound: + return ENOSYS; + case kNtErrorBadCommand: + return EACCES; + case kNtErrorBadLength: + return EACCES; + case kNtErrorBadNetpath: + return ENOENT; + case kNtErrorBadNetName: + return ENOENT; + case kNtErrorBadNetResp: + return ENETDOWN; + case kNtErrorBadPathname: + return ENOENT; + case kNtErrorCannotMake: + return EACCES; + case kNtErrorCommitmentLimit: + return ENOMEM; + case kNtErrorConnectionAborted: + return ECONNABORTED; + case kNtErrorConnectionActive: + return EISCONN; + case kNtErrorConnectionRefused: + return ECONNREFUSED; + case kNtErrorCrc: + return EACCES; + case kNtErrorDirNotEmpty: + return ENOTEMPTY; + case kNtErrorDupName: + return EADDRINUSE; + case kNtErrorFilenameExcedRange: + return ENOENT; + case kNtErrorFileNotFound: + return ENOENT; + case kNtErrorGenFailure: + return EACCES; + case kNtErrorGracefulDisconnect: + return EPIPE; + case kNtErrorHostDown: + return EHOSTUNREACH; + case kNtErrorHostUnreachable: + return EHOSTUNREACH; + case kNtErrorInsufficientBuffer: + return EFAULT; + case kNtErrorInvalidAddress: + return EADDRNOTAVAIL; + case kNtErrorInvalidFunction: + return EINVAL; + case kNtErrorInvalidNetname: + return EADDRNOTAVAIL; + case kNtErrorInvalidUserBuffer: + return EMSGSIZE; + case kNtErrorIoPending: + return EINPROGRESS; + case kNtErrorLockViolation: + return EACCES; + case kNtErrorMoreData: + return EMSGSIZE; + case kNtErrorNetnameDeleted: + return ECONNABORTED; + case kNtErrorNetworkAccessDenied: + return EACCES; + case kNtErrorNetworkBusy: + return ENETDOWN; + case kNtErrorNetworkUnreachable: + return ENETUNREACH; + case kNtErrorNoaccess: + return EFAULT; + case kNtErrorNonpagedSystemResources: + return ENOMEM; + case kNtErrorNotEnoughMemory: + return ENOMEM; + case kNtErrorNotEnoughQuota: + return ENOMEM; + case kNtErrorNotFound: + return ENOENT; + case kNtErrorNotLocked: + return EACCES; + case kNtErrorNotReady: + return EACCES; + case kNtErrorNotSupported: + return ENOTSUP; + case kNtErrorNoMoreFiles: + return ENOENT; + case kNtErrorNoSystemResources: + return ENOMEM; + case kNtErrorOperationAborted: + return EINTR; + case kNtErrorOutOfPaper: + return EACCES; + case kNtErrorPagedSystemResources: + return ENOMEM; + case kNtErrorPagefileQuota: + return ENOMEM; + case kNtErrorPathNotFound: + return ENOENT; + case kNtErrorPipeNotConnected: + return EPIPE; + case kNtErrorPortUnreachable: + return ECONNRESET; + case kNtErrorProtocolUnreachable: + return ENETUNREACH; + case kNtErrorRemNotList: + return ECONNREFUSED; + case kNtErrorRequestAborted: + return EINTR; + case kNtErrorReqNotAccep: + return EWOULDBLOCK; + case kNtErrorSectorNotFound: + return EACCES; + case kNtErrorSemTimeout: + return ETIMEDOUT; + case kNtErrorSharingViolation: + return EACCES; + case kNtErrorTooManyNames: + return ENOMEM; + case kNtErrorUnexpNetErr: + return ECONNABORTED; + case kNtErrorWorkingSetQuota: + return ENOMEM; + case kNtErrorWriteProtect: + return EACCES; + case kNtErrorWrongDisk: + return EACCES; + case WSAEACCES: + return EACCES; + case WSAEDISCON: + return EPIPE; + case WSAEFAULT: + return EFAULT; + case WSAEINPROGRESS: + return EBUSY; + case WSAEINVAL: + return EINVAL; + case WSAEPROCLIM: + return ENOMEM; + case WSAESHUTDOWN: + return EPIPE; + case WSANOTINITIALISED: + return ENETDOWN; + case WSASYSNOTREADY: + return ENETDOWN; + case WSAVERNOTSUPPORTED: + return ENOSYS; + default: + return error; + } +} diff --git a/libc/sock/poll-nt.c b/libc/sock/poll-nt.c index 7e28f5cd..5565be95 100644 --- a/libc/sock/poll-nt.c +++ b/libc/sock/poll-nt.c @@ -21,6 +21,7 @@ #include "libc/nt/struct/pollfd.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/consts/poll.h" #include "libc/sysv/errfuns.h" @@ -30,7 +31,7 @@ textwindows int poll$nt(struct pollfd *fds, uint64_t nfds, int32_t timeout_ms) { struct pollfd$nt ntfds[64]; if (nfds > 64) return einval(); for (i = 0; i < nfds; ++i) { - if (!isfdkind(fds[i].fd, kFdSocket)) return ebadf(); + if (!__isfdkind(fds[i].fd, kFdSocket)) return ebadf(); ntfds[i].handle = g_fds.p[fds[i].fd].handle; ntfds[i].events = fds[i].events & (POLLPRI | POLLIN | POLLOUT); } @@ -40,6 +41,6 @@ textwindows int poll$nt(struct pollfd *fds, uint64_t nfds, int32_t timeout_ms) { } return got; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/recvfrom-nt.c b/libc/sock/recvfrom-nt.c index e9c7994b..9a4c7971 100644 --- a/libc/sock/recvfrom-nt.c +++ b/libc/sock/recvfrom-nt.c @@ -20,6 +20,7 @@ #include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" /** * Performs recv(), recvfrom(), or readv() on Windows NT. @@ -32,12 +33,12 @@ textwindows ssize_t recvfrom$nt(struct Fd *fd, const struct iovec *iov, void *opt_out_srcaddr, uint32_t *opt_inout_srcaddrsize) { uint32_t got; - struct iovec$nt iovnt[16]; + struct NtIovec iovnt[16]; got = 0; if (WSARecvFrom(fd->handle, iovnt, iovec2nt(iovnt, iov, iovlen), &got, &flags, opt_out_srcaddr, opt_inout_srcaddrsize, NULL, NULL) != -1) { return got; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/recvfrom.c b/libc/sock/recvfrom.c index 06c98b4e..3abff169 100644 --- a/libc/sock/recvfrom.c +++ b/libc/sock/recvfrom.c @@ -51,7 +51,7 @@ ssize_t recvfrom(int fd, void *buf, size_t size, uint32_t flags, sockaddr2linux(opt_out_srcaddr); } return got; - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return recvfrom$nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, flags, opt_out_srcaddr, opt_inout_srcaddrsize); } else { diff --git a/libc/sock/select-nt.c b/libc/sock/select-nt.c new file mode 100644 index 00000000..591edf40 --- /dev/null +++ b/libc/sock/select-nt.c @@ -0,0 +1,114 @@ +/*-*- 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 │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/popcnt.h" +#include "libc/calls/internal.h" +#include "libc/macros.h" +#include "libc/mem/mem.h" +#include "libc/nt/winsock.h" +#include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" + +static int GetFdsPopcnt(int nfds, fd_set *fds) { + int i, n = 0; + if (fds) { + for (i = 0; i < nfds; ++i) { + n += popcnt(fds->fds_bits[i]); + } + } + return n; +} + +static int FindFdByHandle(int nfds, int64_t h) { + int i, n; + n = MIN(nfds << 3, g_fds.n); + for (i = 0; i < n; ++i) { + if (h == g_fds.p[i].handle && g_fds.p[i].kind != kFdEmpty) { + return i; + } + } + return -1; +} + +static struct NtFdSet *FdSetToNtFdSet(int nfds, fd_set *fds) { + int i, j, k, n, m, fd; + struct NtFdSet *ntfds; + if (fds && (n = GetFdsPopcnt(nfds, fds))) { + m = MIN(n, ARRAYLEN(ntfds->fd_array)); + ntfds = malloc(sizeof(struct NtFdSet)); + for (k = i = 0; i < nfds; ++i) { + if (fds->fds_bits[i]) { + for (j = 0; j < 64 && k < m; ++j) { + if ((fds->fds_bits[i] & (1ul << j)) && i * 8 + j < g_fds.n) { + ntfds->fd_array[k++] = g_fds.p[i * 8 + j].handle; + } + } + } + } + ntfds->fd_count = m; + return ntfds; + } else { + return NULL; + } +} + +static void NtFdSetToFdSet(int nfds, fd_set *fds, struct NtFdSet *ntfds) { + int i, fd; + if (ntfds) { + for (i = 0; i < nfds; ++i) { + fds->fds_bits[i] = 0; + } + for (i = 0; i < ntfds->fd_count; ++i) { + if ((fd = FindFdByHandle(nfds, ntfds->fd_array[i])) != -1) { + fds->fds_bits[fd >> 3] |= 1ul << (fd & 7); + } + } + } +} + +static struct NtTimeval *TimevalToNtTimeval(struct timeval *tv, + struct NtTimeval *nttv) { + if (tv) { + nttv->tv_sec = tv->tv_sec; + nttv->tv_usec = tv->tv_usec; + return nttv; + } else { + return NULL; + } +} + +int select$nt(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) { + int n, rc; + struct NtTimeval nttimeout, *nttimeoutp; + struct NtFdSet *ntreadfds, *ntwritefds, *ntexceptfds; + nfds = MIN(ARRAYLEN(readfds->fds_bits), ROUNDUP(nfds, 8)) >> 3; + ntreadfds = FdSetToNtFdSet(nfds, readfds); + ntwritefds = FdSetToNtFdSet(nfds, writefds); + ntexceptfds = FdSetToNtFdSet(nfds, exceptfds); + nttimeoutp = TimevalToNtTimeval(timeout, &nttimeout); + rc = __select$nt(0, ntreadfds, ntwritefds, ntexceptfds, nttimeoutp); + NtFdSetToFdSet(nfds, readfds, ntreadfds); + NtFdSetToFdSet(nfds, writefds, ntwritefds); + NtFdSetToFdSet(nfds, exceptfds, ntexceptfds); + free(ntreadfds); + free(ntwritefds); + free(ntexceptfds); + return rc; +} diff --git a/libc/sock/select.c b/libc/sock/select.c index 356cef9d..5d0b06e4 100644 --- a/libc/sock/select.c +++ b/libc/sock/select.c @@ -18,14 +18,19 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/timeval.h" -#include "libc/sock/select.internal.h" +#include "libc/dce.h" +#include "libc/sock/internal.h" #include "libc/sock/sock.h" /** * Does what poll() does except with a complicated bitset API. + * @note windows nt is limited to first 64 socket descriptors */ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { - /* TODO(jart): Windows */ - return select$sysv(nfds, readfds, writefds, exceptfds, timeout); + if (!IsWindows()) { + return select$sysv(nfds, readfds, writefds, exceptfds, timeout); + } else { + return select$nt(nfds, readfds, writefds, exceptfds, timeout); + } } diff --git a/libc/sock/select.internal.h b/libc/sock/select.internal.h deleted file mode 100644 index cdb61cae..00000000 --- a/libc/sock/select.internal.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_SOCK_SELECT_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_SOCK_SELECT_INTERNAL_H_ -#include "libc/sock/select.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -int32_t select$sysv(int32_t, fd_set *, fd_set *, fd_set *, struct timeval *); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_SOCK_SELECT_INTERNAL_H_ */ diff --git a/libc/sock/sendfile.c b/libc/sock/sendfile.c index 0c3ca23c..78ebdedf 100644 --- a/libc/sock/sendfile.c +++ b/libc/sock/sendfile.c @@ -31,7 +31,8 @@ static textwindows ssize_t sendfile$linux2nt(int outfd, int infd, size_t uptobytes) { struct NtOverlapped Overlapped; struct NtOverlapped *lpOverlapped; - if (!isfdkind(outfd, kFdSocket) || !isfdkind(outfd, kFdFile)) return ebadf(); + if (!__isfdkind(outfd, kFdSocket) || !__isfdkind(outfd, kFdFile)) + return ebadf(); if (inout_opt_inoffset) { memset(&Overlapped, 0, sizeof(Overlapped)); Overlapped.Pointer = (void *)(intptr_t)(*inout_opt_inoffset); @@ -44,7 +45,7 @@ static textwindows ssize_t sendfile$linux2nt(int outfd, int infd, lpOverlapped, NULL, 0)) { return uptobytes; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/sendto-nt.c b/libc/sock/sendto-nt.c index 99325f05..90eec025 100644 --- a/libc/sock/sendto-nt.c +++ b/libc/sock/sendto-nt.c @@ -22,6 +22,7 @@ #include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/consts/fileno.h" /** @@ -34,11 +35,11 @@ textwindows ssize_t sendto$nt(struct Fd *fd, const struct iovec *iov, size_t iovlen, uint32_t flags, void *opt_in_addr, uint32_t in_addrsize) { uint32_t sent; - struct iovec$nt iovnt[16]; + struct NtIovec iovnt[16]; if (WSASendTo(fd->handle, iovnt, iovec2nt(iovnt, iov, iovlen), &sent, flags, opt_in_addr, in_addrsize, NULL, NULL) != -1) { return sent; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/sendto.c b/libc/sock/sendto.c index 561702d2..bf3b18c9 100644 --- a/libc/sock/sendto.c +++ b/libc/sock/sendto.c @@ -58,7 +58,7 @@ ssize_t sendto(int fd, const void *buf, size_t size, uint32_t flags, sockaddr2bsd(&addr2); return sendto$sysv(fd, buf, size, flags, &addr2, addrsize); } - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return sendto$nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, flags, opt_addr, addrsize); } else { diff --git a/libc/sock/setsockopt.c b/libc/sock/setsockopt.c index 424fc51e..f83d26b8 100644 --- a/libc/sock/setsockopt.c +++ b/libc/sock/setsockopt.c @@ -39,7 +39,7 @@ static textwindows int setsockopt$nt(struct Fd *fd, int level, int optname, if (__setsockopt$nt(fd->handle, level, optname, optval, optlen) != -1) { return 0; } else { - return winsockerr(); + return __winsockerr(); } } @@ -70,7 +70,7 @@ int setsockopt(int fd, int level, int optname, const void *optval, } } while (setsockopt_polyfill(&optname)); return -1; - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return setsockopt$nt(&g_fds.p[fd], level, optname, optval, optlen); } else { return ebadf(); diff --git a/libc/sock/shutdown.c b/libc/sock/shutdown.c index cd6bece0..afb86cda 100644 --- a/libc/sock/shutdown.c +++ b/libc/sock/shutdown.c @@ -28,7 +28,7 @@ static int shutdown$nt(struct Fd *fd, int how) { if (__shutdown$nt(fd->handle, how) != -1) { return 0; } else { - return winsockerr(); + return __winsockerr(); } } @@ -48,7 +48,7 @@ int shutdown(int fd, int how) { /* TODO(jart): What's wrong with XNU shutdown()? */ return 0; } - } else if (isfdkind(fd, kFdSocket)) { + } else if (__isfdkind(fd, kFdSocket)) { return shutdown$nt(&g_fds.p[fd], how); } else { return ebadf(); diff --git a/libc/sock/sock.mk b/libc/sock/sock.mk index 7f7925fd..7371c5cc 100644 --- a/libc/sock/sock.mk +++ b/libc/sock/sock.mk @@ -19,6 +19,7 @@ LIBC_SOCK_A_CHECKS = \ $(LIBC_SOCK_A_HDRS:%=o/$(MODE)/%.ok) LIBC_SOCK_A_DIRECTDEPS = \ + LIBC_BITS \ LIBC_CALLS \ LIBC_CONV \ LIBC_FMT \ @@ -27,6 +28,8 @@ LIBC_SOCK_A_DIRECTDEPS = \ LIBC_STDIO \ LIBC_STUBS \ LIBC_NT_KERNELBASE \ + LIBC_NT_KERNEL32 \ + LIBC_NT_NTDLL \ LIBC_NT_WS2_32 \ LIBC_NT_MSWSOCK \ LIBC_NEXGEN32E \ diff --git a/libc/sock/socket-nt.c b/libc/sock/socket-nt.c index d0211b3e..3c15c9d2 100644 --- a/libc/sock/socket-nt.c +++ b/libc/sock/socket-nt.c @@ -20,31 +20,31 @@ #include "libc/calls/internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/yoink.inc" #include "libc/sysv/consts/fio.h" #include "libc/sysv/consts/sock.h" -STATIC_YOINK("closesocket$nt"); -STATIC_YOINK("kNtWsaData"); -STATIC_YOINK("recvfrom$nt"); -STATIC_YOINK("sendto$nt"); +#define CLOEXEC 0x00080000 +#define NONBLOCK 0x00000800 textwindows int socket$nt(int family, int type, int protocol) { int fd; - if ((fd = createfd()) == -1) return -1; - if ((g_fds.p[fd].handle = - WSASocket(family, type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK), protocol, - NULL, 0, (type & SOCK_CLOEXEC))) != -1) { - if (type & SOCK_NONBLOCK) { - uint32_t yes = 1; + uint32_t yes; + if ((fd = __getemptyfd()) == -1) return -1; + if ((g_fds.p[fd].handle = WSASocket(family, type & ~(CLOEXEC | NONBLOCK), + protocol, NULL, 0, (type & CLOEXEC))) != + -1) { + if (type & NONBLOCK) { + yes = 1; if (__ioctlsocket$nt(g_fds.p[fd].handle, FIONBIO, &yes) == -1) { __closesocket$nt(g_fds.p[fd].handle); - return winsockerr(); + return __winsockerr(); } } g_fds.p[fd].kind = kFdSocket; - g_fds.p[fd].flags = type; + g_fds.p[fd].flags = type & (CLOEXEC | NONBLOCK); return fd; } else { - return winsockerr(); + return __winsockerr(); } } diff --git a/libc/sock/winsockblock.c b/libc/sock/winsockblock.c index d0c65ac1..231dacc3 100644 --- a/libc/sock/winsockblock.c +++ b/libc/sock/winsockblock.c @@ -28,7 +28,7 @@ textwindows int64_t winsockblock(int64_t fh, unsigned eventbit, int64_t rc) { int64_t eh; struct NtWsaNetworkEvents ev; if (rc != -1) return rc; - if (WSAGetLastError() != EWOULDBLOCK) return winsockerr(); + if (WSAGetLastError() != EWOULDBLOCK) return __winsockerr(); eh = WSACreateEvent(); memset(&ev, 0, sizeof(ev)); if (WSAEventSelect(fh, eh, 1u << eventbit) != -1 && @@ -39,7 +39,7 @@ textwindows int64_t winsockblock(int64_t fh, unsigned eventbit, int64_t rc) { errno = ev.iErrorCode[eventbit]; } } else { - winsockerr(); + __winsockerr(); } WSACloseEvent(eh); return rc; diff --git a/libc/sock/winsockerr.c b/libc/sock/winsockerr.c index fbf616e9..e2c5dc0d 100644 --- a/libc/sock/winsockerr.c +++ b/libc/sock/winsockerr.c @@ -24,7 +24,7 @@ /** * Error return path for winsock wrappers. */ -textwindows int64_t winsockerr(void) { - errno = WSAGetLastError(); +textwindows int64_t __winsockerr(void) { + errno = MapDosErrorToErrno(WSAGetLastError()); return -1; } diff --git a/libc/sock/yoink.inc b/libc/sock/yoink.inc new file mode 100644 index 00000000..45d50cb4 --- /dev/null +++ b/libc/sock/yoink.inc @@ -0,0 +1,4 @@ +STATIC_YOINK("kNtWsaData"); // for winmain +STATIC_YOINK("closesocket$nt"); // for close +STATIC_YOINK("recvfrom$nt"); // for readv +STATIC_YOINK("sendto$nt"); // for writev diff --git a/libc/stdio/favail.c b/libc/stdio/favail.c index b8028a87..8ca598a1 100644 --- a/libc/stdio/favail.c +++ b/libc/stdio/favail.c @@ -19,6 +19,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/stdio.h" +/* TODO(jart): Delete or rework */ + /** * Returns number of bytes available in stream buffer. */ diff --git a/libc/stdio/fclose.c b/libc/stdio/fclose.c index 0971f491..37387409 100644 --- a/libc/stdio/fclose.c +++ b/libc/stdio/fclose.c @@ -37,10 +37,12 @@ */ int fclose(FILE *f) { int rc; - if (!f) return 0; /* good java behavior; glibc crashes */ + if (!f) return 0; _fflushunregister(f); fflush(f); - free_s(&f->buf); + if (!f->nofree) { + free_s(&f->buf); + } f->state = EOF; if (f->noclose) { f->fd = -1; diff --git a/libc/stdio/fdopen.c b/libc/stdio/fdopen.c index 4aae88d7..b0cc42f1 100644 --- a/libc/stdio/fdopen.c +++ b/libc/stdio/fdopen.c @@ -17,6 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/mem/mem.h" #include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/o.h" @@ -31,14 +33,21 @@ * @error ENOMEM */ FILE *fdopen(int fd, const char *mode) { - FILE *res; - if ((res = fmemopen(NULL, BUFSIZ, mode))) { - res->fd = fd; - res->reader = freadbuf; - res->writer = fwritebuf; - if ((res->iomode & O_ACCMODE) != O_RDONLY) { - _fflushregister(res); + FILE *f; + if ((f = calloc(1, sizeof(FILE)))) { + f->fd = fd; + f->reader = __freadbuf; + f->writer = __fwritebuf; + f->bufmode = ischardev(fd) ? _IOLBF : _IOFBF; + f->iomode = fopenflags(mode); + f->size = BUFSIZ; + if ((f->buf = valloc(f->size))) { + if ((f->iomode & O_ACCMODE) != O_RDONLY) { + _fflushregister(f); + } + return f; } + free(f); } - return res; + return NULL; } diff --git a/libc/stdio/feof.c b/libc/stdio/feof.c index fe615b09..d3464e50 100644 --- a/libc/stdio/feof.c +++ b/libc/stdio/feof.c @@ -19,6 +19,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/stdio.h" +/** + * Returns true if stream is in end-of-file state. + */ int feof(FILE *f) { return f->state == -1; } diff --git a/libc/stdio/fflush.c b/libc/stdio/fflush.c index d8813906..4f9bf061 100644 --- a/libc/stdio/fflush.c +++ b/libc/stdio/fflush.c @@ -45,7 +45,6 @@ static struct StdioFlush g_fflush; * * @param f is the stream handle * @return number of bytes written or -1 on error - * @see fwritebuf */ int fflush(FILE *f) { size_t i; @@ -62,19 +61,14 @@ int fflush(FILE *f) { } } } - } else if (f->fd != -1 && (f->iomode & O_WRONLY)) { - if (!f->state) { - while (f->beg != f->end) { - if ((wrote = fwritebuf(f)) != -1) { - res += wrote; - } else { - res = -1; - break; - } + } else if (f->fd != -1) { + while (!f->state && f->beg && !f->end) { + if ((wrote = __fwritebuf(f)) != -1) { + res += wrote; } - } else if (f->state != -1) { - res = fseterr(f, f->state); } + } else if (f->beg && f->beg < f->size) { + f->buf[f->beg] = 0; } return res; } diff --git a/libc/stdio/fgetc.c b/libc/stdio/fgetc.c index fbaf3a33..fdc98572 100644 --- a/libc/stdio/fgetc.c +++ b/libc/stdio/fgetc.c @@ -17,11 +17,17 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" /** * Reads uint8_t from stream. */ int fgetc(FILE *f) { - return getc(f); + int c; + if (f->beg >= f->end) { + if (!f->reader) return __fseteof(f); + if (f->reader(f) == -1) return -1; + } + return f->buf[f->beg++]; } diff --git a/libc/stdio/fmemopen.c b/libc/stdio/fmemopen.c index 92672951..007e1f81 100644 --- a/libc/stdio/fmemopen.c +++ b/libc/stdio/fmemopen.c @@ -17,12 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" #include "libc/bits/popcnt.h" -#include "libc/calls/calls.h" -#include "libc/errno.h" #include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" @@ -31,46 +27,46 @@ /** * Opens buffer as stream. * - * This function is the heart of the streams implementation, and it's - * truly magnificent for unit testing. - * * @param buf becomes owned by this function, and is allocated if NULL * @return new stream or NULL w/ errno - * @error ENOMEM, EINVAL */ FILE *fmemopen(void *buf, size_t size, const char *mode) { - FILE *res; + FILE *f; + char *p; unsigned flags; - if (buf && !size) { + if (size && size > 0x7ffff000) { einval(); return NULL; } - if (size && popcnt(size) != 1) { - einval(); - return NULL; - } - - if (!(res = calloc(1, sizeof(FILE)))) { + if (!(f = calloc(1, sizeof(FILE)))) { return NULL; } if (!buf) { - if (!size) size = FRAMESIZE; - if (!(buf = valloc(size))) { - free(res); + if (!size) size = BUFSIZ; + if (!(buf = calloc(1, size))) { + free(f); return NULL; } + } else { + f->nofree = true; + } + + f->fd = -1; + f->buf = buf; + f->size = size; + f->end = size; + f->iomode = fopenflags(mode); + + if (f->iomode & O_APPEND) { + if ((p = memchr(buf, '\0', size))) { + f->beg = p - (char *)buf; + } else { + f->beg = f->end; + } } - res->fd = -1; - setbuffer(res, buf, size); - res->bufmode = res->buf ? _IOFBF : _IONBF; - flags = fopenflags(mode); - res->iomode = (flags & O_ACCMODE) == O_RDWR - ? O_RDWR - : (flags & O_ACCMODE) == O_WRONLY ? O_WRONLY : O_RDONLY; - - return res; + return f; } diff --git a/libc/stdio/fputc.c b/libc/stdio/fputc.c index c6b522d9..7f589c42 100644 --- a/libc/stdio/fputc.c +++ b/libc/stdio/fputc.c @@ -27,20 +27,19 @@ * @return c (as unsigned char) if written or -1 w/ errno */ noinstrument int fputc(int c, FILE *f) { - if (c != -1) { + if (f->beg < f->size) { c &= 0xff; - f->buf[f->end] = c; - f->end = (f->end + 1) & (f->size - 1); - if (unlikely(f->beg == f->end || f->bufmode == _IONBF || - (f->bufmode == _IOLBF && c == '\n'))) { + f->buf[f->beg++] = c; + if (f->beg == f->size || f->bufmode == _IONBF || + (f->bufmode == _IOLBF && c == '\n')) { if (f->writer) { - return f->writer(f); - } else if (f->beg == f->end) { - return fseteof(f); + if (f->writer(f) == -1) return -1; + } else if (f->beg == f->size) { + f->beg = 0; } } return c; } else { - return fseteof(f); + return __fseteof(f); } } diff --git a/libc/stdio/fputwc.c b/libc/stdio/fputwc.c index 5e6184c1..00912d63 100644 --- a/libc/stdio/fputwc.c +++ b/libc/stdio/fputwc.c @@ -17,11 +17,9 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/limits.h" #include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/str/tpencode.internal.h" +#include "libc/str/tpenc.h" /** * Writes wide character to stream. @@ -29,15 +27,18 @@ * @return wc if written or -1 w/ errno */ wint_t fputwc(wchar_t wc, FILE *f) { - unsigned i, len; - char buf[MB_LEN_MAX]; + uint64_t w; if (wc != -1) { - len = tpencode(buf, sizeof(buf), wc, false); - for (i = 0; i < len; ++i) { - if (fputc(buf[i], f) == -1) return -1; - } + w = tpenc(wc); + do { + if (fputc(w & 0xff, f) != -1) { + w >>= 8; + } else { + return -1; + } + } while (w); return wc; } else { - return fseteof(f); + return __fseteof(f); } } diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c index a1ec63ab..04434b6e 100644 --- a/libc/stdio/fread.c +++ b/libc/stdio/fread.c @@ -41,7 +41,7 @@ size_t fread(void *buf, size_t stride, size_t count, FILE *f) { } else if (!(i % stride)) { return i / stride; } else { - return fseterr(f, EOVERFLOW); + return __fseterr(f, EOVERFLOW); } } return count; diff --git a/libc/stdio/freadbuf.c b/libc/stdio/freadbuf.c index 76b9f043..058f4daa 100644 --- a/libc/stdio/freadbuf.c +++ b/libc/stdio/freadbuf.c @@ -22,12 +22,12 @@ #include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" -int freadbuf(FILE *f) { +int __freadbuf(FILE *f) { ssize_t got; - got = read(f->fd, f->buf, f->size - 1); - if (got == -1) return fseterrno(f); - if (got == 0) return fseteof(f); + got = read(f->fd, f->buf, f->size); + if (got == -1) return __fseterrno(f); + if (got == 0) return __fseteof(f); f->beg = 0; - f->end = got & (f->size - 1); + f->end = got; return got; } diff --git a/libc/stdio/freopen.c b/libc/stdio/freopen.c index 0a598793..6273a59b 100644 --- a/libc/stdio/freopen.c +++ b/libc/stdio/freopen.c @@ -86,7 +86,7 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream) { kNtFileAttributeNormal)) { return stream; } else { - winerr(); + __winerr(); return NULL; } } diff --git a/libc/stdio/freplenish.c b/libc/stdio/freplenish.c index 2eaa90ac..69d3f3e0 100644 --- a/libc/stdio/freplenish.c +++ b/libc/stdio/freplenish.c @@ -24,6 +24,8 @@ #include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" +/* TODO(jart): Delete or rework */ + /** * Fills empty space in buffer with whatever's available. * @@ -34,11 +36,9 @@ int freplenish(FILE *f) { ssize_t rc; size_t got; struct iovec iov[2]; - if (f->beg == f->end) { f->beg = f->end = 0; } - if (f->beg <= f->end) { if (f->beg) { iov[0].iov_base = f->buf + f->end; @@ -57,14 +57,14 @@ int freplenish(FILE *f) { if (rc != -1) { if (rc) { got = rc; - f->end = (f->end + got) & (f->size - 1); + f->end = (f->end + got) % f->size; return got; } else { - return fseteof(f); + return __fseteof(f); } } else if (errno == EINTR || errno == EAGAIN) { return 0; } else { - return fseterrno(f); + return __fseterrno(f); } } diff --git a/libc/stdio/fscanf.c b/libc/stdio/fscanf.c index b136c07b..939e4d47 100644 --- a/libc/stdio/fscanf.c +++ b/libc/stdio/fscanf.c @@ -21,14 +21,25 @@ #include "libc/stdio/stdio.h" /** - * Stream decoder. - * @see libc/fmt/vcscanf.h + * Decodes data from stream. + * + * To read a line of data from a well-formed trustworthy file: + * + * int x, y; + * char text[256]; + * fscanf(f, "%d %d %s\n", &x, &y, text); + * + * Please note that this function is brittle by default, which makes it + * a good fit for yolo coding. With some toil it can be used in a way + * that makes it reasonably hardened although getline() may be better. + * + * @see libc/fmt/vcscanf.c */ int(fscanf)(FILE *stream, const char *fmt, ...) { int rc; va_list va; va_start(va, fmt); - rc = (vcscanf)((int (*)(void *))fgetc, stream, fmt, va); + rc = (vcscanf)((int (*)(void *))fgetc, (void *)ungetc, stream, fmt, va); va_end(va); return rc; } diff --git a/libc/stdio/fseek.c b/libc/stdio/fseek.c index fe582477..1024d0cf 100644 --- a/libc/stdio/fseek.c +++ b/libc/stdio/fseek.c @@ -20,6 +20,7 @@ #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/stdio/stdio.h" +#include "libc/sysv/consts/o.h" /** * Repositions open file stream. @@ -29,20 +30,32 @@ * is in the EOF state, this function can be used to restore it without * needing to reopen the file. * - * @param stream is a non-null stream handle + * @param f is a non-null stream handle * @param offset is the byte delta * @param whence can be SEET_SET, SEEK_CUR, or SEEK_END * @returns new offset or -1 on error */ -long fseek(FILE *stream, long offset, int whence) { - int skew = fflush(stream); - if (whence == SEEK_CUR && skew != -1) offset -= skew; +long fseek(FILE *f, long offset, int whence) { + int skew; int64_t newpos; - if ((newpos = lseek(stream->fd, offset, whence)) != -1) { - stream->state = 0; - return newpos; + if (f->fd != -1) { + if (whence == SEEK_CUR && f->beg < f->end) { + offset -= f->end - f->beg; + } + if (f->beg && !f->end) { + f->writer(f); + } + if ((newpos = lseek(f->fd, offset, whence)) != -1) { + f->state = 0; + f->beg = 0; + f->end = 0; + return newpos; + } else { + f->state = errno == ESPIPE ? EBADF : errno; + return -1; + } } else { - stream->state = errno == ESPIPE ? EBADF : errno; + f->beg = offset % f->size; return -1; } } diff --git a/libc/stdio/fseteof.c b/libc/stdio/fseteof.c index 1c6d12d8..4e095c3b 100644 --- a/libc/stdio/fseteof.c +++ b/libc/stdio/fseteof.c @@ -19,6 +19,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/internal.h" -long fseteof(FILE *f) { - return fseterr(f, -1); +long __fseteof(FILE *f) { + return __fseterr(f, -1); } diff --git a/libc/stdio/fseterr.c b/libc/stdio/fseterr.c index cce329bc..195e1d6a 100644 --- a/libc/stdio/fseterr.c +++ b/libc/stdio/fseterr.c @@ -21,7 +21,7 @@ #include "libc/errno.h" #include "libc/stdio/internal.h" -long fseterr(FILE *f, int err) { +long __fseterr(FILE *f, int err) { if (!err) err = -1; f->state = f->state <= 0 ? err : f->state; if (err > 0) errno = err; diff --git a/libc/stdio/fseterrno.c b/libc/stdio/fseterrno.c index f4f68f0e..a30fd988 100644 --- a/libc/stdio/fseterrno.c +++ b/libc/stdio/fseterrno.c @@ -20,4 +20,6 @@ #include "libc/errno.h" #include "libc/stdio/internal.h" -long fseterrno(FILE *f) { return fseterr(f, errno); } +long __fseterrno(FILE *f) { + return __fseterr(f, errno); +} diff --git a/libc/stdio/ftell.c b/libc/stdio/ftell.c index 988b858d..77d2b912 100644 --- a/libc/stdio/ftell.c +++ b/libc/stdio/ftell.c @@ -26,4 +26,6 @@ * @param stream is a non-null stream handle * @returns current byte offset from beginning of file, or -1 */ -long ftell(FILE *stream) { return fseek(stream, 0, SEEK_CUR); } +long ftell(FILE *stream) { + return fseek(stream, 0, SEEK_CUR); +} diff --git a/libc/stdio/fwrite.c b/libc/stdio/fwrite.c index f5a7e5a4..8effc0ae 100644 --- a/libc/stdio/fwrite.c +++ b/libc/stdio/fwrite.c @@ -37,7 +37,7 @@ size_t fwrite(const void *data, size_t stride, size_t count, FILE *f) { if (!(i % stride)) { return i / stride; } else { - return fseterr(f, EOVERFLOW); + return __fseterr(f, EOVERFLOW); } } } diff --git a/libc/stdio/fwritebuf.c b/libc/stdio/fwritebuf.c index 58259431..d114cd1a 100644 --- a/libc/stdio/fwritebuf.c +++ b/libc/stdio/fwritebuf.c @@ -18,28 +18,21 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/internal.h" -#include "libc/dce.h" #include "libc/errno.h" -#include "libc/nt/files.h" -#include "libc/runtime/runtime.h" #include "libc/stdio/internal.h" -#include "libc/stdio/stdio.h" +#include "libc/str/str.h" -/** - * One-shot writes data from stream buffer to underlying file or device. - * - * @param f is a non-null open stream handle - * @return number of bytes written or -1 on error - */ -int fwritebuf(FILE *f) { - ssize_t put; - unsigned bytes; - bytes = (f->beg < f->end ? f->end : f->size) - f->beg; - if ((put = write(f->fd, &f->buf[f->beg], bytes)) == -1) { +int __fwritebuf(FILE *f) { + ssize_t wrote; + if ((wrote = write(f->fd, f->buf, f->beg)) == -1) { if (errno == EINTR) return 0; - return (int)fseterrno(f); + return __fseterrno(f); } - f->beg = (unsigned)((f->beg + put) & (f->size - 1)); - return bytes; + if (wrote == f->beg) { + f->beg = 0; + } else { + memcpy(f->buf, f->buf + wrote, f->beg - wrote); + f->beg -= wrote; + } + return wrote; } diff --git a/libc/stdio/g_stderr_init.S b/libc/stdio/g_stderr_init.S index 2158bf8e..5a70f24b 100644 --- a/libc/stdio/g_stderr_init.S +++ b/libc/stdio/g_stderr_init.S @@ -35,10 +35,8 @@ ezlea g_stderr_buf,cx mov %rcx,24(%rax) #→ f.buf movl $BUFSIZ,32(%rax) #→ f.size - ezlea fwritebuf,cx - ezlea fswritebuf,dx - testb IsMetal() - cmove %rcx,%rdx + ezlea __fwritebuf,cx + mov %rcx,%rdx mov %rdx,48(%rax) #→ f.writer mov %rax,stderr(%rip) .init.end 400,_init_g_stderr,globl,hidden diff --git a/libc/stdio/g_stdin_init.S b/libc/stdio/g_stdin_init.S index 753e1b57..7a51a9ce 100644 --- a/libc/stdio/g_stdin_init.S +++ b/libc/stdio/g_stdin_init.S @@ -31,10 +31,8 @@ ezlea g_stdin_buf,cx mov %rcx,24(%rax) #→ f.buf movl $BUFSIZ,32(%rax) #→ f.size - ezlea freadbuf,cx - ezlea fsreadbuf,dx - testb IsMetal() - cmove %rcx,%rdx + ezlea __freadbuf,cx + mov %rcx,%rdx mov %rdx,40(%rax) #→ f.reader mov %rax,stdin(%rip) .init.end 400,_init_g_stdin,globl,hidden diff --git a/libc/stdio/g_stdout_init.S b/libc/stdio/g_stdout_init.S index ab91f9ca..e36cb982 100644 --- a/libc/stdio/g_stdout_init.S +++ b/libc/stdio/g_stdout_init.S @@ -33,10 +33,8 @@ ezlea g_stdout_buf,cx mov %rcx,24(%rax) #→ f.buf movl $BUFSIZ,32(%rax) #→ f.size - ezlea fwritebuf,cx - ezlea fswritebuf,dx - testb IsMetal() - cmovz %rcx,%rdx + ezlea __fwritebuf,cx + mov %rcx,%rdx mov %rdx,48(%rax) #→ f.writer mov %rax,stdout(%rip) .init.end 400,_init_g_stdout,globl,hidden diff --git a/libc/stdio/getdelim.c b/libc/stdio/getdelim.c index 63868e32..d5ca6046 100644 --- a/libc/stdio/getdelim.c +++ b/libc/stdio/getdelim.c @@ -51,7 +51,7 @@ ssize_t getdelim(char **line, size_t *n, int delim, FILE *f) { break; } if (i + 2 >= *n && !__grow(line, n, 1, 0)) { - fseterrno(f); + __fseterrno(f); break; } if (((*line)[i++] = c) == delim) { diff --git a/libc/stdio/internal.h b/libc/stdio/internal.h index d89ab135..56b28dab 100644 --- a/libc/stdio/internal.h +++ b/libc/stdio/internal.h @@ -12,13 +12,11 @@ extern unsigned char g_stderrbuf[BUFSIZ]; int _fflushregister(FILE *) hidden; void _fflushunregister(FILE *) hidden; -int freadbuf(FILE *) hidden; -int fwritebuf(FILE *) hidden; -int fsreadbuf(FILE *) hidden; -int fswritebuf(FILE *) hidden; -long fseteof(FILE *) hidden; -long fseterrno(FILE *) hidden; -long fseterr(FILE *, int) hidden; +int __freadbuf(FILE *) hidden; +int __fwritebuf(FILE *) hidden; +long __fseteof(FILE *) hidden; +long __fseterrno(FILE *) hidden; +long __fseterr(FILE *, int) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/stdio/scanf.c b/libc/stdio/scanf.c index 708a3ff6..fb961b08 100644 --- a/libc/stdio/scanf.c +++ b/libc/stdio/scanf.c @@ -28,7 +28,7 @@ int(scanf)(const char *fmt, ...) { int rc; va_list va; va_start(va, fmt); - rc = (vcscanf)((int (*)(void *))fgetc, stdin, fmt, va); + rc = (vcscanf)((int (*)(void *))fgetc, NULL, stdin, fmt, va); va_end(va); return rc; } diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index 32e8058c..c36cfb03 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -18,7 +18,8 @@ typedef struct FILE { uint32_t beg; // 0x10 uint32_t end; // 0x14 uint8_t *buf; // 0x18 - size_t size; // 0x20 + uint32_t size; // 0x20 + uint32_t nofree; // 0x24 int (*reader)(struct FILE *); // 0x28 int (*writer)(struct FILE *); // 0x30 } FILE; @@ -99,10 +100,8 @@ int vfscanf(FILE *, const char *, va_list); │ cosmopolitan § standard i/o » optimizations ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -int __getc_moar(FILE *); - #define putc(c, f) fputc(c, f) -#define getc(f) (f->beg + 1 < f->end ? f->buf[f->beg++] : __getc_moar(f)) +#define getc(f) (f->beg < f->end ? f->buf[f->beg++] : fgetc(f)) #if defined(__GNUC__) && !defined(__STRICT_ANSI__) #define printf(FMT, ...) (printf)(PFLINK(FMT), ##__VA_ARGS__) diff --git a/libc/stdio/ungetc.c b/libc/stdio/ungetc.c index 8f53ba5a..c61da93d 100644 --- a/libc/stdio/ungetc.c +++ b/libc/stdio/ungetc.c @@ -20,7 +20,7 @@ #include "libc/stdio/stdio.h" int ungetc(int c, FILE *f) { + f->beg = (f->beg - 1) & (f->size - 1); f->buf[f->beg] = c; - f->beg = (f->beg - 1u) & (f->size - 1); return c; } diff --git a/libc/stdio/vfscanf.c b/libc/stdio/vfscanf.c index 33730b76..98e002c7 100644 --- a/libc/stdio/vfscanf.c +++ b/libc/stdio/vfscanf.c @@ -25,5 +25,5 @@ * @see libc/fmt/vcscanf.h */ int(vfscanf)(FILE *stream, const char *fmt, va_list ap) { - return (vcscanf)((int (*)(void *))fgetc, stream, fmt, ap); + return (vcscanf)((void *)fgetc, (void *)ungetc, stream, fmt, ap); } diff --git a/libc/stdio/vscanf.c b/libc/stdio/vscanf.c index f5415a3e..50c79150 100644 --- a/libc/stdio/vscanf.c +++ b/libc/stdio/vscanf.c @@ -25,5 +25,5 @@ * @see libc/fmt/vcscanf.h */ int(vscanf)(const char *fmt, va_list ap) { - return (vcscanf)((int (*)(void *))fgetc, stdin, fmt, ap); + return (vcscanf)((int (*)(void *))fgetc, (void *)ungetc, stdin, fmt, ap); } diff --git a/libc/sysv/calls/epoll_create-sysv.s b/libc/sysv/calls/epoll_create-sysv.s new file mode 100644 index 00000000..05aa781a --- /dev/null +++ b/libc/sysv/calls/epoll_create-sysv.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.inc" +.scall epoll_create$sysv 0xffffffffffff00d5 globl diff --git a/libc/sysv/calls/epoll_create.s b/libc/sysv/calls/epoll_create.s deleted file mode 100644 index b2360a0c..00000000 --- a/libc/sysv/calls/epoll_create.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.inc" -.scall epoll_create 0xffffffffffff00d5 globl diff --git a/libc/sysv/calls/epoll_create1-sysv.s b/libc/sysv/calls/epoll_create1-sysv.s new file mode 100644 index 00000000..526544c2 --- /dev/null +++ b/libc/sysv/calls/epoll_create1-sysv.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.inc" +.scall epoll_create1$sysv 0xffffffffffff0123 globl diff --git a/libc/sysv/calls/epoll_create1.s b/libc/sysv/calls/epoll_create1.s deleted file mode 100644 index 10949f86..00000000 --- a/libc/sysv/calls/epoll_create1.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.inc" -.scall epoll_create1 0xffffffffffff0123 globl diff --git a/libc/sysv/calls/epoll_ctl-sysv.s b/libc/sysv/calls/epoll_ctl-sysv.s new file mode 100644 index 00000000..ce2d444e --- /dev/null +++ b/libc/sysv/calls/epoll_ctl-sysv.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.inc" +.scall epoll_ctl$sysv 0xffffffffffff00e9 globl diff --git a/libc/sysv/calls/epoll_ctl.s b/libc/sysv/calls/epoll_ctl.s deleted file mode 100644 index 905e1bbd..00000000 --- a/libc/sysv/calls/epoll_ctl.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.inc" -.scall epoll_ctl 0xffffffffffff00e9 globl diff --git a/libc/sysv/calls/epoll_wait-sysv.s b/libc/sysv/calls/epoll_wait-sysv.s new file mode 100644 index 00000000..5507e9bb --- /dev/null +++ b/libc/sysv/calls/epoll_wait-sysv.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.inc" +.scall epoll_wait$sysv 0xffffffffffff00e8 globl diff --git a/libc/sysv/calls/epoll_wait.s b/libc/sysv/calls/epoll_wait.s deleted file mode 100644 index 7863fee5..00000000 --- a/libc/sysv/calls/epoll_wait.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.inc" -.scall epoll_wait 0xffffffffffff00e8 globl diff --git a/libc/sysv/calls/getdomainname.s b/libc/sysv/calls/getdomainname.s deleted file mode 100644 index ea6d3967..00000000 --- a/libc/sysv/calls/getdomainname.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.inc" -.scall getdomainname 0xffff00a2ffffffff globl diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 9aeb80bc..d56de560 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -35,17 +35,17 @@ syscon errno E2BIG 7 7 7 7 1639 # unix consensus & kNtErrorInvalid syscon errno ENOEXEC 8 8 8 8 193 # unix consensus & kNtErrorBadExeFormat syscon errno EBADF 9 9 9 9 6 # bad file descriptor; cf. EBADFD; unix consensus & kNtErrorInvalidHandle syscon errno ECHILD 10 10 10 10 128 # unix consensus & kNtErrorWaitNoChildren -syscon errno EAGAIN 11 35 35 35 0x2733 # bsd consensus -syscon errno EWOULDBLOCK 11 35 35 35 0x2733 # bsd consensus +syscon errno EAGAIN 11 35 35 35 0x2733 # bsd consensus & WSAEWOULDBLOCK +syscon errno EWOULDBLOCK 11 35 35 35 0x2733 # bsd consensus & WSAEWOULDBLOCK syscon errno ENOMEM 12 12 12 12 14 # unix consensus & kNtErrorOutofmemory syscon errno EACCES 13 13 13 13 5 # unix consensus & kNtErrorAccessDenied syscon errno EFAULT 14 14 14 14 487 # unix consensus & kNtErrorInvalidAddress syscon errno ENOTBLK 15 15 15 15 26 # unix consensus & kNtErrorNotDosDisk syscon errno EBUSY 16 16 16 16 170 # unix consensus & kNtErrorBusy syscon errno EEXIST 17 17 17 17 183 # unix consensus & kNtErrorAlreadyExists (should be kNtErrorFileExists too) -syscon errno EXDEV 18 18 18 18 -1 # unix consensus +syscon errno EXDEV 18 18 18 18 17 # unix consensus & kNtErrorNotSameDevice syscon errno ENODEV 19 19 19 19 1200 # unix consensus & kNtErrorBadDevice -syscon errno ENOTDIR 20 20 20 20 3 # unix consensus & kNtErrorPathNotFound +syscon errno ENOTDIR 20 20 20 20 3 # unix consensus & kNtErrorPathNotFound (TODO) syscon errno EISDIR 21 21 21 21 267 # unix consensus & kNtErrorDirectoryNotSupported syscon errno EINVAL 22 22 22 22 87 # unix consensus & kNtErrorInvalidParameter syscon errno ENFILE 23 23 23 23 331 # unix consensus & kNtErrorTooManyDescriptors @@ -62,43 +62,43 @@ syscon errno EPIPE 32 32 32 32 109 # unix consensus & kNtErrorBrok syscon errno EDOM 33 33 33 33 -1 # bsd consensus syscon errno ERANGE 34 34 34 34 -1 # bsd consensus syscon errno EDEADLK 35 11 11 11 1131 # bsd consensus & kNtErrorPossibleDeadlock -syscon errno ENAMETOOLONG 36 63 63 63 0x274f # bsd consensus +syscon errno ENAMETOOLONG 36 63 63 63 0x274f # bsd consensus & WSAENAMETOOLONG syscon errno ENOLCK 37 77 77 77 -1 # bsd consensus -syscon errno ENOTEMPTY 39 66 66 66 0x2752 # bsd consensus -syscon errno ELOOP 40 62 62 62 0x274e # bsd consensus +syscon errno ENOTEMPTY 39 66 66 66 0x2752 # bsd consensus & WSAENOTEMPTY +syscon errno ELOOP 40 62 62 62 0x274e # bsd consensus & WSAELOOP syscon errno ENOMSG 42 91 83 90 -1 syscon errno EIDRM 43 90 82 89 -1 -syscon errno EUSERS 87 68 68 68 0x2754 # bsd consensus -syscon errno ENOTSOCK 88 38 38 38 0x2736 # bsd consensus -syscon errno EDESTADDRREQ 89 39 39 39 0x2737 # bsd consensus +syscon errno EUSERS 87 68 68 68 0x2754 # bsd consensus & WSAEUSERS +syscon errno ENOTSOCK 88 38 38 38 0x2736 # bsd consensus & WSAENOTSOCK +syscon errno EDESTADDRREQ 89 39 39 39 0x2737 # bsd consensus & WSAEDESTADDRREQ syscon errno EMSGSIZE 90 40 40 40 0x2738 # bsd consensus & WSAEMSGSIZE -syscon errno EPROTOTYPE 91 41 41 41 0x2739 # bsd consensus -syscon errno ENOPROTOOPT 92 42 42 42 0x273a # bsd consensus -syscon errno EPROTONOSUPPORT 93 43 43 43 0x273b # bsd consensus -syscon errno ESOCKTNOSUPPORT 94 44 44 44 0x273c # bsd consensus +syscon errno EPROTOTYPE 91 41 41 41 0x2739 # bsd consensus & WSAEPROTOTYPE +syscon errno ENOPROTOOPT 92 42 42 42 0x273a # bsd consensus & WSAENOPROTOOPT +syscon errno EPROTONOSUPPORT 93 43 43 43 0x273b # bsd consensus & WSAEPROTONOSUPPORT +syscon errno ESOCKTNOSUPPORT 94 44 44 44 0x273c # bsd consensus & WSAESOCKTNOSUPPORT syscon errno ENOTSUP 95 45 45 91 0x273d syscon errno EOPNOTSUPP 95 102 45 45 0x273d -syscon errno EPFNOSUPPORT 96 46 46 46 0x273e # bsd consensus -syscon errno EAFNOSUPPORT 97 47 47 47 0x273f # bsd consensus -syscon errno EADDRINUSE 98 48 48 48 0x2740 # bsd consensus -syscon errno EADDRNOTAVAIL 99 49 49 49 0x2741 # bsd consensus -syscon errno ENETDOWN 100 50 50 50 0x2742 # bsd consensus -syscon errno ENETUNREACH 101 51 51 51 0x2743 # bsd consensus -syscon errno ENETRESET 102 52 52 52 0x2744 # bsd consensus -syscon errno ECONNABORTED 103 53 53 53 0x2745 # bsd consensus +syscon errno EPFNOSUPPORT 96 46 46 46 0x273e # bsd consensus & WSAEPFNOSUPPORT +syscon errno EAFNOSUPPORT 97 47 47 47 0x273f # bsd consensus & WSAEAFNOSUPPORT +syscon errno EADDRINUSE 98 48 48 48 0x2740 # bsd consensus & WSAEADDRINUSE +syscon errno EADDRNOTAVAIL 99 49 49 49 0x2741 # bsd consensus & WSAEADDRNOTAVAIL +syscon errno ENETDOWN 100 50 50 50 0x2742 # bsd consensus & WSAENETDOWN +syscon errno ENETUNREACH 101 51 51 51 0x2743 # bsd consensus & WSAENETUNREACH +syscon errno ENETRESET 102 52 52 52 0x2744 # bsd consensus & WSAENETRESET +syscon errno ECONNABORTED 103 53 53 53 0x2745 # bsd consensus & WSAECONNABORTED syscon errno ECONNRESET 104 54 54 54 0x2746 # bsd consensus & WSAECONNRESET -syscon errno ENOBUFS 105 55 55 55 0x2747 # bsd consensus -syscon errno EISCONN 106 56 56 56 0x2748 # bsd consensus -syscon errno ENOTCONN 107 57 57 57 0x2749 # bsd consensus -syscon errno ESHUTDOWN 108 58 58 58 0x274a # bsd consensus -syscon errno ETOOMANYREFS 109 59 59 59 0x274b # bsd consensus -syscon errno ETIMEDOUT 110 60 60 60 0x274c # bsd consensus -syscon errno ECONNREFUSED 111 61 61 61 0x274d # bsd consensus -syscon errno EHOSTDOWN 112 64 64 64 0x2750 # bsd consensus -syscon errno EHOSTUNREACH 113 65 65 65 0x2751 # bsd consensus -syscon errno EALREADY 114 37 37 37 0x2735 # bsd consensus -syscon errno EINPROGRESS 115 36 36 36 0x2734 # bsd consensus -syscon errno ESTALE 116 70 70 70 0x2756 # bsd consensus +syscon errno ENOBUFS 105 55 55 55 0x2747 # bsd consensus & WSAENOBUFS +syscon errno EISCONN 106 56 56 56 0x2748 # bsd consensus & WSAEISCONN +syscon errno ENOTCONN 107 57 57 57 0x2749 # bsd consensus & WSAENOTCONN +syscon errno ESHUTDOWN 108 58 58 58 0x274a # bsd consensus & WSAESHUTDOWN +syscon errno ETOOMANYREFS 109 59 59 59 0x274b # bsd consensus & WSAETOOMANYREFS +syscon errno ETIMEDOUT 110 60 60 60 0x274c # bsd consensus & WSAETIMEDOUT +syscon errno ECONNREFUSED 111 61 61 61 0x274d # bsd consensus & WSAECONNREFUSED +syscon errno EHOSTDOWN 112 64 64 64 0x2750 # bsd consensus & WSAEHOSTDOWN +syscon errno EHOSTUNREACH 113 65 65 65 0x2751 # bsd consensus & WSAEHOSTUNREACH +syscon errno EALREADY 114 37 37 37 0x2735 # bsd consensus & WSAEALREADY +syscon errno EINPROGRESS 115 36 36 36 0x2734 # bsd consensus & WSAEINPROGRESS +syscon errno ESTALE 116 70 70 70 0x2756 # bsd consensus & WSAESTALE syscon errno ECHRNG 44 -1 -1 -1 -1 # bsd consensus syscon errno EL2NSYNC 45 -1 -1 -1 -1 # bsd consensus syscon errno EL3HLT 46 -1 -1 -1 -1 # bsd consensus @@ -220,7 +220,7 @@ syscon open O_CLOEXEC 0x080000 0x01000000 0x100000 0x010000 0x00080000 # syscon open O_TMPFILE 0x410000 0 0 0 0x04000100 # Linux 3.11+ (c. 2013) & kNtFileAttributeTemporary|kNtFileFlagDeleteOnClose syscon open O_SPARSE 0 0 0 0 0x00040000 # we invented it syscon open O_NDELAY 0x0800 4 4 4 0 # bsd consensus & kNtFileFlagWriteThrough>>8 → 0x00800000 (???) -syscon open O_NONBLOCK 0x0800 4 4 4 0 # bsd consensus +syscon open O_NONBLOCK 0x0800 4 4 4 0x00000800 # bsd consensus & faked on nt to be same as linux syscon open O_ASYNC 0x2000 0x40 0x40 0x40 0 # bsd consensus syscon open O_NOFOLLOW 0x020000 0x0100 0x0100 0x0100 0 # bsd consensus syscon open O_SYNC 0x101000 0x80 0x80 0x80 0 # bsd consensus @@ -388,10 +388,10 @@ syscon fcntl2 F_DUPFD 0 0 0 0 0 # consensus syscon fcntl2 F_GETFD 1 1 1 1 1 # unix consensus & faked nt syscon fcntl2 F_SETFD 2 2 2 2 2 # unix consensus & faked nt -syscon fcntl3 FD_CLOEXEC 1 1 1 1 1 # unix consensus & inverse nt (kNtHandleFlagInherit) +syscon fcntl3 FD_CLOEXEC 1 1 1 1 1 # unix consensus & faked nt syscon fcntl2 F_GETFL 3 3 3 3 3 # unix consensus & faked nt -syscon fcntl2 F_SETFL 4 4 4 4 4 # (unix consensus & faked nt) +syscon fcntl2 F_SETFL 4 4 4 4 4 # unix consensus & faked nt # fcntl3 O_NONBLOCK # fcntl3 O_APPEND # fcntl3 O_ASYNC @@ -498,25 +498,28 @@ syscon clock CLOCK_REALTIME_ALARM 8 -1 0 0 0 # bsd consensus syscon clock CLOCK_BOOTTIME_ALARM 9 -1 0 0 0 # bsd consensus syscon clock CLOCK_TAI 11 -1 0 0 0 # bsd consensus -syscon epoll EPOLLIN 1 0 0 0 0 -syscon epoll EPOLL_CTL_ADD 1 0 0 0 0 -syscon epoll EPOLLPRI 2 0 0 0 0 -syscon epoll EPOLL_CTL_DEL 2 0 0 0 0 -syscon epoll EPOLL_CTL_MOD 3 0 0 0 0 -syscon epoll EPOLLOUT 4 0 0 0 0 -syscon epoll EPOLLERR 8 0 0 0 0 -syscon epoll EPOLLHUP 0x10 0 0 0 0 -syscon epoll EPOLLRDNORM 0x40 0 0 0 0 -syscon epoll EPOLLRDBAND 0x80 0 0 0 0 -syscon epoll EPOLLWRNORM 0x0100 0 0 0 0 -syscon epoll EPOLLWRBAND 0x0200 0 0 0 0 -syscon epoll EPOLLMSG 0x0400 0 0 0 0 -syscon epoll EPOLLRDHUP 0x2000 0 0 0 0 -syscon epoll EPOLL_CLOEXEC 0x080000 0 0 0 0 -syscon epoll EPOLLEXCLUSIVE 0x10000000 0 0 0 0 -syscon epoll EPOLLWAKEUP 0x20000000 0 0 0 0 -syscon epoll EPOLLONESHOT 0x40000000 0 0 0 0 -syscon epoll EPOLLET 0x80000000 0 0 0 0 +# epoll +# +# group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary +syscon epoll EPOLL_CLOEXEC 0x080000 0x01000000 0x100000 0x010000 0x80000 # O_CLOEXEC +syscon epoll EPOLL_CTL_ADD 1 1 1 1 1 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLL_CTL_DEL 2 2 2 2 2 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLL_CTL_MOD 3 3 3 3 3 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLIN 1 1 1 1 1 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLOUT 4 4 4 4 4 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLERR 8 8 8 8 8 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLPRI 2 2 2 2 2 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLHUP 0x10 0x10 0x10 0x10 0x10 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLRDNORM 0x40 0x40 0x40 0x40 0x40 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLRDBAND 0x80 0x80 0x80 0x80 0x80 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLWRNORM 0x0100 0x0100 0x0100 0x0100 0x0100 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLWRBAND 0x0200 0x0200 0x0200 0x0200 0x0200 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLMSG 0x0400 0x0400 0x0400 0x0400 0x0400 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLRDHUP 0x2000 0x2000 0x2000 0x2000 0x2000 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLEXCLUSIVE 0x10000000 0x10000000 0x10000000 0x10000000 0x10000000 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLWAKEUP 0x20000000 0x20000000 0x20000000 0x20000000 0x20000000 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLONESHOT 0x40000000 0x40000000 0x40000000 0x40000000 0x40000000 # forced consensus, linux only natively, polyfilled elsewhere +syscon epoll EPOLLET 0x80000000 0x80000000 0x80000000 0x80000000 0x80000000 # forced consensus, linux only natively, polyfilled elsewhere # {set,get}sockopt(fd, level=SOL_SOCKET, X, ...) # @@ -1646,8 +1649,8 @@ syscon sock SOCK_DGRAM 2 2 2 2 2 # consensus syscon sock SOCK_RAW 3 3 3 3 3 # consensus syscon sock SOCK_RDM 4 4 4 4 4 # consensus syscon sock SOCK_SEQPACKET 5 5 5 5 5 # consensus -syscon sock SOCK_CLOEXEC 0x080000 0x080000 0x10000000 0x8000 0x80 # faked xnu & WSA_FLAG_NO_HANDLE_INHERIT -syscon sock SOCK_NONBLOCK 0x0800 0x0800 0x20000000 0x4000 0x0800 # faked xnu; faked nt; socket() will ioctl(FIONBIO=1) +syscon sock SOCK_CLOEXEC 0x080000 0x080000 0x10000000 0x8000 0x00080000 # faked xnu & faked nt to be some as O_CLOEXEC +syscon sock SOCK_NONBLOCK 0x0800 0x0800 0x20000000 0x4000 0x00000800 # faked xnu & faked nt to be same as O_NONBLOC and socket() will ioctl(FIONBIO=1) syscon sock SOCK_DCCP 6 0 0 0 0 # what is it? syscon sock SOCK_PACKET 10 0 0 0 0 # what is it? diff --git a/libc/sysv/consts/EPOLLERR.s b/libc/sysv/consts/EPOLLERR.s index 83377ce1..25b9e3f2 100644 --- a/libc/sysv/consts/EPOLLERR.s +++ b/libc/sysv/consts/EPOLLERR.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLERR 8 0 0 0 0 +.syscon epoll EPOLLERR 8 8 8 8 8 diff --git a/libc/sysv/consts/EPOLLET.s b/libc/sysv/consts/EPOLLET.s index 4ca1ee3e..b47a4131 100644 --- a/libc/sysv/consts/EPOLLET.s +++ b/libc/sysv/consts/EPOLLET.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLET 0x80000000 0 0 0 0 +.syscon epoll EPOLLET 0x80000000 0x80000000 0x80000000 0x80000000 0x80000000 diff --git a/libc/sysv/consts/EPOLLEXCLUSIVE.s b/libc/sysv/consts/EPOLLEXCLUSIVE.s index 1a736b54..edbd88fb 100644 --- a/libc/sysv/consts/EPOLLEXCLUSIVE.s +++ b/libc/sysv/consts/EPOLLEXCLUSIVE.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLEXCLUSIVE 0x10000000 0 0 0 0 +.syscon epoll EPOLLEXCLUSIVE 0x10000000 0x10000000 0x10000000 0x10000000 0x10000000 diff --git a/libc/sysv/consts/EPOLLHUP.s b/libc/sysv/consts/EPOLLHUP.s index b51205a3..8cb61144 100644 --- a/libc/sysv/consts/EPOLLHUP.s +++ b/libc/sysv/consts/EPOLLHUP.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLHUP 0x10 0 0 0 0 +.syscon epoll EPOLLHUP 0x10 0x10 0x10 0x10 0x10 diff --git a/libc/sysv/consts/EPOLLIN.s b/libc/sysv/consts/EPOLLIN.s index e644a80c..bffe6f48 100644 --- a/libc/sysv/consts/EPOLLIN.s +++ b/libc/sysv/consts/EPOLLIN.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLIN 1 0 0 0 0 +.syscon epoll EPOLLIN 1 1 1 1 1 diff --git a/libc/sysv/consts/EPOLLMSG.s b/libc/sysv/consts/EPOLLMSG.s index 65efc02c..c8e3ce86 100644 --- a/libc/sysv/consts/EPOLLMSG.s +++ b/libc/sysv/consts/EPOLLMSG.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLMSG 0x0400 0 0 0 0 +.syscon epoll EPOLLMSG 0x0400 0x0400 0x0400 0x0400 0x0400 diff --git a/libc/sysv/consts/EPOLLONESHOT.s b/libc/sysv/consts/EPOLLONESHOT.s index 28cb2cc7..dd4c3144 100644 --- a/libc/sysv/consts/EPOLLONESHOT.s +++ b/libc/sysv/consts/EPOLLONESHOT.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLONESHOT 0x40000000 0 0 0 0 +.syscon epoll EPOLLONESHOT 0x40000000 0x40000000 0x40000000 0x40000000 0x40000000 diff --git a/libc/sysv/consts/EPOLLOUT.s b/libc/sysv/consts/EPOLLOUT.s index dc1a59a3..ca7c708b 100644 --- a/libc/sysv/consts/EPOLLOUT.s +++ b/libc/sysv/consts/EPOLLOUT.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLOUT 4 0 0 0 0 +.syscon epoll EPOLLOUT 4 4 4 4 4 diff --git a/libc/sysv/consts/EPOLLPRI.s b/libc/sysv/consts/EPOLLPRI.s index e93cbb55..8a30ab70 100644 --- a/libc/sysv/consts/EPOLLPRI.s +++ b/libc/sysv/consts/EPOLLPRI.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLPRI 2 0 0 0 0 +.syscon epoll EPOLLPRI 2 2 2 2 2 diff --git a/libc/sysv/consts/EPOLLRDBAND.s b/libc/sysv/consts/EPOLLRDBAND.s index e68e983e..8dd39011 100644 --- a/libc/sysv/consts/EPOLLRDBAND.s +++ b/libc/sysv/consts/EPOLLRDBAND.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLRDBAND 0x80 0 0 0 0 +.syscon epoll EPOLLRDBAND 0x80 0x80 0x80 0x80 0x80 diff --git a/libc/sysv/consts/EPOLLRDHUP.s b/libc/sysv/consts/EPOLLRDHUP.s index 932b4605..ead4ebc2 100644 --- a/libc/sysv/consts/EPOLLRDHUP.s +++ b/libc/sysv/consts/EPOLLRDHUP.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLRDHUP 0x2000 0 0 0 0 +.syscon epoll EPOLLRDHUP 0x2000 0x2000 0x2000 0x2000 0x2000 diff --git a/libc/sysv/consts/EPOLLRDNORM.s b/libc/sysv/consts/EPOLLRDNORM.s index 43e20c05..650a6ecc 100644 --- a/libc/sysv/consts/EPOLLRDNORM.s +++ b/libc/sysv/consts/EPOLLRDNORM.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLRDNORM 0x40 0 0 0 0 +.syscon epoll EPOLLRDNORM 0x40 0x40 0x40 0x40 0x40 diff --git a/libc/sysv/consts/EPOLLWAKEUP.s b/libc/sysv/consts/EPOLLWAKEUP.s index 8e0b821d..bc40b694 100644 --- a/libc/sysv/consts/EPOLLWAKEUP.s +++ b/libc/sysv/consts/EPOLLWAKEUP.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLWAKEUP 0x20000000 0 0 0 0 +.syscon epoll EPOLLWAKEUP 0x20000000 0x20000000 0x20000000 0x20000000 0x20000000 diff --git a/libc/sysv/consts/EPOLLWRBAND.s b/libc/sysv/consts/EPOLLWRBAND.s index 9484dfa9..1b4ff926 100644 --- a/libc/sysv/consts/EPOLLWRBAND.s +++ b/libc/sysv/consts/EPOLLWRBAND.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLWRBAND 0x0200 0 0 0 0 +.syscon epoll EPOLLWRBAND 0x0200 0x0200 0x0200 0x0200 0x0200 diff --git a/libc/sysv/consts/EPOLLWRNORM.s b/libc/sysv/consts/EPOLLWRNORM.s index 22a14d40..5ee7238e 100644 --- a/libc/sysv/consts/EPOLLWRNORM.s +++ b/libc/sysv/consts/EPOLLWRNORM.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLLWRNORM 0x0100 0 0 0 0 +.syscon epoll EPOLLWRNORM 0x0100 0x0100 0x0100 0x0100 0x0100 diff --git a/libc/sysv/consts/EPOLL_CLOEXEC.s b/libc/sysv/consts/EPOLL_CLOEXEC.s index 8b6c6d78..840bc8ed 100644 --- a/libc/sysv/consts/EPOLL_CLOEXEC.s +++ b/libc/sysv/consts/EPOLL_CLOEXEC.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLL_CLOEXEC 0x080000 0 0 0 0 +.syscon epoll EPOLL_CLOEXEC 0x080000 0x01000000 0x100000 0x010000 0x80000 diff --git a/libc/sysv/consts/EPOLL_CTL_ADD.s b/libc/sysv/consts/EPOLL_CTL_ADD.s index aba831c8..9a8348dd 100644 --- a/libc/sysv/consts/EPOLL_CTL_ADD.s +++ b/libc/sysv/consts/EPOLL_CTL_ADD.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLL_CTL_ADD 1 0 0 0 0 +.syscon epoll EPOLL_CTL_ADD 1 1 1 1 1 diff --git a/libc/sysv/consts/EPOLL_CTL_DEL.s b/libc/sysv/consts/EPOLL_CTL_DEL.s index 52b96a93..23b981b0 100644 --- a/libc/sysv/consts/EPOLL_CTL_DEL.s +++ b/libc/sysv/consts/EPOLL_CTL_DEL.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLL_CTL_DEL 2 0 0 0 0 +.syscon epoll EPOLL_CTL_DEL 2 2 2 2 2 diff --git a/libc/sysv/consts/EPOLL_CTL_MOD.s b/libc/sysv/consts/EPOLL_CTL_MOD.s index 10fdbdd2..af841d9b 100644 --- a/libc/sysv/consts/EPOLL_CTL_MOD.s +++ b/libc/sysv/consts/EPOLL_CTL_MOD.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon epoll EPOLL_CTL_MOD 3 0 0 0 0 +.syscon epoll EPOLL_CTL_MOD 3 3 3 3 3 diff --git a/libc/sysv/consts/EXDEV.s b/libc/sysv/consts/EXDEV.s index 38789785..2a7a7696 100644 --- a/libc/sysv/consts/EXDEV.s +++ b/libc/sysv/consts/EXDEV.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon errno EXDEV 18 18 18 18 -1 +.syscon errno EXDEV 18 18 18 18 17 diff --git a/libc/sysv/consts/O_NONBLOCK.s b/libc/sysv/consts/O_NONBLOCK.s index da0bd835..d9474532 100644 --- a/libc/sysv/consts/O_NONBLOCK.s +++ b/libc/sysv/consts/O_NONBLOCK.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon open O_NONBLOCK 0x0800 4 4 4 0 +.syscon open O_NONBLOCK 0x0800 4 4 4 0x00000800 diff --git a/libc/sysv/consts/SOCK_CLOEXEC.s b/libc/sysv/consts/SOCK_CLOEXEC.s index 001a21ed..819449f1 100644 --- a/libc/sysv/consts/SOCK_CLOEXEC.s +++ b/libc/sysv/consts/SOCK_CLOEXEC.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon sock SOCK_CLOEXEC 0x080000 0x080000 0x10000000 0x8000 0x80 +.syscon sock SOCK_CLOEXEC 0x080000 0x080000 0x10000000 0x8000 0x00080000 diff --git a/libc/sysv/consts/SOCK_NONBLOCK.s b/libc/sysv/consts/SOCK_NONBLOCK.s index 2ab79e6e..0ca0a8fe 100644 --- a/libc/sysv/consts/SOCK_NONBLOCK.s +++ b/libc/sysv/consts/SOCK_NONBLOCK.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon sock SOCK_NONBLOCK 0x0800 0x0800 0x20000000 0x4000 0x0800 +.syscon sock SOCK_NONBLOCK 0x0800 0x0800 0x20000000 0x4000 0x00000800 diff --git a/libc/sysv/consts/_posix.h b/libc/sysv/consts/_posix.h index 54752d69..2d3ad81c 100644 --- a/libc/sysv/consts/_posix.h +++ b/libc/sysv/consts/_posix.h @@ -1,87 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX_H_ #include "libc/runtime/symbolic.h" - -#define _POSIX_ADVISORY_INFO SYMBOLIC(_POSIX_ADVISORY_INFO) -#define _POSIX_AIO_LISTIO_MAX SYMBOLIC(_POSIX_AIO_LISTIO_MAX) -#define _POSIX_AIO_MAX SYMBOLIC(_POSIX_AIO_MAX) -#define _POSIX_ARG_MAX SYMBOLIC(_POSIX_ARG_MAX) -#define _POSIX_ASYNCHRONOUS_IO SYMBOLIC(_POSIX_ASYNCHRONOUS_IO) -#define _POSIX_BARRIERS SYMBOLIC(_POSIX_BARRIERS) -#define _POSIX_CHILD_MAX SYMBOLIC(_POSIX_CHILD_MAX) -#define _POSIX_CHOWN_RESTRICTED SYMBOLIC(_POSIX_CHOWN_RESTRICTED) -#define _POSIX_CLOCKRES_MIN SYMBOLIC(_POSIX_CLOCKRES_MIN) -#define _POSIX_CLOCK_SELECTION SYMBOLIC(_POSIX_CLOCK_SELECTION) -#define _POSIX_CPUTIME SYMBOLIC(_POSIX_CPUTIME) -#define _POSIX_DELAYTIMER_MAX SYMBOLIC(_POSIX_DELAYTIMER_MAX) -#define _POSIX_FSYNC SYMBOLIC(_POSIX_FSYNC) -#define _POSIX_HOST_NAME_MAX SYMBOLIC(_POSIX_HOST_NAME_MAX) -#define _POSIX_IPV6 SYMBOLIC(_POSIX_IPV6) -#define _POSIX_JOB_CONTROL SYMBOLIC(_POSIX_JOB_CONTROL) -#define _POSIX_LINK_MAX SYMBOLIC(_POSIX_LINK_MAX) -#define _POSIX_LOGIN_NAME_MAX SYMBOLIC(_POSIX_LOGIN_NAME_MAX) -#define _POSIX_MAPPED_FILES SYMBOLIC(_POSIX_MAPPED_FILES) -#define _POSIX_MAX_CANON SYMBOLIC(_POSIX_MAX_CANON) -#define _POSIX_MAX_INPUT SYMBOLIC(_POSIX_MAX_INPUT) -#define _POSIX_MEMLOCK SYMBOLIC(_POSIX_MEMLOCK) -#define _POSIX_MEMLOCK_RANGE SYMBOLIC(_POSIX_MEMLOCK_RANGE) -#define _POSIX_MEMORY_PROTECTION SYMBOLIC(_POSIX_MEMORY_PROTECTION) -#define _POSIX_MESSAGE_PASSING SYMBOLIC(_POSIX_MESSAGE_PASSING) -#define _POSIX_MONOTONIC_CLOCK SYMBOLIC(_POSIX_MONOTONIC_CLOCK) -#define _POSIX_MQ_OPEN_MAX SYMBOLIC(_POSIX_MQ_OPEN_MAX) -#define _POSIX_MQ_PRIO_MAX SYMBOLIC(_POSIX_MQ_PRIO_MAX) -#define _POSIX_NAME_MAX SYMBOLIC(_POSIX_NAME_MAX) -#define _POSIX_NGROUPS_MAX SYMBOLIC(_POSIX_NGROUPS_MAX) -#define _POSIX_NO_TRUNC SYMBOLIC(_POSIX_NO_TRUNC) -#define _POSIX_OPEN_MAX SYMBOLIC(_POSIX_OPEN_MAX) -#define _POSIX_PATH_MAX SYMBOLIC(_POSIX_PATH_MAX) -#define _POSIX_PIPE_BUF SYMBOLIC(_POSIX_PIPE_BUF) -#define _POSIX_RAW_SOCKETS SYMBOLIC(_POSIX_RAW_SOCKETS) -#define _POSIX_READER_WRITER_LOCKS SYMBOLIC(_POSIX_READER_WRITER_LOCKS) -#define _POSIX_REALTIME_SIGNALS SYMBOLIC(_POSIX_REALTIME_SIGNALS) -#define _POSIX_REGEXP SYMBOLIC(_POSIX_REGEXP) -#define _POSIX_RE_DUP_MAX SYMBOLIC(_POSIX_RE_DUP_MAX) -#define _POSIX_RTSIG_MAX SYMBOLIC(_POSIX_RTSIG_MAX) -#define _POSIX_SAVED_IDS SYMBOLIC(_POSIX_SAVED_IDS) -#define _POSIX_SEMAPHORES SYMBOLIC(_POSIX_SEMAPHORES) -#define _POSIX_SEM_NSEMS_MAX SYMBOLIC(_POSIX_SEM_NSEMS_MAX) -#define _POSIX_SEM_VALUE_MAX SYMBOLIC(_POSIX_SEM_VALUE_MAX) -#define _POSIX_SHARED_MEMORY_OBJECTS SYMBOLIC(_POSIX_SHARED_MEMORY_OBJECTS) -#define _POSIX_SHELL SYMBOLIC(_POSIX_SHELL) -#define _POSIX_SIGQUEUE_MAX SYMBOLIC(_POSIX_SIGQUEUE_MAX) -#define _POSIX_SPAWN SYMBOLIC(_POSIX_SPAWN) -#define _POSIX_SPIN_LOCKS SYMBOLIC(_POSIX_SPIN_LOCKS) -#define _POSIX_SSIZE_MAX SYMBOLIC(_POSIX_SSIZE_MAX) -#define _POSIX_SS_REPL_MAX SYMBOLIC(_POSIX_SS_REPL_MAX) -#define _POSIX_STREAM_MAX SYMBOLIC(_POSIX_STREAM_MAX) -#define _POSIX_SYMLINK_MAX SYMBOLIC(_POSIX_SYMLINK_MAX) -#define _POSIX_SYMLOOP_MAX SYMBOLIC(_POSIX_SYMLOOP_MAX) -#define _POSIX_THREADS SYMBOLIC(_POSIX_THREADS) -#define _POSIX_THREAD_ATTR_STACKADDR SYMBOLIC(_POSIX_THREAD_ATTR_STACKADDR) -#define _POSIX_THREAD_ATTR_STACKSIZE SYMBOLIC(_POSIX_THREAD_ATTR_STACKSIZE) -#define _POSIX_THREAD_CPUTIME SYMBOLIC(_POSIX_THREAD_CPUTIME) -#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS \ - SYMBOLIC(_POSIX_THREAD_DESTRUCTOR_ITERATIONS) -#define _POSIX_THREAD_KEYS_MAX SYMBOLIC(_POSIX_THREAD_KEYS_MAX) -#define _POSIX_THREAD_PRIORITY_SCHEDULING \ - SYMBOLIC(_POSIX_THREAD_PRIORITY_SCHEDULING) -#define _POSIX_THREAD_PROCESS_SHARED SYMBOLIC(_POSIX_THREAD_PROCESS_SHARED) -#define _POSIX_THREAD_SAFE_FUNCTIONS SYMBOLIC(_POSIX_THREAD_SAFE_FUNCTIONS) -#define _POSIX_THREAD_THREADS_MAX SYMBOLIC(_POSIX_THREAD_THREADS_MAX) -#define _POSIX_TIMEOUTS SYMBOLIC(_POSIX_TIMEOUTS) -#define _POSIX_TIMERS SYMBOLIC(_POSIX_TIMERS) -#define _POSIX_TIMER_MAX SYMBOLIC(_POSIX_TIMER_MAX) -#define _POSIX_TRACE_EVENT_NAME_MAX SYMBOLIC(_POSIX_TRACE_EVENT_NAME_MAX) -#define _POSIX_TRACE_NAME_MAX SYMBOLIC(_POSIX_TRACE_NAME_MAX) -#define _POSIX_TRACE_SYS_MAX SYMBOLIC(_POSIX_TRACE_SYS_MAX) -#define _POSIX_TRACE_USER_EVENT_MAX SYMBOLIC(_POSIX_TRACE_USER_EVENT_MAX) -#define _POSIX_TTY_NAME_MAX SYMBOLIC(_POSIX_TTY_NAME_MAX) -#define _POSIX_TZNAME_MAX SYMBOLIC(_POSIX_TZNAME_MAX) -#define _POSIX_V6_LP64_OFF64 SYMBOLIC(_POSIX_V6_LP64_OFF64) -#define _POSIX_V7_LP64_OFF64 SYMBOLIC(_POSIX_V7_LP64_OFF64) -#define _POSIX_VDISABLE SYMBOLIC(_POSIX_VDISABLE) -#define _POSIX_VERSION SYMBOLIC(_POSIX_VERSION) - #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -165,4 +84,85 @@ hidden extern const long _POSIX_VERSION; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ + +#define _POSIX_ADVISORY_INFO SYMBOLIC(_POSIX_ADVISORY_INFO) +#define _POSIX_AIO_LISTIO_MAX SYMBOLIC(_POSIX_AIO_LISTIO_MAX) +#define _POSIX_AIO_MAX SYMBOLIC(_POSIX_AIO_MAX) +#define _POSIX_ARG_MAX SYMBOLIC(_POSIX_ARG_MAX) +#define _POSIX_ASYNCHRONOUS_IO SYMBOLIC(_POSIX_ASYNCHRONOUS_IO) +#define _POSIX_BARRIERS SYMBOLIC(_POSIX_BARRIERS) +#define _POSIX_CHILD_MAX SYMBOLIC(_POSIX_CHILD_MAX) +#define _POSIX_CHOWN_RESTRICTED SYMBOLIC(_POSIX_CHOWN_RESTRICTED) +#define _POSIX_CLOCKRES_MIN SYMBOLIC(_POSIX_CLOCKRES_MIN) +#define _POSIX_CLOCK_SELECTION SYMBOLIC(_POSIX_CLOCK_SELECTION) +#define _POSIX_CPUTIME SYMBOLIC(_POSIX_CPUTIME) +#define _POSIX_DELAYTIMER_MAX SYMBOLIC(_POSIX_DELAYTIMER_MAX) +#define _POSIX_FSYNC SYMBOLIC(_POSIX_FSYNC) +#define _POSIX_HOST_NAME_MAX SYMBOLIC(_POSIX_HOST_NAME_MAX) +#define _POSIX_IPV6 SYMBOLIC(_POSIX_IPV6) +#define _POSIX_JOB_CONTROL SYMBOLIC(_POSIX_JOB_CONTROL) +#define _POSIX_LINK_MAX SYMBOLIC(_POSIX_LINK_MAX) +#define _POSIX_LOGIN_NAME_MAX SYMBOLIC(_POSIX_LOGIN_NAME_MAX) +#define _POSIX_MAPPED_FILES SYMBOLIC(_POSIX_MAPPED_FILES) +#define _POSIX_MAX_CANON SYMBOLIC(_POSIX_MAX_CANON) +#define _POSIX_MAX_INPUT SYMBOLIC(_POSIX_MAX_INPUT) +#define _POSIX_MEMLOCK SYMBOLIC(_POSIX_MEMLOCK) +#define _POSIX_MEMLOCK_RANGE SYMBOLIC(_POSIX_MEMLOCK_RANGE) +#define _POSIX_MEMORY_PROTECTION SYMBOLIC(_POSIX_MEMORY_PROTECTION) +#define _POSIX_MESSAGE_PASSING SYMBOLIC(_POSIX_MESSAGE_PASSING) +#define _POSIX_MONOTONIC_CLOCK SYMBOLIC(_POSIX_MONOTONIC_CLOCK) +#define _POSIX_MQ_OPEN_MAX SYMBOLIC(_POSIX_MQ_OPEN_MAX) +#define _POSIX_MQ_PRIO_MAX SYMBOLIC(_POSIX_MQ_PRIO_MAX) +#define _POSIX_NAME_MAX SYMBOLIC(_POSIX_NAME_MAX) +#define _POSIX_NGROUPS_MAX SYMBOLIC(_POSIX_NGROUPS_MAX) +#define _POSIX_NO_TRUNC SYMBOLIC(_POSIX_NO_TRUNC) +#define _POSIX_OPEN_MAX SYMBOLIC(_POSIX_OPEN_MAX) +#define _POSIX_PATH_MAX SYMBOLIC(_POSIX_PATH_MAX) +#define _POSIX_PIPE_BUF SYMBOLIC(_POSIX_PIPE_BUF) +#define _POSIX_RAW_SOCKETS SYMBOLIC(_POSIX_RAW_SOCKETS) +#define _POSIX_READER_WRITER_LOCKS SYMBOLIC(_POSIX_READER_WRITER_LOCKS) +#define _POSIX_REALTIME_SIGNALS SYMBOLIC(_POSIX_REALTIME_SIGNALS) +#define _POSIX_REGEXP SYMBOLIC(_POSIX_REGEXP) +#define _POSIX_RE_DUP_MAX SYMBOLIC(_POSIX_RE_DUP_MAX) +#define _POSIX_RTSIG_MAX SYMBOLIC(_POSIX_RTSIG_MAX) +#define _POSIX_SAVED_IDS SYMBOLIC(_POSIX_SAVED_IDS) +#define _POSIX_SEMAPHORES SYMBOLIC(_POSIX_SEMAPHORES) +#define _POSIX_SEM_NSEMS_MAX SYMBOLIC(_POSIX_SEM_NSEMS_MAX) +#define _POSIX_SEM_VALUE_MAX SYMBOLIC(_POSIX_SEM_VALUE_MAX) +#define _POSIX_SHARED_MEMORY_OBJECTS SYMBOLIC(_POSIX_SHARED_MEMORY_OBJECTS) +#define _POSIX_SHELL SYMBOLIC(_POSIX_SHELL) +#define _POSIX_SIGQUEUE_MAX SYMBOLIC(_POSIX_SIGQUEUE_MAX) +#define _POSIX_SPAWN SYMBOLIC(_POSIX_SPAWN) +#define _POSIX_SPIN_LOCKS SYMBOLIC(_POSIX_SPIN_LOCKS) +#define _POSIX_SSIZE_MAX SYMBOLIC(_POSIX_SSIZE_MAX) +#define _POSIX_SS_REPL_MAX SYMBOLIC(_POSIX_SS_REPL_MAX) +#define _POSIX_STREAM_MAX SYMBOLIC(_POSIX_STREAM_MAX) +#define _POSIX_SYMLINK_MAX SYMBOLIC(_POSIX_SYMLINK_MAX) +#define _POSIX_SYMLOOP_MAX SYMBOLIC(_POSIX_SYMLOOP_MAX) +#define _POSIX_THREADS SYMBOLIC(_POSIX_THREADS) +#define _POSIX_THREAD_ATTR_STACKADDR SYMBOLIC(_POSIX_THREAD_ATTR_STACKADDR) +#define _POSIX_THREAD_ATTR_STACKSIZE SYMBOLIC(_POSIX_THREAD_ATTR_STACKSIZE) +#define _POSIX_THREAD_CPUTIME SYMBOLIC(_POSIX_THREAD_CPUTIME) +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS \ + SYMBOLIC(_POSIX_THREAD_DESTRUCTOR_ITERATIONS) +#define _POSIX_THREAD_KEYS_MAX SYMBOLIC(_POSIX_THREAD_KEYS_MAX) +#define _POSIX_THREAD_PRIORITY_SCHEDULING \ + SYMBOLIC(_POSIX_THREAD_PRIORITY_SCHEDULING) +#define _POSIX_THREAD_PROCESS_SHARED SYMBOLIC(_POSIX_THREAD_PROCESS_SHARED) +#define _POSIX_THREAD_SAFE_FUNCTIONS SYMBOLIC(_POSIX_THREAD_SAFE_FUNCTIONS) +#define _POSIX_THREAD_THREADS_MAX SYMBOLIC(_POSIX_THREAD_THREADS_MAX) +#define _POSIX_TIMEOUTS SYMBOLIC(_POSIX_TIMEOUTS) +#define _POSIX_TIMERS SYMBOLIC(_POSIX_TIMERS) +#define _POSIX_TIMER_MAX SYMBOLIC(_POSIX_TIMER_MAX) +#define _POSIX_TRACE_EVENT_NAME_MAX SYMBOLIC(_POSIX_TRACE_EVENT_NAME_MAX) +#define _POSIX_TRACE_NAME_MAX SYMBOLIC(_POSIX_TRACE_NAME_MAX) +#define _POSIX_TRACE_SYS_MAX SYMBOLIC(_POSIX_TRACE_SYS_MAX) +#define _POSIX_TRACE_USER_EVENT_MAX SYMBOLIC(_POSIX_TRACE_USER_EVENT_MAX) +#define _POSIX_TTY_NAME_MAX SYMBOLIC(_POSIX_TTY_NAME_MAX) +#define _POSIX_TZNAME_MAX SYMBOLIC(_POSIX_TZNAME_MAX) +#define _POSIX_V6_LP64_OFF64 SYMBOLIC(_POSIX_V6_LP64_OFF64) +#define _POSIX_V7_LP64_OFF64 SYMBOLIC(_POSIX_V7_LP64_OFF64) +#define _POSIX_VDISABLE SYMBOLIC(_POSIX_VDISABLE) +#define _POSIX_VERSION SYMBOLIC(_POSIX_VERSION) + #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX_H_ */ diff --git a/libc/sysv/consts/epoll.h b/libc/sysv/consts/epoll.h new file mode 100644 index 00000000..66014e20 --- /dev/null +++ b/libc/sysv/consts/epoll.h @@ -0,0 +1,55 @@ +#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_EPOLL_H_ +#define COSMOPOLITAN_LIBC_SYSV_CONSTS_EPOLL_H_ +#include "libc/runtime/symbolic.h" +#include "libc/sysv/consts/o.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +hidden extern const long EPOLL_CLOEXEC; + +hidden extern const long EPOLL_CTL_ADD; +hidden extern const long EPOLL_CTL_DEL; +hidden extern const long EPOLL_CTL_MOD; + +hidden extern const long EPOLLIN; +hidden extern const long EPOLLPRI; +hidden extern const long EPOLLOUT; +hidden extern const long EPOLLERR; +hidden extern const long EPOLLHUP; +hidden extern const long EPOLLRDNORM; +hidden extern const long EPOLLRDBAND; +hidden extern const long EPOLLWRNORM; +hidden extern const long EPOLLWRBAND; +hidden extern const long EPOLLMSG; +hidden extern const long EPOLLRDHUP; +hidden extern const long EPOLLEXCLUSIVE; +hidden extern const long EPOLLWAKEUP; +hidden extern const long EPOLLONESHOT; +hidden extern const long EPOLLET; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ + +#define EPOLL_CLOEXEC O_CLOEXEC + +#define EPOLL_CTL_ADD LITERALLY(1) +#define EPOLL_CTL_DEL LITERALLY(2) +#define EPOLL_CTL_MOD LITERALLY(3) + +#define EPOLLIN LITERALLY(1) +#define EPOLLPRI LITERALLY(2) +#define EPOLLOUT LITERALLY(4) +#define EPOLLERR LITERALLY(8) +#define EPOLLHUP LITERALLY(0x10) +#define EPOLLRDNORM LITERALLY(0x40) +#define EPOLLRDBAND LITERALLY(0x80) +#define EPOLLWRNORM LITERALLY(0x0100) +#define EPOLLWRBAND LITERALLY(0x0200) +#define EPOLLMSG LITERALLY(0x0400) +#define EPOLLRDHUP LITERALLY(0x2000) +#define EPOLLEXCLUSIVE LITERALLY(0x10000000) +#define EPOLLWAKEUP LITERALLY(0x20000000) +#define EPOLLONESHOT LITERALLY(0x40000000) +#define EPOLLET LITERALLY(0x80000000) + +#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_EPOLL_H_ */ diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 7156acf8..90fcf2fc 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -240,9 +240,9 @@ scall io_getevents 0xffffffffffff00d0 globl scall io_submit 0xffffffffffff00d1 globl scall io_cancel 0xffffffffffff00d2 globl scall lookup_dcookie 0xffffffffffff00d4 globl -scall epoll_create 0xffffffffffff00d5 globl -scall epoll_wait 0xffffffffffff00e8 globl -scall epoll_ctl 0xffffffffffff00e9 globl +scall 'epoll_create$sysv' 0xffffffffffff00d5 globl +scall 'epoll_wait$sysv' 0xffffffffffff00e8 globl +scall 'epoll_ctl$sysv' 0xffffffffffff00e9 globl scall getdents 0x00630110ffff00d9 globl hidden # getdents64 on linux scall set_tid_address 0xffffffffffff00da globl scall restart_syscall 0xffffffffffff00db globl @@ -310,7 +310,7 @@ scall '__accept4$sysv' 0x005d021dffff0120 globl hidden # Linux 2.6.28+ scall '__dup3$sysv' 0x0066ffffffff0124 globl hidden # Linux 2.6.27+ scall '__pipe2$sysv' 0x0065021effff0125 globl hidden # Linux 2.6.27+ scall epoll_pwait 0xffffffffffff0119 globl -scall epoll_create1 0xffffffffffff0123 globl +scall 'epoll_create1$sysv' 0xffffffffffff0123 globl scall perf_event_open 0xffffffffffff012a globl scall inotify_init1 0xffffffffffff0126 globl scall rt_tgsigqueueinfo 0xffffffffffff0129 globl diff --git a/libc/testlib/showerror_.c b/libc/testlib/showerror_.c index a6a05530..1b2c7e78 100644 --- a/libc/testlib/showerror_.c +++ b/libc/testlib/showerror_.c @@ -30,7 +30,7 @@ #include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" -STATIC_YOINK("isfdkind"); +STATIC_YOINK("__isfdkind"); const char *testlib_showerror_errno; const char *testlib_showerror_file; diff --git a/libc/time/times.c b/libc/time/times.c index 9486a514..f065d605 100644 --- a/libc/time/times.c +++ b/libc/time/times.c @@ -45,7 +45,7 @@ static noinline long times2(struct tms *out_times, struct rusage *ru) { struct NtFileTime CreationTime, ExitTime, KernelTime, UserTime; if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime)) { - return winerr(); + return __winerr(); } FileTimeToTimeVal(&tv, UserTime); out_times->tms_utime = convertmicros(&tv, tick); diff --git a/libc/zipos/close.c b/libc/zipos/close.c index 4c6be835..794c462d 100644 --- a/libc/zipos/close.c +++ b/libc/zipos/close.c @@ -20,17 +20,23 @@ #include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/mem/mem.h" -#include "libc/zipos/zipos.h" +#include "libc/nt/runtime.h" +#include "libc/zipos/zipos.internal.h" /** * Closes compressed object. * * @param fd is vetted by close() */ -int __zipos_close(struct ZiposHandle *h) { - if (h) { - free(h->map); - free(h); +int __zipos_close(int fd) { + struct ZiposHandle *h; + h = (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle; + if (!IsWindows()) { + close$sysv(fd); + } else { + CloseHandle(h->handle); } + free(h->freeme); + free(h); return 0; } diff --git a/libc/zipos/find.c b/libc/zipos/find.c index d0da16bf..1b360791 100644 --- a/libc/zipos/find.c +++ b/libc/zipos/find.c @@ -23,7 +23,7 @@ #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/zip.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) { size_t i, cf; diff --git a/libc/zipos/fstat.c b/libc/zipos/fstat.c index 98312d1d..f6eff2e3 100644 --- a/libc/zipos/fstat.c +++ b/libc/zipos/fstat.c @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Reads file metadata from αcτµαlly pδrταblε εxεcµταblε object store. diff --git a/libc/zipos/get.c b/libc/zipos/get.c index 6df01710..3a342f49 100644 --- a/libc/zipos/get.c +++ b/libc/zipos/get.c @@ -26,7 +26,7 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" #include "libc/zip.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Returns pointer to zip central directory of current executable. @@ -38,25 +38,18 @@ struct Zipos *__zipos_get(void) { size_t mapsize; uint8_t *cdir; void *map; - int fd; if (!once) { - if (ZIP_CDIR_MAGIC(__zip_end) == kZipCdirHdrMagic) { - zipos.map = _base; - zipos.cdir = __zip_end; - } else { - exe = (const char *)getauxval(AT_EXECFN); - if ((fd = open(exe, O_RDONLY)) != -1) { - if ((mapsize = getfiledescriptorsize(fd)) != SIZE_MAX && - (map = mmap(NULL, mapsize, PROT_READ, MAP_SHARED, fd, 0)) != - MAP_FAILED) { - if ((cdir = zipfindcentraldir(map, mapsize))) { - zipos.map = map; - zipos.cdir = cdir; - } else { - munmap(map, mapsize); - } + exe = (const char *)getauxval(AT_EXECFN); + if ((zipos.fd = open(exe, O_RDONLY)) != -1) { + if ((mapsize = getfiledescriptorsize(zipos.fd)) != SIZE_MAX && + (map = mmap(NULL, mapsize, PROT_READ, MAP_SHARED, zipos.fd, 0)) != + MAP_FAILED) { + if ((cdir = zipfindcentraldir(map, mapsize))) { + zipos.map = map; + zipos.cdir = cdir; + } else { + munmap(map, mapsize); } - close(fd); } } once = true; diff --git a/libc/zipos/open.c b/libc/zipos/open.c index acd8a555..3ccb0fd1 100644 --- a/libc/zipos/open.c +++ b/libc/zipos/open.c @@ -24,6 +24,7 @@ #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/stat.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/macros.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" @@ -37,7 +38,7 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/zip.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" #include "third_party/zlib/zlib.h" static int __zipos_inflate_fast(struct ZiposHandle *h, uint8_t *data, @@ -48,7 +49,7 @@ static int __zipos_inflate_fast(struct ZiposHandle *h, uint8_t *data, zs.next_in = data; zs.avail_in = size; zs.total_in = size; - zs.next_out = h->mem; + zs.next_out = h->freeme; zs.avail_out = h->size; zs.total_out = h->size; zs.zfree = Z_NULL; @@ -80,7 +81,7 @@ static int __zipos_inflate_fast(struct ZiposHandle *h, uint8_t *data, static int __zipos_inflate_tiny(struct ZiposHandle *h, uint8_t *data, size_t size) { struct DeflateState ds; - return undeflate(h->mem, h->size, data, size, &ds) != -1 ? 0 : eio(); + return undeflate(h->freeme, h->size, data, size, &ds) != -1 ? 0 : eio(); } static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags, @@ -93,39 +94,37 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags, assert(ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf) == kZipCompressionNone || ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf) == kZipCompressionDeflate); - if ((fd = createfd()) == -1) return -1; if (!(h = calloc(1, sizeof(*h)))) return -1; h->cfile = cf; if ((h->size = ZIP_LFILE_UNCOMPRESSEDSIZE(zipos->map + lf))) { if (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) { assert(ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf)); - h->mapsize = h->size; - if ((h->map = malloc(h->mapsize)) != MAP_FAILED) { - h->mem = h->map; - if ((IsTiny() ? __zipos_inflate_tiny : __zipos_inflate_fast)( - h, ZIP_LFILE_CONTENT(zipos->map + lf), - ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf)) == -1) { - fd = -1; - } - } else { - fd = -1; + if ((h->freeme = malloc(h->size)) && + (IsTiny() ? __zipos_inflate_tiny : __zipos_inflate_fast)( + h, ZIP_LFILE_CONTENT(zipos->map + lf), + ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf)) != -1) { + h->mem = h->freeme; } } else { h->mem = ZIP_LFILE_CONTENT(zipos->map + lf); } } - if (!IsTiny() && fd != -1 && + if (!IsTiny() && h->mem && crc32_z(0, h->mem, h->size) != ZIP_LFILE_CRC32(zipos->map + lf)) { - fd = eio(); + errno = EIO; + h->mem = NULL; } - if (fd != -1) { + if (h->mem && (fd = __ensurefds(dup(zipos->fd))) != -1) { + h->handle = g_fds.p[fd].handle; g_fds.p[fd].kind = kFdZip; g_fds.p[fd].handle = (intptr_t)h; g_fds.p[fd].flags = flags; + return fd; } else { - __zipos_close(h); + free(h->freeme); + free(h); + return -1; } - return fd; } /** diff --git a/libc/zipos/parseuri.c b/libc/zipos/parseuri.c index b2dd794d..d5d06b18 100644 --- a/libc/zipos/parseuri.c +++ b/libc/zipos/parseuri.c @@ -18,7 +18,7 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/str/str.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" const char kZiposSchemePrefix[4] = "zip:"; diff --git a/libc/zipos/read.c b/libc/zipos/read.c index 1051ef67..5f3135ec 100644 --- a/libc/zipos/read.c +++ b/libc/zipos/read.c @@ -22,7 +22,7 @@ #include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" #include "libc/str/str.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Reads data from zip store object. diff --git a/libc/zipos/stat-impl.c b/libc/zipos/stat-impl.c index 3cd7888d..8eb0814c 100644 --- a/libc/zipos/stat-impl.c +++ b/libc/zipos/stat-impl.c @@ -24,7 +24,7 @@ #include "libc/str/str.h" #include "libc/sysv/errfuns.h" #include "libc/zip.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) { if (zipos && st) { diff --git a/libc/zipos/stat.c b/libc/zipos/stat.c index f27039d2..626603b8 100644 --- a/libc/zipos/stat.c +++ b/libc/zipos/stat.c @@ -18,7 +18,7 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" /** * Reads file metadata from αcτµαlly pδrταblε εxεcµταblε object store. diff --git a/libc/zipos/zipos.h b/libc/zipos/zipos.internal.h similarity index 55% rename from libc/zipos/zipos.h rename to libc/zipos/zipos.internal.h index cb387267..23d42e50 100644 --- a/libc/zipos/zipos.h +++ b/libc/zipos/zipos.internal.h @@ -7,6 +7,7 @@ struct stat; struct iovec; struct Zipos { + int fd; uint8_t *map; uint8_t *cdir; }; @@ -17,28 +18,28 @@ struct ZiposUri { }; struct ZiposHandle { - uint8_t *mem; /* deflated file base */ - size_t size; /* byte length of file */ + uint8_t *mem; /* uncompressed file memory */ + size_t size; /* byte length of file memory */ size_t pos; /* read/write byte offset state */ uint32_t cfile; /* central directory entry rva */ - uint8_t *map; - size_t mapsize; + int64_t handle; + uint8_t *freeme; }; extern const char kZiposSchemePrefix[4]; -struct Zipos *__zipos_get(void); -ssize_t __zipos_parseuri(const char *, struct ZiposUri *); +int __zipos_close(int) hidden; +struct Zipos *__zipos_get(void) hidden; +ssize_t __zipos_parseuri(const char *, struct ZiposUri *) hidden; ssize_t __zipos_find(struct Zipos *, const struct ZiposUri *); -int __zipos_close(struct ZiposHandle *); -int __zipos_open(const struct ZiposUri *, unsigned, int); -int __zipos_stat(const struct ZiposUri *, struct stat *); -int __zipos_fstat(const struct ZiposHandle *, struct stat *); -int __zipos_stat_impl(struct Zipos *, size_t, struct stat *); +int __zipos_open(const struct ZiposUri *, unsigned, int) hidden; +int __zipos_stat(const struct ZiposUri *, struct stat *) hidden; +int __zipos_fstat(const struct ZiposHandle *, struct stat *) hidden; +int __zipos_stat_impl(struct Zipos *, size_t, struct stat *) hidden; ssize_t __zipos_read(struct ZiposHandle *, const struct iovec *, size_t, - ssize_t); + ssize_t) hidden; ssize_t __zipos_write(struct ZiposHandle *, const struct iovec *, size_t, - ssize_t); + ssize_t) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/zipos/zipos.mk b/libc/zipos/zipos.mk index 82a95029..a07a1683 100644 --- a/libc/zipos/zipos.mk +++ b/libc/zipos/zipos.mk @@ -35,6 +35,8 @@ LIBC_ZIPOS_A_DIRECTDEPS = \ LIBC_SYSV \ LIBC_STR \ LIBC_STUBS \ + LIBC_SYSV_CALLS \ + LIBC_NT_KERNELBASE \ THIRD_PARTY_ZLIB LIBC_ZIPOS_A_DEPS := \ diff --git a/net/http/http.h b/net/http/http.h index c3679773..ddd8931f 100644 --- a/net/http/http.h +++ b/net/http/http.h @@ -95,6 +95,7 @@ int GetHttpMethod(const char *, size_t); int ParseHttpRequest(struct HttpRequest *, const char *, size_t); int NegotiateHttpRequest(int, const char *, uint32_t *, char *, uint32_t *, uint32_t *, bool, long double); +long ParseContentLength(const struct HttpRequest *, const char *); char *FormatHttpDateTime(char[hasatleast 30], struct tm *); bool ParseHttpRange(const char *, size_t, long, long *, long *); unsigned ParseHttpVersion(const char *, size_t); diff --git a/libc/stdio/getcmoar.c b/net/http/parsecontentlength.c similarity index 82% rename from libc/stdio/getcmoar.c rename to net/http/parsecontentlength.c index 9bf51d6f..22271bf8 100644 --- a/libc/stdio/getcmoar.c +++ b/net/http/parsecontentlength.c @@ -17,16 +17,19 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/internal.h" -#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "net/http/http.h" -int __getc_moar(FILE *f) { - int b; - if (f->beg == f->end) { - if (!f->reader) return fseteof(f); - if (f->reader(f) == -1) return -1; +long ParseContentLength(const struct HttpRequest *req, const char *p) { + long i, r, n = 0; + for (i = req->headers[kHttpContentLength].a; + i < req->headers[kHttpContentLength].b; ++i) { + if (isdigit(p[i])) { + if (!__builtin_mul_overflow(n, 10, &r) && + !__builtin_add_overflow(r, p[i] - '0', &r)) { + n = r; + } + } } - b = f->buf[f->beg]; - f->beg = (f->beg + 1) & (f->size - 1); - return b; + return n; } diff --git a/net/http/parsehttprequest.c b/net/http/parsehttprequest.c index be5d1910..d6e7b92a 100644 --- a/net/http/parsehttprequest.c +++ b/net/http/parsehttprequest.c @@ -114,12 +114,12 @@ int ParseHttpRequest(struct HttpRequest *req, const char *p, size_t n) { case LF2: if (c == '\n') { req->length = i + 1; - return 0; + return i + 1; } return ebadmsg(); default: unreachable; } } - return ebadmsg(); + return 0; } diff --git a/test/libc/dns/parsehoststxt_test.c b/test/libc/dns/parsehoststxt_test.c index f4607c69..156e47c3 100644 --- a/test/libc/dns/parsehoststxt_test.c +++ b/test/libc/dns/parsehoststxt_test.c @@ -40,14 +40,14 @@ TEST(parsehoststxt, testEmpty) { } TEST(parsehoststxt, testCorrectlyTokenizesAndSorts) { - const char kInput[] = - "# this is a comment\n" - "# IP HOST1 HOST2\n" - "203.0.113.1 lol.example. lol\n" - "203.0.113.2 cat.example. cat\n"; + const char kInput[] = "# this is a comment\n" + "# IP HOST1 HOST2\n" + "203.0.113.1 lol.example. lol\n" + "203.0.113.2 cat.example. cat\n"; struct HostsTxt *ht = calloc(1, sizeof(struct HostsTxt)); FILE *f = fmemopen(NULL, BUFSIZ, "r+"); - ASSERT_EQ(strlen(kInput), fwrite(kInput, 1, strlen(kInput), f)); + fwrite(kInput, 1, strlen(kInput), f); + rewind(f); ASSERT_EQ(0, parsehoststxt(ht, f)); sorthoststxt(ht); ASSERT_EQ(4, ht->entries.i); @@ -68,12 +68,10 @@ TEST(parsehoststxt, testCorrectlyTokenizesAndSorts) { } TEST(parsehoststxt, testIpv6_isIgnored) { - const char kInput[] = - "::1 boop\n" - "203.0.113.2 cat # ignore me\n"; + const char kInput[] = "::1 boop\n" + "203.0.113.2 cat # ignore me\n"; struct HostsTxt *ht = calloc(1, sizeof(struct HostsTxt)); - FILE *f = fmemopen(NULL, BUFSIZ, "r+"); - ASSERT_EQ(strlen(kInput), fwrite(kInput, 1, strlen(kInput), f)); + FILE *f = fmemopen(kInput, strlen(kInput), "r+"); ASSERT_EQ(0, parsehoststxt(ht, f)); ASSERT_EQ(1, ht->entries.i); EXPECT_STREQ("cat", &ht->strings.p[ht->entries.p[0].name]); diff --git a/test/libc/dns/parseresolvconf_test.c b/test/libc/dns/parseresolvconf_test.c index 9c6f4626..d7a10038 100644 --- a/test/libc/dns/parseresolvconf_test.c +++ b/test/libc/dns/parseresolvconf_test.c @@ -46,8 +46,7 @@ TEST(parseresolvconf, testCorrectlyTokenizes) { "nameserver 203.0.113.2 \n" " nameserver 203.0.113.1\n"; struct ResolvConf *rv = calloc(1, sizeof(struct ResolvConf)); - FILE *f = fmemopen(NULL, BUFSIZ, "r+"); - ASSERT_EQ(strlen(kInput), fwrite(kInput, 1, strlen(kInput), f)); + FILE *f = fmemopen(kInput, strlen(kInput), "r+"); ASSERT_EQ(2, parseresolvconf(rv, f)); ASSERT_EQ(2, rv->nameservers.i); EXPECT_EQ(AF_INET, rv->nameservers.p[0].sin_family); diff --git a/test/libc/dns/resolvehoststxt_test.c b/test/libc/dns/resolvehoststxt_test.c index 93a05d64..80ccacf7 100644 --- a/test/libc/dns/resolvehoststxt_test.c +++ b/test/libc/dns/resolvehoststxt_test.c @@ -48,8 +48,7 @@ static const char kInput[] = "127.0.0.1 localhost\n" TEST(resolvehoststxt, testBasicLookups) { struct HostsTxt *ht = calloc(1, sizeof(struct HostsTxt)); - FILE *f = fmemopen(NULL, BUFSIZ, "r+"); - ASSERT_EQ(strlen(kInput), fwrite(kInput, 1, strlen(kInput), f)); + FILE *f = fmemopen(kInput, strlen(kInput), "r+"); ASSERT_EQ(0, parsehoststxt(ht, f)); sorthoststxt(ht); ASSERT_EQ(5, ht->entries.i); @@ -66,8 +65,7 @@ TEST(resolvehoststxt, testBasicLookups) { TEST(resolvehoststxt, testCanonicalize) { struct HostsTxt *ht = calloc(1, sizeof(struct HostsTxt)); - FILE *f = fmemopen(NULL, BUFSIZ, "r+"); - ASSERT_EQ(strlen(kInput), fwrite(kInput, 1, strlen(kInput), f)); + FILE *f = fmemopen(kInput, strlen(kInput), "r+"); ASSERT_EQ(0, parsehoststxt(ht, f)); sorthoststxt(ht); ASSERT_EQ(5, ht->entries.i); diff --git a/test/libc/release/test.mk b/test/libc/release/test.mk index 87aec6ae..11f50589 100644 --- a/test/libc/release/test.mk +++ b/test/libc/release/test.mk @@ -25,6 +25,7 @@ o/$(MODE)/test/libc/release/smoke.com: \ -nostdlib \ -nostdinc \ -Wl,--oformat=binary \ + -Wl,-z,max-page-size=0x1000 \ -Wl,-T,o/$(MODE)/ape/ape.lds \ -include o/cosmopolitan.h \ test/libc/release/smoke.c \ diff --git a/test/libc/stdio/fgetwc_test.c b/test/libc/stdio/fgetwc_test.c index 1e1cc796..56450140 100644 --- a/test/libc/stdio/fgetwc_test.c +++ b/test/libc/stdio/fgetwc_test.c @@ -24,6 +24,7 @@ TEST(fgetwc, testAscii_oneChar) { FILE *f = fmemopen(NULL, BUFSIZ, "r+"); EXPECT_EQ('A', fputc('A', f)); + rewind(f); EXPECT_EQ('A', fgetc(f)); fclose(f); } @@ -32,6 +33,7 @@ TEST(fgetwc, testAscii_twoChar) { FILE *f = fmemopen(NULL, BUFSIZ, "r+"); EXPECT_EQ('A', fputc('A', f)); EXPECT_EQ('B', fputc('B', f)); + rewind(f); EXPECT_EQ('A', fgetc(f)); EXPECT_EQ('B', fgetc(f)); fclose(f); @@ -40,6 +42,7 @@ TEST(fgetwc, testAscii_twoChar) { TEST(fgetwc, testUnicode_oneChar) { FILE *f = fmemopen(NULL, BUFSIZ, "r+"); EXPECT_EQ(L'𐌰', fputwc(L'𐌰', f)); + rewind(f); EXPECT_EQ(L'𐌰', fgetwc(f)); fclose(f); } @@ -50,7 +53,20 @@ TEST(fgetwc, testUnicode_oneChar_writtenAsRawUtf8) { EXPECT_EQ(0x90, fputc(0x90, f)); EXPECT_EQ(0x8C, fputc(0x8C, f)); EXPECT_EQ(0xB0, fputc(0xB0, f)); + rewind(f); + EXPECT_EQ(L'𐌰', fgetwc(f)); + fclose(f); +} + +TEST(fgetwc, testUnicode_spuriousContChars_synchronizedBeforeRead) { + FILE *f = fmemopen(NULL, BUFSIZ, "r+"); + EXPECT_EQ(0x90, fputc(0x90, f)); + EXPECT_EQ(0x90, fputc(0x90, f)); + EXPECT_EQ(0xF0, fputc(0xF0, f)); + EXPECT_EQ(0x90, fputc(0x90, f)); + EXPECT_EQ(0x8C, fputc(0x8C, f)); + EXPECT_EQ(0xB0, fputc(0xB0, f)); + rewind(f); EXPECT_EQ(L'𐌰', fgetwc(f)); - EXPECT_EQ(-1u, fgetwc(f)); fclose(f); } diff --git a/test/libc/stdio/fgetc_test.c b/test/libc/stdio/fmemopen_test.c similarity index 61% rename from test/libc/stdio/fgetc_test.c rename to test/libc/stdio/fmemopen_test.c index 2ad41cd6..b3be51d7 100644 --- a/test/libc/stdio/fgetc_test.c +++ b/test/libc/stdio/fmemopen_test.c @@ -17,54 +17,23 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" -#include "libc/calls/calls.h" #include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" -int pipefd[2]; -FILE *f, *reader, *writer; - -TEST(fgetc, testEnd) { - f = fmemopen(NULL, BUFSIZ, "r+"); - EXPECT_EQ(EOF, fgetc(f)); - EXPECT_TRUE(feof(f)); - EXPECT_FALSE(ferror(f)); - EXPECT_EQ(0, fclose(f)); +TEST(fmemopen, testWriteRewindRead) { + char c; + FILE *f; + f = fmemopen(NULL, BUFSIZ, "w+"); + EXPECT_EQ(1, fwrite("c", 1, 1, f)); + rewind(f); + EXPECT_EQ(1, fread(&c, 1, 1, f)); + EXPECT_EQ('c', c); } -TEST(fgetwc, testEnd) { - f = fmemopen(NULL, BUFSIZ, "r+"); - EXPECT_EQ(WEOF, fgetwc(f)); - EXPECT_TRUE(feof(f)); - EXPECT_FALSE(ferror(f)); - EXPECT_EQ(0, fclose(f)); -} - -TEST(fgetwc, testMultibyte) { - f = fmemopen(NULL, BUFSIZ, "r+"); - EXPECT_EQ(L'𝑥', fputwc(L'𝑥', f)); - EXPECT_EQ(L'𝑦', fputwc(L'𝑦', f)); - EXPECT_EQ(L'𝑧', fputwc(L'𝑧', f)); - EXPECT_EQ(L'𝑥', fgetwc(f)); - EXPECT_EQ(L'𝑦', fgetwc(f)); - EXPECT_EQ(L'𝑧', fgetwc(f)); - EXPECT_EQ(WEOF, fgetwc(f)); - EXPECT_TRUE(feof(f)); - fclose(f); -} - -TEST(fgetc, testPipe) { - ASSERT_NE(-1, pipe(pipefd)); - writer = fdopen(pipefd[1], "w"); - reader = fdopen(pipefd[0], "r"); - EXPECT_EQ('a', fputc('a', writer)); - EXPECT_EQ('b', fputc('b', writer)); - EXPECT_EQ('c', fputc('c', writer)); - EXPECT_EQ(3, fflush(writer)); - EXPECT_EQ('a', fgetc(reader)); - EXPECT_EQ('b', fgetc(reader)); - EXPECT_EQ('c', fgetc(reader)); - EXPECT_EQ(0, fclose(reader)); - EXPECT_EQ(0, fclose(writer)); -} +/* TEST(fmemopen, testWriteRead_readsNothingButNotEof) { */ +/* char c; */ +/* FILE *f; */ +/* f = fmemopen(NULL, BUFSIZ, "w+"); */ +/* EXPECT_EQ(1, fwrite("c", 1, 1, f)); */ +/* EXPECT_EQ(0, fread(&c, 1, 1, f)); */ +/* } */ diff --git a/test/libc/stdio/getline_test.c b/test/libc/stdio/getline_test.c index 22b3f0c2..2a30c63e 100644 --- a/test/libc/stdio/getline_test.c +++ b/test/libc/stdio/getline_test.c @@ -20,10 +20,11 @@ #include "libc/bits/bits.h" #include "libc/mem/mem.h" #include "libc/stdio/stdio.h" +#include "libc/str/str.h" #include "libc/testlib/testlib.h" TEST(getline, testEmpty) { - FILE *f = fmemopen(NULL, BUFSIZ, "r+"); + FILE *f = fmemopen("", 0, "r+"); char *line = NULL; size_t linesize = 0; EXPECT_EQ(-1, getline(&line, &linesize, f)); @@ -34,8 +35,7 @@ TEST(getline, testEmpty) { } TEST(getline, testOneWithoutLineFeed) { - FILE *f = fmemopen(NULL, BUFSIZ, "r+"); - ASSERT_EQ(5, fwrite("hello", 1, 5, f)); + FILE *f = fmemopen("hello", 5, "r+"); char *line = NULL; size_t linesize = 0; ASSERT_EQ(5, getline(&line, &linesize, f)); @@ -48,8 +48,8 @@ TEST(getline, testOneWithoutLineFeed) { } TEST(getline, testTwoLines) { - FILE *f = fmemopen(NULL, BUFSIZ, "r+"); - ASSERT_EQ(12, fwrite("hello\nthere\n", 1, 12, f)); + const char *s = "hello\nthere\n"; + FILE *f = fmemopen(s, strlen(s), "r+"); char *line = NULL; size_t linesize = 0; ASSERT_EQ(6, getline(&line, &linesize, f)); @@ -64,8 +64,8 @@ TEST(getline, testTwoLines) { } TEST(getline, testBinaryLine_countExcludesOnlyTheBonusNul) { - FILE *f = fmemopen(NULL, BUFSIZ, "r+"); - fwrite("he\0\3o\n", 1, 6, f); + const char s[] = "he\0\3o\n"; + FILE *f = fmemopen(s, sizeof(s), "r+"); char *line = NULL; size_t linesize = 0; ASSERT_EQ(6, getline(&line, &linesize, f)); diff --git a/test/net/http/parsehttprequest_test.c b/test/net/http/parsehttprequest_test.c index 3c6f805a..a2581b24 100644 --- a/test/net/http/parsehttprequest_test.c +++ b/test/net/http/parsehttprequest_test.c @@ -36,13 +36,17 @@ static char *slice(const char *m, struct HttpRequestSlice s) { return p; } -TEST(ParseHttpRequest, testEmpty) { - EXPECT_EQ(-1, ParseHttpRequest(req, "", 0)); +TEST(ParseHttpRequest, testEmpty_tooShort) { + EXPECT_EQ(0, ParseHttpRequest(req, "", 0)); +} + +TEST(ParseHttpRequest, testTooShort) { + EXPECT_EQ(0, ParseHttpRequest(req, "\r\n", 2)); } TEST(ParseHttpRequest, testNoHeaders) { static const char m[] = "GET /foo HTTP/1.0\r\n\r\n"; - EXPECT_EQ(0, ParseHttpRequest(req, m, strlen(m))); + EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m))); EXPECT_EQ(kHttpGet, req->method); EXPECT_STREQ("/foo", gc(slice(m, req->uri))); EXPECT_STREQ("HTTP/1.0", gc(slice(m, req->version))); @@ -54,7 +58,7 @@ POST /foo?bar%20hi HTTP/1.0\r\n\ Host: foo.example\r\n\ Content-Length: 0\r\n\ \r\n"; - EXPECT_EQ(0, ParseHttpRequest(req, m, strlen(m))); + EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m))); EXPECT_EQ(kHttpPost, req->method); EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri))); EXPECT_STREQ("HTTP/1.0", gc(slice(m, req->version))); diff --git a/tool/build/blinkenlights.c b/tool/build/blinkenlights.c index 53b57ea6..f20ac606 100644 --- a/tool/build/blinkenlights.c +++ b/tool/build/blinkenlights.c @@ -898,10 +898,11 @@ void SetupDraw(void) { static long Disassemble(void) { long lines, current; lines = pan.disassembly.bottom - pan.disassembly.top * 2; - CHECK_NE(-1, Dis(dis, m, GetIp(), m->ip, lines)); - current = DisFind(dis, GetIp()); - CHECK_NE(-1, current); - return current; + if (Dis(dis, m, GetIp(), m->ip, lines) != -1) { + return DisFind(dis, GetIp()); + } else { + return -1; + } } static long GetDisIndex(void) { diff --git a/tool/build/lib/syscall.c b/tool/build/lib/syscall.c index b5353a15..dbd05991 100644 --- a/tool/build/lib/syscall.c +++ b/tool/build/lib/syscall.c @@ -1227,7 +1227,11 @@ static int OpGetPpid(struct Machine *m) { } static int OpKill(struct Machine *m, int pid, int sig) { - return kill(pid, sig); + if (pid == getpid()) { + ThrowProtectionFault(m); + } else { + return kill(pid, sig); + } } static int OpGetUid(struct Machine *m) { diff --git a/tool/build/rollup.c b/tool/build/rollup.c index 3da44bd0..951c44ee 100644 --- a/tool/build/rollup.c +++ b/tool/build/rollup.c @@ -17,6 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "ape/relocations.h" #include "libc/alg/arraylist2.internal.h" #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" @@ -139,6 +140,9 @@ int main(int argc, char *argv[]) { APPENDSTR("#define IMAGE_BASE_VIRTUAL "); AppendInt(IMAGE_BASE_VIRTUAL); APPENDSTR("\n"); + APPENDSTR("#define IMAGE_BASE_PHYSICAL "); + AppendInt(IMAGE_BASE_PHYSICAL); + APPENDSTR("\n"); for (i = 1; i < argc; ++i) { Visit(argv[i]); } diff --git a/tool/net/greenbean.c b/tool/net/greenbean.c new file mode 100644 index 00000000..13f669b4 --- /dev/null +++ b/tool/net/greenbean.c @@ -0,0 +1,1293 @@ +/*-*- 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 │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/arraylist2.internal.h" +#include "libc/bits/bits.h" +#include "libc/bits/bswap.h" +#include "libc/bits/safemacros.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/iovec.h" +#include "libc/calls/struct/itimerval.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/weirdtypes.h" +#include "libc/conv/conv.h" +#include "libc/conv/itoa.h" +#include "libc/errno.h" +#include "libc/fmt/fmt.h" +#include "libc/log/check.h" +#include "libc/log/log.h" +#include "libc/macros.h" +#include "libc/math.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/crc32.h" +#include "libc/rand/rand.h" +#include "libc/runtime/gc.h" +#include "libc/runtime/runtime.h" +#include "libc/sock/epoll.h" +#include "libc/sock/sock.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/str/undeflate.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/clock.h" +#include "libc/sysv/consts/epoll.h" +#include "libc/sysv/consts/ex.h" +#include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/fd.h" +#include "libc/sysv/consts/inaddr.h" +#include "libc/sysv/consts/ipproto.h" +#include "libc/sysv/consts/itimer.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/shut.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/so.h" +#include "libc/sysv/consts/sock.h" +#include "libc/sysv/consts/sol.h" +#include "libc/sysv/consts/tcp.h" +#include "libc/sysv/errfuns.h" +#include "libc/time/struct/tm.h" +#include "libc/time/time.h" +#include "libc/x/x.h" +#include "libc/zip.h" +#include "libc/zipos/zipos.internal.h" +#include "net/http/http.h" +#include "third_party/getopt/getopt.h" +#include "third_party/zlib/zlib.h" + +/** + * @fileoverview redbean.c modified for epoll tutorial + * This'll work on Linux and Windows but BSD is TODO + * TODO(jart): fix memory bug + */ + +#define USAGE \ + " [-hvdsm] [-p PORT]\n\ +\n\ +DESCRIPTION\n\ +\n\ + greenbean distributable static web server\n\ +\n\ +FLAGS\n\ +\n\ + -h help\n\ + -v verbosity\n\ + -d daemonize\n\ + -s uniprocess\n\ + -m log messages\n\ + -c INT cache seconds\n\ + -r /X=/Y redirect X to Y\n\ + -l ADDR listen ip [default 0.0.0.0]\n\ + -p PORT listen port [default 8080]\n\ + -L PATH log file location\n\ + -P PATH pid file location\n\ + -U INT daemon set user id\n\ + -G INT daemon set group id\n\ + -B STR changes server header\n\ +\n\ +FEATURES\n\ +\n\ + - HTTP v1.1\n\ + - Content-Encoding\n\ + - Range / Content-Range\n\ + - Last-Modified / If-Modified-Since\n\ +\n\ +USAGE\n\ +\n\ + This executable is also a ZIP file that contains static assets.\n\ +\n\ + unzip -vl greenbean.com # shows listing of zip contents\n\ +\n\ + Audio video content should not be compressed in your ZIP files.\n\ + Uncompressed assets enable browsers to send Range HTTP request.\n\ + On the other hand compressed assets are best for gzip encoding.\n\ +\n\ + zip greenbean.com index.html # adds file\n\ + zip -0 greenbean.com video.mp4 # adds without compression\n\ +\n\ + Each connection uses a point in time snapshot of your ZIP file.\n\ + If your ZIP is deleted then serving continues. If it's replaced\n\ + then issuing SIGUSR1 (or SIGHUP if daemon) will reindex the zip\n\ + for subsequent connections without interrupting active ones. If\n\ + SIGINT or SIGTERM is issued then a graceful shutdown is started\n\ + but if it's issued a second time, active connections are reset.\n\ +\n" + +#define HASH_LOAD_FACTOR /* 1. / */ 4 +#define DEFAULT_PORT 8080 +#define DEFAULT_SERVER "greenbean/0.1" +#define DEFAULT_CONTENT_TYPE "application/octet-stream" +#define DEFAULT_PATH "/tool/net/redbean.html" +#define FAVICON "tool/net/redbean.ico" + +#define STPCPY(p, s) mempcpy(p, s, strlen(s)) +#define AppendHeaderName(p, s) STPCPY(STPCPY(p, s), ": ") + +struct Client { + uint32_t poison; + struct Client *prev; + struct Client *next; + int fd; + int iovlen; + bool close; + uint32_t insize; + uint32_t addrsize; + uint8_t gzip_footer[8]; + struct sockaddr_in addr; + long double startrequest; + long double startconnection; + struct HttpRequest req; + struct iovec iov[8]; + char addrstr[32]; + char outbuf[1500]; + char inbuf[2048]; +}; + +static const struct itimerval kHeartbeat = { + {0, 500000}, + {0, 500000}, +}; + +static const uint8_t kGzipHeader[] = { + 0x1F, // MAGNUM + 0x8B, // MAGNUM + 0x08, // CM: DEFLATE + 0x00, // FLG: NONE + 0x00, // MTIME: NONE + 0x00, // + 0x00, // + 0x00, // + 0x00, // XFL + kZipOsUnix, // OS +}; + +static const struct ContentTypeExtension { + unsigned char ext[8]; + const char *mime; +} kContentTypeExtension[] = { + {"S", "text/plain"}, // + {"bmp", "image/x-ms-bmp"}, // + {"c", "text/plain"}, // + {"cc", "text/plain"}, // + {"css", "text/css"}, // + {"csv", "text/csv"}, // + {"gif", "image/gif"}, // + {"h", "text/plain"}, // + {"html", "text/html"}, // + {"i", "text/plain"}, // + {"ico", "image/vnd.microsoft.icon"}, // + {"jpeg", "image/jpeg"}, // + {"jpg", "image/jpeg"}, // + {"js", "application/javascript"}, // + {"json", "application/json"}, // + {"m4a", "audio/mpeg"}, // + {"mp2", "audio/mpeg"}, // + {"mp3", "audio/mpeg"}, // + {"mp4", "video/mp4"}, // + {"mpg", "video/mpeg"}, // + {"otf", "font/otf"}, // + {"pdf", "application/pdf"}, // + {"png", "image/png"}, // + {"png", "image/png"}, // + {"png", "image/png"}, // + {"png", "image/png"}, // + {"s", "text/plain"}, // + {"svg", "image/svg+xml"}, // + {"tiff", "image/tiff"}, // + {"ttf", "font/ttf"}, // + {"txt", "text/plain"}, // + {"wav", "audio/x-wav"}, // + {"woff", "font/woff"}, // + {"woff2", "font/woff2"}, // + {"xml", "application/xml"}, // + {"zip", "application/zip"}, // +}; + +static struct Redirects { + size_t i, n; + struct Redirect { + const char *path; + size_t pathlen; + const char *dest; + } * p; +} redirects; + +static struct Assets { + uint32_t n; + struct Asset { + uint32_t hash; + uint32_t cf; + int64_t lastmodified; + char *lastmodifiedstr; + } * p; +} assets; + +static bool killed; +static bool notimer; +static bool heartbeat; +static bool daemonize; +static bool terminated; +static bool uniprocess; +static bool legacyhttp; +static bool invalidated; +static bool logmessages; + +static int epfd; +static int gmtoff; +static int server; +static int daemonuid; +static int daemongid; +static int cacheseconds; + +static void *zmap; +static uint8_t *zbase; +static uint8_t *zcdir; +static size_t zmapsize; +static const char *pidpath; +static const char *logpath; +static int64_t programtime; +static struct Client *clients; +static const char *programfile; +static const char *serverheader; + +static long double nowish; +static struct sockaddr_in serveraddr; + +static char currentdate[32]; +static char serveraddrstr[32]; + +static void OnHup(void) { + invalidated = true; +} + +static void OnAlarm(void) { + heartbeat = true; +} + +static void OnTerminate(void) { + if (terminated) { + killed = true; + } else { + terminated = true; + } +} + +static void AddRedirect(const char *arg) { + const char *p; + struct Redirect r; + CHECK_NOTNULL((p = strchr(arg, '='))); + CHECK_GT(p - arg, 0); + r.path = arg; + r.pathlen = p - arg; + r.dest = strdup(p + 1); + APPEND(&redirects.p, &redirects.i, &redirects.n, &r); +} + +static int CompareRedirects(const struct Redirect *a, + const struct Redirect *b) { + return strcmp(a->path, b->path); +} + +static void SortRedirects(void) { + qsort(redirects.p, redirects.i, sizeof(struct Redirect), + (void *)CompareRedirects); +} + +static const char *LookupRedirect(const char *path, size_t n) { + int c, m, l, r, z; + l = 0; + r = redirects.i - 1; + while (l <= r) { + m = (l + r) >> 1; + c = memcmp(redirects.p[m].path, path, MIN(redirects.p[m].pathlen, n)); + if (c < 0) { + l = m + 1; + } else if (c > 0) { + r = m - 1; + } else if (redirects.p[m].pathlen < n) { + l = m + 1; + } else if (redirects.p[m].pathlen > n) { + r = m - 1; + } else { + return redirects.p[m].dest; + } + } + return NULL; +} + +static int CompareInts(const uint64_t x, uint64_t y) { + return x > y ? 1 : x < y ? -1 : 0; +} + +static const char *LookupContentType(uint64_t ext) { + int c, m, l, r; + l = 0; + r = ARRAYLEN(kContentTypeExtension) - 1; + while (l <= r) { + m = (l + r) >> 1; + c = CompareInts(READ64BE(kContentTypeExtension[m].ext), ext); + if (c < 0) { + l = m + 1; + } else if (c > 0) { + r = m - 1; + } else { + return kContentTypeExtension[m].mime; + } + } + return DEFAULT_CONTENT_TYPE; +} + +static const char *GetContentType(const char *path, size_t n) { + size_t i; + uint64_t x; + const char *p; + if ((p = memrchr(path, '.', n))) { + for (x = 0, i = n; i-- > p + 1 - path;) { + x <<= 8; + x |= path[i] & 0xFF; + } + return LookupContentType(bswap_64(x)); + } else { + return DEFAULT_CONTENT_TYPE; + } +} + +static noreturn void PrintUsage(FILE *f, int rc) { + fprintf(f, "SYNOPSIS\n\n %s%s", program_invocation_name, USAGE); + exit(rc); +} + +static void DescribeAddress(char buf[32], const struct sockaddr_in *addr) { + char *p = buf; + const uint8_t *ip4 = (const uint8_t *)&addr->sin_addr.s_addr; + p += uint64toarray_radix10(ip4[0], p), *p++ = '.'; + p += uint64toarray_radix10(ip4[1], p), *p++ = '.'; + p += uint64toarray_radix10(ip4[2], p), *p++ = '.'; + p += uint64toarray_radix10(ip4[3], p), *p++ = ':'; + p += uint64toarray_radix10(ntohs(addr->sin_port), p); + *p = '\0'; +} + +void GetOpts(int argc, char *argv[]) { + int opt; + serveraddr.sin_family = AF_INET; + serveraddr.sin_port = htons(DEFAULT_PORT); + serveraddr.sin_addr.s_addr = INADDR_ANY; + while ((opt = getopt(argc, argv, "hduvml:p:w:r:c:L:P:U:G:B:")) != -1) { + switch (opt) { + case 'v': + g_loglevel++; + break; + case 'd': + daemonize = true; + break; + case 'u': + uniprocess = true; + break; + case 'm': + logmessages = true; + break; + case 'r': + AddRedirect(optarg); + break; + case 'c': + cacheseconds = atoi(optarg); + break; + case 'p': + CHECK_NE(0xFFFF, (serveraddr.sin_port = htons(parseport(optarg)))); + break; + case 'l': + CHECK_EQ(1, inet_pton(AF_INET, optarg, &serveraddr.sin_addr)); + break; + case 'B': + serverheader = optarg; + break; + case 'L': + logpath = optarg; + break; + case 'P': + pidpath = optarg; + break; + case 'U': + daemonuid = atoi(optarg); + break; + case 'G': + daemonuid = atoi(optarg); + break; + case 'h': + PrintUsage(stdout, EXIT_SUCCESS); + default: + PrintUsage(stderr, EX_USAGE); + } + } + SortRedirects(); + if (logpath) { + CHECK_NOTNULL(freopen(logpath, "a", stderr)); + } +} + +static void Daemonize(void) { + char ibuf[21]; + int i, fd, pid; + for (i = 0; i < 128; ++i) close(i); + xsigaction(SIGHUP, OnHup, 0, 0, 0); + CHECK_NE(-1, (pid = fork())); + if (pid > 0) exit(0); + if (pid == -1) return; + CHECK_NE(-1, setsid()); + CHECK_NE(-1, (pid = fork())); + if (pid > 0) _exit(0); + LOGIFNEG1(umask(0)); + if (pidpath) { + CHECK_NE(-1, (fd = open(pidpath, O_CREAT | O_EXCL | O_WRONLY, 0644))); + CHECK_NE(-1, write(fd, ibuf, uint64toarray_radix10(getpid(), ibuf))); + LOGIFNEG1(close(fd)); + } + if (!logpath) logpath = "/dev/null"; + CHECK_NOTNULL(freopen("/dev/null", "r", stdin)); + CHECK_NOTNULL(freopen(logpath, "a", stdout)); + CHECK_NOTNULL(freopen(logpath, "a", stderr)); +} + +static int CompareHeaderValue(struct Client *c, int h, const char *s) { + return strncmp(s, c->inbuf + c->req.headers[h].a, + c->req.headers[h].b - c->req.headers[h].a); +} + +static size_t GetIovSize(struct iovec *iov, int iovlen) { + int i; + size_t size; + for (size = i = 0; i < iovlen; ++i) { + DCHECK_NOTNULL(iov[i].iov_base); + size += iov[i].iov_len; + } + return size; +} + +static ssize_t WriteSome(int fd, struct iovec *iov, int iovlen) { + ssize_t rc; + size_t wrote, n; + if ((rc = writev(fd, iov, iovlen)) != -1) { + wrote = n = rc; + do { + if (n >= iov->iov_len) { + iov->iov_len = 0; + n -= iov->iov_len; + ++iov; + --iovlen; + } else { + iov->iov_base = (char *)iov->iov_base + n; + iov->iov_len -= n; + n = 0; + } + } while (n); + return wrote; + } else if (errno == EINTR) { + return 0; + } else { + return -1; + } +} + +static uint32_t Hash(const void *data, size_t size) { + uint32_t h; + h = crc32c(0, data, size); + if (!h) h = 1; + return h; +} + +static bool HasHeader(struct Client *c, int h) { + return c->req.headers[h].b > c->req.headers[h].a; +} + +int64_t GetGmtOffset(void) { + int64_t t; + struct tm tm; + t = nowl(); + localtime_r(&t, &tm); + return tm.tm_gmtoff; +} + +static int64_t LocoTimeToZulu(int64_t x) { + return x - gmtoff; +} + +static int64_t GetLastModifiedZip(uint8_t *cfile) { + uint8_t *p, *pe; + for (p = ZIP_CFILE_EXTRA(cfile), pe = p + ZIP_CFILE_EXTRASIZE(cfile); p < pe; + p += ZIP_EXTRA_SIZE(p)) { + if (ZIP_EXTRA_HEADERID(p) == kZipExtraNtfs) { + return LocoTimeToZulu(READ64LE(ZIP_EXTRA_CONTENT(p) + 8) / + HECTONANOSECONDS - + MODERNITYSECONDS); + } else if (ZIP_EXTRA_HEADERID(p) == kZipExtraExtendedTimestamp) { + return READ32LE(ZIP_EXTRA_CONTENT(p) + 1); + } + } + return LocoTimeToZulu(DosDateTimeToUnix(ZIP_CFILE_LASTMODIFIEDDATE(cfile), + ZIP_CFILE_LASTMODIFIEDTIME(cfile))); +} + +static bool IsCompressed(struct Asset *a) { + return ZIP_CFILE_COMPRESSIONMETHOD(zbase + a->cf) == kZipCompressionDeflate; +} + +static int GetHttpVersion(struct Client *c) { + return ParseHttpVersion(c->inbuf + c->req.version.a, + c->req.version.b - c->req.version.a); +} + +static bool IsNotModified(struct Client *c, struct Asset *a) { + if (!HasHeader(c, kHttpIfModifiedSince)) return false; + return a->lastmodified >= + ParseHttpDateTime(c->inbuf + c->req.headers[kHttpIfModifiedSince].a, + c->req.headers[kHttpIfModifiedSince].b - + c->req.headers[kHttpIfModifiedSince].a); +} + +static char *FormatUnixHttpDateTime(char *s, int64_t t) { + struct tm tm; + gmtime_r(&t, &tm); + FormatHttpDateTime(s, &tm); + return s; +} + +static void FreeAssetsIndex(struct Asset *p, size_t n) { + int i; + if (p) { + for (i = 0; i < n; ++i) { + free(p[i].lastmodifiedstr); + } + free(p); + } +} + +static bool IndexAssets(const uint8_t *base, const uint8_t *cdir) { + bool ok; + int64_t lm; + struct Asset *p; + uint32_t i, n, m, cf, step, hash; + DCHECK_GE(HASH_LOAD_FACTOR, 2); + n = ZIP_CDIR_RECORDS(cdir); + m = roundup2pow(MAX(1, n) * HASH_LOAD_FACTOR); + p = calloc(m, sizeof(struct Asset)); + ok = ZIP_CDIR_MAGIC(cdir) == kZipCdirHdrMagic; + if (p && ok) { + for (cf = ZIP_CDIR_OFFSET(cdir); n--; cf += ZIP_CFILE_HDRSIZE(base + cf)) { + if (ZIP_CFILE_MAGIC(base + cf) == kZipCfileHdrMagic) { + hash = Hash(ZIP_CFILE_NAME(base + cf), ZIP_CFILE_NAMESIZE(base + cf)); + step = 0; + do { + i = (hash + (step * (step + 1)) >> 1) & (m - 1); + ++step; + } while (p[i].hash); + lm = GetLastModifiedZip(base + cf); + p[i].hash = hash; + p[i].cf = cf; + p[i].lastmodified = lm; + p[i].lastmodifiedstr = FormatUnixHttpDateTime(xmalloc(30), lm); + } else { + WARNF("corrupt zip central directory entry"); + ok = false; + break; + } + } + } else { + WARNF("corrupt zip central directory"); + } + if (ok) { + FreeAssetsIndex(assets.p, assets.n); + assets.p = p; + assets.n = m; + } else { + FreeAssetsIndex(p, m); + } + return ok; +} + +static bool OpenZip(const char *path) { + int fd; + bool ok; + void *map; + struct stat st; + const uint8_t *cdir; + if (zmap) { + LOGIFNEG1(munmap(zmap, zmapsize)); + } + if (!zmap && ZIP_CDIR_MAGIC(__zip_end) == kZipCdirHdrMagic) { + if (IndexAssets(_base, __zip_end)) { + ok = true; + zbase = _base; + zcdir = __zip_end; + } else { + ok = false; + } + } else { + fd = -1; + map = MAP_FAILED; + if ((fd = open(path, O_RDONLY)) != -1 && fstat(fd, &st) != -1 && + st.st_size && + (map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) && + (cdir = zipfindcentraldir(zmap, zmapsize)) && IndexAssets(map, cdir)) { + ok = true; + zmap = map; + zbase = map; + zcdir = cdir; + map = MAP_FAILED; + zmapsize = st.st_size; + } else { + ok = false; + } + if (map != MAP_FAILED) LOGIFNEG1(munmap(map, st.st_size)); + if (fd != -1) LOGIFNEG1(close(fd)); + } + return ok; +} + +static struct Asset *FindAsset(const char *path, size_t pathlen) { + uint32_t i, step, hash; + if (pathlen && path[0] == '/') ++path, --pathlen; + hash = Hash(path, pathlen); + for (step = 0;; ++step) { + i = (hash + (step * (step + 1)) >> 1) & (assets.n - 1); + if (!assets.p[i].hash) return NULL; + if (hash == assets.p[i].hash && + pathlen == ZIP_CFILE_NAMESIZE(zbase + assets.p[i].cf) && + memcmp(path, ZIP_CFILE_NAME(zbase + assets.p[i].cf), pathlen) == 0) { + return &assets.p[i]; + } + } +} + +static struct Asset *FindFile(const char *path, size_t pathlen) { + char *p, *buf; + struct Asset *asset; + if ((asset = FindAsset(path, pathlen))) return asset; + if (pathlen == 12 && memcmp(path, "/favicon.ico", 12) == 0) { + return FindAsset(FAVICON, strlen(FAVICON)); + } else { + return NULL; + } +} + +static void *AddRange(char *content, long start, long length) { + intptr_t mend, mstart; + if (!__builtin_add_overflow((intptr_t)content, start, &mstart) || + !__builtin_add_overflow(mstart, length, &mend) || + ((intptr_t)zbase <= mstart && mstart <= (intptr_t)zbase + zmapsize) || + ((intptr_t)zbase <= mend && mend <= (intptr_t)zbase + zmapsize)) { + return (void *)mstart; + } else { + abort(); + } +} + +static bool IsConnectionClose(struct Client *c) { + int n; + char *p; + p = c->inbuf + c->req.headers[kHttpConnection].a; + n = c->req.headers[kHttpConnection].b - c->req.headers[kHttpConnection].a; + return n == 5 && memcmp(p, "close", 5) == 0; +} + +static char *AppendCrlf(char *p) { + return STPCPY(p, "\r\n"); +} + +#define AppendStatus(p, c, s) AppendStatus(p, c, s, sizeof(s) - 1) +static char *(AppendStatus)(char *p, int c, const char *s, size_t n) { + if (legacyhttp) { + p = STPCPY(p, "HTTP/1.0 "); + } else { + p = STPCPY(p, "HTTP/1.1 "); + } + p += uint64toarray_radix10(c, p); + *p++ = ' '; + p = mempcpy(p, s, n); + return AppendCrlf(p); +} + +static void UpdateCurrentDate(long double now) { + int64_t t; + struct tm tm; + t = nowish = now; + gmtime_r(&t, &tm); + FormatHttpDateTime(currentdate, &tm); +} + +static char *AppendDate(char *p) { + p = AppendHeaderName(p, "Date"); + p = mempcpy(p, currentdate, 29); + return AppendCrlf(p); +} + +static char *AppendLastModified(char *p, const char *s) { + p = AppendHeaderName(p, "Last-Modified"); + p = mempcpy(p, s, 29); + return AppendCrlf(p); +} + +static char *AppendServer(char *p) { + const char *s; + if (*(s = firstnonnull(serverheader, DEFAULT_SERVER))) { + p = AppendHeaderName(p, "Server"); + p = stpcpy(p, s); + p = AppendCrlf(p); + } + return p; +} + +static char *AppendConnectionClose(char *p) { + p = AppendHeaderName(p, "Connection"); + p = STPCPY(p, "close"); + return AppendCrlf(p); +} + +static char *AppendAcceptRangesBytes(char *p) { + p = AppendHeaderName(p, "Accept-Ranges"); + p = STPCPY(p, "bytes"); + return AppendCrlf(p); +} + +static char *AppendNosniff(char *p) { + p = AppendHeaderName(p, "X-Content-Type-Options"); + p = STPCPY(p, "nosniff"); + return AppendCrlf(p); +} + +static char *AppendContentType(char *p, const char *ct) { + p = AppendHeaderName(p, "Content-Type"); + p = stpcpy(p, ct); + if (startswith(ct, "text/")) { + p = STPCPY(p, "; charset=utf-8"); + } + return AppendCrlf(p); +} + +static char *AppendContentTypeTextPlain(char *p) { + return AppendContentType(p, "text/plain"); +} + +static char *AppendExpires(char *p, int64_t t) { + struct tm tm; + gmtime_r(&t, &tm); + p = AppendHeaderName(p, "Expires"); + p = FormatHttpDateTime(p, &tm); + return AppendCrlf(p); +} + +static char *AppendVaryContentEncoding(char *p) { + p = AppendHeaderName(p, "Vary"); + p = STPCPY(p, "Accept-Encoding"); + return AppendCrlf(p); +} + +static char *AppendCache(char *p) { + int x; + x = cacheseconds; + if (!x) return p; + x = MAX(0, x); + p = AppendHeaderName(p, "Cache-Control"); + p = STPCPY(p, "max-age="); + p += uint64toarray_radix10(x, p); + if (x) p = STPCPY(p, ", public"); + p = AppendCrlf(p); + return AppendExpires(p, nowish + cacheseconds); +} + +static char *AppendContentLength(char *p, size_t n) { + p = AppendHeaderName(p, "Content-Length"); + p += uint64toarray_radix10(n, p); + return AppendCrlf(p); +} + +static char *AppendContentRange(char *p, long rangestart, long rangelength, + long contentlength) { + long endrange; + CHECK_GE(rangestart + rangelength, rangestart); + CHECK_LE(rangestart + rangelength, contentlength); + if (__builtin_add_overflow(rangestart, rangelength, &endrange)) abort(); + p = AppendHeaderName(p, "Content-Range"); + p = STPCPY(p, "bytes "); + p += uint64toarray_radix10(rangestart, p); + *p++ = '-'; + p += uint64toarray_radix10(endrange, p); + *p++ = '/'; + p += uint64toarray_radix10(contentlength, p); + return AppendCrlf(p); +} + +static char *AppendContentEncodingGzip(char *p) { + p = AppendHeaderName(p, "Content-Encoding"); + p = STPCPY(p, "gzip"); + return AppendCrlf(p); +} + +static char *AppendRedirect(struct Client *c, char *p, const char *s) { + VERBOSEF("%s %s %.*s redirect %s", c->addrstr, kHttpMethod[c->req.method], + c->req.uri.b - c->req.uri.a, c->inbuf + c->req.uri.a, s); + p = AppendStatus(p, 302, "Temporary Redirect"); + p = AppendHeaderName(p, "Location"); + p = STPCPY(p, s); + return AppendCrlf(p); +} + +static bool InflateTiny(uint8_t *outbuf, size_t outsize, const uint8_t *inbuf, + size_t insize) { + struct DeflateState ds; + return undeflate(outbuf, outsize, inbuf, insize, &ds) != -1; +} + +static bool InflateZlib(uint8_t *outbuf, size_t outsize, const uint8_t *inbuf, + size_t insize) { + bool ok; + z_stream zs; + ok = false; + zs.next_in = inbuf; + zs.avail_in = insize; + zs.total_in = insize; + zs.next_out = outbuf; + zs.avail_out = outsize; + zs.total_out = outsize; + zs.zfree = Z_NULL; + zs.zalloc = Z_NULL; + if (inflateInit2(&zs, -MAX_WBITS) == Z_OK) { + switch (inflate(&zs, Z_NO_FLUSH)) { + case Z_STREAM_END: + ok = true; + break; + case Z_MEM_ERROR: + WARNF("Z_MEM_ERROR"); + break; + case Z_DATA_ERROR: + WARNF("Z_DATA_ERROR"); + break; + case Z_NEED_DICT: + WARNF("Z_NEED_DICT"); + break; + default: + abort(); + } + inflateEnd(&zs); + } + return ok; +} + +static bool Inflate(uint8_t *outbuf, size_t outsize, const uint8_t *inbuf, + size_t insize) { + if (IsTiny()) { + return InflateTiny(outbuf, outsize, inbuf, insize); + } else { + return InflateZlib(outbuf, outsize, inbuf, insize); + } +} + +static void LogRequestLatency(struct Client *c) { + long double now = nowl(); + LOGF("%s latency req %,16ldns conn %,16ldns", c->addrstr, + (long)((now - c->startrequest) * 1e9), + (long)((now - c->startconnection) * 1e9)); +} + +bool HandleRequest(struct Client *c) { + char *p; + int msgsize; + bool gzipped; + void *content; + size_t pathlen; + struct Asset *a; + unsigned version; + const char *path, *location; + long lf, contentlength, actualcontentlength, rangestart, rangelength; + p = c->outbuf; + content = ""; + gzipped = false; + contentlength = -1; + c->close = false; + if ((msgsize = ParseHttpRequest(&c->req, c->inbuf, c->insize)) != -1) { + if (!msgsize) return false; + if (logmessages) { + LOGF("%s received %,d byte message\n%.*s", c->addrstr, c->req.length, + c->req.length - 4, c->inbuf); + } + version = GetHttpVersion(c); + if (version < 101) c->close = true, legacyhttp = true; + if (version <= 101) { + if (IsConnectionClose(c)) c->close = true; + path = c->inbuf + c->req.uri.a; + pathlen = c->req.uri.b - c->req.uri.a; + if (c->req.method == kHttpGet || c->req.method == kHttpHead) { + if (ParseContentLength(&c->req, c->inbuf)) c->close = true; + VERBOSEF( + "%s %s %.*s referer %.*s", c->addrstr, kHttpMethod[c->req.method], + pathlen, path, + c->req.headers[kHttpReferer].b - c->req.headers[kHttpReferer].a, + c->inbuf + c->req.headers[kHttpReferer].a); + if ((location = LookupRedirect(path, pathlen))) { + p = AppendRedirect(c, p, DEFAULT_PATH); + } else if ((a = FindFile(path, pathlen))) { + if (IsNotModified(c, a)) { + VERBOSEF("%s %s %.*s not modified", c->addrstr, + kHttpMethod[c->req.method], pathlen, path); + p = AppendStatus(p, 304, "Not Modified"); + } else { + lf = ZIP_CFILE_OFFSET(zbase + a->cf); + content = ZIP_LFILE_CONTENT(zbase + lf); + contentlength = ZIP_CFILE_COMPRESSEDSIZE(zbase + a->cf); + if (IsCompressed(a)) { + if (memmem(c->inbuf + c->req.headers[kHttpAcceptEncoding].a, + c->req.headers[kHttpAcceptEncoding].b - + c->req.headers[kHttpAcceptEncoding].a, + "gzip", 4)) { + gzipped = true; + memcpy(c->gzip_footer + 0, zbase + a->cf + kZipCfileOffsetCrc32, + 4); + memcpy(c->gzip_footer + 4, + zbase + a->cf + kZipCfileOffsetUncompressedsize, 4); + p = AppendStatus(p, 200, "OK"); + p = AppendContentEncodingGzip(p); + } else if (Inflate( + (content = gc(xmalloc( + ZIP_CFILE_UNCOMPRESSEDSIZE(zbase + a->cf)))), + (contentlength = + ZIP_CFILE_UNCOMPRESSEDSIZE(zbase + a->cf)), + ZIP_LFILE_CONTENT(zbase + lf), + ZIP_CFILE_COMPRESSEDSIZE(zbase + a->cf))) { + p = AppendStatus(p, 200, "OK"); + } else { + WARNF("%s %s %.*s internal server error", c->addrstr, + kHttpMethod[c->req.method], pathlen, path); + p = AppendStatus(p, 500, "Internal Server Error"); + content = "Internal Server Error\r\n"; + contentlength = -1; + } + } else if (HasHeader(c, kHttpRange)) { + if (ParseHttpRange(c->inbuf + c->req.headers[kHttpRange].a, + c->req.headers[kHttpRange].b - + c->req.headers[kHttpRange].a, + contentlength, &rangestart, &rangelength)) { + p = AppendStatus(p, 206, "Partial Content"); + p = AppendContentRange(p, rangestart, rangelength, + contentlength); + content = AddRange(content, rangestart, rangelength); + contentlength = rangelength; + } else { + WARNF( + "%s %s %.*s bad range %`'.*s", c->addrstr, + kHttpMethod[c->req.method], pathlen, path, + c->req.headers[kHttpRange].b - c->req.headers[kHttpRange].a, + c->inbuf + c->req.headers[kHttpRange].a); + p = AppendStatus(p, 416, "Range Not Satisfiable"); + p = AppendContentRange(p, rangestart, rangelength, + contentlength); + content = ""; + contentlength = 0; + } + } else { + p = AppendStatus(p, 200, "OK"); + } + } + p = AppendLastModified(p, a->lastmodifiedstr); + p = AppendContentType(p, GetContentType(path, pathlen)); + p = AppendCache(p); + if (!IsCompressed(a)) { + p = AppendAcceptRangesBytes(p); + } else { + p = AppendVaryContentEncoding(p); + } + } else { + WARNF("%s %s %.*s not found", c->addrstr, kHttpMethod[c->req.method], + pathlen, path); + p = AppendStatus(p, 404, "Not Found"); + p = AppendContentTypeTextPlain(p); + content = "Not Found\r\n"; + } + } else { + WARNF("%s %s %.*s method not allowed", c->addrstr, + kHttpMethod[c->req.method], pathlen, path); + p = AppendStatus(p, 405, "method not allowed"); + p = AppendContentTypeTextPlain(p); + content = "Method Not Allowed\r\n"; + c->close = true; + } + } else { + WARNF("%s http version not supported %`'.*s", c->addrstr, + c->req.version.b - c->req.version.a, c->inbuf + c->req.version.a); + p = AppendStatus(p, 505, "HTTP Version Not Supported"); + p = AppendContentTypeTextPlain(p); + content = "HTTP Version Not Supported\r\n"; + c->close = true; + } + } else { + WARNF("%s parse error %s", c->addrstr, strerror(errno)); + p = AppendStatus(p, 400, "Bad Request"); + p = AppendContentTypeTextPlain(p); + content = "Bad Request\r\n"; + c->close = true; + } + p = AppendDate(p); + p = AppendNosniff(p); + p = AppendServer(p); + if (c->close) p = AppendConnectionClose(p); + if (contentlength == -1) contentlength = strlen(content); + actualcontentlength = contentlength; + if (gzipped) { + actualcontentlength += sizeof(kGzipHeader) + sizeof(c->gzip_footer); + } + p = AppendContentLength(p, actualcontentlength); + p = AppendCrlf(p); + if (logmessages) { + LOGF("%s sending %,d byte message\n%.*s", c->addrstr, p - c->outbuf, + p - c->outbuf - 4, c->outbuf); + } + CHECK_LT(p, c->outbuf + sizeof(c->outbuf)); + c->iovlen = 0; + c->iov[c->iovlen].iov_base = c->outbuf; + c->iov[c->iovlen].iov_len = p - c->outbuf; + ++c->iovlen; + if (c->req.method != kHttpHead) { + if (gzipped) { + c->iov[c->iovlen].iov_base = kGzipHeader; + c->iov[c->iovlen].iov_len = sizeof(kGzipHeader); + ++c->iovlen; + } + c->iov[c->iovlen].iov_base = content; + c->iov[c->iovlen].iov_len = contentlength; + ++c->iovlen; + if (gzipped) { + c->iov[c->iovlen].iov_base = c->gzip_footer; + c->iov[c->iovlen].iov_len = sizeof(c->gzip_footer); + ++c->iovlen; + } + } + /* LogRequestLatency(); */ + if (WriteSome(c->fd, c->iov, c->iovlen) == -1) { + VERBOSEF("%s send error %s", c->addrstr, strerror(errno)); + c->close = true; + c->insize = 0; + c->iovlen = 0; + } + if (!c->close) { + memcpy(c->inbuf, c->inbuf + msgsize, c->insize - msgsize); + c->insize -= msgsize; + } + return true; +} + +static void DeleteClient(struct Client *c) { + DEBUGF("%s close", c->addrstr); + CHECK_NE(-1, epoll_ctl(epfd, EPOLL_CTL_DEL, c->fd, NULL)); + LOGIFNEG1(close(c->fd)); + if (c->next) { + c->next->prev = c->prev; + } + if (c->prev) { + c->prev->next = c->next; + } else { + clients = c->next; + } + memset(c, 0, sizeof(*c)); + free(c); +} + +static void ProcessInbuf(struct Client *c) { + long double now; + for (;;) { + c->startrequest = now = nowl(); + if (now - nowish > 1) UpdateCurrentDate(now); + if (HandleRequest(c)) { + if (GetIovSize(c->iov, c->iovlen)) { + CHECK_NE(-1, epoll_ctl(epfd, EPOLL_CTL_MOD, c->fd, + &(struct epoll_event){EPOLLOUT, {c}})); + } else { + if (c->close && !c->insize) { + DeleteClient(c); + } else if (c->insize) { + continue; + } + } + } else if (c->insize == sizeof(c->inbuf)) { + WARNF("%s http message too big", c->addrstr); + DeleteClient(c); + } + break; + } +} + +static void ProcessRead(struct Client *c) { + char *p; + ssize_t rc; + rc = read(c->fd, c->inbuf + c->insize, sizeof(c->inbuf) - c->insize); + if (rc != -1) { + if (rc) { + c->insize += (size_t)rc; + ProcessInbuf(c); + } else { + DeleteClient(c); + } + } else if (errno != EINTR) { + if (errno == ECONNRESET) { + DEBUGF("%s reset", c->addrstr); + } else { + WARNF("%s recv error %s", c->addrstr, strerror(errno)); + } + DeleteClient(c); + } +} + +static void ProcessWrite(struct Client *c) { + if (WriteSome(c->fd, c->iov, c->iovlen) != -1) { + if (!GetIovSize(c->iov, c->iovlen)) { + if (!c->close) { + CHECK_NE(-1, epoll_ctl(epfd, EPOLL_CTL_MOD, c->fd, + &(struct epoll_event){EPOLLIN, {c}})); + ProcessInbuf(c); + } else { + DeleteClient(c); + } + } + } else { + VERBOSEF("%s send error %s", c->addrstr, strerror(errno)); + DeleteClient(c); + } +} + +static void ProcessConnect(void) { + int fd; + struct Client *c; + c = xmalloc(sizeof(struct Client)); + c->addrsize = sizeof(c->addr); + if ((fd = accept(server, &c->addr, &c->addrsize)) != -1) { + c->fd = fd; + c->prev = NULL; + c->next = clients; + c->insize = 0; + c->iovlen = 0; + c->close = false; + c->startconnection = nowl(); + c->startrequest = c->startconnection; + DescribeAddress(c->addrstr, &c->addr); + CHECK_NE(-1, epoll_ctl(epfd, EPOLL_CTL_ADD, c->fd, + &(struct epoll_event){EPOLLIN, {c}})); + if (clients) clients->prev = c; + clients = c; + DEBUGF("%s accept", c->addrstr); + } else if (errno != EINTR) { + FATALF("%s accept error %s", serveraddrstr, strerror(errno)); + free(c); + } +} + +static void HandleSignals(void) { + if (invalidated) { + if (OpenZip(programfile)) { + LOGF("%s reindexed zip", serveraddrstr); + } else { + WARNF("%s reindexing failed", serveraddrstr); + } + invalidated = false; + } + if (heartbeat | notimer) { + UpdateCurrentDate(nowl()); + heartbeat = false; + } +} + +static void ProcessEvents(void) { + int i, j, n; + struct epoll_event events[16]; + for (;;) { + if (terminated) { + while (clients) { + if (!clients->insize) { + DeleteClient(clients); + } else { + break; + } + } + if (!clients) break; + } + if ((n = epoll_wait(epfd, events, ARRAYLEN(events), 200)) != -1) { + for (i = 0; i < n; ++i) { + if (!events[i].data.ptr) { + CHECK(events[i].events & EPOLLIN); + ProcessConnect(); + } else if (events[i].events & (EPOLLIN | EPOLLERR | EPOLLHUP)) { + ProcessRead(events[i].data.ptr); + } else if (events[i].events & EPOLLOUT) { + ProcessWrite(events[i].data.ptr); + } + HandleSignals(); + } + } else { + CHECK_EQ(EINTR, errno); + HandleSignals(); + } + } +} + +static void TuneServerSocket(void) { + int yes = 1; + LOGIFNEG1(setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))); + LOGIFNEG1(setsockopt(server, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))); + LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes))); + LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_FASTOPEN, &yes, sizeof(yes))); + LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_QUICKACK, &yes, sizeof(yes))); +} + +void GreenBean(void) { + gmtoff = GetGmtOffset(); + programfile = (const char *)getauxval(AT_EXECFN); + CHECK(OpenZip(programfile)); + xsigaction(SIGINT, OnTerminate, 0, 0, 0); + xsigaction(SIGHUP, OnTerminate, 0, 0, 0); + xsigaction(SIGTERM, OnTerminate, 0, 0, 0); + xsigaction(SIGCHLD, SIG_IGN, 0, 0, 0); + xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0); + xsigaction(SIGUSR1, OnHup, 0, 0, 0); + xsigaction(SIGALRM, OnAlarm, 0, 0, 0); + if (setitimer(ITIMER_REAL, &kHeartbeat, NULL) == -1) notimer = true; + CHECK_NE(-1, (epfd = epoll_create1(0))); + CHECK_NE(-1, (server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))); + TuneServerSocket(); + CHECK_NE(-1, bind(server, &serveraddr, sizeof(serveraddr))); + CHECK_NE(-1, listen(server, 10)); + CHECK_NE(-1, epoll_ctl(epfd, EPOLL_CTL_ADD, server, + &(struct epoll_event){EPOLLIN})); + DescribeAddress(serveraddrstr, &serveraddr); + if (daemonize) Daemonize(); + VERBOSEF("%s listen", serveraddrstr); + heartbeat = true; + ProcessEvents(); + CHECK_NE(-1, epoll_ctl(epfd, EPOLL_CTL_DEL, server, NULL)); + VERBOSEF("%s terminated", serveraddrstr); + LOGIFNEG1(close(server)); + LOGIFNEG1(close(epfd)); +} + +int main(int argc, char *argv[]) { + showcrashreports(); + GetOpts(argc, argv); + GreenBean(); + return 0; +} diff --git a/tool/net/net.mk b/tool/net/net.mk index 5163b3a7..2031ceeb 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -73,6 +73,18 @@ o/$(MODE)/tool/net/redbean.com.dbg: \ $(APE) @$(APELINK) +o/$(MODE)/tool/net/greenbean.com.dbg: \ + $(TOOL_NET_DEPS) \ + o/$(MODE)/tool/net/greenbean.o \ + o/$(MODE)/tool/net/redbean.ico.zip.o \ + o/$(MODE)/tool/net/redbean.png.zip.o \ + o/$(MODE)/tool/net/redbean.css.zip.o \ + o/$(MODE)/tool/net/redbean.html.zip.o \ + o/$(MODE)/tool/net/net.pkg \ + $(CRT) \ + $(APE) + @$(APELINK) + # ifeq (,$(MODE)) # $(TOOL_NET_OBJS): \ # OVERRIDE_CFLAGS += \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 82ab65c0..fab8275a 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -68,13 +68,12 @@ #include "libc/time/time.h" #include "libc/x/x.h" #include "libc/zip.h" -#include "libc/zipos/zipos.h" +#include "libc/zipos/zipos.internal.h" #include "net/http/http.h" #include "third_party/getopt/getopt.h" #include "third_party/zlib/zlib.h" /* TODO(jart): Implement more lenient message framing */ -/* TODO(jart): Windows IOCP fiber system call support */ #define USAGE \ " [-hvdsm] [-p PORT]\n\ @@ -1116,7 +1115,7 @@ void ProcessRequests(void) { void ProcessConnection(void) { int pid; clientaddrsize = sizeof(clientaddr); - client = accept4(server, &clientaddr, &clientaddrsize, 0 /* SOCK_CLOEXEC */); + client = accept(server, &clientaddr, &clientaddrsize); if (client != -1) { startconnection = nowl(); if ((pid = uniprocess ? -1 : fork()) > 0) { diff --git a/tool/viz/deathstar.c b/tool/viz/deathstar.c index e3f90ffe..a1fdf663 100644 --- a/tool/viz/deathstar.c +++ b/tool/viz/deathstar.c @@ -130,24 +130,20 @@ static void DrawSphere(long double k, long double ambient) { int main() { long double ang; struct termios old; - if (cancolor()) { - WRITE("\e[?25l"); - if (!setjmp(jb_)) { - xsigaction(SIGINT, OnCtrlC, 0, 0, NULL); - ang = 0; - for (;;) { - WRITE("\e[H"); - light_[1] = cosl(ang * 2); - sincosl(ang, &light_[0], &light_[2]); - Normalize(light_); - ang += .05L; - DrawSphere(1.5L, .01L); - usleep(1.L / FRAMERATE * 1e6); - } + WRITE("\e[?25l"); + if (!setjmp(jb_)) { + xsigaction(SIGINT, OnCtrlC, 0, 0, NULL); + ang = 0; + for (;;) { + WRITE("\e[H"); + light_[1] = cosl(ang * 2); + sincosl(ang, &light_[0], &light_[2]); + Normalize(light_); + ang += .05L; + DrawSphere(1.5L, .01L); + usleep(1.L / FRAMERATE * 1e6); } - WRITE("\e[0m\e[H\e[J\e[?25h"); - return 0; - } else { - return 1; } + WRITE("\e[0m\e[H\e[J\e[?25h"); + return 0; } diff --git a/tool/viz/life.c b/tool/viz/life.c index 2bb0ad93..0252446b 100644 --- a/tool/viz/life.c +++ b/tool/viz/life.c @@ -1387,7 +1387,7 @@ int main(int argc, char *argv[]) { return 1; } } - if (IsWindows()) { + if (0 && IsWindows()) { Gui(); } else { Tui(); diff --git a/tool/viz/printvideo.c b/tool/viz/printvideo.c index afe19752..58a06501 100644 --- a/tool/viz/printvideo.c +++ b/tool/viz/printvideo.c @@ -904,12 +904,12 @@ static int Rebuffer(FILE *f) { f->end = (f->end + got) & (f->size - 1); return got; } else { - return fseteof(f); + return __fseteof(f); } } else if (errno == EINTR || errno == EAGAIN) { return 0; } else { - return fseterrno(f); + return __fseterrno(f); } } diff --git a/usr/share/dict/hangman b/usr/share/dict/hangman new file mode 100644 index 00000000..6ff32ce2 --- /dev/null +++ b/usr/share/dict/hangman @@ -0,0 +1,714 @@ +abruptly +absurd +abyss +affix +askew +avenue +awkward +axiom +azure +bagpipes +bandwagon +banjo +bayou +beekeeper +bikini +blitz +blizzard +boggle +bookworm +boxcar +boxful +buckaroo +buffalo +buffoon +buxom +buzzard +buzzing +buzzwords +caliph +cobweb +cockiness +croquet +crypt +curacao +cycle +daiquiri +dirndl +disavow +dizzying +duplex +dwarves +embezzle +equip +espionage +euouae +exodus +faking +fishhook +fixable +fjord +flapjack +flopping +fluffiness +flyby +foxglove +frazzled +frizzled +fuchsia +funny +gabby +galaxy +galvanize +gazebo +giaour +gizmo +glowworm +glyph +gnarly +gnostic +gossip +grogginess +haiku +haphazard +hyphen +iatrogenic +icebox +injury +ivory +ivy +jackpot +jaundice +jawbreaker +jaywalk +jazziest +jazzy +jelly +jigsaw +jinx +jiujitsu +jockey +jogging +joking +jovial +joyful +juicy +jukebox +jumbo +kayak +kazoo +keyhole +khaki +kilobyte +kiosk +kitsch +kiwifruit +klutz +knapsack +larynx +lengths +lucky +luxury +lymph +marquis +matrix +megahertz +microwave +mnemonic +mystify +naphtha +nightclub +nowadays +numbskull +nymph +onyx +ovary +oxidize +oxygen +pajama +peekaboo +phlegm +pixel +pizazz +pneumonia +polka +pshaw +psyche +puppy +puzzling +quartz +queue +quips +quixotic +quiz +quizzes +quorum +razzmatazz +rhubarb +rhythm +rickshaw +schnapps +scratch +shiv +snazzy +sphinx +spritz +squawk +staff +strength +strengths +stretch +stronghold +stymied +subway +swivel +syndrome +thriftless +thumbscrew +topaz +transcript +transgress +transplant +triphthong +twelfth +twelfths +unknown +unworthy +unzip +uptown +vaporize +vixen +vodka +voodoo +vortex +voyeurism +walkway +waltz +wave +wavy +waxy +wellspring +wheezy +whiskey +whizzing +whomever +wimpy +witchcraft +wizard +woozy +wristwatch +wyvern +xylophone +yachtsman +yippee +yoked +youthful +yummy +zephyr +zigzag +zigzagging +zilch +zipper +zodiac +zombie +about +across +after +against +among +before +between +through +under +every +other +because +though +while +where +again +forward +still +there +together +almost +enough +little +quite +tomorrow +yesterday +north +south +please +account +addition +adjustment +advertisement +agreement +amount +amusement +animal +answer +apparatus +approval +argument +attack +attempt +attention +attraction +authority +balance +behaviour +belief +birth +blood +brass +bread +breath +brother +building +burst +business +butter +canvas +cause +chalk +chance +change +cloth +colour +comfort +committee +company +comparison +competition +condition +connection +control +copper +cotton +cough +country +cover +crack +credit +crime +crush +current +curve +damage +danger +daughter +death +decision +degree +design +desire +destruction +detail +development +digestion +direction +discovery +discussion +disease +disgust +distance +distribution +division +doubt +drink +driving +earth +education +effect +error +event +example +exchange +existence +expansion +experience +expert +family +father +feeling +fiction +field +fight +flame +flight +flower +force +friend +front +fruit +glass +government +grain +grass +group +growth +guide +harbour +harmony +hearing +history +humour +impulse +increase +industry +insect +instrument +insurance +interest +invention +jelly +journey +judge +knowledge +language +laugh +learning +leather +letter +level +light +limit +linen +liquid +machine +manager +market +measure +meeting +memory +metal +middle +minute +money +month +morning +mother +motion +mountain +music +nation +night +noise +number +observation +offer +operation +opinion +order +organization +ornament +owner +paint +paper +paste +payment +peace +person +place +plant +pleasure +point +poison +polish +porter +position +powder +power +price +print +process +produce +profit +property +prose +protest +punishment +purpose +quality +question +range +reaction +reading +reason +record +regret +relation +religion +representative +request +respect +reward +rhythm +river +scale +science +secretary +selection +sense +servant +shade +shake +shame +shock +silver +sister +sleep +slope +smash +smell +smile +smoke +sneeze +society +sound +space +stage +start +statement +steam +steel +stitch +stone +story +stretch +structure +substance +sugar +suggestion +summer +support +surprise +system +taste +teaching +tendency +theory +thing +thought +thunder +touch +trade +transport +trick +trouble +twist +value +verse +vessel +voice +waste +water +weather +weight +winter +woman +wound +writing +angle +apple +basin +basket +berry +blade +board +bottle +brain +brake +branch +brick +bridge +brush +bucket +button +camera +carriage +chain +cheese +chest +church +circle +clock +cloud +collar +curtain +cushion +drain +drawer +dress +engine +feather +finger +floor +frame +garden +glove +hammer +heart +horse +hospital +house +island +jewel +kettle +knife +library +match +monkey +mouth +muscle +needle +nerve +office +orange +parcel +pencil +picture +plane +plate +plough +pocket +potato +prison +receipt +school +scissors +screw +sheep +shelf +shirt +skirt +snake +spade +sponge +spoon +spring +square +stamp +station +stick +stocking +stomach +store +street +table +thread +throat +thumb +ticket +tongue +tooth +train +trousers +umbrella +watch +wheel +whistle +window +angry +automatic +beautiful +black +boiling +bright +broken +brown +cheap +chemical +chief +clean +clear +common +complex +conscious +dependent +early +elastic +electric +equal +fertile +first +fixed +frequent +general +great +hanging +happy +healthy +hollow +important +living +married +material +medical +military +natural +necessary +normal +parallel +physical +political +possible +present +private +probable +quick +quiet +ready +regular +responsible +right +round +second +separate +serious +sharp +smooth +sticky +stiff +straight +strong +sudden +sweet +thick +tight +tired +violent +waiting +yellow +young +awake +bitter +certain +complete +cruel +delicate +different +dirty +false +feeble +female +foolish +future +green +loose +mixed +narrow +opposite +public +rough +secret +short +simple +small +solid +special +strange +white +wrong