@ai-ecoverse/slicc-kernel 1.124.11 → 1.124.13
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/README.md +30 -9
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +308 -53
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +4424 -4329
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +313 -58
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +7111 -7016
- package/dist/process-worker.js.map +4 -4
- package/dist/types/fs/unlinked.d.ts +1 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/kernel/net/tls-ciphers.d.ts +6 -0
- package/dist/types/kernel/net/tls-client.d.ts +13 -1
- package/dist/types/kernel/net/tls-engine.d.ts +10 -0
- package/dist/types/kernel/net/tls-uplink.d.ts +14 -1
- package/dist/types/kernel/net/uplink-socket.d.ts +1 -1
- package/dist/types/kernel/socket-syscalls.d.ts +7 -0
- package/dist/types/kernel/vfs-file.d.ts +2 -0
- package/dist/types/process/js/js-dev.d.ts +1 -0
- package/dist/types/process/js/js-net.d.ts +16 -0
- package/dist/types/process/process-fds.d.ts +1 -1
- package/dist/types/process/wasi/wasi-host.d.ts +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -377,7 +377,7 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
377
377
|
- `sudo(args)`, what the [`sudo`](#sudo) command calls: `args` are its arguments after `sudo`, and it resolves with `{ pid }`, the command the kernel started as the program's child, to `wait` for; `{ run }`, an argv to run as the caller (with `users: 'off'`); `{ list }`, the text of `sudo -l`; or `{ error, errno }`, the message to print;
|
|
378
378
|
- `crontab({ action, user?, text? })`, what the [`crontab`](#cron) command calls: `action` is `'list'`, `'remove'` or `'install'` (with the table as `text`), on the caller's own table or, for an effective uid of 0, on `user`'s. It resolves with `{ text }` (the table for `list`, `''` otherwise) or `{ error }`, the message to print. A malformed call is `EINVAL`;
|
|
379
379
|
- `net`: `listen({ port, host, backlog })` and `connect({ host, port, signal })` (`host` defaults to `127.0.0.1`) on the kernel's loopback, the same sockets WASIX programs use, so a JS server is reached by other processes, by the page as `<port>.kernel.localhost` (`loopbackFetch`, `dial`) and by attached clients. A listener has its `port` (as bound, so port `0` picks a free one), `accept({ signal })`, `close()` and `for await` over its connections. A connection has `local` and `remote` addresses, `readable` and `writable` web streams (closing the writable shuts down the sending side, so the peer reads the end), and `close()`. The loopback is IPv4 (`127.0.0.1` by default, or `0.0.0.0`), so an IPv6 host such as `::1` fails with `EADDRNOTAVAIL` for both `listen` and `connect`, and binding and connecting fail as they would for a WASIX program (`EADDRINUSE`, `ECONNREFUSED`, `EADDRNOTAVAIL`). A blocked accept, read or write waits without holding anything in the kernel, so aborting it or closing the socket leaves nothing behind. A write to a connection the peer closed fails with `EPIPE` and no `SIGPIPE`;
|
|
380
|
-
- `net.connect({ host, port, tls: { servername, alpn, rejectUnauthorized, ca, path } })` takes one of two paths. By default it's **A1, TLS through the transport** ([#914](https://github.com/ai-ecoverse/slicc-kernel/issues/914)), described first below. For a destination the uplink routes it can also be **A2, a TLS handshake in the kernel** ([#922](https://github.com/ai-ecoverse/slicc-kernel/issues/922)), described after it. `conn.tls.path` (`'transport'` or `'kernel'`) and `conn.tls.substitutes` say which one was taken.
|
|
380
|
+
- `net.connect({ host, port, tls: { servername, alpn, rejectUnauthorized, ca, path, cert, key, passphrase, sni, checkHostname } })` takes one of two paths. By default it's **A1, TLS through the transport** ([#914](https://github.com/ai-ecoverse/slicc-kernel/issues/914)), described first below. For a destination the uplink routes it can also be **A2, a TLS handshake in the kernel** ([#922](https://github.com/ai-ecoverse/slicc-kernel/issues/922)), described after it. `conn.tls.path` (`'transport'` or `'kernel'`) and `conn.tls.substitutes` say which one was taken.
|
|
381
381
|
- **What it does:** there's no handshake in the kernel. The connection carries plain HTTP/1.1 (and WebSocket upgrades) to the realm proxy's HTTP path for the origin `https://<servername or host>[:port]`, exactly as a `CONNECT` through `127.0.0.1:3128` does after the kernel terminates its TLS. The transport (the page's `fetch` or WebSocket, or the uplink's) makes the real TLS connection and verifies it. So `https.request` over a socket, keep-alive agents, `agent-base` and `wss://` clients work with the transport's semantics. That includes sealed placeholders in headers, which reach the transport exactly as from `ctx.fetch`.
|
|
382
382
|
- **The `tls` property:** the connection's `tls` is `{ path: 'transport', substitutes: true, protocol: 'transport', alpnProtocol: 'http/1.1', authorized: true, servername }`. `authorized` means the transport verified the server. There's no peer certificate chain and no TLS version of the kernel's own, so don't read it as a real handshake's result.
|
|
383
383
|
- **Options:** the transport always verifies, so `rejectUnauthorized: false` is accepted and changes nothing. Outside the uplink, a custom `ca` is refused (`ENOTSUP`), and so is an `alpn` list without `http/1.1` (`EPROTONOSUPPORT`). The kernel's own loopback (`localhost`, `*.localhost`, `127/8`, `0.0.0.0`, `::1`, as `servername` or as `host`) is `ENOTSUP` at connect, as `ctx.fetch` refuses `https:` and `wss:` there. Connect in plain text to a kernel server. `host.slicc.internal` goes to the transport under its policy, as for `ctx.fetch`.
|
|
@@ -395,15 +395,29 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
395
395
|
| `alpn` without `http/1.1` | **A2** | `EPROTONOSUPPORT` |
|
|
396
396
|
| a well-known non-HTTP TLS port (465, 993, 995, 636, 853, 8883, 6697, 5671 …) | **A2** | `EPROTONOSUPPORT` |
|
|
397
397
|
| `tls: { path: 'kernel' }` | **A2** | `ENOTSUP` |
|
|
398
|
+
| `cert` and `key`, `sni: false` or `checkHostname: false` | **A2** | `ENOTSUP` |
|
|
398
399
|
| `tls: { path: 'transport' }` | A1 | A1 |
|
|
399
400
|
| `connect({ socket: conn, host, tls })` (STARTTLS) | **A2** | `EOPNOTSUPP` |
|
|
400
401
|
|
|
401
|
-
- **A2, the handshake in the kernel** ([#922](https://github.com/ai-ecoverse/slicc-kernel/issues/922)): the kernel dials the destination through the uplink as raw TCP and runs a TLS client there, with [`@ai-ecoverse/wasm-tls-engine`](https://www.npmjs.com/package/@ai-ecoverse/wasm-tls-engine) (4.2.0-
|
|
402
|
+
- **A2, the handshake in the kernel** ([#922](https://github.com/ai-ecoverse/slicc-kernel/issues/922)): the kernel dials the destination through the uplink as raw TCP and runs a TLS client there, with [`@ai-ecoverse/wasm-tls-engine`](https://www.npmjs.com/package/@ai-ecoverse/wasm-tls-engine) (4.2.0-3 or later, loaded on the first A2 connect). The connection is then an ordinary uplink socket that carries the decrypted stream, so any protocol works: SMTP, IMAP, Postgres, MQTT, HTTP/2.
|
|
402
403
|
- **Verification** is required. The chain must reach a trusted root, the leaf must name `servername` (default `host`), and every certificate must be within its validity by the page's clock, with serverAuth usage. The trusted roots are Mozilla's set the engine package ships (`dist/roots.der`, its date in the package's `slicc.tls.roots`), read on the first A2 connect without a `ca`. A `ca` (PEM text, DER bytes, or a list of them) **replaces** them, as in Node.
|
|
403
404
|
- **A refusal** rejects the connect with Node's code: `ERR_TLS_CERT_ALTNAME_INVALID`, `UNABLE_TO_GET_ISSUER_CERT_LOCALLY`, `CERT_HAS_EXPIRED`, `CERT_NOT_YET_VALID` or `INVALID_PURPOSE`, with the engine's reason as `detail`. A failed handshake otherwise is `ERR_SSL_HANDSHAKE_FAILURE`.
|
|
404
405
|
- **`rejectUnauthorized: false`** finishes the handshake whatever the chain says and reports `authorized: false` with `authorizationError`.
|
|
405
406
|
- **`servername`** must be a host name (letters, digits and `-` in labels of at most 63, at most 253 in all, one trailing dot allowed) or an IP literal, else `EINVAL` before anything is dialled. An IP literal sends no SNI and must match an iPAddress subjectAltName.
|
|
406
|
-
- **The `tls` property** is the handshake's: `{ path: 'kernel', substitutes: false, protocol: 'TLSv1.3' or 'TLSv1.2', alpnProtocol (the agreed protocol, or false), authorized, authorizationError?, servername, peerCertificates
|
|
407
|
+
- **The `tls` property** is the handshake's: `{ path: 'kernel', substitutes: false, protocol: 'TLSv1.3' or 'TLSv1.2', alpnProtocol (the agreed protocol, or false), authorized, authorizationError?, servername, peerCertificates, cipher, clientCertificate, sni, hostnameChecked, hostnameMatches? }`.
|
|
408
|
+
- `peerCertificates` is the server's chain as DER, leaf first.
|
|
409
|
+
- `cipher` is the negotiated suite as Node's `getCipher()` gives it: `{ name, standardName, version }`, where `name` is OpenSSL's (`ECDHE-RSA-AES128-GCM-SHA256`) and `standardName` IANA's. As in Node, `version` is the suite's own version, not the session's: `TLSv1.0` for a CBC-SHA suite on a TLS 1.2 session.
|
|
410
|
+
- `clientCertificate` says whether a client certificate was offered. `sni` says whether the server name was sent: it's false with `sni: false` and for an IP-literal `servername`, which TLS never sends.
|
|
411
|
+
- `hostnameChecked` says whether the kernel checked the leaf against `servername`. `hostnameMatches` says whether it names it (absent with `rejectUnauthorized: false`).
|
|
412
|
+
- **A client certificate** ([#1004](https://github.com/ai-ecoverse/slicc-kernel/issues/1004)): `cert` (PEM text, DER bytes or a list: the leaf first, then its intermediates) and `key` (PEM, PKCS#1, SEC1 or PKCS#8, or DER), with `passphrase` for an encrypted key, as in Node.
|
|
413
|
+
- The key goes to the engine before anything is dialled, and the kernel keeps no copy of it (nor of the passphrase) once the engine has it.
|
|
414
|
+
- Before dialling, `cert` without `key` (or the reverse) is `EINVAL`. A key that doesn't match the leaf is `ERR_OSSL_X509_KEY_VALUES_MISMATCH`, a wrong passphrase `ERR_OSSL_BAD_DECRYPT`, a missing one `ERR_OSSL_CRYPTO_INTERRUPTED_OR_CANCELLED`, and a key the engine can't read `ERR_OSSL_UNSUPPORTED`.
|
|
415
|
+
- `pfx` is `ENOTSUP`: pass `cert` and `key`.
|
|
416
|
+
- A server that requires a certificate and gets none refuses with an alert. On TLS 1.2 that fails the connect (`ERR_SSL_HANDSHAKE_FAILURE`). On TLS 1.3 the client's side of the handshake is already done, so the next read or write fails with `ERR_SSL_FATAL_ALERT`, as Node's fails after `secureConnect`.
|
|
417
|
+
- **`sni: false`** sends no server name. The leaf is still checked against `servername` exactly as with SNI.
|
|
418
|
+
- **`checkHostname: false`** checks the chain (trust, validity, usage) but **not that the leaf names `servername`**. The connection reports `hostnameChecked: false`, and `hostnameMatches` says what the skipped check found.
|
|
419
|
+
- **The caller must then check the server's identity itself**, as slicc-jsh does with Node's `checkServerIdentity` and `peerCertificates[0]`; pass it only when the program supplied one.
|
|
420
|
+
- With `rejectUnauthorized: false` it's `EINVAL`.
|
|
407
421
|
- **STARTTLS:** `net.connect({ socket: conn, host, tls })` upgrades an open plain connection to an uplink destination in place, as Node's `tls.connect({ socket })` does. It resolves with a connection whose streams now carry the decrypted bytes and whose `tls` is the session. Don't hold a pending read on the plain connection while it upgrades. Pass `host` (or `tls.servername`): the plain connection only knows the address it reached, so without a name the certificate is checked against that IP, and a certificate for the host name fails.
|
|
408
422
|
- **Sealed values aren't substituted.** The kernel encrypts end to end, so a placeholder written into an A2 connection reaches the server as written, unsubstituted (it isn't the secret, so nothing leaks). A caller that relies on substitution checks `conn.tls.substitutes` and uses `fetch`, or an A1 connection, for sealed values.
|
|
409
423
|
- **Limits:** no revocation checking (neither OCSP nor CRLs, as with Node's default), no session resumption, and the clock is the page's `Date.now()`.
|
|
@@ -762,6 +776,13 @@ WASI and WASIX have no call for file modes, so the kernel adds the import module
|
|
|
762
776
|
| `path_mkfifo(dirfd, path, path_len, mode)` | `mkfifo`/`mkfifoat` and `mknod(S_IFIFO)` (homescoop#878): makes a [FIFO](#filesystem) with `mode & ~umask`, relative to `dirfd` (`AT_FDCWD` is `-2`). It doesn't follow a link in the last component, so an existing name, also a FIFO, a dangling link, a directory or the root, is `EEXIST`, and an empty name is `ENOENT` (#972). A mount without FIFOs answers `EPERM` |
|
|
763
777
|
| `path_access(dirfd, path, path_len, amode, flags)` | `faccessat`: `amode` is `R_OK` 4, `W_OK` 2, `X_OK` 1 or `F_OK` 0; the check uses the real uid and gid, or the effective ones with `flags & 2` (`AT_EACCESS`), and the link itself with `flags & 1`. With `users: 'off'` it checks only that the path exists, as a libc without it does (see [Permissions](#permissions)) |
|
|
764
778
|
|
|
779
|
+
The kernel's device nodes answer the mode and owner an Emscripten program sees for them ([#1028](https://github.com/ai-ecoverse/slicc-kernel/issues/1028)):
|
|
780
|
+
- `/dev/null`, `/dev/zero`, `/dev/full`, `/dev/random`, `/dev/urandom` and `/dev/tty` are root's character devices with `0666`. `/dev/tty` also stats when the process has no controlling terminal; opening it then is `ENXIO`, as on Linux.
|
|
781
|
+
- A pty's `/dev/pts/N` is its owner's, with `0620`.
|
|
782
|
+
- `/dev/stdin`, `/dev/stdout`, `/dev/stderr` and `/dev/fd/N` answer for the descriptor they name.
|
|
783
|
+
|
|
784
|
+
An Emscripten program sees those six character devices at `0666` with `users: 'off'` too.
|
|
785
|
+
|
|
765
786
|
The owner calls follow [File owners](#file-owners): with `users: 'off'`, `fd_chown` and `path_chown` succeed and change nothing, and the four owner calls answer `ENOTSUP`, "owners not tracked", before looking at anything, so a libc can remember that once and take its own `getuid()` and `getgid()` as every file's owner, which is what an Emscripten program built against the credential calls sees. Devices are root's. A pipe's owner is the caller.
|
|
766
787
|
|
|
767
788
|
WASIX's own terminal calls carry only echo and line buffering (`tty_set`) and answer for "the" terminal whatever the descriptor (`tty_get`), so a WASIX program's `cfmakeraw` would leave `^C` a signal and its output post-processed. The kernel adds the import module `slicc_tty`, with the full Linux `struct termios` (musl's 60-byte layout: four `u32` flag words, `c_line`, `c_cc[32]`, then input and output speed; speeds read as `B38400` and are ignored when set) and answers per descriptor, again as a preview1 errno (`ENOTTY` for a descriptor that is no terminal, `EBADF` for one that isn't open, `ENOSYS` from kernels without them):
|
|
@@ -999,7 +1020,7 @@ The kernel is ported from SLICC's `packages/webapp/src/kernel/` with the browser
|
|
|
999
1020
|
|
|
1000
1021
|
- `src/kernel/`: the kernel side, per process (`host.ts` starts the worker and answers its syscalls in `process.ts`) and shared tables (descriptors, pipes, children, jobs, signals, terminals and pseudo-terminals, `select`), and process credentials (`cred.ts`).
|
|
1001
1022
|
- `src/process/`: the runtime inside each process worker. It evaluates the Emscripten glue, mounts the live VFS, routes descriptors through the kernel and implements `fork` by copying the whole linear memory into a new worker (Asyncify). `fork` is the only call allowed to suspend through Asyncify: an `fsync` that Emscripten made asynchronous is answered synchronously by the file's own stream instead. `src/process/wasi/` runs WASI and WASIX programs.
|
|
1002
|
-
- `src/realm/`: the synchronous bridge (`SharedArrayBuffer` + `Atomics.wait`) and the live Emscripten filesystem on top of it.
|
|
1023
|
+
- `src/realm/`: the synchronous bridge (`SharedArrayBuffer` + `Atomics.wait`) and the live Emscripten filesystem on top of it. When a program's file call gets no answer, the bridge treats it like an NFS mount (#990). A read-only call (`read`, `pread`, `stat`, `lstat`, `readdir`, `readlink`, `exists`, `access`, `statfs`) fails with `EIO` once nothing has come back for 25 s; the clock starts again with every chunk of a long answer, so a slow transfer that keeps answering never fails (the host keeps a long answer for 30 s after each chunk it hands over, not after the first), and a late answer is dropped, which is harmless because the call changed nothing. A call that changes the file system (writes, `rename`, `unlink`, `mkdir`, creates, `chmod`, holds…) waits for its answer like a hard mount, however long the backend is gone, so it never returns an error for an effect that lands later. Only a signal that ends the process (`SIGKILL`) or the kernel shutting down ends that wait. JavaScript programs' synchronous `ctx.sync.fs` calls go through their own lane, which waits without a deadline for every call, reads included, so a stalled read hangs there instead of failing with `EIO` (#1033).
|
|
1003
1024
|
- `src/fs/`: the OPFS filesystem and the virtual command directories.
|
|
1004
1025
|
- `src/launcher.ts`, `src/commands.ts`, `src/serve.ts`, `src/index.ts`, `src/users.ts`: command resolution, the kernel worker protocol, the page API and the user database; `src/node.ts`, `src/node-process-worker.ts` and `src/node/` the headless Node entry. An embedder that uses `Launcher` directly, without `createKernel`, calls `await launcher.prepare()` first: it creates `/tmp` and `/home` and writes the CA certificate.
|
|
1005
1026
|
- `src/kernel/net/`: the proxy, HTTP/1.1, TLS termination and the local CA, and the bridge to the page's transport; `src/transport.ts` and `src/local-proxy-transport.ts` are the page side.
|
|
@@ -1024,7 +1045,7 @@ npm run test:unit
|
|
|
1024
1045
|
|
|
1025
1046
|
The recorder behind that coverage changes timing and memory in long runs. It attaches to every page and worker with the debugger, profiler and precise coverage on, and pauses every process worker at its exit breakpoint (`exits` in `test/integration/chrome.mjs`) until the coverage of that worker is collected, keeping every collected script until the run ends. A run with thousands of process exits or hundreds of page loads therefore grows the test process by gigabytes, and each exit waits longer as it grows, so a parent can sit in `proc-exec` for tens of seconds while its child has already finished; each further page load also gets slower (in one tab reloaded 300 times, a boot went from 80 to 500 ms with no kernel in any other tab). When a long run looks stuck or leaky, measure it again with a `launch` that passes `coverage: ['/nothing/']` and no `exits` before blaming the kernel (#991).
|
|
1026
1047
|
|
|
1027
|
-
Unit tests live in `test/unit/`, which stays out of git. They run real wasm bash and coreutils through the whole stack in Node `worker_threads` against an in-memory OPFS. The pre-commit hook runs them under coverage and requires every changed line in `src/` to be covered. `tools/heavy.sh` runs them two files at a time, in one of two unit slots of the host (`/tmp/slicc-unit-N.lock`, waiting while both are taken; a lock it can't take for any other reason, such as a lock file it can't open, ends the run with `lockf`'s status; `SLICC_UNIT_SLOTS` sets how many, `SLICC_UNIT_SLOT=0` takes none, as for CI; a HUP, INT or TERM to `heavy.sh` stops the run it holds or waits for), never in the browser runs' slots (those go through `slicc-heavy`; `SLICC_HEAVY_SLOT=1` runs the unit tests through `~/.bb/coord-bin/slicc-heavy` instead, when it is there), and stops the run if its processes use more than 3,000,000 KB together or 2,000,000 KB in one process (2,800,000 KB for the command itself, the coverage tool that merges the report at the end; `SLICC_HEAVY_ROOT_KB`, never below the per-process cap), or
|
|
1048
|
+
Unit tests live in `test/unit/`, which stays out of git. They run real wasm bash and coreutils through the whole stack in Node `worker_threads` against an in-memory OPFS. The pre-commit hook runs them under coverage and requires every changed line in `src/` to be covered. `tools/heavy.sh` runs them two files at a time, in one of two unit slots of the host (`/tmp/slicc-unit-N.lock`, waiting while both are taken; a lock it can't take for any other reason, such as a lock file it can't open, ends the run with `lockf`'s status; `SLICC_UNIT_SLOTS` sets how many, `SLICC_UNIT_SLOT=0` takes none, as for CI; a HUP, INT or TERM to `heavy.sh` stops the run it holds or waits for), never in the browser runs' slots (those go through `slicc-heavy`; `SLICC_HEAVY_SLOT=1` runs the unit tests through `~/.bb/coord-bin/slicc-heavy` instead, when it is there), and stops the run if its processes use more than 3,000,000 KB together or 2,000,000 KB in one process (2,800,000 KB for the command itself, the coverage tool that merges the report at the end; `SLICC_HEAVY_ROOT_KB`, never below the per-process cap), or make no progress for 900 s once it has its slot, that is, no process of the run starts or ends in that time (`SLICC_HEAVY_TIMEOUT`; each test also has 180 s), so a long selection that keeps moving is never cut off (`SLICC_HEAVY_REPORT=1` prints the peak). `SLICC_HEAVY_TREE_KB` and `SLICC_HEAVY_PROC_KB` raise the caps, but only for measuring a run under a higher ceiling that still stops a runaway, never to get a commit through. The guard follows the command's process group and every process it has seen, including children that leave the group, but a process that forks, calls `setsid` and whose parent exits within one 0.25 s sample can escape: macOS has no unprivileged containment, and a test that daemonizes is a bug. The compiler tests (zig, rustc and cargo), each a few GB, are integration tests in `test/integration/compilers.test.mjs`, which CI runs.
|
|
1028
1049
|
|
|
1029
1050
|
The Biome, TypeScript, lefthook, Renovate and CI configuration comes from [slicc-shared-web](https://github.com/ai-ecoverse/slicc-shared-web), which also provides the `slicc-lint-comments` (no comments anywhere), `slicc-no-unit-tests` (no unit tests in git) and `slicc-diff-cover` (100% coverage of changed lines) commands that `npm run lint` and the pre-commit hook use.
|
|
1030
1051
|
|
|
@@ -1239,16 +1260,16 @@ A WASIX C program for `test/integration/sig-inherited.test.mjs` (#820), built on
|
|
|
1239
1260
|
|
|
1240
1261
|
### `sigjmptest`
|
|
1241
1262
|
|
|
1242
|
-
A WASIX C program for `test/integration/sigjmp.test.mjs` ([#975](https://github.com/ai-ecoverse/slicc-kernel/issues/975), homescoop#1059), built on wasix-sysroot 2025.9.30-
|
|
1263
|
+
A WASIX C program for `test/integration/sigjmp.test.mjs` ([#975](https://github.com/ai-ecoverse/slicc-kernel/issues/975), homescoop#1059), built on wasix-sysroot 2025.9.30-36. Each run takes a mode as its only argument:
|
|
1243
1264
|
- `term`: a `SIGTERM` handler with `SIGUSR1` in its `sa_mask` sends itself `SIGUSR1`, which must wait until the handler returns: `term-done usr1`.
|
|
1244
1265
|
- `longjmp`: `longjmp` out of a `SIGUSR1` handler that has `SIGUSR2` in `sa_mask`. It prints the mask after the jump, then unblocks everything and raises `SIGUSR1` again with a handler that returns.
|
|
1245
1266
|
- `lsleep`, `lnanosleep`, `lread`, `lpoll`: the same with a `SIGALRM` that interrupts `sleep`, `nanosleep`, a `read` of an empty pipe, or `poll`. `sleep`, `nanosleep`, `read`, `poll` (no `l`) and `raise` use `sigsetjmp`/`siglongjmp`.
|
|
1246
1267
|
- `direct`, `directmask`: `siglongjmp` with no signal at all. `fork` is `raise` in a forked child.
|
|
1247
|
-
- `hold` holds `flock` on `sigjmp.lock` for 3 seconds.
|
|
1268
|
+
- `hold` holds `flock` on `sigjmp.lock` for 3 seconds, and creates `sigjmp.held` once it has the lock. `flock` is a `SIGALRM` that interrupts a wait for that lock.
|
|
1248
1269
|
|
|
1249
|
-
|
|
1270
|
+
Up to -35, `sigsetjmp` was an ordinary libc function rather than the `setjmp` that LLVM's wasm setjmp/longjmp lowering rewrites, so every `siglongjmp` escaped the program (`[object WebAssembly.Exception]`, exit 70), even `direct` (homescoop#1059). In -36 it is a `setjmp` call site that records the mask, and `siglongjmp` restores it.
|
|
1250
1271
|
|
|
1251
|
-
**Build:** `
|
|
1272
|
+
**Build:** `WASIX_SYSROOT_36=/path/to/wasix-sysroot ./build.sh`, with `wasixcc` 0.4.7 and binaryen's `wasm-opt` 133 on `PATH`. Asyncify runs with `asyncify-ignore-indirect`, because functions with wasm `try` blocks can't be flattened. `bin/sigjmptest.wasm` is 52,855 bytes, sha256 `0de328b9cbe3f802dd159db200daa973cc857d3daa4227bca652864b55ebb93f`.
|
|
1252
1273
|
|
|
1253
1274
|
### `handlertest`
|
|
1254
1275
|
|