@ai-ecoverse/slicc-kernel 1.116.0 → 1.116.2

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 CHANGED
@@ -589,6 +589,7 @@ Directories are renamed with `FileSystemHandle.move()` where available, else by
589
589
  - **The node's identity, not its name, picks the FIFO:** an open FIFO keeps working after an `unlink` or a `rename`, and a new `mkfifo` at the same name is another FIFO. `/proc/<pid>/fd/N` names the FIFO's path, also in children that inherit the end.
590
590
  - **One kernel per storage root:** the table of open FIFOs is per kernel, so two tabs (two kernels on one origin) opening the same FIFO don't connect, as with record locks ([#675](https://github.com/ai-ecoverse/slicc-kernel/issues/675)).
591
591
  - GNU make 4.4's `-jN` uses a FIFO jobserver this way (`--jobserver-auth=fifo:PATH`), so sub-makes join it by path.
592
+ - **Below an open** ([#908](https://github.com/ai-ecoverse/slicc-kernel/issues/908)): a FIFO or socket node is never read or written as a plain file. A JS program's `ctx.fs.readFile`/`writeFile` and the kernel API's `readFile`/`writeFile` (and the positioned reads, writes and truncates under them) fail with `ENXIO` on either, because they read or write a whole file rather than open one. Emscripten and WASI programs' opens of a FIFO go through the kernel's FIFO open as above, and a WASI program's open of a socket node is `ENXIO`, as on Linux.
592
593
 
593
594
  `mmap` of a regular file works on every file system, for Emscripten programs built with `mmap` (whose glue exports `emscripten_builtin_memalign`) as well as for WASIX ones: the kernel reads the mapped range into fresh memory of the process, as Emscripten's MEMFS does. A `MAP_PRIVATE` mapping may be written, and its changes stay in the process. A writable `MAP_SHARED` mapping of such a file is `ENODEV`; a file in the process's own in-memory file system (MEMFS, as under `/dev`) keeps Emscripten's own mapping, which writes back. A read-only `MAP_SHARED` mapping is a copy taken at `mmap`, so later changes to the file do not show in it.
594
595
 
@@ -802,7 +803,7 @@ The kernel runs scheduled jobs itself, with no daemon process: a cron service st
802
803
  - **`crontab`** is a command of the kernel itself, in `/usr/bin` (a package's `crontab` never wins over it), with Vixie's options: `crontab -l` prints the caller's table, `crontab -r` removes it, `crontab <file>` and `crontab -` (standard input) install one, and `crontab -e` copies the table to `/tmp`, runs `$VISUAL` (or `$EDITOR`) on the copy through `/bin/bash -c '<editor> "$1"'`, and installs it if it changed and the editor exited with 0. If the edit doesn't parse, the copy is kept and its path is printed. `-u <user>` works on another user's table and needs an effective uid of 0 (`sudo crontab -u cone -l`). An install is checked first: a table with a line that doesn't parse is refused with every error (`"-":<line>: <message>`), and the old one stays. The table becomes the user's `0600` file in the spool, written to a dotfile of a random name and renamed, so the service, which skips dotfiles, never reads half a table. The command only reads the file it is given and passes it on (`ctx.crontab`, see [JS programs](#js-programs)). The kernel decides whose table it is from the caller's real uid, so a user reaches only their own table, and `/etc/crontab` stays root's to edit.
803
804
  - **Format:** Vixie's. Five fields (minute, hour, day of month, month, weekday) with lists, ranges, steps and the names `jan`–`dec` and `sun`–`sat` (7 is Sunday too), or one of `@yearly`/`@annually`, `@monthly`, `@weekly`, `@daily`/`@midnight`, `@hourly` and `@reboot`. As in cronie, when the day of month or the weekday starts with `*`, a day must match both fields; when both are restricted, it may match either. `/etc/crontab` has a user field after the schedule; a user's table doesn't. `NAME=value` lines set the environment for the jobs after them, `#` lines are comments, and `# name: <id>` on the line above a job gives it a stable id (letters, digits, `_`, `.`, `-`), which names its log. Without one, the id is a hash of the table, the user, the schedule and the command. `%` is not special. Times are the kernel's local time.
804
805
  - **Environment:** only what a login of that user gets, and nothing from the page or the embedder's `env`. That is `HOME`, `USER`, `LOGNAME`, `PATH`, `PNPM_HOME`, `NPM_CONFIG_PREFIX`, the kernel's network settings (`http_proxy`, `SSL_CERT_FILE`, …), `SHELL`, `PWD`, the table's `NAME=value` lines, and `SLICC_CRON=1`, `SLICC_CRON_JOB=<id>`, `SLICC_CRON_DUE=<ISO time of the due run>` and `SLICC_CRON_MISSED=<runs folded into this one>`. An `agent send` from a job therefore knows it was scheduled.
805
- - **Missed runs:** when the kernel wasn't running (a closed tab) or time jumped forward, every due run since the last one is folded into **one** run, with `SLICC_CRON_MISSED` saying how many more were due (counted up to 100 000). The next run is then computed from now. A clock set back more than two minutes runs nothing until the next due time after the new time.
806
+ - **Missed runs:** when the kernel wasn't running (a closed tab) or time jumped forward, every due run since the last one is folded into **one** run, with `SLICC_CRON_MISSED` saying how many more were due (counted up to 100 000) and `SLICC_CRON_DUE` naming the latest due time, even past the count's cap ([#843](https://github.com/ai-ecoverse/slicc-kernel/issues/843)). The next run is then computed from now. A clock set back more than two minutes runs nothing until the next due time after the new time.
806
807
  - **At most once:** the service writes each due run to `/var/spool/cron/stamp` before it starts the job. A kernel that ends mid-job (a reload, a crash) never runs that due again; a stamp that can't be read counts every job as run just now.
807
808
  - **`@reboot`** runs once per kernel boot, when that kernel first fires jobs.
808
809
  - **Overlap:** a job's runs don't wait for each other, so a slow job can still be running when it is due again. At most 16 jobs of one user run at once; a run past that is skipped, with a line in the job's log and in `/var/log/cron/cron.err`.
@@ -1699,13 +1699,20 @@ var OpfsFs = class {
1699
1699
  if (handle.kind === "directory") throw fsError("EISDIR", path);
1700
1700
  return handle;
1701
1701
  }
1702
+ async refuseNode(path, empty) {
1703
+ if (!empty) return;
1704
+ const [entry] = await this.meta.get([(await this.locate(path, true)).path]);
1705
+ if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
1706
+ }
1702
1707
  async readFileBuffer(path) {
1703
1708
  const handle = await this.existing(path);
1704
- return this.retrying(
1709
+ const bytes = await this.retrying(
1705
1710
  path,
1706
1711
  async () => new Uint8Array(await (await handle.getFile()).arrayBuffer()),
1707
1712
  false
1708
1713
  );
1714
+ await this.refuseNode(path, bytes.length === 0);
1715
+ return bytes;
1709
1716
  }
1710
1717
  async readVersioned(path, cached = false) {
1711
1718
  const handle = await this.existing(path);
@@ -1715,6 +1722,7 @@ var OpfsFs = class {
1715
1722
  const stamp = await this.stamp(path, cached);
1716
1723
  const file = await handle.getFile();
1717
1724
  const bytes = new Uint8Array(await file.arrayBuffer());
1725
+ await this.refuseNode(path, bytes.length === 0);
1718
1726
  return { bytes, version: `${file.lastModified}:${file.size}:${stamp}` };
1719
1727
  },
1720
1728
  false
@@ -1736,7 +1744,11 @@ var OpfsFs = class {
1736
1744
  const handle = await this.existing(path);
1737
1745
  return this.retrying(
1738
1746
  path,
1739
- async () => new Uint8Array(await (await handle.getFile()).slice(offset, offset + length2).arrayBuffer()),
1747
+ async () => {
1748
+ const file = await handle.getFile();
1749
+ await this.refuseNode(path, file.size === 0);
1750
+ return new Uint8Array(await file.slice(offset, offset + length2).arrayBuffer());
1751
+ },
1740
1752
  false
1741
1753
  );
1742
1754
  }
@@ -1752,6 +1764,7 @@ var OpfsFs = class {
1752
1764
  }
1753
1765
  async truncate(path, size) {
1754
1766
  const handle = await this.existing(path, true);
1767
+ await this.refuseNode(path, (await handle.getFile()).size === 0);
1755
1768
  const resolve2 = this.ranged || this.remote?.watched();
1756
1769
  const target = resolve2 || this.counted.length > 0 ? (await this.locate(path, true)).path : path;
1757
1770
  await this.counting(target);
@@ -1804,6 +1817,10 @@ var OpfsFs = class {
1804
1817
  }
1805
1818
  async fileFor(path) {
1806
1819
  const [target, existed] = await this.writeTarget(path);
1820
+ if (existed) {
1821
+ const [entry] = await this.meta.get([target]);
1822
+ if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
1823
+ }
1807
1824
  const [dir, name2] = await this.parent(target);
1808
1825
  try {
1809
1826
  const handle = await this.inDir(
@@ -6241,7 +6258,7 @@ function satisfies(version, range2) {
6241
6258
  }
6242
6259
 
6243
6260
  // src/version.ts
6244
- var KERNEL_VERSION = true ? "1.116.0" : "0.0.0-dev";
6261
+ var KERNEL_VERSION = true ? "1.116.2" : "0.0.0-dev";
6245
6262
  var OS_RELEASE = "6.0.0-slicc";
6246
6263
  function procVersion() {
6247
6264
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -6889,7 +6906,25 @@ function missedFires(schedule, from, until, utc = false, cap = MISSED_CAP) {
6889
6906
  count2++;
6890
6907
  }
6891
6908
  const next3 = nextFire(schedule, last, utc);
6892
- return { count: count2, last, more: next3 !== null && next3 <= until };
6909
+ if (next3 === null || next3 > until) return { count: count2, last, more: false };
6910
+ return { count: count2, last: latestFire(schedule, last, until, utc), more: true };
6911
+ }
6912
+ function latestFire(schedule, from, until, utc = false) {
6913
+ let found = from;
6914
+ for (let span = 6e4; ; span *= 2) {
6915
+ const start2 = Math.max(from, until - span);
6916
+ const first = nextFire(schedule, start2, utc);
6917
+ if (first !== null && first <= until) {
6918
+ found = first;
6919
+ break;
6920
+ }
6921
+ if (start2 === from) return from;
6922
+ }
6923
+ for (let at = nextFire(schedule, found, utc); at !== null && at <= until; ) {
6924
+ found = at;
6925
+ at = nextFire(schedule, at, utc);
6926
+ }
6927
+ return found;
6893
6928
  }
6894
6929
 
6895
6930
  // src/cron/table.ts