mirror of
https://github.com/jart/cosmopolitan.git
synced 2026-11-08 08:08:04 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af2ef0d223 | ||
|
|
8111462789 | ||
|
|
f52f65b2e3 | ||
|
|
997ce29ddc | ||
|
|
d38700687a | ||
|
|
0f89140882 | ||
|
|
0cee831da3 | ||
|
|
b41f91c658 | ||
|
|
e7329b7cba | ||
|
|
c19ac8729d | ||
|
|
9d2f993364 | ||
|
|
31dab8a75d | ||
|
|
7a06760e6f | ||
|
|
9209ea63a7 | ||
|
|
84b9b8ed87 | ||
|
|
e557058ac8 | ||
|
|
467a332e38 | ||
|
|
7ae556463a | ||
|
|
c424352a0a | ||
|
|
d5910e2673 | ||
|
|
9b7c8db846 | ||
|
|
4a6fd3d910 | ||
|
|
38df0a4186 | ||
|
|
1c0adb556d | ||
|
|
8aca94f951 | ||
|
|
38e3ab57a6 | ||
|
|
672ccda37c | ||
|
|
9849b4c7ba | ||
|
|
3421b9a580 | ||
|
|
29d695f4c9 | ||
|
|
7d6835c0dd | ||
|
|
05197afca2 | ||
|
|
bc353f454b | ||
|
|
7bc24888fe | ||
|
|
59ac141e49 | ||
|
|
60b68d7152 | ||
|
|
a5b483f2d4 | ||
|
|
d3efa47f81 | ||
|
|
7822917fc2 | ||
|
|
81ee11a16e | ||
|
|
f155205eb0 | ||
|
|
e46ef487a8 | ||
|
|
afc07b9339 | ||
|
|
edb8fef06c | ||
|
|
b75a4654cf | ||
|
|
fe3216e961 | ||
|
|
174d288e66 | ||
|
|
9f9b240f3d | ||
|
|
726f04e8aa | ||
|
|
556697cbd8 | ||
|
|
3ee3736a01 | ||
|
|
01bd7d1008 | ||
|
|
32321ab1e9 | ||
|
|
3b4fcd8575 | ||
|
|
6ba3b448f3 | ||
|
|
91e167caaf | ||
|
|
ccbae7799e | ||
|
|
af24c19db3 | ||
|
|
950a1b310b | ||
|
|
daca5499b9 | ||
|
|
2526a9b8c7 | ||
|
|
7549a5755e | ||
|
|
795d295590 | ||
|
|
3f3cb0650b | ||
|
|
acd8900071 | ||
|
|
6c60793f3a | ||
|
|
ad97775370 | ||
|
|
0b5f84dd20 | ||
|
|
27b5deefb1 | ||
|
|
ecb2ef7c39 | ||
|
|
304cf8869c | ||
|
|
3e0ddf70e9 | ||
|
|
5005f2e446 | ||
|
|
bc8532688b | ||
|
|
09811e739f | ||
|
|
24988f2351 | ||
|
|
b46f763ee7 | ||
|
|
87708c5d6e |
No files matched your search
@@ -21,8 +21,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: support ape bins
|
||||
run: sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register"
|
||||
- name: support ape bins 1
|
||||
run: sudo cp build/bootstrap/ape.elf /usr/bin/ape
|
||||
|
||||
- name: support ape bins 2
|
||||
run: sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
|
||||
|
||||
- name: make matrix
|
||||
run: V=0 make -j2 MODE=${{ matrix.mode }}
|
||||
@@ -121,6 +121,7 @@ include third_party/compiler_rt/compiler_rt.mk # │
|
||||
include libc/str/str.mk # │
|
||||
include third_party/xed/xed.mk # │
|
||||
include third_party/zlib/zlib.mk # │
|
||||
include third_party/double-conversion/dc.mk # │
|
||||
include libc/elf/elf.mk # │
|
||||
include ape/ape.mk # │
|
||||
include libc/fmt/fmt.mk # │
|
||||
@@ -160,6 +161,7 @@ include net/https/https.mk # │
|
||||
include third_party/regex/regex.mk #─┘
|
||||
include third_party/tidy/tidy.mk
|
||||
include third_party/third_party.mk
|
||||
include third_party/nsync/testing/testing.mk
|
||||
include libc/testlib/testlib.mk
|
||||
include tool/viz/lib/vizlib.mk
|
||||
include tool/args/args.mk
|
||||
@@ -167,7 +169,7 @@ include test/tool/args/test.mk
|
||||
include third_party/linenoise/linenoise.mk
|
||||
include third_party/maxmind/maxmind.mk
|
||||
include net/finger/finger.mk
|
||||
include third_party/double-conversion/double-conversion.mk
|
||||
include third_party/double-conversion/test/test.mk
|
||||
include third_party/lua/lua.mk
|
||||
include third_party/sed/sed.mk
|
||||
include third_party/awk/awk.mk
|
||||
@@ -202,6 +204,7 @@ include tool/hash/hash.mk
|
||||
include tool/net/net.mk
|
||||
include tool/viz/viz.mk
|
||||
include tool/tool.mk
|
||||
include net/turfwar/turfwar.mk
|
||||
include test/libc/tinymath/test.mk
|
||||
include test/libc/intrin/test.mk
|
||||
include test/libc/mem/test.mk
|
||||
@@ -307,7 +310,7 @@ COSMOPOLITAN_OBJECTS = \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_REGEX \
|
||||
LIBC_THREAD \
|
||||
THIRD_PARTY_NSYNC_MALLOC \
|
||||
THIRD_PARTY_NSYNC_MEM \
|
||||
LIBC_MEM \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
LIBC_RUNTIME \
|
||||
@@ -400,6 +403,15 @@ $(SRCS): \
|
||||
libc/integral/lp64arg.inc \
|
||||
libc/integral/lp64.inc
|
||||
|
||||
.PHONY: toolchain
|
||||
toolchain: o/cosmopolitan.h \
|
||||
o/$(MODE)/ape/public/ape.lds \
|
||||
o/$(MODE)/libc/crt/crt.o \
|
||||
o/$(MODE)/ape/ape.o \
|
||||
o/$(MODE)/ape/ape-copy-self.o \
|
||||
o/$(MODE)/ape/ape-no-modify-self.o \
|
||||
o/$(MODE)/cosmopolitan.a
|
||||
|
||||
# UNSPECIFIED PREREQUISITES TUTORIAL
|
||||
#
|
||||
# A build rule must exist for all files that make needs to consider in
|
||||
|
||||
@@ -23,8 +23,8 @@ If you're doing your development work on Linux or BSD then you need just
|
||||
five files to get started. Here's what you do on Linux:
|
||||
|
||||
```sh
|
||||
wget https://justine.lol/cosmopolitan/cosmopolitan-amalgamation-2.1.zip
|
||||
unzip cosmopolitan-amalgamation-2.1.zip
|
||||
wget https://justine.lol/cosmopolitan/cosmopolitan-amalgamation-2.1.1.zip
|
||||
unzip cosmopolitan-amalgamation-2.1.1.zip
|
||||
printf 'main() { printf("hello world\\n"); }\n' >hello.c
|
||||
gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \
|
||||
-fno-omit-frame-pointer -pg -mnop-mcount -mno-tls-direct-seg-refs -gdwarf-4 \
|
||||
@@ -258,8 +258,10 @@ Funding for this project is crowdsourced using
|
||||
[GitHub Sponsors](https://github.com/sponsors/jart) and
|
||||
[Patreon](https://www.patreon.com/jart). Your support is what makes this
|
||||
project possible. Thank you! We'd also like to give special thanks to
|
||||
the following individuals:
|
||||
the following groups and individuals:
|
||||
|
||||
- [Joe Drumgoole](https://github.com/jdrumgoole)
|
||||
- [Rob Figueiredo](https://github.com/robfig)
|
||||
- [Wasmer](https://wasmer.io/)
|
||||
|
||||
For publicly sponsoring our work at the highest tier.
|
||||
@@ -125,6 +125,7 @@ ape_mz: .asciz "MZqFpD='\n" # Mark 'Zibo' Joseph Zbikowski
|
||||
.org 0x24 # MZ: bytes reserved for you
|
||||
.ascii "JT" # MZ: OEM identifier
|
||||
.short 0 # MZ: OEM information
|
||||
.ascii "' <<'@'\n"
|
||||
.org 0x40-4 # MZ: bytes reserved for you
|
||||
#if SupportsWindows() || SupportsMetal()
|
||||
.long RVA(ape_pe) # PE: the new technology
|
||||
@@ -250,7 +251,6 @@ pc: cld
|
||||
xor %cx,%cx # current cylinder
|
||||
xor %dh,%dh # current head
|
||||
mov $v_ape_realsectors,%di # total sectors
|
||||
sub $v_ape_realslacksectors,%di
|
||||
3: call pcread
|
||||
dec %di
|
||||
jnz 3b
|
||||
@@ -549,7 +549,7 @@ ape_disk:
|
||||
the bourne executable & linkable format */
|
||||
|
||||
#if SupportsWindows() || SupportsMetal() || SupportsXnu()
|
||||
apesh: .ascii "'\n#'\"\n" # sixth edition shebang
|
||||
apesh: .ascii "\n@\n#'\"\n" # sixth edition shebang
|
||||
// Until all operating systems can be updated to support APE,
|
||||
// we have a beautiful, yet imperfect workaround, which is to
|
||||
// modify the binary to follow the local system's convention.
|
||||
@@ -874,7 +874,7 @@ ape_macho:
|
||||
.long (520f-510f)/4 # count
|
||||
510: .quad 0 # rax
|
||||
.quad IMAGE_BASE_VIRTUAL # rbx
|
||||
.quad XNU # rcx
|
||||
.quad _HOSTXNU # rcx
|
||||
.quad 0 # rdx
|
||||
.quad 0 # rdi
|
||||
.quad 0 # rsi
|
||||
@@ -1137,12 +1137,17 @@ sconf: .short 1843200/*hz*/ / 16/*wut*/ / 9600/*baud*/
|
||||
.endobj sconf,global,hidden
|
||||
|
||||
// Global Descriptor Table
|
||||
//
|
||||
// @note address portion only concern legacy modes
|
||||
.align 8
|
||||
gdt: .short 2f-1f # table byte length
|
||||
.long REAL(1f),0 # table address
|
||||
.zero 2
|
||||
_gdtrphy:
|
||||
.short 2f-1f-1 # table byte length
|
||||
.long REAL(1f) # table address (physical space)
|
||||
.endobj _gdtrphy,global,hidden
|
||||
_gdtr:
|
||||
.short 2f-1f-1 # table byte length
|
||||
.quad 1f # table address (final virtual space)
|
||||
.endobj _gdtr,global,hidden
|
||||
.align 8
|
||||
_gdt:
|
||||
1:
|
||||
// ┌G:granularity (1 → limit *= 0x1000)
|
||||
// │┌D/B:default operation size (0 = 16|64bit, 1 = 32-bit)
|
||||
@@ -1172,7 +1177,9 @@ gdt: .short 2f-1f # table byte length
|
||||
.quad 0b0000000011001111100100100000000000000000000000001111111111111111 #32
|
||||
.quad 0b0000000010101111100110110000000000000000000000001111111111111111 #40
|
||||
.quad 0b0000000010101111100100110000000000000000000000001111111111111111 #48
|
||||
2: .endobj gdt,global,hidden
|
||||
.tssdescstub _tss #56,64
|
||||
2:
|
||||
.endobj _gdt,global,hidden
|
||||
|
||||
/*─────────────────────────────────────────────────────────────────────────────╗
|
||||
│ αcτµαlly pδrταblε εxεcµταblε § real mode │
|
||||
@@ -1278,8 +1285,8 @@ lcheck: pushf # check for i8086 / i8088 / i80186
|
||||
jl 10f
|
||||
mov %edi,%eax
|
||||
cpuid
|
||||
mov $1<<29,%edi # need nexgen32e long mode support
|
||||
and %edi,%edx
|
||||
mov $1<<29|1<<20,%edi # need nexgen32e long mode support
|
||||
and %edi,%edx # & nx support
|
||||
cmp %edi,%edx
|
||||
jne 10f
|
||||
xor %ax,%ax
|
||||
@@ -1408,7 +1415,7 @@ pinit: push %ds
|
||||
movl $0x79000+PAGE_V+PAGE_RW,0x7b000-SEG # PD←PDT (+)
|
||||
movl $0x79000+PAGE_V+PAGE_RW,0x7a000-SEG # PD←PDT (-)
|
||||
mov $512,%cx # PD±2MB
|
||||
mov $PAGE_V+PAGE_RW,%eax
|
||||
mov $PAGE_V+PAGE_RSRV+PAGE_RW,%eax
|
||||
xor %di,%di
|
||||
0: stosl
|
||||
add $0x1000,%eax
|
||||
@@ -1431,9 +1438,9 @@ golong: cli
|
||||
mov %eax,%cr4
|
||||
movl $EFER,%ecx
|
||||
rdmsr
|
||||
or $EFER_LME|EFER_SCE,%eax
|
||||
or $EFER_LME|EFER_SCE|EFER_NXE,%eax
|
||||
wrmsr
|
||||
lgdt REAL(gdt)
|
||||
lgdt REAL(_gdtrphy)
|
||||
mov %cr0,%eax
|
||||
or $CR0_PE|CR0_PG|CR0_MP,%eax
|
||||
and $~CR0_EM,%eax
|
||||
@@ -1451,6 +1458,8 @@ long: xor %eax,%eax
|
||||
mov %eax,%fs
|
||||
mov %eax,%gs
|
||||
mov $0x80000,%esp
|
||||
mov $GDT_LONG_TSS,%al
|
||||
ltr %ax
|
||||
xor %r12d,%r12d
|
||||
xor %r13d,%r13d
|
||||
xor %r14d,%r14d
|
||||
@@ -1464,6 +1473,8 @@ long: xor %eax,%eax
|
||||
call __map_phdrs
|
||||
push $0x037f
|
||||
fldcw (%rsp)
|
||||
lgdt _gdtr # reload GDTR for
|
||||
# virtual memory space
|
||||
movabs $kernel,%rax
|
||||
jmp *%rax
|
||||
.endfn long
|
||||
@@ -1544,7 +1555,7 @@ kernel: movabs $ape_stack_vaddr,%rsp
|
||||
.byte 0x0f,0x1f,0207 # nop rdi binbase
|
||||
.long (IMAGE_BASE_VIRTUAL-IMAGE_BASE_REAL)/512
|
||||
#endif
|
||||
push $METAL # sets __hostos in crt.S
|
||||
push $_HOSTMETAL # sets __hostos in crt.S
|
||||
pop %rcx
|
||||
push $0
|
||||
mov %rsp,%rbp
|
||||
@@ -1586,7 +1597,6 @@ kernel: movabs $ape_stack_vaddr,%rsp
|
||||
.ldsvar _end
|
||||
.ldsvar _etext
|
||||
.ldsvar v_ape_realsectors
|
||||
.ldsvar v_ape_realslacksectors
|
||||
.ldsvar v_ape_highsectors
|
||||
.ldsvar ape_idata_ro
|
||||
.ldsvar ape_pad_rodata
|
||||
|
||||
+3
-5
@@ -427,7 +427,7 @@ SECTIONS {
|
||||
|
||||
KEEP(*(.bssepilogue))
|
||||
/*END: NT FORK COPYING */
|
||||
. = ALIGN(FRAMESIZE); /* for brk()/sbrk() allocation */
|
||||
. = ALIGN(PAGESIZE);
|
||||
HIDDEN(_end = .);
|
||||
PROVIDE_HIDDEN(end = .);
|
||||
} :Bss
|
||||
@@ -565,13 +565,11 @@ SHSTUB2(ape_loader_dd_count,
|
||||
#if SupportsMetal()
|
||||
HIDDEN(v_ape_realsectors =
|
||||
MIN(0x70000 - IMAGE_BASE_REAL,
|
||||
ROUNDUP(RVA(_edata), 4096)) / 512);
|
||||
HIDDEN(v_ape_realslacksectors =
|
||||
v_ape_realsectors - MIN(0x70000 - IMAGE_BASE_REAL,
|
||||
ROUNDUP(RVA(_edata), 512)) / 512);
|
||||
ROUNDUP(RVA(_edata), 512)) / 512);
|
||||
HIDDEN(v_ape_realpages = v_ape_realsectors / (4096 / 512));
|
||||
HIDDEN(v_ape_highsectors =
|
||||
(ROUNDUP(RVA(_edata), 512) / 512) - v_ape_realsectors);
|
||||
TSSDESCSTUB2(_tss, _tss, _tss_end ? _tss_end - _tss - 1 : 0);
|
||||
#endif
|
||||
|
||||
/* ZIP End of Central Directory header */
|
||||
|
||||
+2
-2
@@ -175,7 +175,7 @@ macho: .long 0xFEEDFACE+1
|
||||
510: .quad 0 # rax
|
||||
.quad 0 # rbx
|
||||
.quad 0 # rcx
|
||||
.quad XNU # rdx
|
||||
.quad _HOSTXNU # rdx
|
||||
.quad 0 # rdi
|
||||
.quad 0 # rsi
|
||||
.quad 0 # rbp
|
||||
@@ -221,7 +221,7 @@ _start:
|
||||
jne 0f
|
||||
cmp $+1,%edx
|
||||
jne 0f
|
||||
mov $XNU,%dl
|
||||
mov $_HOSTXNU,%dl
|
||||
xor %ecx,%ecx
|
||||
|
||||
0: mov %rsp,%rsi
|
||||
|
||||
+2
-2
@@ -90,7 +90,7 @@ macho: .long 0xFEEDFACE+1
|
||||
510: .quad 0 # rax
|
||||
.quad 0 # rbx
|
||||
.quad 0 # rcx
|
||||
.quad XNU # rdx
|
||||
.quad _HOSTXNU # rdx
|
||||
.quad 0 # rdi
|
||||
.quad 0 # rsi
|
||||
.quad 0 # rbp
|
||||
@@ -121,7 +121,7 @@ _start:
|
||||
jne 0f
|
||||
cmp $+1,%edx
|
||||
jne 0f
|
||||
mov $XNU,%dl
|
||||
mov $_HOSTXNU,%dl
|
||||
xor %ecx,%ecx
|
||||
|
||||
0: mov %rsp,%rsi
|
||||
|
||||
@@ -180,6 +180,20 @@
|
||||
.stub \name\()_bcs\n,long
|
||||
.endm
|
||||
|
||||
// Task State Segment Descriptor Entries.
|
||||
.macro .tssdescstub name:req
|
||||
.ifndef \name
|
||||
.weak \name
|
||||
.set \name,0
|
||||
.endif
|
||||
.ifndef \name\()_end
|
||||
.weak \name\()_end
|
||||
.set \name\()_end,0
|
||||
.endif
|
||||
.stub \name\()_desc_ent0,quad
|
||||
.stub \name\()_desc_ent1,quad
|
||||
.endm
|
||||
|
||||
/* clang-format on */
|
||||
#elif defined(__LINKER__)
|
||||
|
||||
@@ -257,5 +271,22 @@
|
||||
(X) % 10 * 0x1000000 \
|
||||
: 0xffffffffffffffff)
|
||||
|
||||
/**
|
||||
* Laying out the GDT entries for a TSS for bare metal operation.
|
||||
*/
|
||||
#define TSSDESCSTUB2(SYM, BASE, LIM) \
|
||||
HIDDEN(SYM##_desc_ent0 = TSSDESC_ENT0(BASE, LIM)); \
|
||||
HIDDEN(SYM##_desc_ent1 = TSSDESC_ENT1(BASE)); \
|
||||
ASSERT((LIM) >= 0 && (LIM) <= 0xffff, "bare metal TSS is suspiciously fat")
|
||||
#define TSSDESC_ENT0(BASE, LIM) \
|
||||
(((LIM) << 0 & 0x000000000000ffff) | \
|
||||
((BASE) << 16 & 0x000000ffffff0000) | \
|
||||
0x89 << 40 | \
|
||||
((LIM) >> 16 << 48 & 0x000f000000000000) | \
|
||||
0x2 << 52 | \
|
||||
((BASE) >> 24 << 56 & 0xff00000000000000))
|
||||
#define TSSDESC_ENT1(BASE) \
|
||||
((BASE) >> 32 << 0 & 0x00000000ffffffff)
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* APE_MACROS_H_ */
|
||||
Binary file not shown.
+15
-23
@@ -73,33 +73,25 @@ IMAGE_BASE_VIRTUAL ?= 0x400000
|
||||
IGNORE := $(shell $(ECHO) -2 ♥cosmo)
|
||||
IGNORE := $(shell $(MKDIR) o/tmp)
|
||||
|
||||
ifneq ("$(wildcard o/third_party/gcc/bin/x86_64-pc-linux-gnu-as.exe)","")
|
||||
AS = o/third_party/gcc/bin/x86_64-pc-linux-gnu-as.exe
|
||||
CC = o/third_party/gcc/bin/x86_64-pc-linux-gnu-gcc.exe
|
||||
CXX = o/third_party/gcc/bin/x86_64-pc-linux-gnu-g++.exe
|
||||
CXXFILT = o/third_party/gcc/bin/x86_64-pc-linux-gnu-c++filt.exe
|
||||
LD = o/third_party/gcc/bin/x86_64-pc-linux-gnu-ld.bfd.exe
|
||||
NM = o/third_party/gcc/bin/x86_64-pc-linux-gnu-nm.exe
|
||||
GCC = o/third_party/gcc/bin/x86_64-pc-linux-gnu-gcc.exe
|
||||
STRIP = o/third_party/gcc/bin/x86_64-pc-linux-gnu-strip.exe
|
||||
OBJCOPY = o/third_party/gcc/bin/x86_64-pc-linux-gnu-objcopy.exe
|
||||
OBJDUMP = o/third_party/gcc/bin/x86_64-pc-linux-gnu-objdump.exe
|
||||
ADDR2LINE = $(PWD)/o/third_party/gcc/bin/x86_64-pc-linux-gnu-addr2line.exe
|
||||
ifneq ("$(wildcard o/third_party/gcc/bin/x86_64-pc-linux-gnu-*)","")
|
||||
PREFIX = o/third_party/gcc/bin/x86_64-pc-linux-gnu-
|
||||
else
|
||||
IGNORE := $(shell build/bootstrap/unbundle.com)
|
||||
AS = o/third_party/gcc/bin/x86_64-linux-musl-as
|
||||
CC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
|
||||
CXX = o/third_party/gcc/bin/x86_64-linux-musl-g++
|
||||
CXXFILT = o/third_party/gcc/bin/x86_64-linux-musl-c++filt
|
||||
LD = o/third_party/gcc/bin/x86_64-linux-musl-ld.bfd
|
||||
NM = o/third_party/gcc/bin/x86_64-linux-musl-nm
|
||||
GCC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
|
||||
STRIP = o/third_party/gcc/bin/x86_64-linux-musl-strip
|
||||
OBJCOPY = o/third_party/gcc/bin/x86_64-linux-musl-objcopy
|
||||
OBJDUMP = o/third_party/gcc/bin/x86_64-linux-musl-objdump
|
||||
ADDR2LINE = $(PWD)/o/third_party/gcc/bin/x86_64-linux-musl-addr2line
|
||||
PREFIX = o/third_party/gcc/bin/x86_64-linux-musl-
|
||||
endif
|
||||
|
||||
AS = $(PREFIX)as
|
||||
CC = $(PREFIX)gcc
|
||||
CXX = $(PREFIX)g++
|
||||
CXXFILT = $(PREFIX)c++filt
|
||||
LD = $(PREFIX)ld.bfd
|
||||
NM = $(PREFIX)nm
|
||||
GCC = $(PREFIX)gcc
|
||||
STRIP = $(PREFIX)strip
|
||||
OBJCOPY = $(PREFIX)objcopy
|
||||
OBJDUMP = $(PREFIX)objdump
|
||||
ADDR2LINE = $(PWD)/$(PREFIX)addr2line
|
||||
|
||||
export ADDR2LINE
|
||||
export LC_ALL
|
||||
export MKDIR
|
||||
|
||||
@@ -73,6 +73,7 @@ EXAMPLES_DIRECTDEPS = \
|
||||
NET_HTTPS \
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
THIRD_PARTY_DOUBLECONVERSION \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_LIBCXX \
|
||||
|
||||
+1
-8
@@ -9,19 +9,12 @@
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/nt/nt/process.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
dontinline void dostuff(const char *s) {
|
||||
int i, us;
|
||||
srand(rand64()); /* seeds rand() w/ intel rdrnd, auxv, etc. */
|
||||
srand(_rand64()); /* seeds rand() w/ intel rdrnd, auxv, etc. */
|
||||
for (i = 0; i < 5; ++i) {
|
||||
us = rand() % 500000;
|
||||
usleep(us);
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
int main() {
|
||||
kprintf("%d\n", GetCpuCount());
|
||||
kprintf("%d\n", _getcpucount());
|
||||
}
|
||||
@@ -102,7 +102,7 @@ uint64_t rngset64(void) {
|
||||
static unsigned i;
|
||||
static uint64_t s;
|
||||
if (!i) {
|
||||
s = rand64();
|
||||
s = _rand64();
|
||||
i = (s + 1) & (511);
|
||||
}
|
||||
return MarsagliaXorshift64(&s);
|
||||
@@ -215,7 +215,7 @@ const struct Function {
|
||||
{"moby", moby}, //
|
||||
{"mt19937", _mt19937}, //
|
||||
{"python", pythonx2}, //
|
||||
{"rand64", rand64}, //
|
||||
{"rand64", _rand64}, //
|
||||
{"rdrand", rdrand}, //
|
||||
{"rdrnd", rdrand}, //
|
||||
{"rdseed", rdseed}, //
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sock/struct/pollfd.h"
|
||||
#include "libc/sock/struct/sockaddr.h"
|
||||
@@ -137,7 +136,7 @@ void *Worker(void *id) {
|
||||
setsockopt(server, SOL_TCP, TCP_QUICKACK, &yes, sizeof(yes));
|
||||
errno = 0;
|
||||
|
||||
if (bind(server, &addr, sizeof(addr)) == -1) {
|
||||
if (bind(server, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
kprintf("%s() failed %m\n", "socket");
|
||||
goto CloseWorker;
|
||||
}
|
||||
@@ -167,7 +166,7 @@ void *Worker(void *id) {
|
||||
|
||||
// wait for client connection
|
||||
clientaddrsize = sizeof(clientaddr);
|
||||
client = accept(server, &clientaddr, &clientaddrsize);
|
||||
client = accept(server, (struct sockaddr *)&clientaddr, &clientaddrsize);
|
||||
|
||||
// accept() can raise a very diverse number of errors but none of
|
||||
// them are really true showstoppers that would necessitate us to
|
||||
@@ -299,7 +298,7 @@ int main(int argc, char *argv[]) {
|
||||
PORT);
|
||||
}
|
||||
|
||||
threads = argc > 1 ? atoi(argv[1]) : GetCpuCount();
|
||||
threads = argc > 1 ? atoi(argv[1]) : _getcpucount();
|
||||
if (!(1 <= threads && threads <= 100000)) {
|
||||
kprintf("error: invalid number of threads: %d\n", threads);
|
||||
exit(1);
|
||||
|
||||
+436
-357
File diff suppressed because it is too large.
Load diff
+2
-2
@@ -49,12 +49,12 @@ int main(int argc, char *argv[]) {
|
||||
printf("res = %d?\n", res);
|
||||
return -1;
|
||||
}
|
||||
printf("Hello World! %.19Lg\n", atan2l(x, y));
|
||||
printf("\e[92;44mHello World!\e[0m %.19Lg\n", atan2l(x, y));
|
||||
|
||||
// read/print loop so machine doesn't reset on metal
|
||||
for (;;) {
|
||||
if ((res = readansi(0, buf, 16)) > 0) {
|
||||
printf("got %`'.*s\r\n", res, buf);
|
||||
printf("got \e[97m%`'.*s\e[0m\r\n", res, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/termios.h"
|
||||
#include "libc/isystem/unistd.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Demo of program crash reporting with Bare Metal VGA TTY.
|
||||
*
|
||||
* make -j8 o//examples/vga2.com
|
||||
* qemu-system-x86_64 -hda o//examples/vga2.com -serial stdio
|
||||
*/
|
||||
|
||||
STATIC_YOINK("vga_console");
|
||||
STATIC_YOINK("_idt");
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
volatile int x = 1;
|
||||
volatile int y = 2;
|
||||
printf("\e[92;44mHello World!\e[0m %d\n", 1 / (x + y - 3));
|
||||
for (;;);
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/termios.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/append.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/time/struct/tm.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
#include "third_party/musl/passwd.h"
|
||||
|
||||
/**
|
||||
* @fileoverview program for broadcasting message to all terminals, e.g.
|
||||
*
|
||||
* o//examples/wall.com hello friends
|
||||
*
|
||||
* Will place some bold text into the top left corners of all terminals:
|
||||
*
|
||||
* Broadcast message from jart@host (pts/4) (Tue Oct 11 03:52:24 2022):
|
||||
* hello friends
|
||||
*
|
||||
* It's useful for having one terminal tab notify your other ones when
|
||||
* something like a long running job completes. In the old days back
|
||||
* when unix systems had multiple users, the wall command provided the
|
||||
* ultimate lulz especially if it's a setuid binary with root privs.
|
||||
*/
|
||||
|
||||
#define GETOPTS "v"
|
||||
#define USAGE \
|
||||
"\
|
||||
Usage: wall.com [-v] [TEXTMSG] [<TEXTMSG]\n\
|
||||
-v increase verbosity\n\
|
||||
Examples:\n\
|
||||
wall.com 'hello my friends'\n\
|
||||
echo hello my friends | wall.com\n\
|
||||
"
|
||||
|
||||
int g_verbose;
|
||||
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, GETOPTS)) != -1) {
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
g_verbose++;
|
||||
break;
|
||||
case '?':
|
||||
write(1, USAGE, sizeof(USAGE) - 1);
|
||||
exit(0);
|
||||
default:
|
||||
write(2, USAGE, sizeof(USAGE) - 1);
|
||||
exit(64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *GetHost(void) {
|
||||
static char host[254];
|
||||
if (gethostname(host, sizeof(host))) {
|
||||
strcpy(host, "localhost");
|
||||
}
|
||||
return host;
|
||||
}
|
||||
|
||||
char *GetTime(void) {
|
||||
int64_t t;
|
||||
struct tm tm;
|
||||
struct timespec ts;
|
||||
clock_gettime(0, &ts);
|
||||
localtime_r(&ts.tv_sec, &tm);
|
||||
return _chomp(asctime(&tm));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
GetOpts(argc, argv);
|
||||
|
||||
// create broadcast banner
|
||||
// we use \e[1m so the text is strong & visually distinct
|
||||
// we use \e[K so it doesn't get lost in a forest of text
|
||||
// we map \n → \r\n because teletype might be in raw mode
|
||||
char *msg = 0;
|
||||
const char *s;
|
||||
appends(&msg, "\e7"); // save cursor position
|
||||
appends(&msg, "\e[H"); // goto top left corne
|
||||
appends(&msg, "\e[K"); // clear line forward
|
||||
appends(&msg, "\e[1m"); // bold text
|
||||
appendf(&msg, "Broadcast message from %s@%s", getpwuid(getuid())->pw_name,
|
||||
GetHost());
|
||||
if (isatty(0) && (s = ttyname(0))) appendf(&msg, " (%s)", s);
|
||||
appendf(&msg, " (%s):\r\n", GetTime());
|
||||
appends(&msg, "\e[K");
|
||||
|
||||
// append broadcast message
|
||||
if (optind < argc) {
|
||||
// use cli arguments as message if they exist
|
||||
for (int i = 0; optind + i < argc; ++i) {
|
||||
if (i) appends(&msg, " ");
|
||||
for (s = argv[optind + i]; *s; ++s) {
|
||||
if (*s == '\n') {
|
||||
appends(&msg, "\r\n\e[K");
|
||||
} else {
|
||||
appendw(&msg, *s & 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// otherwise use stdin as message
|
||||
ssize_t i, rc;
|
||||
char buf[512];
|
||||
while ((rc = read(0, buf, sizeof(buf))) > 0) {
|
||||
for (i = 0; i < rc; ++i) {
|
||||
if (buf[i] == '\n') {
|
||||
appends(&msg, "\r\n\e[K"); // clear line forward
|
||||
} else {
|
||||
appendw(&msg, buf[i] & 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
appends(&msg, "\r\n\e[K\e[0m\e8"); // restore
|
||||
|
||||
// try to send message to all pseudoteletypewriters
|
||||
for (int i = 0;; ++i) {
|
||||
int fd;
|
||||
char pts[32];
|
||||
snprintf(pts, sizeof(pts), "/dev/pts/%d", i);
|
||||
if ((fd = open(pts, O_WRONLY | O_NOCTTY)) == -1) {
|
||||
if (errno == ENOENT) break;
|
||||
if (g_verbose) perror(pts);
|
||||
}
|
||||
write(fd, msg, appendz(msg).i);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+50
-9
@@ -16,19 +16,60 @@ void __assert_fail(const char *, const char *, int) hidden relegated;
|
||||
#define static_assert _Static_assert
|
||||
#endif
|
||||
|
||||
#define _unassert(x) \
|
||||
do { \
|
||||
if (__builtin_expect(!(x), 0)) { \
|
||||
unreachable; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef __GNUC__
|
||||
#ifndef TINY
|
||||
/**
|
||||
* Specifies expression can't possibly be false.
|
||||
*
|
||||
* This macro uses the `notpossible` keyword and is intended for things
|
||||
* like systems integration, where we know for a fact that something is
|
||||
* never going to happen, but there's no proof. So we don't want to add
|
||||
* extra bloat for filenames and line numbers, since `ShowCrashReports`
|
||||
* can print a backtrace if we just embed the UD2 instruction to crash.
|
||||
* That's useful for systems code, for the following reason. Invariants
|
||||
* make sense with _unassert() since they usually get placed at the top
|
||||
* of functions. But if you used _unassert() to test a system call does
|
||||
* not fail, then check happens at the end of your function usually and
|
||||
* is therefore likely to result in a failure condition where execution
|
||||
* falls through into a different function, which is shocking to debug.
|
||||
*
|
||||
* In `MODE=tiny` these assertions are redefined as undefined behavior.
|
||||
*/
|
||||
#define _npassert(x) \
|
||||
do { \
|
||||
({ \
|
||||
if (__builtin_expect(!(x), 0)) { \
|
||||
notpossible; \
|
||||
} \
|
||||
} while (0)
|
||||
(void)0; \
|
||||
})
|
||||
#else
|
||||
#define _npassert(x) _unassert(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
/**
|
||||
* Specifies expression being false is undefined behavior.
|
||||
*
|
||||
* This is a good way to tell the compiler about your invariants, which
|
||||
* leads to smaller faster programs. The expression is never removed by
|
||||
* the preprocessor so it's recommended that it be free of side-effects
|
||||
* if you intend for it to be removed. At the same time, this guarantee
|
||||
* makes this assertion useful for things like system calls, since they
|
||||
* won't be removed by `NDEBUG` mode.
|
||||
*
|
||||
* If this assertion fails, the worst possible things can happen unless
|
||||
* you've built your binary in `MODE=dbg` since UBSAN is the only thing
|
||||
* that's capable of debugging this macro.
|
||||
*/
|
||||
#define _unassert(x) \
|
||||
({ \
|
||||
if (__builtin_expect(!(x), 0)) { \
|
||||
unreachable; \
|
||||
} \
|
||||
(void)0; \
|
||||
})
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/sig.internal.h"
|
||||
|
||||
bool __time_critical;
|
||||
struct Signals __sig;
|
||||
@@ -16,26 +16,22 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* @fileoverview UNIX signals for the New Technology, Part 2.
|
||||
* @threadsafe
|
||||
*/
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
/**
|
||||
* Allocates piece of memory for storing pending signal.
|
||||
@@ -61,6 +57,18 @@ static textwindows void __sig_free(struct Signal *mem) {
|
||||
mem->used = false;
|
||||
}
|
||||
|
||||
static inline textwindows int __sig_is_masked(int sig) {
|
||||
if (__tls_enabled) {
|
||||
return __get_tls()->tib_sigmask & (1ull << (sig - 1));
|
||||
} else {
|
||||
return __sig.sigmask & (1ull << (sig - 1));
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int __sig_is_applicable(struct Signal *s) {
|
||||
return s->tid <= 0 || s->tid == gettid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeues signal that isn't masked.
|
||||
* @return signal or null if empty or none unmasked
|
||||
@@ -70,7 +78,7 @@ static textwindows struct Signal *__sig_remove(void) {
|
||||
if (__sig.queue) {
|
||||
__sig_lock();
|
||||
for (prev = 0, res = __sig.queue; res; prev = res, res = res->next) {
|
||||
if (!sigismember(&__sig.mask, res->sig)) {
|
||||
if (__sig_is_applicable(res) && !__sig_is_masked(res->sig)) {
|
||||
if (res == __sig.queue) {
|
||||
__sig.queue = res->next;
|
||||
} else if (prev) {
|
||||
@@ -78,8 +86,6 @@ static textwindows struct Signal *__sig_remove(void) {
|
||||
}
|
||||
res->next = 0;
|
||||
break;
|
||||
} else {
|
||||
STRACE("%G is masked", res->sig);
|
||||
}
|
||||
}
|
||||
__sig_unlock();
|
||||
@@ -117,7 +123,7 @@ static bool __sig_deliver(bool restartable, int sig, int si_code,
|
||||
// setup the somewhat expensive information args
|
||||
// only if they're requested by the user in sigaction()
|
||||
if (flags & SA_SIGINFO) {
|
||||
__repstosb(&info, 0, sizeof(info));
|
||||
bzero(&info, sizeof(info));
|
||||
info.si_signo = sig;
|
||||
info.si_code = si_code;
|
||||
infop = &info;
|
||||
@@ -134,7 +140,11 @@ static bool __sig_deliver(bool restartable, int sig, int si_code,
|
||||
// since sigaction() is @asyncsignalsafe we only restore it if the
|
||||
// user didn't change it during the signal handler. we also don't
|
||||
// need to do anything if this was a oneshot signal or nodefer.
|
||||
_lockcmpxchg(__sighandrvas + sig, (int32_t)(intptr_t)SIG_DFL, rva);
|
||||
__sig_lock();
|
||||
if (__sighandrvas[sig] == (int32_t)(intptr_t)SIG_DFL) {
|
||||
__sighandrvas[sig] = rva;
|
||||
}
|
||||
__sig_unlock();
|
||||
}
|
||||
|
||||
if (!restartable) {
|
||||
@@ -150,7 +160,7 @@ static bool __sig_deliver(bool restartable, int sig, int si_code,
|
||||
/**
|
||||
* Returns true if signal default action is to end process.
|
||||
*/
|
||||
static textwindows bool __sig_isfatal(int sig) {
|
||||
static textwindows bool __sig_is_fatal(int sig) {
|
||||
if (sig == SIGCHLD || sig == SIGURG || sig == SIGWINCH) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -168,7 +178,7 @@ bool __sig_handle(bool restartable, int sig, int si_code, ucontext_t *ctx) {
|
||||
bool delivered;
|
||||
switch (__sighandrvas[sig]) {
|
||||
case (intptr_t)SIG_DFL:
|
||||
if (__sig_isfatal(sig)) {
|
||||
if (__sig_is_fatal(sig)) {
|
||||
STRACE("terminating on %G", sig);
|
||||
_Exitr(128 + sig);
|
||||
}
|
||||
@@ -194,20 +204,17 @@ bool __sig_handle(bool restartable, int sig, int si_code, ucontext_t *ctx) {
|
||||
* @threadsafe
|
||||
*/
|
||||
textwindows int __sig_raise(int sig, int si_code) {
|
||||
int rc;
|
||||
int candeliver;
|
||||
__sig_lock();
|
||||
candeliver = !sigismember(&__sig.mask, sig);
|
||||
__sig_unlock();
|
||||
switch (candeliver) {
|
||||
case 1:
|
||||
if (1 <= sig && sig <= 64) {
|
||||
if (!__sig_is_masked(sig)) {
|
||||
++__sig_count;
|
||||
__sig_handle(false, sig, si_code, 0);
|
||||
return 0;
|
||||
case 0:
|
||||
} else {
|
||||
STRACE("%G is masked", sig);
|
||||
return __sig_add(sig, si_code);
|
||||
default:
|
||||
return -1; // sigismember() validates `sig`
|
||||
return __sig_add(gettid(), sig, si_code);
|
||||
}
|
||||
} else {
|
||||
return einval();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,10 +223,10 @@ textwindows int __sig_raise(int sig, int si_code) {
|
||||
* @return 0 on success, otherwise -1 w/ errno
|
||||
* @threadsafe
|
||||
*/
|
||||
textwindows int __sig_add(int sig, int si_code) {
|
||||
textwindows int __sig_add(int tid, int sig, int si_code) {
|
||||
int rc;
|
||||
struct Signal *mem;
|
||||
if (1 <= sig && sig <= NSIG) {
|
||||
if (1 <= sig && sig <= 64) {
|
||||
__sig_lock();
|
||||
if (__sighandrvas[sig] == (unsigned)(intptr_t)SIG_IGN) {
|
||||
STRACE("ignoring %G", sig);
|
||||
@@ -228,6 +235,7 @@ textwindows int __sig_add(int sig, int si_code) {
|
||||
STRACE("enqueuing %G", sig);
|
||||
++__sig_count;
|
||||
if ((mem = __sig_alloc())) {
|
||||
mem->tid = tid;
|
||||
mem->sig = sig;
|
||||
mem->si_code = si_code;
|
||||
mem->next = __sig.queue;
|
||||
@@ -276,7 +284,7 @@ textwindows bool __sig_check(bool restartable) {
|
||||
textwindows void __sig_check_ignore(const int sig, const unsigned rva) {
|
||||
struct Signal *cur, *prev, *next;
|
||||
if (rva != (unsigned)(intptr_t)SIG_IGN &&
|
||||
(rva != (unsigned)(intptr_t)SIG_DFL || __sig_isfatal(sig))) {
|
||||
(rva != (unsigned)(intptr_t)SIG_DFL || __sig_is_fatal(sig))) {
|
||||
return;
|
||||
}
|
||||
if (__sig.queue) {
|
||||
@@ -298,25 +306,3 @@ textwindows void __sig_check_ignore(const int sig, const unsigned rva) {
|
||||
__sig_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the pending signals on New Technology.
|
||||
*
|
||||
* @param pending is to hold the pending signals
|
||||
* @threadsafe
|
||||
*/
|
||||
textwindows void __sig_pending(sigset_t *pending) {
|
||||
struct Signal *cur;
|
||||
sigemptyset(pending);
|
||||
if (__sig.queue) {
|
||||
__sig_lock();
|
||||
for (cur = __sig.queue; cur; cur = cur->next) {
|
||||
if (__sighandrvas[cur->sig] != (unsigned)(intptr_t)SIG_IGN) {
|
||||
pending->__bits[(cur->sig - 1) >> 6] |= (1ull << ((cur->sig - 1) & 63));
|
||||
} else {
|
||||
STRACE("%G is ignored", cur->sig);
|
||||
}
|
||||
}
|
||||
__sig_unlock();
|
||||
}
|
||||
}
|
||||
@@ -17,49 +17,39 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
/**
|
||||
* @fileoverview UNIX signals for the New Technology, Part 1.
|
||||
* @threadsafe
|
||||
*/
|
||||
#define GetSigBit(x) (1ull << (((x)-1) & 63))
|
||||
|
||||
struct Signals __sig; // TODO(jart): Need TLS
|
||||
|
||||
#define GetSigBit(XXSIG) (1ull << (((XXSIG)-1) & 63))
|
||||
|
||||
/**
|
||||
* Changes signal mask for main thread.
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
*/
|
||||
textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) {
|
||||
int i;
|
||||
uint64_t a, b;
|
||||
uint64_t x, y, *mask;
|
||||
if (how == SIG_BLOCK || how == SIG_UNBLOCK || how == SIG_SETMASK) {
|
||||
__sig_lock();
|
||||
if (__tls_enabled) {
|
||||
mask = &__get_tls()->tib_sigmask;
|
||||
} else {
|
||||
mask = &__sig.sigmask;
|
||||
}
|
||||
if (old) {
|
||||
*old = __sig.mask;
|
||||
old->__bits[0] = *mask;
|
||||
old->__bits[1] = 0;
|
||||
}
|
||||
if (neu) {
|
||||
for (i = 0; i < ARRAYLEN(__sig.mask.__bits); ++i) {
|
||||
if (how == SIG_BLOCK) {
|
||||
__sig.mask.__bits[i] |= neu->__bits[i];
|
||||
} else if (how == SIG_UNBLOCK) {
|
||||
__sig.mask.__bits[i] &= ~neu->__bits[i];
|
||||
} else {
|
||||
__sig.mask.__bits[i] = neu->__bits[i];
|
||||
}
|
||||
x = *mask;
|
||||
y = neu->__bits[0];
|
||||
if (how == SIG_BLOCK) {
|
||||
x |= y;
|
||||
} else if (how == SIG_UNBLOCK) {
|
||||
x &= ~y;
|
||||
} else {
|
||||
x = y;
|
||||
}
|
||||
__sig.mask.__bits[0] &= ~(GetSigBit(SIGKILL) | GetSigBit(SIGSTOP));
|
||||
x &= ~(GetSigBit(SIGKILL) | GetSigBit(SIGSTOP) | GetSigBit(SIGABRT));
|
||||
*mask = x;
|
||||
}
|
||||
__sig_unlock();
|
||||
return 0;
|
||||
} else {
|
||||
return einval();
|
||||
}
|
||||
}
|
||||
|
||||
#undef GetSigBit
|
||||
@@ -0,0 +1,43 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
|
||||
/**
|
||||
* Determines the pending signals on New Technology.
|
||||
*
|
||||
* @param pending is to hold the pending signals
|
||||
* @threadsafe
|
||||
*/
|
||||
textwindows void __sig_pending(sigset_t *pending) {
|
||||
struct Signal *s;
|
||||
sigemptyset(pending);
|
||||
if (__sig.queue) {
|
||||
__sig_lock();
|
||||
for (s = __sig.queue; s; s = s->next) {
|
||||
if (__sig_is_applicable(s) &&
|
||||
__sighandrvas[s->sig] != (unsigned)(intptr_t)SIG_IGN) {
|
||||
sigaddset(pending, s->sig);
|
||||
}
|
||||
}
|
||||
__sig_unlock();
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
||||
/**
|
||||
* Compares two nanosecond timestamps.
|
||||
* Compares nanosecond timestamps.
|
||||
*/
|
||||
int _timespec_cmp(struct timespec a, struct timespec b) {
|
||||
int cmp;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
* @param ts receives `CLOCK_REALTIME` timestamp
|
||||
* @param base must be `TIME_UTC`
|
||||
* @return `base` on success, or `0` on failure
|
||||
* @see _timespec_real()
|
||||
*/
|
||||
int timespec_get(struct timespec *ts, int base) {
|
||||
if (base == TIME_UTC && !clock_gettime(CLOCK_REALTIME, ts)) {
|
||||
|
||||
@@ -20,6 +20,13 @@
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
|
||||
/**
|
||||
* Returns current monotonic time.
|
||||
*
|
||||
* This function uses a `CLOCK_MONOTONIC` clock and never fails.
|
||||
*
|
||||
* @see _timespec_real()
|
||||
*/
|
||||
struct timespec _timespec_mono(void) {
|
||||
struct timespec ts;
|
||||
_npassert(!clock_gettime(CLOCK_MONOTONIC_FAST, &ts));
|
||||
|
||||
@@ -20,6 +20,15 @@
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
|
||||
/**
|
||||
* Returns current time.
|
||||
*
|
||||
* This function uses a `CLOCK_REALTIME` clock and never fails. Unlike
|
||||
* clock_gettime() or timespec_real() this interface avoids the use of
|
||||
* pointers which lets time handling code become more elegant.
|
||||
*
|
||||
* @see _timespec_mono()
|
||||
*/
|
||||
struct timespec _timespec_real(void) {
|
||||
struct timespec ts;
|
||||
_npassert(!clock_gettime(CLOCK_REALTIME_FAST, &ts));
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
|
||||
/**
|
||||
* Sleeps for specified delay.
|
||||
*
|
||||
* @return unslept time which may be non-zero if the call was interrupted
|
||||
*/
|
||||
struct timespec _timespec_sleep(struct timespec delay) {
|
||||
int rc;
|
||||
struct timespec remain;
|
||||
if (!(rc = clock_nanosleep(CLOCK_REALTIME, 0, &delay, &remain))) {
|
||||
return (struct timespec){0};
|
||||
} else {
|
||||
_npassert(rc == EINTR);
|
||||
return remain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
|
||||
/**
|
||||
* Sleeps until the specified time.
|
||||
*
|
||||
* @return 0 on success, or EINTR if interrupted
|
||||
*/
|
||||
int _timespec_sleep_until(struct timespec abs_deadline) {
|
||||
int rc;
|
||||
rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &abs_deadline, 0);
|
||||
_npassert(!rc || rc == EINTR);
|
||||
return rc;
|
||||
}
|
||||
@@ -20,14 +20,35 @@
|
||||
#include "libc/limits.h"
|
||||
|
||||
/**
|
||||
* Converts timespec interval to microseconds.
|
||||
* Reduces `ts` from 1e-9 to 1e-6 granularity w/ ceil rounding.
|
||||
*
|
||||
* This function uses ceiling rounding. For example, if `ts` is one
|
||||
* nanosecond, then one microsecond will be returned. Ceil rounding
|
||||
* is needed by many interfaces, e.g. setitimer(), because the zero
|
||||
* timestamp has a special meaning.
|
||||
*
|
||||
* This function also detects overflow in which case `INT64_MAX` or
|
||||
* `INT64_MIN` may be returned. The `errno` variable isn't changed.
|
||||
*
|
||||
* @return 64-bit scalar holding microseconds since epoch
|
||||
* @see _timespec_totimeval()
|
||||
*/
|
||||
int64_t _timespec_tomicros(struct timespec x) {
|
||||
int64_t _timespec_tomicros(struct timespec ts) {
|
||||
int64_t us;
|
||||
if (!__builtin_mul_overflow(x.tv_sec, 1000000ul, &us) &&
|
||||
!__builtin_add_overflow(us, x.tv_nsec / 1000, &us)) {
|
||||
// reduce precision from nanos to micros
|
||||
if (ts.tv_nsec <= 999999000) {
|
||||
ts.tv_nsec = (ts.tv_nsec + 999) / 1000;
|
||||
} else {
|
||||
ts.tv_nsec = 0;
|
||||
if (ts.tv_sec < INT64_MAX) {
|
||||
ts.tv_sec += 1;
|
||||
}
|
||||
}
|
||||
// convert to scalar result
|
||||
if (!__builtin_mul_overflow(ts.tv_sec, 1000000ul, &us) &&
|
||||
!__builtin_add_overflow(us, ts.tv_nsec, &us)) {
|
||||
return us;
|
||||
} else if (x.tv_sec < 0) {
|
||||
} else if (ts.tv_sec < 0) {
|
||||
return INT64_MIN;
|
||||
} else {
|
||||
return INT64_MAX;
|
||||
|
||||
@@ -20,14 +20,34 @@
|
||||
#include "libc/limits.h"
|
||||
|
||||
/**
|
||||
* Converts timespec interval to milliseconds.
|
||||
* Reduces `ts` from 1e-9 to 1e-3 granularity w/ ceil rounding.
|
||||
*
|
||||
* This function uses ceiling rounding. For example, if `ts` is one
|
||||
* nanosecond, then one millisecond will be returned. Ceil rounding
|
||||
* is needed by many interfaces, e.g. setitimer(), because the zero
|
||||
* timestamp has a special meaning.
|
||||
*
|
||||
* This function also detects overflow in which case `INT64_MAX` or
|
||||
* `INT64_MIN` may be returned. The `errno` variable isn't changed.
|
||||
*
|
||||
* @return 64-bit scalar milliseconds since epoch
|
||||
*/
|
||||
int64_t _timespec_tomillis(struct timespec x) {
|
||||
int64_t _timespec_tomillis(struct timespec ts) {
|
||||
int64_t ms;
|
||||
if (!__builtin_mul_overflow(x.tv_sec, 1000ul, &ms) &&
|
||||
!__builtin_add_overflow(ms, x.tv_nsec / 1000000, &ms)) {
|
||||
// reduce precision from nanos to millis
|
||||
if (ts.tv_nsec <= 999000000) {
|
||||
ts.tv_nsec = (ts.tv_nsec + 999999) / 1000000;
|
||||
} else {
|
||||
ts.tv_nsec = 0;
|
||||
if (ts.tv_sec < INT64_MAX) {
|
||||
ts.tv_sec += 1;
|
||||
}
|
||||
}
|
||||
// convert to scalar result
|
||||
if (!__builtin_mul_overflow(ts.tv_sec, 1000ul, &ms) &&
|
||||
!__builtin_add_overflow(ms, ts.tv_nsec, &ms)) {
|
||||
return ms;
|
||||
} else if (x.tv_sec < 0) {
|
||||
} else if (ts.tv_sec < 0) {
|
||||
return INT64_MIN;
|
||||
} else {
|
||||
return INT64_MAX;
|
||||
|
||||
@@ -20,7 +20,12 @@
|
||||
#include "libc/limits.h"
|
||||
|
||||
/**
|
||||
* Converts timespec interval to nanoseconds.
|
||||
* Converts timespec to scalar.
|
||||
*
|
||||
* This function will detect overflow in which case `INT64_MAX` or
|
||||
* `INT64_MIN` may be returned. The `errno` variable isn't changed.
|
||||
*
|
||||
* @return 64-bit integer holding nanoseconds since epoch
|
||||
*/
|
||||
int64_t _timespec_tonanos(struct timespec x) {
|
||||
int64_t ns;
|
||||
|
||||
@@ -18,6 +18,21 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Reduces `ts` from 1e-9 to 1e-6 granularity w/ ceil rounding.
|
||||
*
|
||||
* This function uses ceiling rounding. For example, if `ts` is one
|
||||
* nanosecond, then one microsecond will be returned. Ceil rounding
|
||||
* is needed by many interfaces, e.g. setitimer(), because the zero
|
||||
* timestamp has a special meaning.
|
||||
*
|
||||
* @return microseconds since epoch
|
||||
* @see _timespec_tomicros()
|
||||
*/
|
||||
struct timeval _timespec_totimeval(struct timespec ts) {
|
||||
return (struct timeval){ts.tv_sec, ts.tv_nsec / 1000};
|
||||
if (ts.tv_nsec < 1000000000 - 999) {
|
||||
return (struct timeval){ts.tv_sec, (ts.tv_nsec + 999) / 1000};
|
||||
} else {
|
||||
return (struct timeval){ts.tv_sec + 1, 0};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Compares microseconds timestamps.
|
||||
*/
|
||||
int _timeval_cmp(struct timeval a, struct timeval b) {
|
||||
int cmp;
|
||||
if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec))) {
|
||||
cmp = (a.tv_usec > b.tv_usec) - (a.tv_usec < b.tv_usec);
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Checks if 𝑥 = 𝑦.
|
||||
*/
|
||||
bool _timeval_eq(struct timeval x, struct timeval y) {
|
||||
return x.tv_sec == y.tv_sec && x.tv_usec == y.tv_usec;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Returns true if timeval `x` is greater than `y`.
|
||||
*/
|
||||
bool _timeval_gt(struct timeval x, struct timeval y) {
|
||||
if (x.tv_sec > y.tv_sec) {
|
||||
return true;
|
||||
}
|
||||
if (x.tv_sec == y.tv_sec) {
|
||||
if (x.tv_usec > y.tv_usec) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Checks if 𝑥 ≥ 𝑦.
|
||||
*/
|
||||
bool _timeval_gte(struct timeval x, struct timeval y) {
|
||||
if (x.tv_sec > y.tv_sec) return true;
|
||||
if (x.tv_sec < y.tv_sec) return false;
|
||||
return x.tv_usec >= y.tv_usec;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Subtracts two nanosecond timestamps.
|
||||
*/
|
||||
struct timeval _timeval_sub(struct timeval a, struct timeval b) {
|
||||
a.tv_sec -= b.tv_sec;
|
||||
if (a.tv_usec < b.tv_usec) {
|
||||
a.tv_usec += 1000000;
|
||||
a.tv_sec--;
|
||||
}
|
||||
a.tv_usec -= b.tv_usec;
|
||||
return a;
|
||||
}
|
||||
@@ -18,6 +18,9 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
||||
/**
|
||||
* Coerces `tv` from 1e-6 to 1e-9 granularity.
|
||||
*/
|
||||
struct timespec _timeval_totimespec(struct timeval tv) {
|
||||
return (struct timespec){tv.tv_sec, tv.tv_usec * 1000};
|
||||
}
|
||||
+6
-1
@@ -22,9 +22,14 @@
|
||||
/**
|
||||
* Checks if effective user can access path in particular ways.
|
||||
*
|
||||
* This is equivalent to saying:
|
||||
*
|
||||
* faccessat(AT_FDCWD, path, mode, 0);
|
||||
*
|
||||
* @param path is a filename or directory
|
||||
* @param mode can be R_OK, W_OK, X_OK, F_OK
|
||||
* @return 0 if ok, or -1 and sets errno
|
||||
* @return 0 if ok, or -1 w/ errno
|
||||
* @see faccessat() for further documentation
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int access(const char *path, int mode) {
|
||||
|
||||
+1
-3
@@ -32,12 +32,10 @@
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
unsigned alarm(unsigned seconds) {
|
||||
int rc;
|
||||
struct itimerval it;
|
||||
bzero(&it, sizeof(it));
|
||||
it.it_value.tv_sec = seconds;
|
||||
rc = setitimer(ITIMER_REAL, &it, &it);
|
||||
assert(rc != -1);
|
||||
_npassert(!setitimer(ITIMER_REAL, &it, &it));
|
||||
if (!it.it_value.tv_sec && !it.it_value.tv_usec) {
|
||||
return 0;
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_
|
||||
#include "libc/intrin/asmflag.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/intrin/asmflag.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
@@ -14,6 +15,10 @@ forceinline bool __asan_is_valid_timespec(const struct timespec *ts) {
|
||||
return zf;
|
||||
}
|
||||
|
||||
forceinline bool __asan_is_valid_timeval(const struct timeval *tv) {
|
||||
return __asan_is_valid_timespec((const struct timespec *)tv);
|
||||
}
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_ASAN_INTERNAL_H_ */
|
||||
+14
-12
@@ -84,7 +84,8 @@ int execv(const char *, char *const[]);
|
||||
int execve(const char *, char *const[], char *const[]);
|
||||
int execvp(const char *, char *const[]);
|
||||
int execvpe(const char *, char *const[], char *const[]);
|
||||
int faccessat(int, const char *, int, uint32_t);
|
||||
int fexecve(int, char *const[], char *const[]);
|
||||
int faccessat(int, const char *, int, int);
|
||||
int fadvise(int, uint64_t, uint64_t, int);
|
||||
int fchdir(int);
|
||||
int fchmod(int, uint32_t) dontthrow;
|
||||
@@ -127,8 +128,9 @@ int linkat(int, const char *, int, const char *, int);
|
||||
int madvise(void *, uint64_t, int);
|
||||
int memfd_create(const char *, unsigned int);
|
||||
int mincore(void *, size_t, unsigned char *);
|
||||
int mkdir(const char *, uint32_t);
|
||||
int mkdirat(int, const char *, uint32_t);
|
||||
int mkdir(const char *, unsigned);
|
||||
int mkdirat(int, const char *, unsigned);
|
||||
int makedirs(const char *, unsigned);
|
||||
int mkfifo(const char *, uint32_t);
|
||||
int mkfifoat(int, const char *, uint32_t);
|
||||
int mknod(const char *, uint32_t, uint64_t);
|
||||
@@ -147,7 +149,7 @@ int pipe(int[hasatleast 2]);
|
||||
int pipe2(int[hasatleast 2], int);
|
||||
int pivot_root(const char *, const char *);
|
||||
int pledge(const char *, const char *);
|
||||
int posix_fadvise(int, uint64_t, uint64_t, int);
|
||||
int posix_fadvise(int, int64_t, int64_t, int);
|
||||
int posix_madvise(void *, uint64_t, int);
|
||||
int prctl(int, ...);
|
||||
int raise(int);
|
||||
@@ -161,10 +163,10 @@ int sched_yield(void);
|
||||
int seccomp(unsigned, unsigned, void *);
|
||||
int setegid(uint32_t);
|
||||
int seteuid(uint32_t);
|
||||
int setfsgid(int);
|
||||
int setfsuid(int);
|
||||
int setgid(int);
|
||||
int setgroups(size_t size, const uint32_t list[]);
|
||||
int setfsgid(unsigned);
|
||||
int setfsuid(unsigned);
|
||||
int setgid(unsigned);
|
||||
int setgroups(size_t, const uint32_t[]);
|
||||
int setpgid(int, int);
|
||||
int setpgrp(void);
|
||||
int setpriority(int, unsigned, int);
|
||||
@@ -173,7 +175,7 @@ int setresgid(uint32_t, uint32_t, uint32_t);
|
||||
int setresuid(uint32_t, uint32_t, uint32_t);
|
||||
int setreuid(uint32_t, uint32_t);
|
||||
int setsid(void);
|
||||
int setuid(int);
|
||||
int setuid(unsigned);
|
||||
int sigignore(int);
|
||||
int siginterrupt(int, int);
|
||||
int symlink(const char *, const char *);
|
||||
@@ -186,9 +188,9 @@ int tgkill(int, int, int);
|
||||
int tkill(int, int);
|
||||
int tmpfd(void);
|
||||
int touch(const char *, uint32_t);
|
||||
int truncate(const char *, uint64_t);
|
||||
int truncate(const char *, int64_t);
|
||||
int ttyname_r(int, char *, size_t);
|
||||
int umask(int);
|
||||
unsigned umask(unsigned);
|
||||
int unlink(const char *);
|
||||
int unlink_s(const char **);
|
||||
int unlinkat(int, const char *, int);
|
||||
@@ -203,7 +205,7 @@ long ptrace(int, ...);
|
||||
ssize_t copy_file_range(int, long *, int, long *, size_t, uint32_t);
|
||||
ssize_t copyfd(int, int64_t *, int, int64_t *, size_t, uint32_t);
|
||||
ssize_t getfiledescriptorsize(int);
|
||||
ssize_t lseek(int, int64_t, unsigned);
|
||||
ssize_t lseek(int, int64_t, int);
|
||||
ssize_t pread(int, void *, size_t, int64_t);
|
||||
ssize_t pwrite(int, const void *, size_t, int64_t);
|
||||
ssize_t read(int, void *, size_t);
|
||||
|
||||
@@ -65,6 +65,13 @@ $(LIBC_CALLS_A).pkg: \
|
||||
$(LIBC_CALLS_A_OBJS) \
|
||||
$(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
# we can't use asan because:
|
||||
# siginfo_t memory is owned by kernels
|
||||
o/$(MODE)/libc/calls/siginfo2cosmo.o: private \
|
||||
OVERRIDE_COPTS += \
|
||||
-ffreestanding \
|
||||
-fno-sanitize=address
|
||||
|
||||
# we can't use asan because:
|
||||
# ucontext_t memory is owned by xnu kernel
|
||||
o/$(MODE)/libc/calls/sigenter-xnu.o: private \
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "libc/calls/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int cfsetispeed(struct termios *t, int speed) {
|
||||
int cfsetispeed(struct termios *t, unsigned speed) {
|
||||
if (speed) {
|
||||
if (CBAUD) {
|
||||
if (speed & ~CBAUD) return einval();
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int cfsetospeed(struct termios *t, int speed) {
|
||||
int cfsetospeed(struct termios *t, unsigned speed) {
|
||||
if (CBAUD) {
|
||||
if (!(speed & ~CBAUD)) {
|
||||
t->c_cflag &= ~CBAUD;
|
||||
|
||||
+3
-1
@@ -16,11 +16,12 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
@@ -34,6 +35,7 @@
|
||||
*/
|
||||
int chdir(const char *path) {
|
||||
int rc;
|
||||
GetProgramExecutableName(); // XXX: ugly workaround
|
||||
if (!path || (IsAsan() && !__asan_is_valid(path, 1))) {
|
||||
rc = efault();
|
||||
} else if (!IsWindows()) {
|
||||
|
||||
+28
-2
@@ -17,17 +17,43 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Changes root directory.
|
||||
*
|
||||
* Please consider using unveil() instead of chroot(). If you use this
|
||||
* system call then consider using both chdir() and closefrom() before
|
||||
* calling this function. Otherwise there's a small risk that fchdir()
|
||||
* could be used to escape the chroot() environment. Cosmopolitan Libc
|
||||
* focuses on static binaries which make chroot() infinitely easier to
|
||||
* use since you don't need to construct an entire userspace each time
|
||||
* however unveil() is still better to use on modern Linux and OpenBSD
|
||||
* because it doesn't require root privileges.
|
||||
*
|
||||
* @param path shall become the new root directory
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EACCES if we don't have permission to search a component of `path`
|
||||
* @raise ENOTDIR if a directory component in `path` exists as non-directory
|
||||
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
|
||||
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
|
||||
* @raise EPERM if not root or pledge() is in play
|
||||
* @raise EIO if a low-level i/o error occurred
|
||||
* @raise EFAULT if `path` is bad memory
|
||||
* @raise ENOENT if `path` doesn't exist
|
||||
* @raise ENOSYS on Windows
|
||||
*/
|
||||
int chroot(const char *path) {
|
||||
int rc;
|
||||
rc = sys_chroot(path);
|
||||
if (!path || (IsAsan() && !__asan_is_valid(path, 1))) {
|
||||
rc = efault();
|
||||
} else {
|
||||
rc = sys_chroot(path);
|
||||
}
|
||||
STRACE("chroot(%s) → %d% m", path, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -17,10 +17,10 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/asan.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/time/time.h"
|
||||
@@ -49,6 +49,12 @@ static int sys_clock_getres_xnu(int clock, struct timespec *ts) {
|
||||
|
||||
/**
|
||||
* Returns granularity of clock.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @error EPERM if pledge() is in play without stdio promise
|
||||
* @error EINVAL if `clock` isn't supported on this system
|
||||
* @error EFAULT if `ts` points to bad memory
|
||||
* @threadsafe
|
||||
*/
|
||||
int clock_getres(int clock, struct timespec *ts) {
|
||||
int rc;
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "libc/calls/asan.internal.h"
|
||||
#include "libc/calls/clock_gettime.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
@@ -30,9 +29,11 @@
|
||||
#include "libc/intrin/asmflag.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
/**
|
||||
* Returns nanosecond time.
|
||||
@@ -48,12 +49,30 @@
|
||||
* __clock_gettime l: 35𝑐 11𝑛𝑠
|
||||
* sys_clock_gettime l: 220𝑐 71𝑛𝑠
|
||||
*
|
||||
* @param clock can be CLOCK_REALTIME, CLOCK_MONOTONIC, etc.
|
||||
* @param clock can be one of:
|
||||
* - `CLOCK_REALTIME`: universally supported
|
||||
* - `CLOCK_REALTIME_FAST`: ditto but faster on freebsd
|
||||
* - `CLOCK_MONOTONIC`: universally supported
|
||||
* - `CLOCK_MONOTONIC_FAST`: ditto but faster on freebsd
|
||||
* - `CLOCK_MONOTONIC_RAW`: nearly universally supported
|
||||
* - `CLOCK_PROCESS_CPUTIME_ID`: linux and bsd
|
||||
* - `CLOCK_THREAD_CPUTIME_ID`: linux and bsd
|
||||
* - `CLOCK_REALTIME_COARSE`: : linux and openbsd
|
||||
* - `CLOCK_MONOTONIC_COARSE`: linux
|
||||
* - `CLOCK_PROF`: linux and netbsd
|
||||
* - `CLOCK_BOOTTIME`: linux and openbsd
|
||||
* - `CLOCK_REALTIME_ALARM`: linux-only
|
||||
* - `CLOCK_BOOTTIME_ALARM`: linux-only
|
||||
* - `CLOCK_TAI`: linux-only
|
||||
* @param ts is where the result is stored
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @error EINVAL if clock isn't supported on this system
|
||||
* @error EPERM if pledge() is in play without stdio promise
|
||||
* @error EINVAL if `clock` isn't supported on this system
|
||||
* @error EFAULT if `ts` points to bad memory
|
||||
* @see strftime(), gettimeofday()
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int clock_gettime(int clock, struct timespec *ts) {
|
||||
int rc;
|
||||
@@ -65,7 +84,7 @@ int clock_gettime(int clock, struct timespec *ts) {
|
||||
rc = __clock_gettime(clock, ts);
|
||||
}
|
||||
#if SYSDEBUG
|
||||
if (!__time_critical) {
|
||||
if (!(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
|
||||
STRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock),
|
||||
DescribeTimespec(rc, ts), rc);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_clock_nanosleep_nt(int clock, int flags,
|
||||
const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
struct timespec now, abs;
|
||||
if (flags & TIMER_ABSTIME) {
|
||||
abs = *req;
|
||||
for (;;) {
|
||||
if (sys_clock_gettime_nt(clock, &now)) return -1;
|
||||
if (_timespec_gte(now, abs)) return 0;
|
||||
if (_check_interrupts(false, g_fds.p)) return eintr();
|
||||
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
|
||||
_timespec_tomillis(_timespec_sub(abs, now))),
|
||||
false);
|
||||
}
|
||||
} else {
|
||||
if (sys_clock_gettime_nt(clock, &now)) return -1;
|
||||
abs = _timespec_add(now, *req);
|
||||
for (;;) {
|
||||
sys_clock_gettime_nt(clock, &now);
|
||||
if (_timespec_gte(now, abs)) return 0;
|
||||
if (_check_interrupts(false, g_fds.p)) {
|
||||
if (rem) *rem = _timespec_sub(abs, now);
|
||||
return eintr();
|
||||
}
|
||||
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
|
||||
_timespec_tomillis(_timespec_sub(abs, now))),
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
@@ -18,19 +18,28 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/struct/timeval.internal.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_utimes_nt(const char *path, const struct timeval tv[2]) {
|
||||
struct timespec ts[2];
|
||||
if (tv) {
|
||||
ts[0].tv_sec = tv[0].tv_sec;
|
||||
ts[0].tv_nsec = tv[0].tv_usec * 1000;
|
||||
ts[1].tv_sec = tv[1].tv_sec;
|
||||
ts[1].tv_nsec = tv[1].tv_usec * 1000;
|
||||
return sys_utimensat_nt(AT_FDCWD, path, ts, 0);
|
||||
int sys_clock_nanosleep_openbsd(int clock, int flags,
|
||||
const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
int res;
|
||||
struct timespec now, rel;
|
||||
if (clock == CLOCK_REALTIME) {
|
||||
if (!flags) {
|
||||
res = sys_nanosleep(req, rem);
|
||||
} else {
|
||||
sys_clock_gettime(clock, &now);
|
||||
if (_timespec_gt(*req, now)) {
|
||||
rel = _timespec_sub(*req, now);
|
||||
res = sys_nanosleep(&rel, 0);
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return sys_utimensat_nt(AT_FDCWD, path, NULL, 0);
|
||||
res = enotsup();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -16,30 +16,34 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/struct/timeval.internal.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
// we don't want instrumentation because:
|
||||
// - nanosleep() depends on this and ftrace can take microsecs
|
||||
|
||||
/**
|
||||
* Converts `struct timespec` to `struct timeval`.
|
||||
*
|
||||
* This divides ts.tv_nsec by 1000 with upward rounding and overflow
|
||||
* handling. Your ts.tv_nsec must be on the interval `[0,1000000000)`
|
||||
* otherwise `{-1, -1}` is returned.
|
||||
*
|
||||
* @return converted timeval whose tv_usec will be -1 on error
|
||||
*/
|
||||
noinstrument struct timeval _timespec2timeval(struct timespec ts) {
|
||||
if (0 <= ts.tv_nsec && ts.tv_nsec < 1000000000) {
|
||||
if (0 <= ts.tv_nsec && ts.tv_nsec < 1000000000 - 999) {
|
||||
return (struct timeval){ts.tv_sec, (ts.tv_nsec + 999) / 1000};
|
||||
int sys_clock_nanosleep_xnu(int clock, int flags, const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
int res;
|
||||
struct timeval now, abs, rel;
|
||||
if (clock == CLOCK_REALTIME) {
|
||||
if (flags & TIMER_ABSTIME) {
|
||||
abs = _timespec_totimeval(*req);
|
||||
sys_gettimeofday_xnu(&now, 0, 0);
|
||||
if (_timeval_gt(abs, now)) {
|
||||
rel = _timeval_sub(abs, now);
|
||||
res = sys_select(0, 0, 0, 0, &rel);
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
} else {
|
||||
return (struct timeval){ts.tv_sec + 1, 0};
|
||||
res = sys_nanosleep_xnu(req, rem);
|
||||
}
|
||||
} else {
|
||||
return (struct timeval){-1, -1};
|
||||
res = enotsup();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/asan.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/struct/timeval.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
/**
|
||||
* Sleeps for particular amount of time.
|
||||
*
|
||||
* Here's how you could sleep for one second:
|
||||
*
|
||||
* clock_nanosleep(0, 0, &(struct timespec){1}, 0);
|
||||
*
|
||||
* Your sleep will be interrupted automatically if you do something like
|
||||
* press ctrl-c during the wait. That's an `EINTR` error and it lets you
|
||||
* immediately react to status changes. This is always the case, even if
|
||||
* you're using `SA_RESTART` since this is a `@norestart` system call.
|
||||
*
|
||||
* void OnCtrlC(int sig) {} // EINTR only happens after delivery
|
||||
* signal(SIGINT, OnCtrlC); // do delivery rather than kill proc
|
||||
* printf("save me from sleeping forever by pressing ctrl-c\n");
|
||||
* clock_nanosleep(0, 0, &(struct timespec){INT_MAX}, 0);
|
||||
* printf("you're my hero\n");
|
||||
*
|
||||
* If you want to perform an uninterruptible sleep without having to use
|
||||
* sigprocmask() to block all signals then this function provides a good
|
||||
* solution to that problem. For example:
|
||||
*
|
||||
* struct timespec rel, now, abs;
|
||||
* clock_gettime(CLOCK_REALTIME, &now);
|
||||
* rel = _timespec_frommillis(100);
|
||||
* abs = _timespec_add(now, rel);
|
||||
* while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &abs, 0));
|
||||
*
|
||||
* will accurately spin on `EINTR` errors. That way you're not impeding
|
||||
* signal delivery and you're not losing precision on your wait timeout.
|
||||
* This function has first-class support on Linux, FreeBSD, and NetBSD;
|
||||
* on OpenBSD it's good; on XNU it's bad; and on Windows it's ugly.
|
||||
*
|
||||
* @param clock should be `CLOCK_REALTIME` and you may consult the docs
|
||||
* of your preferred platforms to see what other clocks might work
|
||||
* @param flags can be 0 for relative and `TIMER_ABSTIME` for absolute
|
||||
* @param req can be a relative or absolute time, depending on `flags`
|
||||
* @param rem shall be updated with the remainder of unslept time when
|
||||
* (1) it's non-null; (2) `flags` is 0; and (3) -1 w/ `EINTR` is
|
||||
* returned; if this function returns 0 then `rem` is undefined;
|
||||
* if flags is `TIMER_ABSTIME` then `rem` is ignored
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EINTR when a signal got delivered while we were waiting
|
||||
* @raise ENOTSUP if `clock` is known but we can't use it here
|
||||
* @raise EFAULT if `req` or null or bad memory was passed
|
||||
* @raise EINVAL if `clock` is unknown to current platform
|
||||
* @raise EINVAL if `flags` has an unrecognized value
|
||||
* @raise EINVAL if `req->tv_nsec ∉ [0,1000000000)`
|
||||
* @raise ENOSYS on bare metal
|
||||
* @returnserrno
|
||||
* @norestart
|
||||
*/
|
||||
errno_t clock_nanosleep(int clock, int flags, const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
int rc, e = errno;
|
||||
|
||||
if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) ||
|
||||
(rem && !__asan_is_valid_timespec(rem))))) {
|
||||
rc = efault();
|
||||
} else if (clock == 127 || //
|
||||
(flags & ~TIMER_ABSTIME) || //
|
||||
req->tv_sec < 0 || //
|
||||
!(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {
|
||||
rc = einval();
|
||||
} else if (IsLinux() || IsFreebsd() || IsNetbsd()) {
|
||||
rc = sys_clock_nanosleep(clock, flags, req, rem);
|
||||
} else if (IsXnu()) {
|
||||
rc = sys_clock_nanosleep_xnu(clock, flags, req, rem);
|
||||
} else if (IsOpenbsd()) {
|
||||
rc = sys_clock_nanosleep_openbsd(clock, flags, req, rem);
|
||||
} else if (IsMetal()) {
|
||||
rc = enosys();
|
||||
} else {
|
||||
rc = sys_clock_nanosleep_nt(clock, flags, req, rem);
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
rc = errno;
|
||||
errno = e;
|
||||
}
|
||||
|
||||
#if SYSDEBUG
|
||||
if (!(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
|
||||
STRACE("clock_nanosleep(%s, %s, %s, [%s]) → %s", DescribeClockName(clock),
|
||||
DescribeSleepFlags(flags), DescribeTimespec(0, req),
|
||||
DescribeTimespec(rc, rem), DescribeErrnoResult(rc));
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
+17
-9
@@ -32,17 +32,25 @@
|
||||
/**
|
||||
* Closes file descriptor.
|
||||
*
|
||||
* This function may be used for file descriptors returned by functions
|
||||
* like open, socket, accept, epoll_create, and landlock_create_ruleset.
|
||||
* This function releases resources returned by functions such as:
|
||||
*
|
||||
* This function should never be called twice for the same file
|
||||
* descriptor, regardless of whether or not an error happened. However
|
||||
* that doesn't mean the error should be ignored.
|
||||
* - openat()
|
||||
* - socket()
|
||||
* - accept()
|
||||
* - epoll_create()
|
||||
* - landlock_create_ruleset()
|
||||
*
|
||||
* This function should never be reattempted if an error is returned;
|
||||
* however, that doesn't mean the error should be ignored. This goes
|
||||
* against the conventional wisdom of looping on `EINTR`.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @error EINTR means a signal was received while closing in which case
|
||||
* close() does not need to be called again, since the fd will close
|
||||
* in the background
|
||||
* @raise EINTR if signal was delivered; do *not* retry
|
||||
* @raise EBADF if `fd` is negative or not open; however, an exception
|
||||
* is made by Cosmopolitan Libc for `close(-1)` which returns zero
|
||||
* and does nothing, in order to assist with code that may wish to
|
||||
* close the same resource multiple times without dirtying `errno`
|
||||
* @raise EIO if a low-level i/o error occurred
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
@@ -51,7 +59,7 @@ int close(int fd) {
|
||||
if (fd == -1) {
|
||||
rc = 0;
|
||||
} else if (fd < 0) {
|
||||
rc = einval();
|
||||
rc = ebadf();
|
||||
} else {
|
||||
// for performance reasons we want to avoid holding __fds_lock()
|
||||
// while sys_close() is happening. this leaves the kernel / libc
|
||||
|
||||
+13
-11
@@ -35,27 +35,29 @@
|
||||
* }
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @error EBADF if `first` is negative
|
||||
* @error EBADF on OpenBSD if `first` is greater than highest fd
|
||||
* @error EINVAL if flags are bad or first is greater than last
|
||||
* @error EMFILE if a weird race condition happens on Linux
|
||||
* @error ENOSYS if not Linux 5.9+, FreeBSD 8+, or OpenBSD
|
||||
* @error EINTR possibly on OpenBSD
|
||||
* @error ENOMEM on Linux maybe
|
||||
* @raise EBADF if `first` is negative
|
||||
* @raise ENOSYS if not Linux 5.9+, FreeBSD 8+, OpenBSD, or NetBSD
|
||||
* @raise EBADF on OpenBSD if `first` is greater than highest fd
|
||||
* @raise EINVAL if flags are bad or first is greater than last
|
||||
* @raise EMFILE if a weird race condition happens on Linux
|
||||
* @raise EINTR possibly on OpenBSD
|
||||
* @raise ENOMEM on Linux maybe
|
||||
*/
|
||||
int closefrom(int first) {
|
||||
int rc, err;
|
||||
if (IsNetbsd() || IsWindows() || IsMetal()) {
|
||||
rc = enosys();
|
||||
} else if (first < 0) {
|
||||
if (first < 0) {
|
||||
// consistent with openbsd
|
||||
// freebsd allows this but it's dangerous
|
||||
// necessary on linux due to type signature
|
||||
rc = ebadf();
|
||||
} else if (IsFreebsd() || IsOpenbsd()) {
|
||||
rc = sys_closefrom(first);
|
||||
} else if (IsLinux()) {
|
||||
rc = sys_close_range(first, 0xffffffffu, 0);
|
||||
} else if (IsNetbsd()) {
|
||||
rc = __sys_fcntl(first, 10 /*F_CLOSEM*/, first);
|
||||
} else {
|
||||
rc = sys_closefrom(first);
|
||||
rc = enosys();
|
||||
}
|
||||
STRACE("closefrom(%d) → %d% m", first, rc);
|
||||
return rc;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
@@ -79,6 +80,7 @@ static void copy_file_range_init(void) {
|
||||
* @raise EXDEV if source and destination are on different filesystems
|
||||
* @raise EBADF if `infd` or `outfd` aren't open files or append-only
|
||||
* @raise EPERM if `fdout` refers to an immutable file on Linux
|
||||
* @raise ENOTSUP if `infd` or `outfd` is a zip file descriptor
|
||||
* @raise EINVAL if ranges overlap or `flags` is non-zero
|
||||
* @raise EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded
|
||||
* @raise EFAULT if one of the pointers memory is bad
|
||||
@@ -104,6 +106,8 @@ ssize_t copy_file_range(int infd, int64_t *opt_in_out_inoffset, int outfd,
|
||||
(opt_in_out_outoffset &&
|
||||
!__asan_is_valid(opt_in_out_outoffset, 8)))) {
|
||||
rc = efault();
|
||||
} else if (__isfdkind(infd, kFdZip) || __isfdkind(outfd, kFdZip)) {
|
||||
rc = enotsup();
|
||||
} else {
|
||||
rc = sys_copy_file_range(infd, opt_in_out_inoffset, outfd,
|
||||
opt_in_out_outoffset, uptobytes, flags);
|
||||
|
||||
+11
-10
@@ -21,20 +21,21 @@
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Creates new file, returning open()'d file descriptor.
|
||||
* Creates file.
|
||||
*
|
||||
* This function is shorthand for:
|
||||
* This is equivalent to saying:
|
||||
*
|
||||
* open(file, O_CREAT | O_WRONLY | O_TRUNC, mode)
|
||||
* int fd = openat(AT_FDCWD, file, O_CREAT | O_WRONLY | O_TRUNC, mode);
|
||||
*
|
||||
* @param file is a UTF-8 string, which is truncated if it exists
|
||||
* @param mode is an octal user/group/other permission, e.g. 0755
|
||||
* @return a number registered with the system to track the open file,
|
||||
* which must be stored using a 64-bit type in order to support both
|
||||
* System V and Windows, and must be closed later on using close()
|
||||
* @see open(), touch()
|
||||
* @param file specifies filesystem path to create
|
||||
* @param mode is octal bits, e.g. 0644 usually
|
||||
* @return file descriptor, or -1 w/ errno
|
||||
* @see openat() for further documentation
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
dontdiscard int creat(const char *file, uint32_t mode) {
|
||||
int creat(const char *file, uint32_t mode) {
|
||||
return openat(AT_FDCWD, file, O_CREAT | O_WRONLY | O_TRUNC, mode);
|
||||
}
|
||||
+4
-3
@@ -32,15 +32,16 @@
|
||||
*
|
||||
* @param fd remains open afterwards
|
||||
* @return some arbitrary new number for fd
|
||||
* @raise EOPNOTSUPP if zipos file
|
||||
* @raise EBADF if fd isn't open
|
||||
* @raise EPERM if pledge() is in play without stdio
|
||||
* @raise ENOTSUP if `fd` is a zip file descriptor
|
||||
* @raise EBADF if `fd` is negative or not open
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int dup(int fd) {
|
||||
int rc;
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
rc = eopnotsupp();
|
||||
rc = enotsup();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_dup(fd);
|
||||
} else {
|
||||
|
||||
+7
-4
@@ -18,10 +18,10 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
@@ -38,16 +38,19 @@
|
||||
* @param newfd if already assigned, is silently closed beforehand;
|
||||
* unless it's equal to oldfd, in which case dup2() is a no-op
|
||||
* @return new file descriptor, or -1 w/ errno
|
||||
* @raise EBADF is oldfd isn't open
|
||||
* @raise EBADF is newfd negative or too big
|
||||
* @raise EPERM if pledge() is in play without stdio
|
||||
* @raise EMFILE if `RLIMIT_NOFILE` has been reached
|
||||
* @raise ENOTSUP if `oldfd` is on zip file system
|
||||
* @raise EINTR if a signal handler was called
|
||||
* @raise EBADF is `newfd` negative or too big
|
||||
* @raise EBADF is `oldfd` isn't open
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int dup2(int oldfd, int newfd) {
|
||||
int rc;
|
||||
if (__isfdkind(oldfd, kFdZip)) {
|
||||
rc = eopnotsupp();
|
||||
rc = enotsup();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_dup2(oldfd, newfd);
|
||||
} else if (newfd < 0) {
|
||||
|
||||
+7
-5
@@ -38,11 +38,13 @@
|
||||
* @param flags may have O_CLOEXEC which is needed to preserve the
|
||||
* close-on-execve() state after file descriptor duplication
|
||||
* @return newfd on success, or -1 w/ errno
|
||||
* @raise EINVAL if flags has unsupported bits
|
||||
* @raise EINVAL if newfd equals oldfd
|
||||
* @raise EBADF is oldfd isn't open
|
||||
* @raise EBADF is newfd negative or too big
|
||||
* @raise ENOTSUP if `oldfd` is a zip file descriptor
|
||||
* @raise EPERM if pledge() is in play without stdio
|
||||
* @raise EINVAL if `flags` has unsupported bits
|
||||
* @raise EINTR if a signal handler was called
|
||||
* @raise EBADF is `newfd` negative or too big
|
||||
* @raise EINVAL if `newfd` equals oldfd
|
||||
* @raise EBADF is `oldfd` isn't open
|
||||
* @see dup(), dup2()
|
||||
*/
|
||||
int dup3(int oldfd, int newfd, int flags) {
|
||||
@@ -52,7 +54,7 @@ int dup3(int oldfd, int newfd, int flags) {
|
||||
} else if (oldfd < 0 || newfd < 0) {
|
||||
rc = ebadf();
|
||||
} else if (__isfdkind(oldfd, kFdZip)) {
|
||||
rc = eopnotsupp();
|
||||
rc = enotsup();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_dup3(oldfd, newfd, flags);
|
||||
} else {
|
||||
|
||||
@@ -99,7 +99,7 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
|
||||
startinfo.hStdError = __getfdhandleactual(2);
|
||||
|
||||
// spawn the process
|
||||
rc = ntspawn(program, argv, envp, 0, 0, 0, 1, 0, 0, &startinfo, &procinfo);
|
||||
rc = ntspawn(program, argv, envp, 0, 0, 0, true, 0, 0, &startinfo, &procinfo);
|
||||
if (rc == -1) {
|
||||
STRACE("panic: unrecoverable ntspawn(%#s) error: %m", program);
|
||||
__imp_ExitProcess(6543);
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
/**
|
||||
* Replaces current process with program.
|
||||
*
|
||||
* On Windows, `argv` and `envp` can't contain binary strings. They need
|
||||
* to be valid UTF-8 in order to round-trip the WIN32 API, without being
|
||||
* corrupted.
|
||||
*
|
||||
* @param program will not be PATH searched, see commandv()
|
||||
* @param argv[0] is the name of the program to run
|
||||
* @param argv[1,n-2] optionally specify program arguments
|
||||
|
||||
+22
-12
@@ -17,6 +17,7 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
@@ -35,33 +36,42 @@
|
||||
* @param dirfd is normally AT_FDCWD but if it's an open directory and
|
||||
* file is a relative path, then file is opened relative to dirfd
|
||||
* @param path is a filename or directory
|
||||
* @param mode can be R_OK, W_OK, X_OK, F_OK
|
||||
* @param flags can have AT_EACCESS, AT_SYMLINK_NOFOLLOW
|
||||
* @note on Linux flags is only supported on Linux 5.8+
|
||||
* @param amode can be `R_OK`, `W_OK`, `X_OK`, or `F_OK`
|
||||
* @param flags can have `AT_EACCESS` and/or `AT_SYMLINK_NOFOLLOW`
|
||||
* @return 0 if ok, or -1 and sets errno
|
||||
* @raise EINVAL if `mode` has bad value
|
||||
* @raise EPERM if pledge() is in play without rpath promise
|
||||
* @raise EACCES if access for requested `mode` would be denied
|
||||
* @raise ENOTDIR if a directory component in `path` exists as non-directory
|
||||
* @raise ENOENT if component of `path` doesn't exist or `path` is empty
|
||||
* @raise ENOTSUP if `path` is a zip file and `dirfd` isn't `AT_FDCWD`
|
||||
* @note on Linux `flags` is only supported on Linux 5.8+
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int faccessat(int dirfd, const char *path, int mode, uint32_t flags) {
|
||||
int faccessat(int dirfd, const char *path, int amode, int flags) {
|
||||
int e, rc;
|
||||
if (IsAsan() && !__asan_is_valid(path, 1)) {
|
||||
struct ZiposUri zipname;
|
||||
if (!path || (IsAsan() && !__asan_is_valid(path, 1))) {
|
||||
rc = efault();
|
||||
} else if (_weaken(__zipos_notat) &&
|
||||
_weaken(__zipos_notat)(dirfd, path) == -1) {
|
||||
rc = -1; /* TODO(jart): implement me */
|
||||
} else if (__isfdkind(dirfd, kFdZip)) {
|
||||
rc = enotsup();
|
||||
} else if (_weaken(__zipos_open) &&
|
||||
_weaken(__zipos_parseuri)(path, &zipname) != -1) {
|
||||
rc = _weaken(__zipos_access)(&zipname, amode);
|
||||
} else if (!IsWindows()) {
|
||||
e = errno;
|
||||
if (!flags) goto NoFlags;
|
||||
if ((rc = sys_faccessat2(dirfd, path, mode, flags)) == -1) {
|
||||
if ((rc = sys_faccessat2(dirfd, path, amode, flags)) == -1) {
|
||||
if (errno == ENOSYS) {
|
||||
errno = e;
|
||||
NoFlags:
|
||||
rc = sys_faccessat(dirfd, path, mode, flags);
|
||||
rc = sys_faccessat(dirfd, path, amode, flags);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rc = sys_faccessat_nt(dirfd, path, mode, flags);
|
||||
rc = sys_faccessat_nt(dirfd, path, amode, flags);
|
||||
}
|
||||
STRACE("faccessat(%s, %#s, %#o, %#x) → %d% m", DescribeDirfd(dirfd), path,
|
||||
mode, flags, rc);
|
||||
amode, flags, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -37,6 +37,7 @@
|
||||
* @param path must exist
|
||||
* @param mode contains octal flags (base 8)
|
||||
* @param flags can have `AT_SYMLINK_NOFOLLOW`
|
||||
* @raise ENOTSUP if `dirfd` or `path` use zip file system
|
||||
* @errors ENOENT, ENOTDIR, ENOSYS
|
||||
* @asyncsignalsafe
|
||||
* @see fchmod()
|
||||
@@ -47,7 +48,7 @@ int fchmodat(int dirfd, const char *path, uint32_t mode, int flags) {
|
||||
rc = efault();
|
||||
} else if (_weaken(__zipos_notat) &&
|
||||
(rc = __zipos_notat(dirfd, path)) == -1) {
|
||||
rc = eopnotsupp();
|
||||
rc = enotsup();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_fchmodat(dirfd, path, mode, flags);
|
||||
} else {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
* @param gid is group id, or -1 to not change
|
||||
* @param flags can have AT_SYMLINK_NOFOLLOW, etc.
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ENOTSUP if `dirfd` or `path` use zip file system
|
||||
* @see chown(), lchown() for shorthand notation
|
||||
* @see /etc/passwd for user ids
|
||||
* @see /etc/group for group ids
|
||||
@@ -46,7 +47,7 @@ int fchownat(int dirfd, const char *path, uint32_t uid, uint32_t gid,
|
||||
rc = efault();
|
||||
} else if (_weaken(__zipos_notat) &&
|
||||
(rc = __zipos_notat(dirfd, path)) == -1) {
|
||||
STRACE("zipos fchownat not supported yet");
|
||||
rc = enotsup();
|
||||
} else {
|
||||
rc = sys_fchownat(dirfd, path, uid, gid, flags);
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
bool __force_sqlite_to_work_until_we_can_fix_it;
|
||||
|
||||
static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) {
|
||||
if (start < 0) return einval();
|
||||
return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start);
|
||||
@@ -110,11 +108,7 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int cmd, uintptr_t arg) {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if (ok || __force_sqlite_to_work_until_we_can_fix_it) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
if (l->l_type == F_UNLCK) {
|
||||
|
||||
+90
-8
@@ -18,11 +18,15 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/flock.h"
|
||||
#include "libc/calls/struct/flock.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
|
||||
@@ -44,9 +48,49 @@
|
||||
* Please be warned that locks currently do nothing on Windows since
|
||||
* figuring out how to polyfill them correctly is a work in progress.
|
||||
*
|
||||
* @param cmd can be F_{GET,SET}{FD,FL}, etc.
|
||||
* @param fd is the file descriptor
|
||||
* @param cmd can be one of:
|
||||
* - `F_GETFD` gets `FD_CLOEXEC` status of `fd
|
||||
* - `F_SETFD` sets `FD_CLOEXEC` status of `arg` file descriptor
|
||||
* - `F_GETFL` returns file descriptor status flags
|
||||
* - `F_SETFL` sets file descriptor status flags
|
||||
* - `F_DUPFD` is like dup() but `arg` is a minimum result, e.g. 3
|
||||
* - `F_DUPFD_CLOEXEC` ditto but sets `O_CLOEXEC` on returned fd
|
||||
* - `F_SETLK` for record locking where `arg` is `struct flock`
|
||||
* - `F_SETLKW` ditto but waits (i.e. blocks) for lock
|
||||
* - `F_GETLK` to retrieve information about a record lock
|
||||
* - `F_OFD_SETLK` for better locks on Linux and XNU
|
||||
* - `F_OFD_SETLKW` for better locks on Linux and XNU
|
||||
* - `F_OFD_GETLK` for better locks on Linux and XNU
|
||||
* - `F_FULLFSYNC` on MacOS for fsync() with release barrier
|
||||
* - `F_BARRIERFSYNC` on MacOS for fsync() with even more barriers
|
||||
* - `F_SETNOSIGPIPE` on MacOS and NetBSD to control `SIGPIPE`
|
||||
* - `F_GETNOSIGPIPE` on MacOS and NetBSD to control `SIGPIPE`
|
||||
* - `F_GETPATH` on MacOS and NetBSD where arg is `char[PATH_MAX]`
|
||||
* - `F_MAXFD` on NetBSD to get max open file descriptor
|
||||
* - `F_NOCACHE` on MacOS to toggle data caching
|
||||
* - `F_GETPIPE_SZ` on Linux to get pipe size
|
||||
* - `F_SETPIPE_SZ` on Linux to set pipe size
|
||||
* - `F_NOTIFY` raise `SIGIO` upon `fd` events in `arg` on Linux
|
||||
* - `DN_ACCESS` for file access
|
||||
* - `DN_MODIFY` for file modifications
|
||||
* - `DN_CREATE` for file creations
|
||||
* - `DN_DELETE` for file deletions
|
||||
* - `DN_RENAME` for file renames
|
||||
* - `DN_ATTRIB` for file attribute changes
|
||||
* - `DN_MULTISHOT` bitwise or for realtime signals (non-coalesced)
|
||||
* @param arg can be FD_CLOEXEC, etc. depending
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EBADF if `fd` isn't a valid open file descriptor
|
||||
* @raise EINVAL if `cmd` is unknown or unsupported by os
|
||||
* @raise EINVAL if `cmd` is invalid or unsupported by os
|
||||
* @raise EPERM if pledge() is in play w/o `stdio` or `flock` promise
|
||||
* @raise ENOLCK if `F_SETLKW` would have exceeded `RLIMIT_LOCKS`
|
||||
* @raise EPERM if `cmd` is `F_SETOWN` and we weren't authorized
|
||||
* @raise ESRCH if `cmd` is `F_SETOWN` and process group not found
|
||||
* @raise EDEADLK if `cmd` was `F_SETLKW` and waiting would deadlock
|
||||
* @raise EMFILE if `cmd` is `F_DUPFD` or `F_DUPFD_CLOEXEC` and
|
||||
* `RLIMIT_NOFILE` would be exceeded
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
*/
|
||||
@@ -54,20 +98,58 @@ int fcntl(int fd, int cmd, ...) {
|
||||
int rc;
|
||||
va_list va;
|
||||
uintptr_t arg;
|
||||
|
||||
va_start(va, cmd);
|
||||
arg = va_arg(va, uintptr_t);
|
||||
va_end(va);
|
||||
|
||||
if (fd >= 0) {
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = _weaken(__zipos_fcntl)(fd, cmd, arg);
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_fcntl(fd, cmd, arg);
|
||||
if (cmd >= 0) {
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = _weaken(__zipos_fcntl)(fd, cmd, arg);
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_fcntl(fd, cmd, arg);
|
||||
} else {
|
||||
rc = sys_fcntl_nt(fd, cmd, arg);
|
||||
}
|
||||
} else {
|
||||
rc = sys_fcntl_nt(fd, cmd, arg);
|
||||
rc = einval();
|
||||
}
|
||||
} else {
|
||||
rc = einval();
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("fcntl(%d, %d, %p) → %#x% m", fd, cmd, arg, rc);
|
||||
|
||||
#ifdef SYSDEBUG
|
||||
if (cmd == F_GETFD || //
|
||||
cmd == F_GETOWN || //
|
||||
cmd == F_FULLFSYNC || //
|
||||
cmd == F_BARRIERFSYNC || //
|
||||
cmd == F_MAXFD) {
|
||||
STRACE("fcntl(%d, %s) → %d% m", fd, DescribeFcntlCmd(cmd), rc);
|
||||
} else if (cmd == F_GETFL) {
|
||||
STRACE("fcntl(%d, %s) → %s% m", fd, DescribeFcntlCmd(cmd),
|
||||
DescribeOpenFlags(rc));
|
||||
} else if (cmd == F_SETLK || //
|
||||
cmd == F_SETLKW || //
|
||||
cmd == F_GETLK || //
|
||||
((SupportsLinux() || SupportsXnu()) && //
|
||||
cmd != -1 && //
|
||||
(cmd == F_OFD_SETLK || //
|
||||
cmd == F_OFD_SETLKW || //
|
||||
cmd == F_OFD_GETLK))) {
|
||||
STRACE("fcntl(%d, %s, %s) → %d% m", fd, DescribeFcntlCmd(cmd),
|
||||
DescribeFlock(cmd, (struct flock *)arg), rc);
|
||||
} else if ((SupportsNetbsd() || SupportsXnu()) && cmd != -1 &&
|
||||
cmd == F_GETPATH) {
|
||||
STRACE("fcntl(%d, %s, [%s]) → %d% m", fd, DescribeFcntlCmd(cmd),
|
||||
!rc ? (const char *)arg : "n/a", rc);
|
||||
} else if (SupportsLinux() && cmd != -1 && cmd == F_NOTIFY) {
|
||||
STRACE("fcntl(%d, %s, %s) → %d% m", fd, DescribeFcntlCmd(cmd),
|
||||
DescribeDnotifyFlags(arg), rc);
|
||||
} else {
|
||||
STRACE("fcntl(%d, %s, %ld) → %#x% m", fd, DescribeFcntlCmd(cmd), arg, rc);
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -17,10 +17,11 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,7 @@
|
||||
*/
|
||||
int fdatasync(int fd) {
|
||||
int rc;
|
||||
struct stat st;
|
||||
if (__nosync != 0x5453455454534146) {
|
||||
if (!IsWindows()) {
|
||||
rc = sys_fdatasync(fd);
|
||||
@@ -41,8 +43,8 @@ int fdatasync(int fd) {
|
||||
}
|
||||
STRACE("fdatasync(%d) → %d% m", fd, rc);
|
||||
} else {
|
||||
rc = 0;
|
||||
STRACE("fdatasync(%d) → disabled% m", fd);
|
||||
rc = fstat(fd, &st);
|
||||
STRACE("fdatasync_fake(%d) → %d% m", fd, rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/likely.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int sys_fexecve(int, char *const[], char *const[]);
|
||||
|
||||
/**
|
||||
* Executes binary executable at file descriptor.
|
||||
*
|
||||
* This is only supported on Linux and FreeBSD. APE binaries currently
|
||||
* aren't supported.
|
||||
*
|
||||
* @param fd is opened executable and current file position is ignored
|
||||
* @return doesn't return on success, otherwise -1 w/ errno
|
||||
* @raise ENOEXEC if file at `fd` isn't an assimilated ELF executable
|
||||
* @raise ENOSYS on Windows, XNU, OpenBSD, NetBSD, and Metal
|
||||
*/
|
||||
int fexecve(int fd, char *const argv[], char *const envp[]) {
|
||||
int rc;
|
||||
size_t i;
|
||||
if (!argv || !envp ||
|
||||
(IsAsan() &&
|
||||
(!__asan_is_valid_strlist(argv) || !__asan_is_valid_strlist(envp)))) {
|
||||
rc = efault();
|
||||
} else {
|
||||
#ifdef SYSDEBUG
|
||||
if (UNLIKELY(__strace > 0)) {
|
||||
kprintf(STRACE_PROLOGUE "fexecve(%d, {", fd);
|
||||
for (i = 0; argv[i]; ++i) {
|
||||
if (i) kprintf(", ");
|
||||
kprintf("%#s", argv[i]);
|
||||
}
|
||||
kprintf("}, {");
|
||||
for (i = 0; envp[i]; ++i) {
|
||||
if (i) kprintf(", ");
|
||||
kprintf("%#s", envp[i]);
|
||||
}
|
||||
kprintf("})\n");
|
||||
}
|
||||
#endif
|
||||
if (IsLinux()) {
|
||||
char path[14 + 12];
|
||||
FormatInt32(stpcpy(path, "/proc/self/fd/"), fd);
|
||||
rc = __sys_execve(path, argv, envp);
|
||||
} else if (IsFreebsd()) {
|
||||
rc = sys_fexecve(fd, argv, envp);
|
||||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
}
|
||||
STRACE("fexecve(%d) failed %d% m", fd, rc);
|
||||
return rc;
|
||||
}
|
||||
+5
-3
@@ -17,10 +17,11 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,7 @@
|
||||
*/
|
||||
int fsync(int fd) {
|
||||
int rc;
|
||||
struct stat st;
|
||||
if (__nosync != 0x5453455454534146) {
|
||||
if (!IsWindows()) {
|
||||
rc = sys_fsync(fd);
|
||||
@@ -41,8 +43,8 @@ int fsync(int fd) {
|
||||
}
|
||||
STRACE("fysnc(%d) → %d% m", fd, rc);
|
||||
} else {
|
||||
rc = 0;
|
||||
STRACE("fsync(%d) → disabled% m", fd);
|
||||
rc = fstat(fd, &st);
|
||||
STRACE("fsync_fake(%d) → %d% m", fd, rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -16,9 +16,12 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/enum/filemovemethod.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_ftruncate_nt(int64_t handle, uint64_t length) {
|
||||
@@ -28,7 +31,13 @@ textwindows int sys_ftruncate_nt(int64_t handle, uint64_t length) {
|
||||
if ((ok = SetFilePointerEx(handle, 0, &tell, kNtFileCurrent))) {
|
||||
ok = SetFilePointerEx(handle, length, NULL, kNtFileBegin) &&
|
||||
SetEndOfFile(handle);
|
||||
SetFilePointerEx(handle, tell, NULL, kNtFileBegin);
|
||||
_npassert(SetFilePointerEx(handle, tell, NULL, kNtFileBegin));
|
||||
}
|
||||
if (ok) {
|
||||
return 0;
|
||||
} else if (GetLastError() == kNtErrorAccessDenied) {
|
||||
return einval(); // ftruncate() doesn't raise EACCES
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
return ok ? 0 : __winerr();
|
||||
}
|
||||
+42
-8
@@ -18,31 +18,65 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Changes size of file.
|
||||
* Changes size of open file.
|
||||
*
|
||||
* If the file size is increased, the extended area shall appear as if
|
||||
* it were zero-filled. If your file size is decreased, the extra data
|
||||
* shall be lost.
|
||||
*
|
||||
* This function never changes the file position. This is true even if
|
||||
* ftruncate() causes the position to become beyond the end of file in
|
||||
* which case, the rules described in the lseek() documentation apply.
|
||||
*
|
||||
* Some operating systems implement an optimization, where `length` is
|
||||
* treated as a logical size and the requested physical space won't be
|
||||
* allocated until non-zero values get written into it. Our tests show
|
||||
* this happens on Linux (usually with 4096 byte granularity), FreeBSD
|
||||
* (which favors 512-byte granularity), and MacOS (prefers 4096 bytes)
|
||||
* however Windows, OpenBSD, and NetBSD always reserve physical space.
|
||||
* This may be inspected using fstat() and consulting stat::st_blocks.
|
||||
*
|
||||
* @param fd must be open for writing
|
||||
* @param length may be greater than current current file size, in which
|
||||
* case System V guarantees it'll be zero'd but Windows NT doesn't;
|
||||
* since the prior extends logically and the latter physically
|
||||
* @param length may be greater than current current file size
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EINVAL if `length` is negative
|
||||
* @raise EINTR if signal was delivered instead
|
||||
* @raise EIO if a low-level i/o error happened
|
||||
* @raise EFBIG or EINVAL if `length` is too huge
|
||||
* @raise ENOTSUP if `fd` is a zip file descriptor
|
||||
* @raise EBADF if `fd` isn't an open file descriptor
|
||||
* @raise EINVAL if `fd` is a non-file, e.g. pipe, socket
|
||||
* @raise EINVAL if `fd` wasn't opened in a writeable mode
|
||||
* @raise ENOSYS on bare metal
|
||||
* @see truncate()
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
*/
|
||||
int ftruncate(int fd, int64_t length) {
|
||||
int rc;
|
||||
if (fd < 0) {
|
||||
rc = einval();
|
||||
rc = ebadf();
|
||||
} else if (__isfdkind(fd, kFdZip)) {
|
||||
rc = enotsup();
|
||||
} else if (IsMetal()) {
|
||||
rc = enosys();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_ftruncate(fd, length, length);
|
||||
} else {
|
||||
if (fd >= g_fds.n) rc = ebadf();
|
||||
if (IsNetbsd() && rc == -1 && errno == ENOSPC) {
|
||||
errno = EFBIG; // POSIX doesn't specify ENOSPC for ftruncate()
|
||||
}
|
||||
} else if (__isfdopen(fd)) {
|
||||
rc = sys_ftruncate_nt(g_fds.p[fd].handle, length);
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("ftruncate(%d, %'ld) → %d% m", fd, length, rc);
|
||||
return rc;
|
||||
|
||||
+25
-5
@@ -16,16 +16,36 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Sets atime/mtime on file descriptor.
|
||||
* Changes access/modified time on open file, the modern way.
|
||||
*
|
||||
* This function is the same as `utimensat(fd, 0, ts, 0)`.
|
||||
* XNU only has microsecond (1e-6) accuracy. Windows only has
|
||||
* hectonanosecond (1e-7) accuracy. RHEL5 (Linux c. 2007) doesn't
|
||||
* support this system call.
|
||||
*
|
||||
* @param ts is atime/mtime, or null for current time
|
||||
* @raise ENOSYS on RHEL5
|
||||
* @param fd is file descriptor of file whose timestamps will change
|
||||
* @param ts is {access, modified} timestamps, or null for current time
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ENOTSUP if `fd` is on the zip filesystem
|
||||
* @raise EINVAL if `flags` had an unrecognized value
|
||||
* @raise EPERM if pledge() is in play without `fattr` promise
|
||||
* @raise EINVAL if `ts` specifies a nanosecond value that's out of range
|
||||
* @raise EBADF if `fd` isn't an open file descriptor
|
||||
* @raise EFAULT if `ts` memory was invalid
|
||||
* @raise ENOSYS on RHEL5 or bare metal
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
*/
|
||||
int futimens(int fd, const struct timespec ts[2]) {
|
||||
return utimensat(fd, 0, ts, 0);
|
||||
int rc;
|
||||
rc = __utimens(fd, 0, ts, 0);
|
||||
STRACE("futimens(%d, {%s, %s}) → %d% m", fd, DescribeTimespec(0, ts),
|
||||
DescribeTimespec(0, ts ? ts + 1 : 0), rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -16,25 +16,45 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/asan.internal.h"
|
||||
#include "libc/calls/struct/itimerval.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Sets atime/mtime on file descriptor.
|
||||
*
|
||||
* @param ts is atime/mtime, or null for current time
|
||||
* @note better than microsecond precision on most platforms
|
||||
* @see fstat() for reading timestamps
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ENOTSUP if `fd` is on zip filesystem
|
||||
* @raise EBADF if `fd` isn't an open file descriptor
|
||||
* @raise EPERM if pledge() is in play without `fattr` promise
|
||||
* @raise EINVAL if `tv` specifies a microsecond value that's out of range
|
||||
* @raise ENOSYS on RHEL5 or bare metal
|
||||
* @see futimens() for modern version
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
*/
|
||||
int futimes(int fd, const struct timeval tv[hasatleast 2]) {
|
||||
int futimes(int fd, const struct timeval tv[2]) {
|
||||
int rc;
|
||||
struct timespec ts[2];
|
||||
|
||||
if (tv) {
|
||||
ts[0].tv_sec = tv[0].tv_sec;
|
||||
ts[0].tv_nsec = tv[0].tv_usec * 1000;
|
||||
ts[1].tv_sec = tv[1].tv_sec;
|
||||
ts[1].tv_nsec = tv[1].tv_usec * 1000;
|
||||
return utimensat(fd, NULL, ts, 0);
|
||||
rc = __utimens(fd, 0, ts, 0);
|
||||
} else {
|
||||
return utimensat(fd, NULL, NULL, 0);
|
||||
rc = __utimens(fd, 0, 0, 0);
|
||||
}
|
||||
|
||||
STRACE("futimes(%d, {%s, %s}) → %d% m", fd, DescribeTimeval(0, tv),
|
||||
DescribeTimeval(0, tv ? tv + 1 : 0), rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -19,7 +19,5 @@
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
// TODO(jart): These should be _Thread_local but doing that currently
|
||||
// causes a regression with runitd.com on Windows.
|
||||
unsigned __sighandrvas[NSIG];
|
||||
unsigned __sighandflags[NSIG];
|
||||
@@ -34,7 +34,7 @@
|
||||
#define HW_NCPUONLINE_NETBSD 16
|
||||
#define ALL_PROCESSOR_GROUPS 0xffff
|
||||
|
||||
static unsigned GetCpuCountLinux(void) {
|
||||
static unsigned _getcpucount_linux(void) {
|
||||
cpu_set_t s = {0};
|
||||
if (sys_sched_getaffinity(0, sizeof(s), &s) != -1) {
|
||||
return CPU_COUNT(&s);
|
||||
@@ -43,7 +43,7 @@ static unsigned GetCpuCountLinux(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned GetCpuCountBsd(void) {
|
||||
static unsigned _getcpucount_bsd(void) {
|
||||
size_t n;
|
||||
int c, cmd[2];
|
||||
n = sizeof(c);
|
||||
@@ -62,12 +62,12 @@ static unsigned GetCpuCountBsd(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned GetCpuCountImpl(void) {
|
||||
static unsigned _getcpucount_impl(void) {
|
||||
if (!IsWindows()) {
|
||||
if (!IsBsd()) {
|
||||
return GetCpuCountLinux();
|
||||
return _getcpucount_linux();
|
||||
} else {
|
||||
return GetCpuCountBsd();
|
||||
return _getcpucount_bsd();
|
||||
}
|
||||
} else {
|
||||
return GetMaximumProcessorCount(ALL_PROCESSOR_GROUPS);
|
||||
@@ -77,18 +77,24 @@ static unsigned GetCpuCountImpl(void) {
|
||||
static int g_cpucount;
|
||||
|
||||
// precompute because process affinity on linux may change later
|
||||
__attribute__((__constructor__)) static void init(void) {
|
||||
g_cpucount = GetCpuCountImpl();
|
||||
__attribute__((__constructor__)) static void _getcpucount_init(void) {
|
||||
g_cpucount = _getcpucount_impl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of CPUs in system.
|
||||
*
|
||||
* This is the same as the standard interface:
|
||||
*
|
||||
* sysconf(_SC_NPROCESSORS_ONLN);
|
||||
*
|
||||
* Except this function isn't a bloated diamond dependency.
|
||||
*
|
||||
* On Intel systems with HyperThreading this will return the number of
|
||||
* cores multiplied by two.
|
||||
*
|
||||
* @return cpu count or 0 if it couldn't be determined
|
||||
*/
|
||||
unsigned GetCpuCount(void) {
|
||||
unsigned _getcpucount(void) {
|
||||
return g_cpucount;
|
||||
}
|
||||
@@ -52,7 +52,7 @@ char *getcwd(char *buf, size_t size) {
|
||||
return 0;
|
||||
}
|
||||
} else if (_weaken(malloc)) {
|
||||
assert(!__vforked);
|
||||
_unassert(!__vforked);
|
||||
if (!size) size = PATH_MAX;
|
||||
if (!(p = _weaken(malloc)(size))) {
|
||||
STRACE("getcwd(%p, %'zu) %m", buf, size);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
|
||||
/**
|
||||
* Gets file descriptor table size.
|
||||
*
|
||||
* @return current limit on the number of open files per process
|
||||
*/
|
||||
int getdtablesize(void) {
|
||||
return g_fds.n;
|
||||
}
|
||||
@@ -20,10 +20,10 @@
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
#define FT(x) (x.dwLowDateTime | (uint64_t)x.dwHighDateTime << 32)
|
||||
|
||||
@@ -60,7 +60,7 @@ static textstartup void sys_getloadavg_nt_init(void) {
|
||||
double a[3];
|
||||
if (IsWindows()) {
|
||||
load = 1;
|
||||
cpus = GetCpuCount() / 2;
|
||||
cpus = _getcpucount() / 2;
|
||||
cpus = MAX(1, cpus);
|
||||
GetSystemTimes(&idle1, &kern1, &user1);
|
||||
}
|
||||
|
||||
+12
-3
@@ -16,18 +16,26 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
|
||||
/**
|
||||
* Returns parent process id.
|
||||
*
|
||||
* @return parent process id (always successful)
|
||||
* @note slow on Windows; needs to iterate process tree
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int getppid(void) {
|
||||
int rc;
|
||||
if (!IsWindows()) {
|
||||
if (IsMetal()) {
|
||||
rc = 1;
|
||||
} else if (!IsWindows()) {
|
||||
if (!IsNetbsd()) {
|
||||
rc = sys_getppid();
|
||||
} else {
|
||||
@@ -36,6 +44,7 @@ int getppid(void) {
|
||||
} else {
|
||||
rc = sys_getppid_nt();
|
||||
}
|
||||
STRACE("getppid() → %d% m", rc);
|
||||
_npassert(rc >= 0);
|
||||
STRACE("%s() → %d", "getppid", rc);
|
||||
return rc;
|
||||
}
|
||||
+58
-19
@@ -16,28 +16,67 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/kntprioritycombos.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nexgen32e/ffs.h"
|
||||
#include "libc/nt/enum/processaccess.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/prio.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_getpriority_nt(int ignored) {
|
||||
size_t i;
|
||||
uint32_t tier, lg2tier, wut;
|
||||
if ((tier = GetPriorityClass(GetCurrentProcess())) != 0 &&
|
||||
(wut = GetThreadPriority(GetCurrentThread())) != -1u) {
|
||||
lg2tier = ffs(tier);
|
||||
for (i = 0; i < kNtPriorityCombosLen; ++i) {
|
||||
if (kNtPriorityCombos[i].lg2tier == lg2tier &&
|
||||
kNtPriorityCombos[i].wut == wut) {
|
||||
return kNtPriorityCombos[i].nice;
|
||||
}
|
||||
}
|
||||
abort();
|
||||
} else {
|
||||
return __winerr();
|
||||
textwindows int sys_getpriority_nt(int which, unsigned pid) {
|
||||
int rc;
|
||||
uint32_t tier;
|
||||
int64_t h, closeme = -1;
|
||||
|
||||
if (which != PRIO_PROCESS) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
if (!pid || pid == getpid()) {
|
||||
h = GetCurrentProcess();
|
||||
} else if (__isfdkind(pid, kFdProcess)) {
|
||||
h = g_fds.p[pid].handle;
|
||||
} else {
|
||||
h = OpenProcess(kNtProcessQueryInformation, false, pid);
|
||||
if (!h) return __winerr();
|
||||
closeme = h;
|
||||
}
|
||||
|
||||
if ((tier = GetPriorityClass(h))) {
|
||||
switch (tier) {
|
||||
case kNtRealtimePriorityClass:
|
||||
rc = -16;
|
||||
break;
|
||||
case kNtHighPriorityClass:
|
||||
rc = -10;
|
||||
break;
|
||||
case kNtAboveNormalPriorityClass:
|
||||
rc = -5;
|
||||
break;
|
||||
case kNtNormalPriorityClass:
|
||||
rc = 0;
|
||||
break;
|
||||
case kNtBelowNormalPriorityClass:
|
||||
rc = 5;
|
||||
break;
|
||||
case kNtIdlePriorityClass:
|
||||
rc = 15;
|
||||
break;
|
||||
default:
|
||||
notpossible;
|
||||
}
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
|
||||
if (closeme != -1) {
|
||||
CloseHandle(closeme);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
+40
-10
@@ -17,10 +17,13 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asmflag.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/limits.h"
|
||||
|
||||
/**
|
||||
* Returns nice value of thing.
|
||||
@@ -29,20 +32,47 @@
|
||||
* to clear `errno` beforehand and see if it changed, in order to truly
|
||||
* determine if an error happened.
|
||||
*
|
||||
* @param which can be PRIO_PROCESS, PRIO_PGRP, PRIO_USER
|
||||
* On Windows, there's only six priority classes. We define them as -16
|
||||
* (realtime), -10 (high), -5 (above), 0 (normal), 5 (below), 15 (idle)
|
||||
* which are the only values that'll roundtrip getpriority/setpriority.
|
||||
*
|
||||
* @param which can be one of:
|
||||
* - `PRIO_PROCESS` is supported universally
|
||||
* - `PRIO_PGRP` is supported on unix
|
||||
* - `PRIO_USER` is supported on unix
|
||||
* @param who is the pid, pgid, or uid (0 means current)
|
||||
* @return value ∈ [-NZERO,NZERO) or -1 w/ errno
|
||||
* @see setpriority(), nice()
|
||||
* @raise EINVAL if `which` was invalid or unsupported
|
||||
* @raise EPERM if access to process was denied
|
||||
* @raise ESRCH if no such process existed
|
||||
* @see setpriority()
|
||||
*/
|
||||
int getpriority(int which, unsigned who) {
|
||||
privileged int getpriority(int which, unsigned who) {
|
||||
int rc;
|
||||
if (!IsWindows()) {
|
||||
if ((rc = sys_getpriority(which, who)) != -1) {
|
||||
rc = 20 - rc;
|
||||
char cf;
|
||||
if (IsLinux()) {
|
||||
asm volatile("syscall"
|
||||
: "=a"(rc)
|
||||
: "0"(140), "D"(which), "S"(who)
|
||||
: "rcx", "r11", "memory");
|
||||
if (rc >= 0) {
|
||||
rc = NZERO - rc;
|
||||
} else {
|
||||
errno = -rc;
|
||||
rc = -1;
|
||||
}
|
||||
} else if (IsBsd()) {
|
||||
asm volatile(CFLAG_ASM("syscall")
|
||||
: CFLAG_CONSTRAINT(cf), "=a"(rc)
|
||||
: "1"((IsXnu() ? 0x2000000 : 0) | 100), "D"(which), "S"(who)
|
||||
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory");
|
||||
if (cf) {
|
||||
errno = rc;
|
||||
rc = -1;
|
||||
}
|
||||
} else {
|
||||
rc = sys_getsetpriority_nt(which, who, 0, sys_getpriority_nt);
|
||||
rc = sys_getpriority_nt(which, who);
|
||||
}
|
||||
STRACE("getpriority(%d, %u) → %d% m", which, who, rc);
|
||||
STRACE("getpriority(%s, %u) → %d% m", DescribeWhichPrio(which), who, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_getrusage_nt(int who, struct rusage *usage) {
|
||||
int64_t me, nsignals;
|
||||
int64_t me;
|
||||
struct NtIoCounters iocount;
|
||||
struct NtProcessMemoryCountersEx memcount;
|
||||
struct NtFileTime ftExit, ftUser, ftKernel, ftCreation;
|
||||
@@ -48,9 +48,6 @@ textwindows int sys_getrusage_nt(int who, struct rusage *usage) {
|
||||
!GetProcessIoCounters(me, &iocount)) {
|
||||
return __winerr();
|
||||
}
|
||||
__sig_lock();
|
||||
nsignals = __sig_count;
|
||||
__sig_unlock();
|
||||
*usage = (struct rusage){
|
||||
.ru_utime = WindowsDurationToTimeVal(ReadFileTime(ftUser)),
|
||||
.ru_stime = WindowsDurationToTimeVal(ReadFileTime(ftKernel)),
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/struct/itimerval.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/struct/timeval.internal.h"
|
||||
@@ -26,7 +25,9 @@
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/time/struct/timezone.h"
|
||||
|
||||
typedef axdx_t gettimeofday_f(struct timeval *, struct timezone *, void *);
|
||||
@@ -62,7 +63,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) {
|
||||
rc = __gettimeofday(tv, tz, 0).ax;
|
||||
}
|
||||
#if SYSDEBUG
|
||||
if (!__time_critical) {
|
||||
if (!(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
|
||||
STRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz, rc);
|
||||
}
|
||||
#endif
|
||||
|
||||
+19
-8
@@ -16,10 +16,12 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
@@ -38,7 +40,7 @@ static textwindows dontinline uint32_t GetUserNameHash(void) {
|
||||
char16_t buf[257];
|
||||
uint32_t size = ARRAYLEN(buf);
|
||||
GetUserName(&buf, &size);
|
||||
return KnuthMultiplicativeHash32(buf, size >> 1);
|
||||
return KnuthMultiplicativeHash32(buf, size >> 1) & INT_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,18 +49,22 @@ static textwindows dontinline uint32_t GetUserNameHash(void) {
|
||||
* This never fails. On Windows, which doesn't really have this concept,
|
||||
* we return a deterministic value that's likely to work.
|
||||
*
|
||||
* @return user id (always successful)
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
uint32_t getuid(void) {
|
||||
uint32_t rc;
|
||||
if (!IsWindows()) {
|
||||
int rc;
|
||||
if (IsMetal()) {
|
||||
rc = 0;
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_getuid();
|
||||
} else {
|
||||
rc = GetUserNameHash();
|
||||
}
|
||||
|
||||
STRACE("%s() → %u% m", "getuid", rc);
|
||||
_npassert(rc >= 0);
|
||||
STRACE("%s() → %d", "getuid", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -68,16 +74,21 @@ uint32_t getuid(void) {
|
||||
* This never fails. On Windows, which doesn't really have this concept,
|
||||
* we return a deterministic value that's likely to work.
|
||||
*
|
||||
* @return group id (always successful)
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
uint32_t getgid(void) {
|
||||
uint32_t rc;
|
||||
if (!IsWindows()) {
|
||||
int rc;
|
||||
if (IsMetal()) {
|
||||
rc = 0;
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_getgid();
|
||||
} else {
|
||||
rc = GetUserNameHash();
|
||||
}
|
||||
STRACE("%s() → %u% m", "getgid", rc);
|
||||
_npassert(rc >= 0);
|
||||
STRACE("%s() → %d", "getgid", rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -31,11 +31,11 @@
|
||||
|
||||
textwindows bool _check_interrupts(bool restartable, struct Fd *fd) {
|
||||
bool res;
|
||||
if (__time_critical) return false;
|
||||
if (__threaded && __threaded != gettid()) return false;
|
||||
if (_weaken(_check_sigalrm)) _weaken(_check_sigalrm)();
|
||||
if (_weaken(_check_sigchld)) _weaken(_check_sigchld)();
|
||||
if (fd && _weaken(_check_sigwinch)) _weaken(_check_sigwinch)(fd);
|
||||
if (!__tls_enabled || !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
|
||||
if (_weaken(_check_sigchld)) _weaken(_check_sigchld)();
|
||||
if (fd && _weaken(_check_sigwinch)) _weaken(_check_sigwinch)(fd);
|
||||
}
|
||||
res = _weaken(__sig_check) && _weaken(__sig_check)(restartable);
|
||||
return res;
|
||||
}
|
||||
@@ -253,7 +253,7 @@ static int createHostInfo(struct NtIpAdapterAddresses *firstAdapter) {
|
||||
int count, i;
|
||||
|
||||
/* __hostInfo must be empty */
|
||||
assert(__hostInfo == NULL);
|
||||
_unassert(__hostInfo == NULL);
|
||||
|
||||
for (aa = firstAdapter; aa; aa = aa->Next) {
|
||||
/* Skip all the interfaces with no address and the ones that are not AF_INET
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/calls/kntprioritycombos.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/ffs.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/enum/threadpriority.h"
|
||||
|
||||
hidden const struct NtPriorityCombo kNtPriorityCombos[] = {
|
||||
{-20, ffs(kNtHighPriorityClass), kNtThreadPriorityHighest, 15},
|
||||
{-18, ffs(kNtHighPriorityClass), kNtThreadPriorityTimeCritical, 15},
|
||||
{-17, ffs(kNtNormalPriorityClass), kNtThreadPriorityTimeCritical, 15},
|
||||
{-15, ffs(kNtIdlePriorityClass), kNtThreadPriorityTimeCritical, 15},
|
||||
{-13, ffs(kNtHighPriorityClass), kNtThreadPriorityAboveNormal, 14},
|
||||
{-11, ffs(kNtHighPriorityClass), kNtThreadPriorityNormal, 13},
|
||||
{-9, ffs(kNtHighPriorityClass), kNtThreadPriorityBelowNormal, 12},
|
||||
{-7, ffs(kNtNormalPriorityClass), kNtThreadPriorityHighest, 11},
|
||||
{-5, ffs(kNtHighPriorityClass), kNtThreadPriorityLowest, 11},
|
||||
{-3, ffs(kNtNormalPriorityClass), kNtThreadPriorityAboveNormal, 10},
|
||||
{-1, ffs(kNtNormalPriorityClass), kNtThreadPriorityHighest, 9},
|
||||
{0, ffs(kNtNormalPriorityClass), kNtThreadPriorityNormal, 9},
|
||||
{1, ffs(kNtNormalPriorityClass), kNtThreadPriorityAboveNormal, 8},
|
||||
{2, ffs(kNtNormalPriorityClass), kNtThreadPriorityBelowNormal, 8},
|
||||
{3, ffs(kNtNormalPriorityClass), kNtThreadPriorityNormal, 7},
|
||||
{4, ffs(kNtNormalPriorityClass), kNtThreadPriorityLowest, 7},
|
||||
{5, ffs(kNtIdlePriorityClass), kNtThreadPriorityHighest, 6},
|
||||
{6, ffs(kNtNormalPriorityClass), kNtThreadPriorityBelowNormal, 6},
|
||||
{7, ffs(kNtIdlePriorityClass), kNtThreadPriorityAboveNormal, 5},
|
||||
{9, ffs(kNtNormalPriorityClass), kNtThreadPriorityLowest, 5},
|
||||
{11, ffs(kNtIdlePriorityClass), kNtThreadPriorityNormal, 4},
|
||||
{13, ffs(kNtIdlePriorityClass), kNtThreadPriorityBelowNormal, 3},
|
||||
{15, ffs(kNtIdlePriorityClass), kNtThreadPriorityLowest, 2},
|
||||
{17, ffs(kNtHighPriorityClass), kNtThreadPriorityIdle, 1},
|
||||
{18, ffs(kNtNormalPriorityClass), kNtThreadPriorityIdle, 1},
|
||||
{19, ffs(kNtIdlePriorityClass), kNtThreadPriorityIdle, 1},
|
||||
};
|
||||
|
||||
hidden const unsigned kNtPriorityCombosLen = ARRAYLEN(kNtPriorityCombos);
|
||||
Regular → Executable
-18
@@ -1,18 +0,0 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_CALLS_KNTPRIORITYCOMBOS_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_KNTPRIORITYCOMBOS_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct NtPriorityCombo {
|
||||
int8_t nice; /* sorted by this */
|
||||
int8_t lg2tier;
|
||||
int8_t wut;
|
||||
int8_t prio;
|
||||
};
|
||||
|
||||
hidden extern const unsigned kNtPriorityCombosLen;
|
||||
hidden extern const struct NtPriorityCombo kNtPriorityCombos[];
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_KNTPRIORITYCOMBOS_H_ */
|
||||
+43
-5
@@ -28,15 +28,53 @@
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
|
||||
/**
|
||||
* Changes current position of file descriptor/handle.
|
||||
* Changes current position of file descriptor, e.g.
|
||||
*
|
||||
* int fd = open("hello.bin", O_RDONLY);
|
||||
* lseek(fd, 100, SEEK_SET); // set position to 100th byte
|
||||
* read(fd, buf, 8); // read bytes 100 through 107
|
||||
*
|
||||
* This function may be used to inspect the current position:
|
||||
*
|
||||
* int64_t pos = lseek(fd, 0, SEEK_CUR);
|
||||
*
|
||||
* You may seek past the end of file. If a write happens afterwards
|
||||
* then the gap leading up to it will be filled with zeroes. Please
|
||||
* note that lseek() by itself will not extend the physical medium.
|
||||
*
|
||||
* If dup() is used then the current position will be shared across
|
||||
* multiple file descriptors. If you seek in one it will implicitly
|
||||
* seek the other too.
|
||||
*
|
||||
* The current position of a file descriptor is shared between both
|
||||
* processes and threads. For example, if an fd is inherited across
|
||||
* fork(), and both the child and parent want to read from it, then
|
||||
* changes made by one are observable to the other.
|
||||
*
|
||||
* The pread() and pwrite() functions obfuscate the need for having
|
||||
* global shared file position state. Consider using them, since it
|
||||
* helps avoid the gotchas of this interface described above.
|
||||
*
|
||||
* This function is supported by all OSes within our support vector
|
||||
* and our unit tests demonstrate the behaviors described above are
|
||||
* consistent across platforms.
|
||||
*
|
||||
* @param fd is a number returned by open()
|
||||
* @param offset is the relative byte count
|
||||
* @param whence can be SEEK_SET, SEEK_CUR, or SEEK_END
|
||||
* @return new position relative to beginning, or -1 on error
|
||||
* @param offset is 0-indexed byte count w.r.t. `whence`
|
||||
* @param whence can be one of:
|
||||
* - `SEEK_SET`: Sets the file position to `offset` [default]
|
||||
* - `SEEK_CUR`: Sets the file position to `position + offset`
|
||||
* - `SEEK_END`: Sets the file position to `filesize + offset`
|
||||
* @return new position relative to beginning, or -1 w/ errno
|
||||
* @raise ESPIPE if `fd` is a pipe, socket, or fifo
|
||||
* @raise EBADF if `fd` isn't an open file descriptor
|
||||
* @raise EINVAL if resulting offset would be negative
|
||||
* @raise EINVAL if `whence` isn't valid
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int64_t lseek(int fd, int64_t offset, unsigned whence) {
|
||||
int64_t lseek(int fd, int64_t offset, int whence) {
|
||||
int64_t rc;
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = _weaken(__zipos_lseek)(
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/str/path.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Creates directory and parent components.
|
||||
*
|
||||
* This function is similar to mkdir() except it iteratively creates
|
||||
* parent directories and it won't fail if the directory already exists.
|
||||
*
|
||||
* @param path is a UTF-8 string, preferably relative w/ forward slashes
|
||||
* @param mode can be, for example, 0755
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @raise EEXIST if named file already exists as non-directory
|
||||
* @raise ENOTDIR if directory component in `path` existed as non-directory
|
||||
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
|
||||
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
|
||||
* @raise EROFS if parent directory is on read-only filesystem
|
||||
* @raise ENOSPC if file system or parent directory is full
|
||||
* @raise EACCES if write permission was denied on parent directory
|
||||
* @raise EACCES if search permission was denied on component in `path`
|
||||
* @raise ENOENT if `path` is an empty string
|
||||
* @raise ELOOP if loop was detected resolving components of `path`
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
*/
|
||||
int makedirs(const char *path, unsigned mode) {
|
||||
int c, e, i, n;
|
||||
struct stat st;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
e = errno;
|
||||
n = strlen(path);
|
||||
if (n >= PATH_MAX) return enametoolong();
|
||||
memcpy(buf, path, n + 1);
|
||||
i = n;
|
||||
|
||||
// descend
|
||||
while (i) {
|
||||
if (!mkdir(buf, mode)) break;
|
||||
if (errno == EEXIST) {
|
||||
if (i == n) goto CheckTop;
|
||||
break;
|
||||
}
|
||||
if (errno != ENOENT) return -1;
|
||||
while (i && _isdirsep(buf[i - 1])) buf[--i] = 0;
|
||||
while (i && !_isdirsep(buf[i - 1])) buf[--i] = 0;
|
||||
}
|
||||
|
||||
// ascend
|
||||
for (;;) {
|
||||
if (mkdir(buf, mode)) {
|
||||
if (errno != EEXIST) return -1;
|
||||
if (i == n) goto CheckTop;
|
||||
}
|
||||
if (i == n) break;
|
||||
while (i < n && !_isdirsep((c = path[i]))) buf[i++] = c;
|
||||
while (i < n && _isdirsep((c = path[i]))) buf[i++] = c;
|
||||
}
|
||||
|
||||
Finish:
|
||||
errno = e;
|
||||
return 0;
|
||||
|
||||
CheckTop:
|
||||
if (stat(path, &st)) return -1;
|
||||
if (S_ISDIR(st.st_mode)) goto Finish;
|
||||
return eexist();
|
||||
}
|
||||
+14
-7
@@ -17,12 +17,7 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Creates directory a.k.a. folder.
|
||||
@@ -36,9 +31,21 @@
|
||||
* @param path is a UTF-8 string, preferably relative w/ forward slashes
|
||||
* @param mode can be, for example, 0755
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @error ENAMETOOLONG if >246 characters on NT
|
||||
* @raise EEXIST if named file already exists
|
||||
* @raise ENOTDIR if directory component in `path` existed as non-directory
|
||||
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
|
||||
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
|
||||
* @raise EROFS if parent directory is on read-only filesystem
|
||||
* @raise ENOSPC if file system or parent directory is full
|
||||
* @raise EACCES if write permission was denied on parent directory
|
||||
* @raise EACCES if search permission was denied on component in `path`
|
||||
* @raise ENOENT if a component within `path` didn't exist
|
||||
* @raise ENOENT if `path` is an empty string
|
||||
* @raise ELOOP if loop was detected resolving components of `path`
|
||||
* @see makedirs() which is higher-level
|
||||
* @see mkdirat() for modern call
|
||||
* @asyncsignalsafe
|
||||
* @see makedirs()
|
||||
* @threadsafe
|
||||
*/
|
||||
int mkdir(const char *path, unsigned mode) {
|
||||
return mkdirat(AT_FDCWD, path, mode);
|
||||
|
||||
+15
-4
@@ -31,12 +31,23 @@
|
||||
/**
|
||||
* Creates directory a.k.a. folder.
|
||||
*
|
||||
* @param dirfd is normally AT_FDCWD but if it's an open directory and
|
||||
* @param dirfd is normally `AT_FDCWD` but if it's an open directory and
|
||||
* path is relative, then path becomes relative to dirfd
|
||||
* @param path is a UTF-8 string, preferably relative w/ forward slashes
|
||||
* @param mode can be, for example, 0755
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @error EEXIST, ENOTDIR, ENAMETOOLONG, EACCES, ENOENT
|
||||
* @param mode is permissions bits, which is usually 0755
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EEXIST if named file already exists
|
||||
* @raise EBADF if `path` is relative and `dirfd` isn't `AT_FDCWD` or valid
|
||||
* @raise ENOTDIR if directory component in `path` existed as non-directory
|
||||
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
|
||||
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
|
||||
* @raise EROFS if parent directory is on read-only filesystem
|
||||
* @raise ENOSPC if file system or parent directory is full
|
||||
* @raise EACCES if write permission was denied on parent directory
|
||||
* @raise EACCES if search permission was denied on component in `path`
|
||||
* @raise ENOENT if a component within `path` didn't exist
|
||||
* @raise ENOENT if `path` is an empty string
|
||||
* @raise ELOOP if loop was detected resolving components of `path`
|
||||
* @asyncsignalsafe
|
||||
* @see makedirs()
|
||||
*/
|
||||
|
||||
+28
-22
@@ -34,8 +34,15 @@
|
||||
static bool NeedsQuotes(const char *s) {
|
||||
if (!*s) return true;
|
||||
do {
|
||||
if (*s == ' ' || *s == '\t') {
|
||||
return true;
|
||||
switch (*s) {
|
||||
case '"':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\n':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (*s++);
|
||||
return false;
|
||||
@@ -45,19 +52,21 @@ static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts System V argv to Windows-style command line.
|
||||
*
|
||||
* Escaping is performed and it's designed to round-trip with
|
||||
* GetDosArgv() or GetDosArgv(). This function does NOT escape
|
||||
* command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd).
|
||||
*
|
||||
* @param cmdline is output buffer
|
||||
* @param prog is used as argv[0]
|
||||
* @param argv is an a NULL-terminated array of UTF-8 strings
|
||||
* @return freshly allocated lpCommandLine or NULL w/ errno
|
||||
* @see libc/runtime/dosargv.c
|
||||
*/
|
||||
// Converts System V argv to Windows-style command line.
|
||||
//
|
||||
// Escaping is performed and it's designed to round-trip with
|
||||
// GetDosArgv() or GetDosArgv(). This function does NOT escape
|
||||
// command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd).
|
||||
//
|
||||
// TODO(jart): this needs fuzzing and security review
|
||||
//
|
||||
// @param cmdline is output buffer
|
||||
// @param prog is frontloaded as argv[0]
|
||||
// @param argv is an a NULL-terminated array of UTF-8 strings
|
||||
// @return 0 on success, or -1 w/ errno
|
||||
// @raise E2BIG if everything is too huge
|
||||
// @see "Everyone quotes command line arguments the wrong way" MSDN
|
||||
// @see libc/runtime/getdosargv.c
|
||||
textwindows int mkntcmdline(char16_t cmdline[ARG_MAX / 2], const char *prog,
|
||||
char *const argv[]) {
|
||||
char *arg;
|
||||
@@ -102,8 +111,8 @@ textwindows int mkntcmdline(char16_t cmdline[ARG_MAX / 2], const char *prog,
|
||||
} else {
|
||||
// turn stuff like `less /c/...`
|
||||
// into `less c:/...`
|
||||
// turn stuff like `more <\\\"/c/...\\\"`
|
||||
// into `more <\\\"c:/...\\\"`
|
||||
// turn stuff like `more <"/c/..."`
|
||||
// into `more <"c:/..."`
|
||||
if (k > 3 && IsAlpha(cmdline[k - 1]) &&
|
||||
(cmdline[k - 2] == '/' || cmdline[k - 2] == '\\') &&
|
||||
(cmdline[k - 3] == '"' || cmdline[k - 3] == ' ')) {
|
||||
@@ -115,11 +124,8 @@ textwindows int mkntcmdline(char16_t cmdline[ARG_MAX / 2], const char *prog,
|
||||
if (x == '\\') {
|
||||
++slashes;
|
||||
} else if (x == '"') {
|
||||
for (s = 0; s < slashes * 2; ++s) {
|
||||
APPEND(u'\\');
|
||||
}
|
||||
slashes = 0;
|
||||
APPEND(u'\\');
|
||||
APPEND(u'"');
|
||||
APPEND(u'"');
|
||||
APPEND(u'"');
|
||||
} else {
|
||||
for (s = 0; s < slashes; ++s) {
|
||||
|
||||
+21
-2
@@ -17,8 +17,8 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/ntmagicpaths.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/systeminfo.h"
|
||||
#include "libc/str/oldutf16.internal.h"
|
||||
@@ -173,5 +173,24 @@ textwindows int __mkntpath2(const char *path,
|
||||
p[j] = 0;
|
||||
n = j;
|
||||
|
||||
return x + m + n;
|
||||
// our path is now stored at `path16` with length `n`
|
||||
n = x + m + n;
|
||||
|
||||
// To avoid toil like this:
|
||||
//
|
||||
// CMD.EXE was started with the above path as the current directory.
|
||||
// UNC paths are not supported. Defaulting to Windows directory.
|
||||
// Access is denied.
|
||||
//
|
||||
// Remove \\?\ prefix if we're within 260 character limit.
|
||||
if (n > 4 && n < 260 && //
|
||||
path16[0] == '\\' && //
|
||||
path16[1] == '\\' && //
|
||||
path16[2] == '?' && //
|
||||
path16[3] == '\\') {
|
||||
memmove(path16, path16 + 4, (n - 4 + 1) * sizeof(char16_t));
|
||||
n -= 4;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/time/clockstonanos.internal.h"
|
||||
|
||||
textwindows int sys_nanosleep_nt(const struct timespec *req,
|
||||
struct timespec *rem) {
|
||||
bool alertable;
|
||||
uint32_t slice;
|
||||
uint64_t begin;
|
||||
int64_t ms, toto, nanos;
|
||||
struct timespec elapsed;
|
||||
|
||||
// check req is legal timespec
|
||||
if (!(0 <= req->tv_nsec && req->tv_nsec < 1000000000)) {
|
||||
return einval();
|
||||
}
|
||||
|
||||
// save beginning timestamp
|
||||
if (!__time_critical && rem) {
|
||||
begin = rdtsc();
|
||||
} else {
|
||||
begin = 0; // to prevent uninitialized warning
|
||||
}
|
||||
|
||||
// convert timespec to milliseconds
|
||||
if (__builtin_mul_overflow(req->tv_sec, 1000, &ms) ||
|
||||
__builtin_add_overflow(ms, (req->tv_nsec + 999999) / 1000000, &ms)) {
|
||||
ms = INT64_MAX;
|
||||
}
|
||||
|
||||
for (toto = ms;;) {
|
||||
|
||||
// check if signal was delivered
|
||||
if (!__time_critical && _check_interrupts(false, g_fds.p)) {
|
||||
if (rem) {
|
||||
nanos = ClocksToNanos(rdtsc(), begin);
|
||||
elapsed.tv_sec = nanos / 1000000000;
|
||||
elapsed.tv_nsec = nanos % 1000000000;
|
||||
*rem = _timespec_sub(*req, elapsed);
|
||||
if (rem->tv_sec < 0) {
|
||||
rem->tv_sec = 0;
|
||||
rem->tv_nsec = 0;
|
||||
}
|
||||
}
|
||||
return eintr();
|
||||
}
|
||||
|
||||
// configure the sleep
|
||||
slice = MIN(__SIG_POLLING_INTERVAL_MS, ms);
|
||||
if (__time_critical) {
|
||||
alertable = false;
|
||||
} else {
|
||||
alertable = true;
|
||||
POLLTRACE("... sleeping %'ldms of %'ld", toto - ms, toto);
|
||||
}
|
||||
|
||||
// perform the sleep
|
||||
if (SleepEx(slice, alertable) == kNtWaitIoCompletion) {
|
||||
POLLTRACE("IOCP EINTR"); // in case we ever figure it out
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if full duration has elapsed
|
||||
if ((ms -= slice) <= 0) {
|
||||
if (rem) {
|
||||
rem->tv_sec = 0;
|
||||
rem->tv_nsec = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-20
@@ -19,40 +19,34 @@
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/struct/timeval.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
|
||||
// nanosleep() on xnu: a bloodbath of a polyfill
|
||||
// consider using clock_nanosleep(TIMER_ABSTIME)
|
||||
int sys_nanosleep_xnu(const struct timespec *req, struct timespec *rem) {
|
||||
int rc;
|
||||
axdx_t axdx;
|
||||
struct timeval tv;
|
||||
struct timespec begin, end, elapsed;
|
||||
|
||||
if (rem) {
|
||||
if (sys_clock_gettime_xnu(CLOCK_MONOTONIC, &begin) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
tv = _timespec2timeval(*req);
|
||||
rc = sys_select(0, 0, 0, 0, &tv);
|
||||
|
||||
struct timeval wt, t1, t2, td;
|
||||
if (rem) sys_gettimeofday_xnu(&t1, 0, 0);
|
||||
wt = _timespec_totimeval(*req); // rounds up
|
||||
rc = sys_select(0, 0, 0, 0, &wt);
|
||||
if (rem) {
|
||||
if (!rc) {
|
||||
rem->tv_sec = 0;
|
||||
rem->tv_nsec = 0;
|
||||
} else if (rc == -1 && errno == EINTR) {
|
||||
sys_clock_gettime_xnu(CLOCK_MONOTONIC, &end);
|
||||
elapsed = _timespec_sub(end, begin);
|
||||
*rem = _timespec_sub(*req, elapsed);
|
||||
if (rem->tv_sec < 0) {
|
||||
// xnu select() doesn't modify timeout
|
||||
// so we need, yet another system call
|
||||
sys_gettimeofday_xnu(&t2, 0, 0);
|
||||
td = _timeval_sub(t2, t1);
|
||||
if (_timeval_gte(td, wt)) {
|
||||
rem->tv_sec = 0;
|
||||
rem->tv_nsec = 0;
|
||||
} else {
|
||||
*rem = _timeval_totimespec(_timeval_sub(wt, td));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
+12
-49
@@ -16,67 +16,30 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/asan.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
|
||||
/**
|
||||
* Sleeps for a particular amount of time.
|
||||
* Sleeps for relative amount of time.
|
||||
*
|
||||
* @param req is the duration of time we should sleep
|
||||
* @param rem if non-NULL will receive the amount of time that wasn't
|
||||
* slept because a signal was delivered. If no signal's delivered
|
||||
* then this value will be set to `{0, 0}`. It's also fine to set
|
||||
* this value to the same pointer as `req`.
|
||||
* @param rem if non-null will be updated with the remainder of unslept
|
||||
* time when -1 w/ `EINTR` is returned otherwise `rem` is undefined
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EINVAL if `req->tv_nsec ∉ [0,1000000000)`
|
||||
* @raise EINTR if a signal was delivered, and `rem` is updated
|
||||
* @raise EINTR if a signal was delivered and `rem` is updated
|
||||
* @raise EFAULT if `req` is NULL or `req` / `rem` is a bad pointer
|
||||
* @raise ENOSYS on bare metal
|
||||
* @note POSIX.1 specifies nanosleep() measures against `CLOCK_REALTIME`
|
||||
* however Linux measures uses `CLOCK_MONOTONIC`. This shouldn't
|
||||
* matter, since POSIX.1 further specifies that discontinuous
|
||||
* changes in `CLOCK_REALTIME` shouldn't impact nanosleep()
|
||||
* @see clock_nanosleep()
|
||||
* @norestart
|
||||
*/
|
||||
int nanosleep(const struct timespec *req, struct timespec *rem) {
|
||||
int rc;
|
||||
|
||||
if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) ||
|
||||
(rem && !__asan_is_valid_timespec(rem))))) {
|
||||
rc = efault();
|
||||
} else if (req->tv_sec < 0 ||
|
||||
!(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {
|
||||
rc = einval();
|
||||
} else if (!IsWindows() && !IsMetal() && !IsXnu()) {
|
||||
rc = sys_nanosleep(req, rem);
|
||||
} else if (IsXnu()) {
|
||||
rc = sys_nanosleep_xnu(req, rem);
|
||||
} else if (IsMetal()) {
|
||||
rc = enosys(); /* TODO: Sleep on Metal */
|
||||
if (!(rc = clock_nanosleep(CLOCK_REALTIME, 0, req, rem))) {
|
||||
return 0;
|
||||
} else {
|
||||
rc = sys_nanosleep_nt(req, rem);
|
||||
errno = rc;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Linux Kernel doesn't change the remainder value on success, but
|
||||
// some kernels like OpenBSD will. POSIX doesn't specify the Linux
|
||||
// behavior. So we polyfill it here.
|
||||
if (!rc && rem) {
|
||||
rem->tv_sec = 0;
|
||||
rem->tv_nsec = 0;
|
||||
}
|
||||
|
||||
#ifdef SYSDEBUG
|
||||
if (!__time_critical) {
|
||||
STRACE("nanosleep(%s, [%s]) → %d% m", DescribeTimespec(rc, req),
|
||||
DescribeTimespec(rc, rem), rc);
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
Loaded 100 of 914 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user