@amritk/nish-x86_64-linux 0.17.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL.md +43 -25
- package/bin/nish +0 -0
- package/package.json +2 -2
- package/runtime/nish.d.ts +55 -5
- package/runtime/nish.h +231 -20
- package/runtime/nish.mjs +16 -0
- package/runtime/runtime-aes.c +490 -0
- package/runtime/runtime-cross.c +366 -0
- package/runtime/runtime-host.c +7 -6
- package/runtime/runtime-net.c +93 -34
- package/runtime/runtime-os.c +213 -15
- package/runtime/runtime-parallel.c +590 -107
- package/runtime/runtime-simd.c +203 -0
- package/runtime/runtime-wasm.c +16 -3
- package/runtime/runtime.c +89 -23
- package/runtime/shim.mjs +71 -1
- package/scripts/build.sh +225 -20
- package/std/README.md +86 -55
- package/std/bytes.ts +163 -0
- package/std/crypto/LICENSE-libsecp256k1 +19 -0
- package/std/crypto/aes.ts +238 -76
- package/std/crypto/chacha20poly1305.ts +43 -9
- package/std/crypto/ed25519.ts +840 -0
- package/std/crypto/hkdf.ts +304 -12
- package/std/crypto/hmac.ts +245 -53
- package/std/crypto/p256.ts +1376 -42
- package/std/crypto/x25519.ts +55 -37
- package/std/crypto/x509-random.ts +71 -0
- package/std/crypto/x509.ts +9 -9
- package/std/json-tokenizer.ts +1296 -0
- package/std/json-typed.ts +1014 -0
- package/std/json-value.ts +1163 -0
- package/std/json.ts +1412 -70
- package/std/net/buffer-pool.ts +151 -0
- package/std/net/hpack.ts +416 -112
- package/std/net/http-fields.ts +93 -0
- package/std/net/http1-server.ts +417 -55
- package/std/net/http1.ts +31 -6
- package/std/net/http2-tls.ts +112 -24
- package/std/net/http2.ts +333 -75
- package/std/net/http3-server.ts +231 -45
- package/std/net/http3.ts +187 -28
- package/std/net/prefork.ts +412 -0
- package/std/net/quic-conn-params.ts +86 -16
- package/std/net/quic-listener.ts +576 -219
- package/std/net/quic-packet.ts +443 -58
- package/std/net/quic-recovery.ts +361 -30
- package/std/net/quic-stream.ts +434 -28
- package/std/net/quic.ts +922 -206
- package/std/net/router.ts +676 -0
- package/std/net/tls/codec.ts +426 -27
- package/std/net/tls/record-server.ts +478 -132
- package/std/net/tls/record.ts +133 -42
- package/std/net/tls/schedule.ts +464 -55
- package/std/net/tls/ticket.ts +610 -0
- package/std/net/tls-tcp.ts +204 -57
- package/std/net/tls.ts +1132 -305
- package/std/net/url.ts +571 -0
- package/std/random.ts +94 -0
- package/std/text.ts +77 -0
- package/std/threads.ts +438 -14
package/runtime/runtime-os.c
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* wrapper raised the ceiling over the arena and the string functions as well,
|
|
12
12
|
* and the number a reader saw for "the runtime" moved for a reason that had
|
|
13
13
|
* nothing to do with it. Each file now carries its own measured ceiling in
|
|
14
|
-
* tests/
|
|
15
|
-
* rows and the reasoning.
|
|
14
|
+
* tests/runtime-budgets.js; docs/wp7-runtime.md §"Runtime additions and budget"
|
|
15
|
+
* has both rows and the reasoning.
|
|
16
16
|
*
|
|
17
17
|
* What a linked binary pays is unchanged: `-ffunction-sections
|
|
18
18
|
* -Wl,--gc-sections` drops every function no program calls, whichever
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
* compiling against the header a C host sees is what keeps a definition here
|
|
26
26
|
* from drifting from the contract it is published under.
|
|
27
27
|
*/
|
|
28
|
+
#if defined(__linux__) && !defined(_GNU_SOURCE)
|
|
29
|
+
#define _GNU_SOURCE /* `sched_getaffinity` and `CPU_COUNT`, for `nish_cpu_online` */
|
|
30
|
+
#endif
|
|
28
31
|
#define _POSIX_C_SOURCE 200809L
|
|
29
32
|
/* `realpath` is XSI rather than POSIX base, so _POSIX_C_SOURCE alone does not
|
|
30
33
|
declare it (measured on glibc 2.39: undeclared, and clang then infers
|
|
@@ -46,10 +49,14 @@
|
|
|
46
49
|
#include <unistd.h>
|
|
47
50
|
#ifndef __wasi__
|
|
48
51
|
/* WASI has no processes, so `spawn.h` and `wait.h` do not exist there. */
|
|
52
|
+
#include <signal.h>
|
|
49
53
|
#include <spawn.h>
|
|
50
54
|
#include <sys/wait.h>
|
|
51
55
|
extern char **environ;
|
|
52
56
|
#endif
|
|
57
|
+
#ifdef __linux__
|
|
58
|
+
#include <sched.h>
|
|
59
|
+
#endif
|
|
53
60
|
|
|
54
61
|
#include "nish.h"
|
|
55
62
|
|
|
@@ -73,6 +80,7 @@ extern char **environ;
|
|
|
73
80
|
/* ---- Process and files */
|
|
74
81
|
void nish_exit(int32_t code) { exit(code); }
|
|
75
82
|
static NISH_COLD void nish_io_fail(const char *what, const nish_str *path) {
|
|
83
|
+
nish_panic_claim();
|
|
76
84
|
dprintf(2, "nish: cannot %s%.*s\n", what, (int)path->len, path->data);
|
|
77
85
|
_exit(1);
|
|
78
86
|
}
|
|
@@ -215,18 +223,20 @@ _Bool nish_mkdir(const nish_str *path) {
|
|
|
215
223
|
value rather than through a child that has already run.
|
|
216
224
|
|
|
217
225
|
`out` and `err` are paths for the child's stdout and stderr, or NULL to let
|
|
218
|
-
it inherit this process's.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
226
|
+
it inherit this process's. Every spawn builtin starts its child here, so the
|
|
227
|
+
argv vector and the redirects are written once; it is `static`, so a program
|
|
228
|
+
that spawns nothing loses it to `--gc-sections`. The answer is the child's
|
|
229
|
+
pid, or a negative errno: -EINVAL for an empty argv or an argument holding a
|
|
230
|
+
NUL, and `posix_spawnp`'s own error otherwise (-ENOENT for a command that is
|
|
231
|
+
not on PATH, on glibc). */
|
|
232
|
+
static int32_t nish_spawn_start(const nish_array *argv, const nish_str *out, const nish_str *err) {
|
|
223
233
|
#ifdef __wasi__
|
|
224
234
|
(void)argv;
|
|
225
235
|
(void)out;
|
|
226
236
|
(void)err;
|
|
227
|
-
return -
|
|
237
|
+
return -ENOSYS;
|
|
228
238
|
#else
|
|
229
|
-
if (!argv->len) return -
|
|
239
|
+
if (!argv->len) return -EINVAL; /* argv[0] would read past an empty array */
|
|
230
240
|
nish_str **s = (nish_str **)argv->data;
|
|
231
241
|
/* NULL-terminated, borrowing each string's bytes; the arena owns it, so no
|
|
232
242
|
path out of here has anything to free. */
|
|
@@ -237,14 +247,15 @@ static int32_t nish_spawn_impl(const nish_array *argv, const nish_str *out, cons
|
|
|
237
247
|
the one whose C string is not its own bytes. */
|
|
238
248
|
for (; i < argv->len; i++) {
|
|
239
249
|
v[i] = (char *)nish_cpath(s[i]);
|
|
240
|
-
if (v[i] != s[i]->data) return -
|
|
250
|
+
if (v[i] != s[i]->data) return -EINVAL;
|
|
241
251
|
}
|
|
242
252
|
v[i] = 0;
|
|
243
253
|
/* One list of file actions whether or not a stream is redirected: an empty
|
|
244
254
|
list is the same spawn as none, and one path through is less code than
|
|
245
255
|
creating the list only when a stream needs it. */
|
|
246
256
|
posix_spawn_file_actions_t fa;
|
|
247
|
-
|
|
257
|
+
int failed = posix_spawn_file_actions_init(&fa);
|
|
258
|
+
if (failed) return -failed;
|
|
248
259
|
const nish_str *to[2] = { out, err };
|
|
249
260
|
for (int k = 0; k < 2; k++) {
|
|
250
261
|
/* The child opens the file, not this process: a redirect that this process
|
|
@@ -253,12 +264,31 @@ static int32_t nish_spawn_impl(const nish_array *argv, const nish_str *out, cons
|
|
|
253
264
|
if (to[k]) posix_spawn_file_actions_addopen(&fa, k + 1, nish_cpath(to[k]), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644);
|
|
254
265
|
}
|
|
255
266
|
pid_t pid;
|
|
256
|
-
|
|
257
|
-
int failed = posix_spawnp(&pid, v[0], &fa, 0, v, environ);
|
|
267
|
+
failed = posix_spawnp(&pid, v[0], &fa, 0, v, environ);
|
|
258
268
|
posix_spawn_file_actions_destroy(&fa);
|
|
259
|
-
|
|
260
|
-
|
|
269
|
+
return failed ? -failed : (int32_t)pid;
|
|
270
|
+
#endif
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
#ifndef __wasi__
|
|
274
|
+
/* A wait status in spawnSync's convention: the exit code, or 128 plus the
|
|
275
|
+
signal that ended the child, as a shell reports it. */
|
|
276
|
+
static int32_t nish_exit_code(int status) {
|
|
261
277
|
return WIFSIGNALED(status) ? 128 + WTERMSIG(status) : WEXITSTATUS(status);
|
|
278
|
+
}
|
|
279
|
+
#endif
|
|
280
|
+
|
|
281
|
+
/* The two blocking builtins: start, then wait for that one child. A failed
|
|
282
|
+
start is -1, the answer they have always given. */
|
|
283
|
+
static int32_t nish_spawn_impl(const nish_array *argv, const nish_str *out, const nish_str *err) {
|
|
284
|
+
int32_t pid = nish_spawn_start(argv, out, err);
|
|
285
|
+
if (pid < 0) return -1;
|
|
286
|
+
#ifdef __wasi__
|
|
287
|
+
return -1;
|
|
288
|
+
#else
|
|
289
|
+
int status;
|
|
290
|
+
if (waitpid(pid, &status, 0) < 0) return -1;
|
|
291
|
+
return nish_exit_code(status);
|
|
262
292
|
#endif
|
|
263
293
|
}
|
|
264
294
|
|
|
@@ -279,6 +309,174 @@ int32_t nish_spawn_to(const nish_array *argv, const nish_str *out, const nish_st
|
|
|
279
309
|
return nish_spawn_impl(argv, out->len ? out : 0, err->len ? err : 0);
|
|
280
310
|
}
|
|
281
311
|
|
|
312
|
+
/* `spawn(argv)`: the child's pid, or a negative errno, without waiting. The
|
|
313
|
+
child is this process's until a `waitPid` reaps it; one never reaped stays
|
|
314
|
+
a zombie until the program exits, as it would in C. */
|
|
315
|
+
int32_t nish_spawn_child(const nish_array *argv) { return nish_spawn_start(argv, 0, 0); }
|
|
316
|
+
|
|
317
|
+
/* `kill(pid, sig)`: 0, or a negative errno. Only the four signals a program
|
|
318
|
+
asks a child to stop or report with are sent -- 0 (is it there), SIGINT,
|
|
319
|
+
SIGKILL and SIGTERM, whose numbers are the same on Linux and Darwin -- and
|
|
320
|
+
any other is -EINVAL, so the number a program writes means the same signal
|
|
321
|
+
on every host. A pid of 0 or below is -EINVAL too: `kill(2)` reads those as
|
|
322
|
+
a process group or every process the user owns, which is not one process.
|
|
323
|
+
A positive pid may be any process of this uid, not only a child: the same
|
|
324
|
+
reach `kill(1)` has (docs/security/runtime.md, RT-19). */
|
|
325
|
+
int32_t nish_kill(int32_t pid, int32_t sig) {
|
|
326
|
+
#ifdef __wasi__
|
|
327
|
+
(void)pid;
|
|
328
|
+
(void)sig;
|
|
329
|
+
return -ENOSYS;
|
|
330
|
+
#else
|
|
331
|
+
if (pid <= 0 || !(sig == 0 || sig == SIGINT || sig == SIGKILL || sig == SIGTERM)) return -EINVAL;
|
|
332
|
+
return kill(pid, sig) == 0 ? 0 : -errno;
|
|
333
|
+
#endif
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* `waitPid(pid, flags)`: `[pid, status]`, a fresh two-element `i32[]`. `pid`
|
|
337
|
+
is a child's or -1 for any child; flag 1 is WNOHANG, and any other bit, like
|
|
338
|
+
a pid of 0 or below -1 (a process group), is -EINVAL. The status is
|
|
339
|
+
spawnSync's: the exit code, or 128 plus the signal. `[0, 0]` is a WNOHANG
|
|
340
|
+
wait with a child left and none exited, and a failure is `[-errno, 0]`:
|
|
341
|
+
-ECHILD with no child to wait for, -EINTR when a signal interrupted it. */
|
|
342
|
+
nish_array *nish_wait_pid(int32_t pid, int32_t flags) {
|
|
343
|
+
nish_array *out = nish_alloc_array(sizeof(int32_t), 2);
|
|
344
|
+
int32_t *w = (int32_t *)out->data;
|
|
345
|
+
w[1] = 0;
|
|
346
|
+
#ifdef __wasi__
|
|
347
|
+
(void)pid;
|
|
348
|
+
(void)flags;
|
|
349
|
+
w[0] = -ENOSYS;
|
|
350
|
+
#else
|
|
351
|
+
if ((pid <= 0 && pid != -1) || (flags & ~1)) {
|
|
352
|
+
w[0] = -EINVAL;
|
|
353
|
+
return out;
|
|
354
|
+
}
|
|
355
|
+
int status = 0;
|
|
356
|
+
pid_t got = waitpid(pid, &status, flags ? WNOHANG : 0);
|
|
357
|
+
w[0] = got < 0 ? -errno : (int32_t)got;
|
|
358
|
+
if (got > 0) w[1] = nish_exit_code(status);
|
|
359
|
+
#endif
|
|
360
|
+
return out;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
#ifdef __linux__
|
|
364
|
+
/* Reads up to `cap - 1` bytes of a small file into `buf`, terminated; 0 bytes
|
|
365
|
+
* when it cannot be opened. */
|
|
366
|
+
static void nish_read_small(const char *path, char *buf, size_t cap) {
|
|
367
|
+
ssize_t got = -1;
|
|
368
|
+
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
|
369
|
+
if (fd >= 0) {
|
|
370
|
+
got = read(fd, buf, cap - 1);
|
|
371
|
+
close(fd);
|
|
372
|
+
}
|
|
373
|
+
buf[got > 0 ? got : 0] = 0;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* The cgroup v2 CPU quota of this process and of every cgroup above it, as a
|
|
377
|
+
* whole number of CPUs rounded up, and `n` when it is lower or there is none.
|
|
378
|
+
* A container limited to 1.5 CPUs on a 64-core host is what this is for: its
|
|
379
|
+
* affinity mask still names all 64, and 63 workers time-sliced into a budget
|
|
380
|
+
* of 1.5 cores is the slowest way there is to run a loop. `cpu.max` reads
|
|
381
|
+
* `<quota> <period>`, or `max <period>` when there is no limit, which the
|
|
382
|
+
* digit test leaves out. The path is the `0::` line of /proc/self/cgroup under
|
|
383
|
+
* /sys/fs/cgroup, where a v2-only system and a container mount the hierarchy;
|
|
384
|
+
* a v1 system has no `cpu.max` there and keeps `n`. */
|
|
385
|
+
static int64_t nish_cgroup_cpus(int64_t n) {
|
|
386
|
+
char text[512];
|
|
387
|
+
char path[512] = "/sys/fs/cgroup";
|
|
388
|
+
nish_read_small("/proc/self/cgroup", text, sizeof text);
|
|
389
|
+
char *at = strstr(text, "0::/");
|
|
390
|
+
if (!at || (at != text && at[-1] != '\n')) return n;
|
|
391
|
+
size_t len = strcspn(at + 3, "\n");
|
|
392
|
+
if (len > sizeof path - 24) return n;
|
|
393
|
+
memcpy(path + 14, at + 3, len);
|
|
394
|
+
for (;;) {
|
|
395
|
+
memcpy(path + 14 + len, "/cpu.max", 9);
|
|
396
|
+
nish_read_small(path, text, sizeof text);
|
|
397
|
+
if (text[0] >= '1' && text[0] <= '9') {
|
|
398
|
+
char *rest;
|
|
399
|
+
int64_t quota = strtoll(text, &rest, 10);
|
|
400
|
+
int64_t period = strtoll(rest, 0, 10);
|
|
401
|
+
if (period > 0) {
|
|
402
|
+
int64_t cpus = quota / period + (quota % period != 0);
|
|
403
|
+
if (cpus < n) n = cpus;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/* Up one level, by dropping the last component, until the root is read. */
|
|
407
|
+
if (len <= 1) return n;
|
|
408
|
+
while (len > 1 && path[14 + len - 1] != '/') len--;
|
|
409
|
+
if (len > 1) len--;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
#endif
|
|
413
|
+
|
|
414
|
+
#ifdef __APPLE__
|
|
415
|
+
/* <sys/sysctl.h> does not compile at the strict feature level this file
|
|
416
|
+
selects, so the one function is declared by hand, as Darwin's libc has it. */
|
|
417
|
+
int sysctlbyname(const char *, void *, size_t *, void *, size_t);
|
|
418
|
+
#endif
|
|
419
|
+
|
|
420
|
+
/* The CPUs this program may use: the smallest of the CPUs online, the ones
|
|
421
|
+
this thread may run on (`taskset`, a container's cpuset), the cgroup quota,
|
|
422
|
+
and `NISH_NUM_THREADS` when it is a positive number. The variable can lower
|
|
423
|
+
the count and never raise it, so it caps a program's threads (a CI job
|
|
424
|
+
sharing its machine, a benchmark pinned to one core) without ever putting
|
|
425
|
+
more of them on the machine than it has. At least 1, and 1 on WASI.
|
|
426
|
+
|
|
427
|
+
It lives here rather than in runtime-parallel.c because two builtins read
|
|
428
|
+
it: `availableParallelism`, in every build, and `nish_cpu_count`, which
|
|
429
|
+
divides regions by it under `--threads` and answers 1 without, as a
|
|
430
|
+
sequential build divides nothing. Uncached: runtime-parallel.c caches its
|
|
431
|
+
own answer, and a program asking how many workers to start asks once. */
|
|
432
|
+
int64_t nish_cpu_online(void) {
|
|
433
|
+
#ifdef __wasi__
|
|
434
|
+
return 1;
|
|
435
|
+
#else
|
|
436
|
+
#ifdef __APPLE__
|
|
437
|
+
/* `_SC_NPROCESSORS_ONLN` is hidden on Darwin at this file's feature level. */
|
|
438
|
+
int got = 0;
|
|
439
|
+
size_t size = sizeof got;
|
|
440
|
+
if (sysctlbyname("hw.activecpu", &got, &size, 0, 0) != 0) got = 0;
|
|
441
|
+
#else
|
|
442
|
+
long got = sysconf(_SC_NPROCESSORS_ONLN);
|
|
443
|
+
#endif
|
|
444
|
+
int64_t n = got > 0 ? (int64_t)got : 1;
|
|
445
|
+
#ifdef __linux__
|
|
446
|
+
cpu_set_t set;
|
|
447
|
+
if (sched_getaffinity(0, sizeof set, &set) == 0) {
|
|
448
|
+
int mine = CPU_COUNT(&set);
|
|
449
|
+
if (mine > 0 && mine < n) n = mine;
|
|
450
|
+
}
|
|
451
|
+
n = nish_cgroup_cpus(n);
|
|
452
|
+
#endif
|
|
453
|
+
const char *env = getenv("NISH_NUM_THREADS");
|
|
454
|
+
if (env) {
|
|
455
|
+
char *end;
|
|
456
|
+
long long want = strtoll(env, &end, 10);
|
|
457
|
+
if (end != env && *end == 0 && want > 0 && want < n) n = (int64_t)want;
|
|
458
|
+
}
|
|
459
|
+
return n;
|
|
460
|
+
#endif
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/* `totalMemory()`: bytes of physical memory, or -1 when the host does not say.
|
|
464
|
+
The machine's, not a cgroup's limit: what a container may use is a policy
|
|
465
|
+
the program reads elsewhere. */
|
|
466
|
+
int64_t nish_total_memory(void) {
|
|
467
|
+
#if defined(__APPLE__)
|
|
468
|
+
uint64_t bytes = 0;
|
|
469
|
+
size_t size = sizeof bytes;
|
|
470
|
+
return sysctlbyname("hw.memsize", &bytes, &size, 0, 0) == 0 ? (int64_t)bytes : -1;
|
|
471
|
+
#elif defined(__linux__)
|
|
472
|
+
long pages = sysconf(_SC_PHYS_PAGES);
|
|
473
|
+
long page = sysconf(_SC_PAGESIZE);
|
|
474
|
+
return pages > 0 && page > 0 ? (int64_t)pages * page : -1;
|
|
475
|
+
#else
|
|
476
|
+
return -1;
|
|
477
|
+
#endif
|
|
478
|
+
}
|
|
479
|
+
|
|
282
480
|
/* `readdirSync(path)`: the listing a driver needs to discover its own inputs.
|
|
283
481
|
|
|
284
482
|
**Sorted by bytes**, which `readdir(3)` and Node are not, for two reasons
|