@amritk/nish-x86_64-linux 0.24.0 → 0.25.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/bin/nish +0 -0
- package/docs/AI.md +24 -4
- package/package.json +1 -1
- package/runtime/browser.mjs +4 -3
- package/runtime/core/host.d.ts +8 -0
- package/runtime/core/host.mjs +50 -2
- package/runtime/nish.d.ts +100 -0
- package/runtime/nish.h +34 -0
- package/runtime/nish.mjs +21 -0
- package/runtime/node.mjs +57 -6
- package/runtime/runtime-net.c +44 -3
- package/runtime/runtime-os.c +115 -12
- package/scripts/build.sh +13 -3
- package/std/README.md +25 -8
- package/std/cli-args.ts +1216 -0
- package/std/cli-flags.ts +585 -0
- package/std/cli.ts +308 -19
- package/std/compress/checksum.ts +132 -20
- package/std/compress/deflate.ts +638 -248
- package/std/compress/gzip.ts +20 -6
- package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
- package/std/crypto/p256.ts +63 -1
- package/std/crypto/p384.ts +6044 -0
- package/std/crypto/pki-roots.ts +322 -0
- package/std/crypto/pki.ts +1207 -0
- package/std/crypto/rsa.ts +663 -0
- package/std/crypto/x509.ts +1054 -58
- package/std/net/dns-config.ts +734 -0
- package/std/net/dns-message.ts +517 -0
- package/std/net/dns-resolver.ts +1015 -0
- package/std/net/dns.ts +478 -0
- package/std/net/http1-client.ts +1227 -0
- package/std/net/http1-server.ts +7 -2
- package/std/net/http1.ts +9 -9
- package/std/net/tls/client.ts +1094 -0
- package/std/net/tls/codec.ts +585 -3
- package/std/net/tls/record-client.ts +1192 -0
- package/std/net/tls/verify.ts +221 -0
- package/std/net/tls-tcp.ts +642 -11
- package/std/net/tls.ts +2 -2
- package/std/net/url.ts +559 -4
- package/std/yaml-write.ts +415 -0
package/bin/nish
CHANGED
|
Binary file
|
package/docs/AI.md
CHANGED
|
@@ -2133,13 +2133,20 @@ a `\u` escape `nish/json` decodes) is one unit per byte, and counts its bytes.
|
|
|
2133
2133
|
**Files and the system.** `readFileSync(path)` (exits 1 on failure),
|
|
2134
2134
|
`readFileSyncOrNull(path)` (`string | null`), `readFileBytesSync(path)`
|
|
2135
2135
|
(`u8[] | null`, the bytes as they are on disk), `writeFileSync(path, data)`,
|
|
2136
|
-
`appendFileSync(path, data)`, `
|
|
2136
|
+
`appendFileSync(path, data)`, `writeFileBytesSync(path, bytes)` (a `u8[]` as
|
|
2137
|
+
it is; `i32`: the length, or a negative errno), `renameSync(from, to)` (`0` or
|
|
2138
|
+
a negative errno; atomic on one filesystem, `-18` across two, so write the
|
|
2139
|
+
temporary beside the target), `unlinkSync(path)` (`0` or a negative errno; a
|
|
2140
|
+
directory is refused), `writeBytes(bytes, off, len)` (a `u8[]` range to stdout
|
|
2141
|
+
as it is, the length or a negative errno; the range panics like
|
|
2142
|
+
`netWrite`'s), `mkdirSync(path)` (one level, `boolean`),
|
|
2137
2143
|
`isDirectorySync(path)`, `readdirSync(path)` (`string[] | null`, sorted by
|
|
2138
2144
|
bytes, no `.`/`..`), `spawnSync(argv)`, `spawnSyncTo(argv, outPath, errPath)`,
|
|
2139
2145
|
`getenv(name)` (`string | null` — unset and empty are different answers),
|
|
2140
2146
|
`realpathSync(path)` (`string | null`; symbolic links resolved, absolute, and
|
|
2141
2147
|
`null` when it does not resolve),
|
|
2142
|
-
`monotonicNanos()` (`i64`; elapsed time only),
|
|
2148
|
+
`monotonicNanos()` (`i64`; elapsed time only), `wallClockNanos()` (`i64`,
|
|
2149
|
+
nanoseconds since the Unix epoch; the wall clock, which can step), and, for checking a path before
|
|
2143
2150
|
trusting it, `lstatOwnerModeSync(path)` (an `i64`: the owner's uid in the high
|
|
2144
2151
|
32 bits, `st_mode` in the low 32, `-1` when it does not resolve; a symbolic
|
|
2145
2152
|
link answers for itself), `geteuid()` (`i64`) and `isExecutableSync(path)`
|
|
@@ -2167,6 +2174,12 @@ also watches the signal fd. None of `spawn`, `kill`, `waitPid` may be called in
|
|
|
2167
2174
|
a task or a parallel body; the two host facts may. `availableParallelism()` (`i32`, at least 1, no `--threads` needed) and
|
|
2168
2175
|
`totalMemory()` (`i64` bytes, or `-1`) size a program to the machine. None of
|
|
2169
2176
|
the five exists on a wasm target, and `spawn` and `waitPid` throw under Node.
|
|
2177
|
+
`setNice(n)` sets the nice value (clamped to -20..19; lowering it unprivileged
|
|
2178
|
+
is `-13`) and `setIdlePriority()` moves the program to Linux's `SCHED_IDLE`
|
|
2179
|
+
(`-95` on Darwin); each answers 0 or a negative errno. Call them at the top of
|
|
2180
|
+
`main`, never in a task. An unprivileged process can never leave `SCHED_IDLE`
|
|
2181
|
+
and an idle one can starve on a saturated machine, so keep a normal-priority
|
|
2182
|
+
fallback. Neither exists on wasm; both throw under Node.
|
|
2170
2183
|
|
|
2171
2184
|
**Sockets** (`nish:net`, or the same names as globals). `tcpListen(host, port,
|
|
2172
2185
|
backlog)`, `tcpAccept(fd, peer)`, `netRead(fd, buf, off, len)`,
|
|
@@ -2179,7 +2192,8 @@ and there is no `async` and no callback: your `main` is the loop. A client calls
|
|
|
2179
2192
|
`tcpConnect(addr)`, which answers the descriptor while it is still connecting;
|
|
2180
2193
|
wait for it to be writable, then `connectResult(fd)` is 0 or the failure. Buffers
|
|
2181
2194
|
are `u8[]` only; an address is 18 bytes of one (IPv4 as `::ffff:a.b.c.d`, then
|
|
2182
|
-
the port big-endian); hosts are numeric literals,
|
|
2195
|
+
the port big-endian); hosts are numeric literals; to reach a name, resolve it first with
|
|
2196
|
+
`nish/net/dns` (`DnsClient` on your loop, or the blocking `resolveHost`).
|
|
2183
2197
|
`netRead` / `netWrite` panic on a range outside the buffer.
|
|
2184
2198
|
`tcpListenFlags(host, port, backlog, flags)` is `tcpListen` with flag 1
|
|
2185
2199
|
`SO_REUSEPORT`, so several listeners, in one process or many, share a port (Linux
|
|
@@ -2198,7 +2212,13 @@ loop, `pollAdd(loop, fd, events, token)` / `pollModify(...)` watch `fd` for
|
|
|
2198
2212
|
for `-1`), fills `ready: i32[]` with pairs — `ready[2k]` a token,
|
|
2199
2213
|
`ready[2k + 1]` its events, 4 added for a hang-up or error — and answers how
|
|
2200
2214
|
many, **0 on a timeout or a signal**. It is level-triggered, so what you leave
|
|
2201
|
-
unread is reported again. Add `signalFd()` to the loop to wake on SIGTERM.
|
|
2215
|
+
unread is reported again. Add `signalFd()` to the loop to wake on SIGTERM.
|
|
2216
|
+
`netSetOption(fd, option, value)` sets one socket option from a whitelist:
|
|
2217
|
+
`NET_TCP_NODELAY`, `NET_TCP_QUICKACK` (Linux; `-95` on Darwin),
|
|
2218
|
+
`NET_SO_KEEPALIVE`, `NET_SO_SNDBUF` and `NET_SO_RCVBUF`, `i32` ids exported
|
|
2219
|
+
beside it; any other id is `-22`. Linux drops quick-ACK mode on its own, so set
|
|
2220
|
+
it again after each response. The ids are not module constants: pass them
|
|
2221
|
+
directly. None
|
|
2202
2222
|
exists on a wasm target, and under Node each throws.
|
|
2203
2223
|
|
|
2204
2224
|
**Arena.** `Arena.mark()`, `Arena.used()` and `using a = arena()` — see below.
|
package/package.json
CHANGED
package/runtime/browser.mjs
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* - `exit(code)` throws a `NishExit` carrying the code, since a page cannot
|
|
11
11
|
* end itself; the caller that started the program catches it, and does
|
|
12
12
|
* not call the module again (docs/security/runtime-browser.md, BROWSER-2).
|
|
13
|
-
* - `monotonicNanos` reads `performance.now()`, `
|
|
14
|
-
* `
|
|
15
|
-
* is the program alone.
|
|
13
|
+
* - `monotonicNanos` reads `performance.now()`, `wallClockNanos` `Date.now()`,
|
|
14
|
+
* `getenv` answers `null`, `isTerminal` answers `false` and `terminalSize`
|
|
15
|
+
* `[-25, 0]`, and `argv()` is the program alone.
|
|
16
16
|
* - Every file-system, process and machine call panics, naming itself and
|
|
17
17
|
* the browser.
|
|
18
18
|
*/
|
|
@@ -69,6 +69,7 @@ installHost({
|
|
|
69
69
|
call(name) {
|
|
70
70
|
if (name === "getenv") return null;
|
|
71
71
|
if (name === "monotonicNanos") return BigInt(Math.round(performance.now() * 1e6));
|
|
72
|
+
if (name === "wallClockNanos") return BigInt(Date.now()) * 1000000n;
|
|
72
73
|
// A page has no terminal, which `nish:term` answers as a pipe does.
|
|
73
74
|
if (name === "isTerminal") return false;
|
|
74
75
|
if (name === "terminalSize") return [-25, 0];
|
package/runtime/core/host.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare function log(x: unknown): void;
|
|
|
35
35
|
export declare function error(x: unknown): void;
|
|
36
36
|
export declare function write(s: string): void;
|
|
37
37
|
export declare function writeError(s: string): void;
|
|
38
|
+
export declare function writeBytes(bytes: readonly u8[], off: i32, len: i32): i32;
|
|
38
39
|
export declare function panic(message: string): never;
|
|
39
40
|
export declare function exit(code: i32): never;
|
|
40
41
|
export declare function argv(): string[];
|
|
@@ -43,6 +44,9 @@ export declare function readFileSyncOrNull(path: string): string | null;
|
|
|
43
44
|
export declare function readFileBytesSync(path: string): u8[] | null;
|
|
44
45
|
export declare function writeFileSync(path: string, data: string): void;
|
|
45
46
|
export declare function appendFileSync(path: string, data: string): void;
|
|
47
|
+
export declare function writeFileBytesSync(path: string, bytes: readonly u8[]): i32;
|
|
48
|
+
export declare function renameSync(from: string, to: string): i32;
|
|
49
|
+
export declare function unlinkSync(path: string): i32;
|
|
46
50
|
export declare function mkdirSync(path: string): boolean;
|
|
47
51
|
export declare function isDirectorySync(path: string): boolean;
|
|
48
52
|
export declare function readdirSync(path: string): string[] | null;
|
|
@@ -56,6 +60,7 @@ export declare function kill(pid: i32, sig: i32): i32;
|
|
|
56
60
|
export declare function availableParallelism(): i32;
|
|
57
61
|
export declare function totalMemory(): i64;
|
|
58
62
|
export declare function monotonicNanos(): i64;
|
|
63
|
+
export declare function wallClockNanos(): i64;
|
|
59
64
|
export declare function statMtimeSync(path: string): f64;
|
|
60
65
|
export declare function lstatOwnerModeSync(path: string): i64;
|
|
61
66
|
export declare function geteuid(): i64;
|
|
@@ -69,6 +74,9 @@ export declare function spawn(argv: string[]): i32;
|
|
|
69
74
|
export declare function waitPid(pid: i32, flags: i32): i32[];
|
|
70
75
|
export declare function signalFd(): i32;
|
|
71
76
|
export declare function readSignal(fd: i32): i32;
|
|
77
|
+
/** These two have no reading under Node: each throws, naming itself. */
|
|
78
|
+
export declare function setNice(n: i32): i32;
|
|
79
|
+
export declare function setIdlePriority(): i32;
|
|
72
80
|
/** What each `nish:net` function is under the core: a throw naming `name`. */
|
|
73
81
|
export declare function noNetReading(name: string): never;
|
|
74
82
|
/** What `readStdin`, `waitStdin`, `setRawMode` and `setTerminalRestore` are under the core: a throw naming `name`. */
|
package/runtime/core/host.mjs
CHANGED
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
* its own fails loudly rather than writing nowhere.
|
|
16
16
|
*
|
|
17
17
|
* The functions that have no synchronous reading under Node (`spawn`,
|
|
18
|
-
* `waitPid`, `signalFd`, `readSignal`, the `nish:net` set)
|
|
19
|
-
* asking any
|
|
18
|
+
* `waitPid`, `signalFd`, `readSignal`, the `nish:net` set), and `setNice` and
|
|
19
|
+
* `setIdlePriority`, which have none at all, throw here without asking any
|
|
20
|
+
* host.
|
|
20
21
|
*/
|
|
22
|
+
import { panicSlice, toIndex } from "./checks.mjs";
|
|
21
23
|
|
|
22
24
|
/** What every member of the default host throws: no adapter has been imported. */
|
|
23
25
|
export class NishNoHost extends Error {
|
|
@@ -86,6 +88,18 @@ export function writeError(s) {
|
|
|
86
88
|
host.writeErr(s);
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
/**
|
|
92
|
+
* `writeBytes(bytes, off, len)`: the range checked as the compiled call checks
|
|
93
|
+
* it, `0 <= off <= off + len <= bytes.length`, through the slice panic, then
|
|
94
|
+
* the host's write of those bytes to stdout.
|
|
95
|
+
*/
|
|
96
|
+
export function writeBytes(bytes, off, len) {
|
|
97
|
+
const from = toIndex(off);
|
|
98
|
+
const to = from + toIndex(len);
|
|
99
|
+
if (!(from >= 0 && from <= to && to <= bytes.length)) panicSlice(from, to, bytes.length);
|
|
100
|
+
return host.call("writeBytes", [bytes, from, to]);
|
|
101
|
+
}
|
|
102
|
+
|
|
89
103
|
/** `panic(message)`: the message on stderr, then exit 1. */
|
|
90
104
|
export function panic(message) {
|
|
91
105
|
host.writeErr(`${message}\n`);
|
|
@@ -131,6 +145,18 @@ export function appendFileSync(path, data) {
|
|
|
131
145
|
return host.call("appendFileSync", [path, data]);
|
|
132
146
|
}
|
|
133
147
|
|
|
148
|
+
export function writeFileBytesSync(path, bytes) {
|
|
149
|
+
return host.call("writeFileBytesSync", [path, bytes]);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function renameSync(from, to) {
|
|
153
|
+
return host.call("renameSync", [from, to]);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function unlinkSync(path) {
|
|
157
|
+
return host.call("unlinkSync", [path]);
|
|
158
|
+
}
|
|
159
|
+
|
|
134
160
|
export function mkdirSync(path) {
|
|
135
161
|
return host.call("mkdirSync", [path]);
|
|
136
162
|
}
|
|
@@ -183,6 +209,10 @@ export function monotonicNanos() {
|
|
|
183
209
|
return host.call("monotonicNanos", []);
|
|
184
210
|
}
|
|
185
211
|
|
|
212
|
+
export function wallClockNanos() {
|
|
213
|
+
return host.call("wallClockNanos", []);
|
|
214
|
+
}
|
|
215
|
+
|
|
186
216
|
export function statMtimeSync(path) {
|
|
187
217
|
return host.call("statMtimeSync", [path]);
|
|
188
218
|
}
|
|
@@ -259,6 +289,24 @@ export function waitPid() {
|
|
|
259
289
|
return noProcessReading("waitPid");
|
|
260
290
|
}
|
|
261
291
|
|
|
292
|
+
/**
|
|
293
|
+
* `setNice(n)` and `setIdlePriority()` have no reading under Node: Node has one
|
|
294
|
+
* of the nice value (`os.setPriority`) but none of `SCHED_IDLE`, and a reading
|
|
295
|
+
* of one without the other would differ from the native build on the same host.
|
|
296
|
+
* Each throws, naming itself, as `runtime/nish.mjs` does.
|
|
297
|
+
*/
|
|
298
|
+
function noSchedulingReading(name) {
|
|
299
|
+
throw new Error(`\`${name}\` has no reading under Node: it changes how the kernel schedules the native program`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function setNice() {
|
|
303
|
+
return noSchedulingReading("setNice");
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function setIdlePriority() {
|
|
307
|
+
return noSchedulingReading("setIdlePriority");
|
|
308
|
+
}
|
|
309
|
+
|
|
262
310
|
/**
|
|
263
311
|
* `signalFd()` and `readSignal(fd)` have no faithful reading under Node, and
|
|
264
312
|
* these say so rather than answer something else. Node delivers a signal to
|
package/runtime/nish.d.ts
CHANGED
|
@@ -227,6 +227,12 @@ declare function readCString(p: CPtr): string;
|
|
|
227
227
|
declare function write(s: string): void;
|
|
228
228
|
/** `s` to stderr, likewise. */
|
|
229
229
|
declare function writeError(s: string): void;
|
|
230
|
+
/**
|
|
231
|
+
* `bytes[off .. off + len)` to stdout, every byte as it is, partial writes and
|
|
232
|
+
* interruptions retried: the count, or a negative errno. A range outside
|
|
233
|
+
* `bytes` panics, as `dst.set` does. The shape is `netWrite`'s.
|
|
234
|
+
*/
|
|
235
|
+
declare function writeBytes(bytes: readonly u8[], off: i32, len: i32): i32;
|
|
230
236
|
/**
|
|
231
237
|
* `message` and a newline to stderr, then exit 1. Terminates control flow, so
|
|
232
238
|
* it is `never`: that is what lets `tsc` agree that a function ending in a
|
|
@@ -257,6 +263,19 @@ declare function readFileBytesSync(path: string): u8[] | null;
|
|
|
257
263
|
declare function writeFileSync(path: string, data: string): void;
|
|
258
264
|
/** `data` added at the end of the file, with the same refusal of a symbolic link. */
|
|
259
265
|
declare function appendFileSync(path: string, data: string): void;
|
|
266
|
+
/**
|
|
267
|
+
* The file's contents replaced by `bytes`, created at 0644 when missing, with
|
|
268
|
+
* the same refusal of a symbolic link: the byte count, or a negative errno.
|
|
269
|
+
* Never an exit.
|
|
270
|
+
*/
|
|
271
|
+
declare function writeFileBytesSync(path: string, bytes: readonly u8[]): i32;
|
|
272
|
+
/**
|
|
273
|
+
* `from` moved to `to`, replacing a file there in one step on one file system:
|
|
274
|
+
* 0, or a negative errno (-18 across file systems, -2 for a missing `from`).
|
|
275
|
+
*/
|
|
276
|
+
declare function renameSync(from: string, to: string): i32;
|
|
277
|
+
/** The name `path` removed: 0, or a negative errno. A directory is refused, not removed. */
|
|
278
|
+
declare function unlinkSync(path: string): i32;
|
|
260
279
|
/** One directory, not recursive; whether a directory is there afterwards. */
|
|
261
280
|
declare function mkdirSync(path: string): boolean;
|
|
262
281
|
/** Whether a directory is at `path` right now. One `stat`, and never an exit. */
|
|
@@ -302,6 +321,21 @@ declare function waitPid(pid: i32, flags: i32): i32[];
|
|
|
302
321
|
declare function availableParallelism(): i32;
|
|
303
322
|
/** Bytes of physical memory, or -1 when the host does not say. */
|
|
304
323
|
declare function totalMemory(): i64;
|
|
324
|
+
/**
|
|
325
|
+
* Set this process's nice value to `n`: 0, or a negative errno. The kernel
|
|
326
|
+
* clamps `n` to -20..19, and lowering it below the current value needs the
|
|
327
|
+
* privilege to (-13 without it on Linux). Children started afterwards inherit
|
|
328
|
+
* it. Throws under Node.
|
|
329
|
+
*/
|
|
330
|
+
declare function setNice(n: i32): i32;
|
|
331
|
+
/**
|
|
332
|
+
* Move this process to `SCHED_IDLE`, which runs it only when nothing else wants
|
|
333
|
+
* the CPU: 0, or a negative errno, and -95 where the host has no such policy
|
|
334
|
+
* (Darwin). An unprivileged process can never leave `SCHED_IDLE` again, and an
|
|
335
|
+
* idle process can starve on a saturated machine, so a caller keeps a
|
|
336
|
+
* normal-priority fallback for work that must finish. Throws under Node.
|
|
337
|
+
*/
|
|
338
|
+
declare function setIdlePriority(): i32;
|
|
305
339
|
/** One environment variable, or `null` when it is unset (an empty value is a set variable). */
|
|
306
340
|
declare function getenv(name: string): string | null;
|
|
307
341
|
/**
|
|
@@ -309,6 +343,11 @@ declare function getenv(name: string): string | null;
|
|
|
309
343
|
* origin is arbitrary, so only the difference between two reads is meaningful.
|
|
310
344
|
*/
|
|
311
345
|
declare function monotonicNanos(): i64;
|
|
346
|
+
/**
|
|
347
|
+
* The wall clock in nanoseconds since the Unix epoch, for dating an event. It
|
|
348
|
+
* steps when the system time is set, so a duration is `monotonicNanos`'s.
|
|
349
|
+
*/
|
|
350
|
+
declare function wallClockNanos(): i64;
|
|
312
351
|
/**
|
|
313
352
|
* The modification time of `path` in milliseconds since the epoch, with the
|
|
314
353
|
* sub-millisecond fraction the file system keeps (Node's `mtimeMs`), or NaN
|
|
@@ -381,6 +420,26 @@ declare function pollModify(loop: i32, fd: i32, events: i32, token: i32): i32;
|
|
|
381
420
|
declare function pollRemove(loop: i32, fd: i32): i32;
|
|
382
421
|
/** Wait up to `timeoutMs` (negative: forever); pairs `ready[2k]` token, `ready[2k + 1]` events (4: hang-up or error). */
|
|
383
422
|
declare function pollWait(loop: i32, ready: i32[], timeoutMs: i32): i32;
|
|
423
|
+
/**
|
|
424
|
+
* Set one socket option from a whitelist: `option` is one of the `NET_*` ids
|
|
425
|
+
* below, and any other is -22; there is no raw level and option number. 0, or
|
|
426
|
+
* a negative errno (-9 a closed descriptor, -88 not a socket).
|
|
427
|
+
*/
|
|
428
|
+
declare function netSetOption(fd: i32, option: i32, value: i32): i32;
|
|
429
|
+
/** `TCP_NODELAY`: `value` 1 sends a small write at once, 0 lets Nagle batch it. */
|
|
430
|
+
declare const NET_TCP_NODELAY: i32;
|
|
431
|
+
/**
|
|
432
|
+
* `TCP_QUICKACK` (Linux; -95 elsewhere): `value` 1 acknowledges at once, 0
|
|
433
|
+
* delays. Linux does not keep quick-ACK mode: the kernel leaves it on its own,
|
|
434
|
+
* so a server that wants a mode sets it again after every response.
|
|
435
|
+
*/
|
|
436
|
+
declare const NET_TCP_QUICKACK: i32;
|
|
437
|
+
/** `SO_KEEPALIVE`: `value` 1 probes an idle connection, 0 does not. */
|
|
438
|
+
declare const NET_SO_KEEPALIVE: i32;
|
|
439
|
+
/** `SO_SNDBUF`: the send buffer, `value` in bytes; Linux doubles the value it stores. */
|
|
440
|
+
declare const NET_SO_SNDBUF: i32;
|
|
441
|
+
/** `SO_RCVBUF`: the receive buffer, `value` in bytes; Linux doubles the value it stores. */
|
|
442
|
+
declare const NET_SO_RCVBUF: i32;
|
|
384
443
|
|
|
385
444
|
// ---- `nish:term` ------------------------------------------------------------------
|
|
386
445
|
//
|
|
@@ -441,6 +500,12 @@ declare module "nish:fs" {
|
|
|
441
500
|
export function writeFileSync(path: string, data: string): void;
|
|
442
501
|
/** Refuses a symbolic link as the last component of the path, as the global does. */
|
|
443
502
|
export function appendFileSync(path: string, data: string): void;
|
|
503
|
+
/** The byte count, or a negative errno; refuses a symbolic link as `writeFileSync` does. */
|
|
504
|
+
export function writeFileBytesSync(path: string, bytes: readonly u8[]): i32;
|
|
505
|
+
/** 0, or a negative errno: -18 across file systems. */
|
|
506
|
+
export function renameSync(from: string, to: string): i32;
|
|
507
|
+
/** 0, or a negative errno: a directory is refused, not removed. */
|
|
508
|
+
export function unlinkSync(path: string): i32;
|
|
444
509
|
/** `true` when the directory was created, `false` when it already existed. */
|
|
445
510
|
export function mkdirSync(path: string): boolean;
|
|
446
511
|
export function isDirectorySync(path: string): boolean;
|
|
@@ -475,11 +540,21 @@ declare module "nish:process" {
|
|
|
475
540
|
export function availableParallelism(): i32;
|
|
476
541
|
/** Bytes of physical memory, or -1. */
|
|
477
542
|
export function totalMemory(): i64;
|
|
543
|
+
/** Set this process's nice value (clamped to -20..19): 0, or a negative errno. */
|
|
544
|
+
export function setNice(n: i32): i32;
|
|
545
|
+
/**
|
|
546
|
+
* Move this process to `SCHED_IDLE`: 0, a negative errno, or -95 off Linux.
|
|
547
|
+
* An unprivileged process can never leave it, and an idle process can starve
|
|
548
|
+
* on a saturated machine, so a caller keeps a normal-priority fallback.
|
|
549
|
+
*/
|
|
550
|
+
export function setIdlePriority(): i32;
|
|
478
551
|
/**
|
|
479
552
|
* A monotonic clock in nanoseconds. The origin is arbitrary, so only the
|
|
480
553
|
* difference between two reads is meaningful.
|
|
481
554
|
*/
|
|
482
555
|
export function monotonicNanos(): i64;
|
|
556
|
+
/** The wall clock in nanoseconds since the Unix epoch; it steps when the system time is set. */
|
|
557
|
+
export function wallClockNanos(): i64;
|
|
483
558
|
/** A descriptor readable when SIGTERM or SIGINT arrives; the same one every call, or -1. */
|
|
484
559
|
export function signalFd(): i32;
|
|
485
560
|
/** Block until SIGTERM or SIGINT arrives: 15 or 2, or -1 for a descriptor that is not `signalFd()`'s. */
|
|
@@ -538,6 +613,26 @@ declare module "nish:net" {
|
|
|
538
613
|
export function pollRemove(loop: i32, fd: i32): i32;
|
|
539
614
|
/** Wait up to `timeoutMs` (negative: forever); pairs `ready[2k]` token, `ready[2k + 1]` events (4: hang-up or error). */
|
|
540
615
|
export function pollWait(loop: i32, ready: i32[], timeoutMs: i32): i32;
|
|
616
|
+
/**
|
|
617
|
+
* Set one socket option from a whitelist: `option` is one of the `NET_*` ids
|
|
618
|
+
* below, and any other is -22; there is no raw level and option number. 0, or
|
|
619
|
+
* a negative errno (-9 a closed descriptor, -88 not a socket).
|
|
620
|
+
*/
|
|
621
|
+
export function netSetOption(fd: i32, option: i32, value: i32): i32;
|
|
622
|
+
/** `TCP_NODELAY`: `value` 1 sends a small write at once, 0 lets Nagle batch it. */
|
|
623
|
+
export const NET_TCP_NODELAY: i32;
|
|
624
|
+
/**
|
|
625
|
+
* `TCP_QUICKACK` (Linux; -95 elsewhere): `value` 1 acknowledges at once, 0
|
|
626
|
+
* delays. Linux does not keep quick-ACK mode: the kernel leaves it on its own,
|
|
627
|
+
* so a server that wants a mode sets it again after every response.
|
|
628
|
+
*/
|
|
629
|
+
export const NET_TCP_QUICKACK: i32;
|
|
630
|
+
/** `SO_KEEPALIVE`: `value` 1 probes an idle connection, 0 does not. */
|
|
631
|
+
export const NET_SO_KEEPALIVE: i32;
|
|
632
|
+
/** `SO_SNDBUF`: the send buffer, `value` in bytes; Linux doubles the value it stores. */
|
|
633
|
+
export const NET_SO_SNDBUF: i32;
|
|
634
|
+
/** `SO_RCVBUF`: the receive buffer, `value` in bytes; Linux doubles the value it stores. */
|
|
635
|
+
export const NET_SO_RCVBUF: i32;
|
|
541
636
|
}
|
|
542
637
|
|
|
543
638
|
declare module "nish:unsafe" {
|
|
@@ -560,6 +655,11 @@ declare module "nish:io" {
|
|
|
560
655
|
export function write(s: string): void;
|
|
561
656
|
/** The same, on stderr. */
|
|
562
657
|
export function writeError(s: string): void;
|
|
658
|
+
/**
|
|
659
|
+
* `bytes[off .. off + len)` to stdout, every byte as it is: the count, or a
|
|
660
|
+
* negative errno. A range outside `bytes` panics, as `dst.set` does.
|
|
661
|
+
*/
|
|
662
|
+
export function writeBytes(bytes: readonly u8[], off: i32, len: i32): i32;
|
|
563
663
|
/** `message` to stderr, then exit 1. A terminator, as `process.exit` is. */
|
|
564
664
|
export function panic(message: string): never;
|
|
565
665
|
}
|
package/runtime/nish.h
CHANGED
|
@@ -396,6 +396,22 @@ nish_array *nish_readdir(const nish_str *path);
|
|
|
396
396
|
* and the bytes live in the arena, so copy what you keep before the next reset
|
|
397
397
|
* or release. */
|
|
398
398
|
nish_array *nish_read_file_bytes(const nish_str *path);
|
|
399
|
+
/* `renameSync(from, to)`, `unlinkSync(path)` and `writeFileBytesSync(path,
|
|
400
|
+
* bytes)`: rename(2), unlink(2), and every element of a `u8[]` written to a
|
|
401
|
+
* file opened as `nish_write_file` opens it (`O_TRUNC`, 0644, a link refused).
|
|
402
|
+
* None exits: each answers 0 (the write, the length written) or a negative
|
|
403
|
+
* errno — -EXDEV for a rename across file systems, which moves nothing, and
|
|
404
|
+
* -EISDIR or -EPERM for a directory handed to `nish_unlink`, which never calls
|
|
405
|
+
* rmdir. A path holding a NUL answers -EINVAL, not -ENOENT, so a program can
|
|
406
|
+
* tell a malformed path from a missing file. */
|
|
407
|
+
int32_t nish_rename(const nish_str *from, const nish_str *to);
|
|
408
|
+
int32_t nish_unlink(const nish_str *path);
|
|
409
|
+
int32_t nish_write_file_bytes(const nish_str *path, const nish_array *bytes);
|
|
410
|
+
/* `writeBytes(bytes, off, len)` (nish:io), in runtime-os.c: the bytes
|
|
411
|
+
* `[off, off + len)` of a `u8[]` on stdout, a short write finished and `EINTR`
|
|
412
|
+
* retried. The caller has range-checked `off` and `len`. Answers `len`, or the
|
|
413
|
+
* negative errno of the write that failed; never exits. */
|
|
414
|
+
int32_t nish_write_bytes(const nish_array *bytes, int64_t off, int64_t len);
|
|
399
415
|
/* `spawnSync(argv)`: run element 0 of `argv` (searched on `PATH`) with `argv`
|
|
400
416
|
* as its argument vector, wait for it, and answer its exit status, or
|
|
401
417
|
* `128 + n` when signal `n` killed it. -1 when `argv` is empty, when the
|
|
@@ -438,6 +454,15 @@ nish_array *nish_wait_pid(int32_t pid, int32_t flags);
|
|
|
438
454
|
/* `totalMemory()`: bytes of physical memory (Linux: `_SC_PHYS_PAGES` times the
|
|
439
455
|
* page size; Darwin: `hw.memsize`), or -1 where the host does not say. */
|
|
440
456
|
int64_t nish_total_memory(void);
|
|
457
|
+
/* `setNice(n)`: this process's nice value set to `n` (`setpriority`), 0 or a
|
|
458
|
+
* negative errno. The kernel clamps `n` to -20..19; lowering it below the
|
|
459
|
+
* current value without the privilege is -EACCES (or -EPERM). -ENOSYS under
|
|
460
|
+
* WASI. */
|
|
461
|
+
int32_t nish_set_nice(int32_t n);
|
|
462
|
+
/* `setIdlePriority()`: this process moved to `SCHED_IDLE` (Linux), 0 or a
|
|
463
|
+
* negative errno; -95 (Linux's EOPNOTSUPP) elsewhere, -ENOSYS under WASI. An
|
|
464
|
+
* unprivileged process cannot leave the policy again. */
|
|
465
|
+
int32_t nish_set_idle_priority(void);
|
|
441
466
|
|
|
442
467
|
/* ---- The environment (WP19 R1) ------------------------------------------
|
|
443
468
|
* `getenv(name)`: the value of environment variable `name`, copied into the
|
|
@@ -485,6 +510,10 @@ const nish_str *nish_arch(void);
|
|
|
485
510
|
* purpose: `readnone` would let LLVM fold the two reads around a measured
|
|
486
511
|
* region into one and measure zero. */
|
|
487
512
|
int64_t nish_monotonic_nanos(void);
|
|
513
|
+
/* `wallClockNanos()`: `CLOCK_REALTIME` in nanoseconds since the epoch, the
|
|
514
|
+
* clock `Date.now` reads at a millisecond's grain. It steps when the system
|
|
515
|
+
* time is set, so it dates an event and measures nothing. */
|
|
516
|
+
int64_t nish_wall_clock_nanos(void);
|
|
488
517
|
|
|
489
518
|
/* ---- The host (WP34 N3), runtime/runtime-host.c --------------------------
|
|
490
519
|
* `Date.now()`: the wall clock, `CLOCK_REALTIME`, in whole milliseconds since
|
|
@@ -542,6 +571,10 @@ bool nish_is_executable(const nish_str *path);
|
|
|
542
571
|
* len)` of `buf`, which the compiled call has range-checked; a write to a
|
|
543
572
|
* gone peer is -32, never SIGPIPE. `nish_net_shutdown(fd, how)`: 0 read, 1
|
|
544
573
|
* write, 2 both. `nish_net_close(fd)`.
|
|
574
|
+
* `nish_net_set_option(fd, option, value)`: one socket option from a
|
|
575
|
+
* whitelist, by the runtime's own id (1 `TCP_NODELAY`, 2 `TCP_QUICKACK`, 3
|
|
576
|
+
* `SO_KEEPALIVE`, 4 `SO_SNDBUF`, 5 `SO_RCVBUF`); any other id is -22, and
|
|
577
|
+
* `TCP_QUICKACK` off Linux is -95.
|
|
545
578
|
* `nish_tcp_connect(addr)`: a new socket connecting to `addr`, answered at
|
|
546
579
|
* once; its `EINPROGRESS` is not a failure, and any other closes it.
|
|
547
580
|
* `nish_connect_result(fd)`: `SO_ERROR` once the socket is writable, 0 when
|
|
@@ -576,6 +609,7 @@ int32_t nish_net_read(int32_t fd, nish_array *buf, int64_t off, int64_t len);
|
|
|
576
609
|
int32_t nish_net_write(int32_t fd, const nish_array *buf, int64_t off, int64_t len);
|
|
577
610
|
int32_t nish_net_shutdown(int32_t fd, int32_t how);
|
|
578
611
|
int32_t nish_net_close(int32_t fd);
|
|
612
|
+
int32_t nish_net_set_option(int32_t fd, int32_t option, int32_t value);
|
|
579
613
|
int32_t nish_tcp_connect(const nish_array *addr);
|
|
580
614
|
int32_t nish_connect_result(int32_t fd);
|
|
581
615
|
int32_t nish_udp_bind(const nish_str *host, int32_t port, int32_t flags);
|
package/runtime/nish.mjs
CHANGED
|
@@ -84,12 +84,16 @@ globalThis.console = {
|
|
|
84
84
|
// `Array#sort`'s UTF-16 order — the two agree on ASCII names only.
|
|
85
85
|
provide("write", shim.write);
|
|
86
86
|
provide("writeError", shim.writeError);
|
|
87
|
+
provide("writeBytes", shim.writeBytes);
|
|
87
88
|
provide("panic", shim.panic);
|
|
88
89
|
provide("readFileSync", shim.readFileSync);
|
|
89
90
|
provide("readFileSyncOrNull", shim.readFileSyncOrNull);
|
|
90
91
|
provide("readFileBytesSync", shim.readFileBytesSync);
|
|
91
92
|
provide("writeFileSync", shim.writeFileSync);
|
|
92
93
|
provide("appendFileSync", shim.appendFileSync);
|
|
94
|
+
provide("writeFileBytesSync", shim.writeFileBytesSync);
|
|
95
|
+
provide("renameSync", shim.renameSync);
|
|
96
|
+
provide("unlinkSync", shim.unlinkSync);
|
|
93
97
|
provide("mkdirSync", shim.mkdirSync);
|
|
94
98
|
provide("isDirectorySync", shim.isDirectorySync);
|
|
95
99
|
provide("readdirSync", shim.readdirSync);
|
|
@@ -103,6 +107,14 @@ provide("kill", shim.kill);
|
|
|
103
107
|
provide("waitPid", shim.waitPid);
|
|
104
108
|
provide("availableParallelism", shim.availableParallelism);
|
|
105
109
|
provide("totalMemory", shim.totalMemory);
|
|
110
|
+
// `setNice` and `setIdlePriority` throw, naming themselves: Node has a reading
|
|
111
|
+
// of the nice value (`os.setPriority`) but none of `SCHED_IDLE`, and a reading
|
|
112
|
+
// of one without the other would differ from the native build on the same host.
|
|
113
|
+
for (const name of ["setNice", "setIdlePriority"]) {
|
|
114
|
+
provide(name, () => {
|
|
115
|
+
throw new Error(`\`${name}\` has no reading under Node: it changes how the kernel schedules the native program`);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
106
118
|
provide("getenv", shim.getenv);
|
|
107
119
|
provide("statMtimeSync", shim.statMtimeSync);
|
|
108
120
|
provide("signalFd", shim.signalFd);
|
|
@@ -130,9 +142,17 @@ for (const name of [
|
|
|
130
142
|
"pollModify",
|
|
131
143
|
"pollRemove",
|
|
132
144
|
"pollWait",
|
|
145
|
+
"netSetOption",
|
|
133
146
|
]) {
|
|
134
147
|
provide(name, () => shim.noNetReading(name));
|
|
135
148
|
}
|
|
149
|
+
// `netSetOption`'s option ids are plain numbers, so they read the same here
|
|
150
|
+
// as natively: only the call that takes one throws.
|
|
151
|
+
provide("NET_TCP_NODELAY", 1);
|
|
152
|
+
provide("NET_TCP_QUICKACK", 2);
|
|
153
|
+
provide("NET_SO_KEEPALIVE", 3);
|
|
154
|
+
provide("NET_SO_SNDBUF", 4);
|
|
155
|
+
provide("NET_SO_RCVBUF", 5);
|
|
136
156
|
|
|
137
157
|
// `nish:term`. Whether a descriptor is a terminal and how big stdout's is have
|
|
138
158
|
// Node's answers; reading stdin, waiting for it and raw mode throw, each naming
|
|
@@ -180,6 +200,7 @@ if (!("set" in Array.prototype)) {
|
|
|
180
200
|
// describes. A printed reading can never match a native run: the two origins are
|
|
181
201
|
// both arbitrary, and only the difference between two reads means anything.
|
|
182
202
|
provide("monotonicNanos", shim.monotonicNanos);
|
|
203
|
+
provide("wallClockNanos", shim.wallClockNanos);
|
|
183
204
|
|
|
184
205
|
// Conversions. In f64 mode `toF64` is the identity and `toI32` is the one that
|
|
185
206
|
// matters (it saturates, where a JavaScript cast would not).
|
package/runtime/node.mjs
CHANGED
|
@@ -72,6 +72,48 @@ function appendFileSync(path, data) {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* The negative errno a failed `fs` call names, as `runtime-os.c` answers it.
|
|
77
|
+
* Node names the errno by its code; one it gives no number for, such as the
|
|
78
|
+
* `ERR_INVALID_ARG_VALUE` a path holding a NUL throws, is -22 (`EINVAL`), the
|
|
79
|
+
* runtime's answer to that path.
|
|
80
|
+
*/
|
|
81
|
+
function negErrno(e) {
|
|
82
|
+
return -(os.constants.errno[e.code] ?? 22);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** `ok` when `f` returns, or the negative errno of what it threw. */
|
|
86
|
+
function errnoOr(ok, f) {
|
|
87
|
+
try {
|
|
88
|
+
f();
|
|
89
|
+
return ok;
|
|
90
|
+
} catch (e) {
|
|
91
|
+
return negErrno(e);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** `renameSync(from, to)`: 0, or a negative errno (-18 across file systems). */
|
|
96
|
+
const renameSync = (from, to) => errnoOr(0, () => fs.renameSync(from, to));
|
|
97
|
+
|
|
98
|
+
/** `unlinkSync(path)`: 0, or a negative errno; a directory is refused, not removed. */
|
|
99
|
+
const unlinkSync = (path) => errnoOr(0, () => fs.unlinkSync(path));
|
|
100
|
+
|
|
101
|
+
/** `writeFileBytesSync(path, bytes)`: the byte count, or a negative errno; mode 0644 and `WRITE_FLAGS`. */
|
|
102
|
+
const writeFileBytesSync = (path, bytes) =>
|
|
103
|
+
errnoOr(bytes.length, () => fs.writeFileSync(path, Uint8Array.from(bytes), { flag: WRITE_FLAGS, mode: 0o644 }));
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* `writeBytes`'s write, after the core checked the range `[from, to)`: every
|
|
107
|
+
* byte to stdout, then the count, or a negative errno. `fs.writeSync` may
|
|
108
|
+
* write less than it was given, so it is asked again for the rest.
|
|
109
|
+
*/
|
|
110
|
+
function writeBytes(bytes, from, to) {
|
|
111
|
+
const buf = Uint8Array.from(bytes.slice(from, to));
|
|
112
|
+
return errnoOr(buf.length, () => {
|
|
113
|
+
for (let at = 0; at < buf.length; ) at += fs.writeSync(1, buf, at, buf.length - at);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
75
117
|
/**
|
|
76
118
|
* `mkdirSync(path)` (WP14 D4): one directory, not recursive, answering whether
|
|
77
119
|
* a directory is there afterwards. Node throws where the runtime answers
|
|
@@ -234,12 +276,7 @@ function spawnSyncTo(argv, out, err) {
|
|
|
234
276
|
*/
|
|
235
277
|
function kill(pid, sig) {
|
|
236
278
|
if (pid <= 0 || !(sig === 0 || sig === 2 || sig === 9 || sig === 15)) return -22;
|
|
237
|
-
|
|
238
|
-
process.kill(pid, sig);
|
|
239
|
-
return 0;
|
|
240
|
-
} catch (e) {
|
|
241
|
-
return -(os.constants.errno[e.code] ?? 22);
|
|
242
|
-
}
|
|
279
|
+
return errnoOr(0, () => process.kill(pid, sig));
|
|
243
280
|
}
|
|
244
281
|
|
|
245
282
|
/**
|
|
@@ -275,6 +312,15 @@ function monotonicNanos() {
|
|
|
275
312
|
return process.hrtime.bigint();
|
|
276
313
|
}
|
|
277
314
|
|
|
315
|
+
/**
|
|
316
|
+
* `wallClockNanos()`: the wall clock in nanoseconds since the epoch, a BigInt
|
|
317
|
+
* like `monotonicNanos`'s. Node's wall clock is `Date.now`, whole
|
|
318
|
+
* milliseconds, so the last six digits are zeros where a native run has them.
|
|
319
|
+
*/
|
|
320
|
+
function wallClockNanos() {
|
|
321
|
+
return BigInt(Date.now()) * 1000000n;
|
|
322
|
+
}
|
|
323
|
+
|
|
278
324
|
/**
|
|
279
325
|
* `statMtimeSync(path)`: Node's `mtimeMs` for the path, or NaN when it cannot
|
|
280
326
|
* be stat'd, which is the native answer too. `mtimeMs` is the same arithmetic
|
|
@@ -344,6 +390,10 @@ const CALLS = {
|
|
|
344
390
|
readFileBytesSync,
|
|
345
391
|
writeFileSync,
|
|
346
392
|
appendFileSync,
|
|
393
|
+
writeFileBytesSync,
|
|
394
|
+
renameSync,
|
|
395
|
+
unlinkSync,
|
|
396
|
+
writeBytes,
|
|
347
397
|
mkdirSync,
|
|
348
398
|
isDirectorySync,
|
|
349
399
|
readdirSync,
|
|
@@ -357,6 +407,7 @@ const CALLS = {
|
|
|
357
407
|
availableParallelism,
|
|
358
408
|
totalMemory,
|
|
359
409
|
monotonicNanos,
|
|
410
|
+
wallClockNanos,
|
|
360
411
|
statMtimeSync,
|
|
361
412
|
lstatOwnerModeSync,
|
|
362
413
|
geteuid,
|