Skip to content

Commit b107d27

Browse files
committed
Add /statusz page to redbean plus other enhancements
redbean improvements: - Explicitly disable corking - Simulate Python regex API for Lua - Send warmup requests in main process on startup - Add Class-A granular IPv4 network classification - Add /statusz page so you can monitor your redbean's health - Fix regressions on OpenBSD/NetBSD caused by recent changes - Plug Authorization header into Lua GetUser and GetPass APIs - Recognize X-Forwarded-{For,Host} from local reverse proxies - Add many additional functions to redbean Lua server page API - Report resource usage of child processes on `/` listing page - Introduce `-a` flag for logging child process resource usage - Introduce `-t MILLIS` flag and `ProgramTimeout(ms)` init API - Introduce `-H "Header: value"` flag and `ProgramHeader(k,v)` API Cosmopolitan Libc improvements: - Make strerror() simpler - Make inet_pton() not depend on sscanf() - Fix OpenExecutable() which broke .data section earlier - Fix stdio in cases where it overflows kernel tty buffer - Fix bugs in crash reporting w/o .com.dbg binary present - Add polyfills for SO_LINGER, SO_RCVTIMEO, and SO_SNDTIMEO - Polyfill TCP_CORK on BSD and XNU using TCP_NOPUSH magnums New netcat clone in examples/nc.c: While testing some of the failure conditions for redbean, I noticed that BusyBox's `nc` command is pretty busted, if you use it as an interactive tool, rather than having it be part of a pipeline. Unfortunately this'll only work on UNIX since Windows doesn't let us poll on stdio and sockets at the same time because I don't think they want tools like this running on their platform. So if you want forbidden fruit, it's here so enjoy it
1 parent 4effa23 commit b107d27

File tree

163 files changed

+4381
-2060
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

163 files changed

+4381
-2060
lines changed

examples/nc.c

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#if 0
2+
/*─────────────────────────────────────────────────────────────────╗
3+
│ To the extent possible under law, Justine Tunney has waived │
4+
│ all copyright and related or neighboring rights to this file, │
5+
│ as it is written in the following disclaimers: │
6+
│ • http://unlicense.org/ │
7+
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
8+
╚─────────────────────────────────────────────────────────────────*/
9+
#endif
10+
#include "libc/calls/calls.h"
11+
#include "libc/fmt/conv.h"
12+
#include "libc/log/log.h"
13+
#include "libc/macros.internal.h"
14+
#include "libc/runtime/runtime.h"
15+
#include "libc/sock/sock.h"
16+
#include "libc/str/str.h"
17+
#include "libc/sysv/consts/af.h"
18+
#include "libc/sysv/consts/ipproto.h"
19+
#include "libc/sysv/consts/poll.h"
20+
#include "libc/sysv/consts/shut.h"
21+
#include "libc/sysv/consts/so.h"
22+
#include "libc/sysv/consts/sock.h"
23+
#include "libc/sysv/consts/sol.h"
24+
25+
/**
26+
* @fileoverview netcat clone
27+
*
28+
* Implemented because BusyBox's netcat doesn't detect remote close and
29+
* lingers in the CLOSE_WAIT wait possibly due to file descriptor leaks
30+
*
31+
* Once upon time we called this command "Telnet"
32+
*/
33+
34+
int main(int argc, char *argv[]) {
35+
ssize_t rc;
36+
size_t i, got;
37+
char buf[1500];
38+
int err, toto, sock;
39+
struct linger linger = {true, 1};
40+
struct sockaddr_in addr = {AF_INET};
41+
struct pollfd fds[2] = {{-1, POLLIN}, {-1, POLLIN}};
42+
43+
if (argc != 3) exit(1);
44+
inet_pton(AF_INET, argv[1], &addr.sin_addr);
45+
addr.sin_port = htons(atoi(argv[2]));
46+
47+
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
48+
perror("socket");
49+
exit(1);
50+
}
51+
52+
if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) == -1) {
53+
perror("setsockopt(SO_LINGER)");
54+
exit(1);
55+
}
56+
57+
if (connect(sock, &addr, sizeof(addr)) == -1) {
58+
perror("connect");
59+
exit(1);
60+
}
61+
62+
fds[0].fd = 0;
63+
fds[1].fd = sock;
64+
for (;;) {
65+
fds[0].revents = 0;
66+
fds[1].revents = 0;
67+
if (poll(fds, ARRAYLEN(fds), -1) == -1) {
68+
perror("poll");
69+
exit(1);
70+
}
71+
72+
if (fds[0].revents & (POLLIN | POLLERR | POLLHUP)) {
73+
if ((rc = read(0, buf, 1400)) == -1) {
74+
perror("read(stdin)");
75+
exit(1);
76+
}
77+
if (!(got = rc)) {
78+
shutdown(sock, SHUT_WR);
79+
fds[0].fd = -1;
80+
}
81+
for (i = 0; i < got; i += rc) {
82+
if ((rc = write(sock, buf + i, got - i)) == -1) {
83+
perror("write(sock)");
84+
exit(1);
85+
}
86+
}
87+
}
88+
89+
if (fds[1].revents & (POLLIN | POLLERR | POLLHUP)) {
90+
if ((rc = read(sock, buf, 1500)) == -1) {
91+
perror("read(sock)");
92+
exit(1);
93+
}
94+
if (!(got = rc)) {
95+
break;
96+
}
97+
for (i = 0; i < got; i += rc) {
98+
if ((rc = write(1, buf + i, got - i)) == -1) {
99+
perror("write(stdout)");
100+
exit(1);
101+
}
102+
}
103+
}
104+
}
105+
106+
if (close(sock) == -1) {
107+
perror("close");
108+
exit(1);
109+
}
110+
111+
return 0;
112+
}

libc/calls/now.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ static long double MeasureNanosPerCycle(void) {
5656
return avg;
5757
}
5858

59-
static void InitTime(void) {
59+
void RefreshTime(void) {
6060
struct Now now;
6161
now.cpn = MeasureNanosPerCycle();
6262
now.r0 = dtime(CLOCK_REALTIME);
@@ -66,7 +66,7 @@ static void InitTime(void) {
6666
}
6767

6868
long double ConvertTicksToNanos(uint64_t ticks) {
69-
if (!g_now.once) InitTime();
69+
if (!g_now.once) RefreshTime();
7070
return ticks * g_now.cpn; /* pico scale */
7171
}
7272

@@ -80,7 +80,7 @@ long double nowl_sys(void) {
8080

8181
long double nowl_art(void) {
8282
uint64_t ticks;
83-
if (!g_now.once) InitTime();
83+
if (!g_now.once) RefreshTime();
8484
ticks = unsignedsubtract(rdtsc(), g_now.k0);
8585
return g_now.r0 + ConvertTicksToSeconds(ticks);
8686
}

libc/fmt/strerror_r.c

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "libc/dce.h"
2121
#include "libc/errno.h"
2222
#include "libc/fmt/fmt.h"
23+
#include "libc/fmt/itoa.h"
2324
#include "libc/macros.internal.h"
2425
#include "libc/nt/enum/formatmessageflags.h"
2526
#include "libc/nt/process.h"
@@ -165,7 +166,7 @@ const struct Error {
165166

166167
static const char *geterrname(long x) {
167168
int i;
168-
if (!IsTiny() && x) {
169+
if (x) {
169170
for (i = 0; i < ARRAYLEN(kErrors); ++i) {
170171
if (x == *kErrors[i].x) {
171172
return kErrors[i].s;
@@ -180,24 +181,19 @@ static const char *geterrname(long x) {
180181
* @return 0 on success, or error code
181182
*/
182183
int strerror_r(int err, char *buf, size_t size) {
184+
char *p;
183185
const char *s;
184-
char16_t buf16[100];
185-
int winstate, sysvstate;
186+
err &= 0xFFFF;
186187
s = geterrname(err);
187-
if (!SupportsWindows()) {
188-
(snprintf)(buf, size, "%s[%d]", s, err);
189-
} else {
190-
winstate = GetLastError();
191-
sysvstate = errno;
192-
if (FormatMessage(
193-
kNtFormatMessageFromSystem | kNtFormatMessageIgnoreInserts, NULL,
194-
err, 0, buf16, ARRAYLEN(buf16) - 1, 0) > 0) {
195-
chomp16(buf16);
196-
} else {
197-
buf16[0] = u'\0';
198-
}
199-
(snprintf)(buf, size, "%s/err=%d/errno:%d/GetLastError:%d%s%hs", s, err,
200-
sysvstate, winstate, buf16[0] ? " " : "", buf16);
188+
p = buf;
189+
if (strlen(s) + 1 + 5 + 1 + 1 <= size) {
190+
p = stpcpy(p, s);
191+
*p++ = '[';
192+
p += uint64toarray_radix10(err, p);
193+
*p++ = ']';
194+
}
195+
if (p - buf < size) {
196+
*p++ = '\0';
201197
}
202198
return 0;
203199
}

libc/nexgen32e/crc32.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
COSMOPOLITAN_C_START_
55

66
void crc32init(uint32_t[hasatleast 256], uint32_t);
7+
uint32_t crc32c(uint32_t, const void *, size_t);
78
uint32_t crc32_z(uint32_t, const void *, size_t);
8-
extern uint32_t (*const crc32c)(uint32_t, const void *, size_t);
99
uint32_t crc32c_pure(uint32_t, const void *, size_t) strlenesque hidden;
1010
uint32_t crc32c_sse42(uint32_t, const void *, size_t) strlenesque hidden;
1111
uint32_t crc32_pclmul(uint32_t, const void *, size_t) hidden;

libc/runtime/finddebugbinary.c

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,40 +29,28 @@
2929
/**
3030
* Returns path of binary with the debug information, or null.
3131
*
32-
* @return path to debug binary, or -1 w/ errno
32+
* @return path to debug binary, or NULL
3333
*/
3434
const char *FindDebugBinary(void) {
35-
unsigned i, len;
36-
char buf[2][PATH_MAX];
37-
static char res[PATH_MAX];
38-
const char *bins[4], *pwd, *comdbg;
39-
if (res[0]) return res;
40-
if ((comdbg = emptytonull(getenv("COMDBG")))) return comdbg;
41-
if (res[0]) return res;
42-
bins[0] = program_invocation_name;
43-
bins[1] = (const char *)getauxval(AT_EXECFN);
44-
pwd = emptytonull(getenv("PWD"));
45-
for (i = 0; i < 2; ++i) {
46-
if (pwd && bins[i] && bins[i][0] != '/' && bins[i][0] != '\\' &&
47-
strlen(pwd) + 1 + strlen(bins[i]) + 1 <= ARRAYLEN(buf[0])) {
48-
strcpy(buf[i], pwd);
49-
strcat(buf[i], "/");
50-
strcat(buf[i], bins[i]);
51-
bins[i + 2] = buf[i];
35+
static bool once;
36+
static char *res;
37+
static char buf[PATH_MAX + 1];
38+
char *p;
39+
size_t n;
40+
if (!once) {
41+
if (!(res = getenv("COMDBG"))) {
42+
p = (char *)getauxval(AT_EXECFN);
43+
n = strlen(p);
44+
if (n > 4 && !memcmp(p + n - 4, ".dbg", 4)) {
45+
res = p;
46+
} else if (n + 4 <= PATH_MAX) {
47+
mempcpy(mempcpy(buf, p, n), ".dbg", 5);
48+
if (fileexists(buf)) {
49+
res = buf;
50+
}
51+
}
5252
}
53+
once = true;
5354
}
54-
for (i = 0; i < 4; ++i) {
55-
if (!bins[i]) continue;
56-
len = strlen(bins[i]);
57-
memcpy(res, bins[i], len + 1);
58-
if (!endswith(res, ".dbg") && len + 3 + 1 <= ARRAYLEN(res)) {
59-
strcat(res, ".dbg");
60-
}
61-
if (fileexists(res)) {
62-
return res;
63-
}
64-
}
65-
res[0] = '\0';
66-
errno = ENOENT;
67-
return NULL;
55+
return res;
6856
}

libc/runtime/ftrace.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ textstartup int ftrace_init(int argc, char *argv[]) {
141141
g_buf[1] = ' ';
142142
if ((g_symbols = OpenSymbolTable(FindDebugBinary()))) {
143143
__hook(ftrace_hook, g_symbols);
144+
} else {
145+
write(2, "error: --ftrace needs the concomitant .com.dbg binary\n", 54);
144146
}
145147
}
146148
return argc;

libc/runtime/openexecutable.S

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ OpenExecutable:
4141
pushq MAP_PRIVATE(%rip) # -0x30(%rbp)
4242
pushq MAP_FIXED(%rip) # -0x38(%rbp)
4343
pushq MAP_SHARED(%rip) # -0x40(%rbp)
44+
pushq __NR_mprotect(%rip) # -0x48(%rbp)
45+
pushq __NR_mprotect(%rip) # -0x50(%rbp)
4446
push %rbx # code buffer
4547
push %r12 # data buffer
4648
push %r14 # filename
@@ -55,7 +57,7 @@ OpenExecutable:
5557
mov -0x10(%rbp),%eax # __NR_mmap
5658
xor %edi,%edi
5759
mov $PAGESIZE,%esi
58-
mov $PROT_READ|PROT_WRITE|PROT_EXEC,%edx
60+
mov $PROT_READ|PROT_WRITE,%edx
5961
mov -0x28(%rbp),%r10d # MAP_ANONYMOUS
6062
or -0x30(%rbp),%r10d # MAP_PRIVATE
6163
mov $-1,%r8
@@ -94,6 +96,14 @@ OpenExecutable:
9496
mov $8f,%esi
9597
mov $9f-8f,%ecx
9698
rep movsb
99+
100+
// Change protection.
101+
mov -0x48(%rbp),%eax # __NR_mprotect
102+
mov %rbx,%rdi
103+
mov $PAGESIZE,%esi
104+
mov $PROT_READ|PROT_EXEC,%edx
105+
syscall
106+
97107
jmp *%rbx
98108

99109
// <LIMBO>
@@ -150,7 +160,7 @@ OpenExecutable:
150160

151161
// Put data back.
152162
mov $ape_ram_vaddr,%edi
153-
xchg %eax,%esi
163+
mov %r12,%rsi
154164
mov $ape_ram_filesz,%ecx
155165
rep movsb
156166

libc/sock/inet_pton.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
#include "libc/fmt/fmt.h"
2020
#include "libc/sock/internal.h"
2121
#include "libc/sock/sock.h"
22-
#include "libc/sysv/errfuns.h"
2322
#include "libc/sysv/consts/af.h"
2423
#include "libc/sysv/consts/inaddr.h"
24+
#include "libc/sysv/errfuns.h"
2525

2626
/**
2727
* Converts internet address string to binary.
@@ -32,16 +32,23 @@
3232
* @return 1 on success, 0 on src malformed, or -1 w/ errno
3333
*/
3434
int inet_pton(int af, const char *src, void *dst) {
35-
if (af == AF_INET) {
36-
unsigned char *p = (unsigned char *)dst;
37-
if (sscanf(src, "%hhu.%hhu.%hhu.%hhu", &p[0], &p[1], &p[2], &p[3]) == 4) {
38-
return 1;
35+
uint8_t *p;
36+
int b, c, j;
37+
if (af != AF_INET) return eafnosupport();
38+
j = 0;
39+
p = dst;
40+
p[0] = 0;
41+
while ((c = *src++)) {
42+
if (isdigit(c)) {
43+
b = c - '0' + p[j] * 10;
44+
p[j] = MIN(255, b);
45+
if (b > 255) return 0;
46+
} else if (c == '.') {
47+
if (++j == 4) return 0;
48+
p[j] = 0;
3949
} else {
40-
*(uint32_t *)dst = htonl(INADDR_TESTNET3);
4150
return 0;
4251
}
43-
} else {
44-
*(uint32_t *)dst = htonl(INADDR_TESTNET3);
45-
return eafnosupport();
4652
}
53+
return j == 3 ? 1 : 0;
4754
}

libc/sock/internal.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ struct msghdr_bsd {
4747
};
4848

4949
struct sockaddr_un_bsd {
50-
uint8_t sun_len; /* sockaddr len including NUL on freebsd but excluding it on openbsd/xnu */
50+
uint8_t sun_len; /* sockaddr len including NUL on freebsd but excluding it on
51+
openbsd/xnu */
5152
uint8_t sun_family;
52-
char sun_path[108];
53+
char sun_path[108];
5354
};
5455

5556
struct SockFd {
@@ -121,6 +122,7 @@ int sys_socketpair_nt_dgram(int, int, int, int[2]) hidden;
121122
int sys_socketpair_nt(int, int, int, int[2]) hidden;
122123
int sys_select_nt(int, fd_set *, fd_set *, fd_set *, struct timeval *) hidden;
123124
int sys_shutdown_nt(struct Fd *, int) hidden;
125+
int sys_setsockopt_nt(struct Fd *, int, int, const void *, uint32_t) hidden;
124126

125127
size_t __iovec2nt(struct NtIovec[hasatleast 16], const struct iovec *,
126128
size_t) hidden;

0 commit comments

Comments
 (0)