@ai-ecoverse/slicc-kernel 1.34.1 → 1.35.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/README.md CHANGED
@@ -421,6 +421,22 @@ An exec'd program takes over the pid of the process it replaces, as on Linux: `g
421
421
  - **Emscripten:** `Module.sliccKernel.execve(file, argv, env, cwd)` runs `file` as this process's new image on its fds 0, 1 and 2, waits for it, and returns its wait status, or a negative errno when it cannot start. `env` and `cwd` may be `null` for this process's own. An exec shim detects it with `typeof Module.sliccKernel.execve === 'function'` and exits with the status it returns, as `execve` never returns on success. Older shims spawn the program and then call `Module.sliccKernel.execWait(pid)`; that works the same way: a new program asks the kernel for its pid before `main`, and the kernel answers once its parent has made its next call, so an image an older shim execs also reports the pid it replaced (the wait starts once the program is loaded; should the parent make no call within 100 ms after that, the kernel answers anyway, and the image may then report its own pid; `launcher.identities` counts the programs that asked and those answered by that timeout).
422
422
  - **WASIX:** `proc_exec` does this by itself.
423
423
 
424
+ ### File modes and the umask
425
+
426
+ Every process has a umask in the kernel, `022` for a process the embedder starts, inherited across fork, spawn and exec and shown in `/proc/<pid>/status` (`Umask:`). The kernel applies it when a program creates a file or directory, whatever its libc does: an Emscripten program's `open` with `O_CREAT`, `mkdir` and `mknod` (so `umask 077; touch f` gives `600` in a child too), and a WASI program's `path_open` with `O_CREAT` and `path_create_directory` (`0666` and `0777` less the umask).
427
+
428
+ WASI and WASIX have no call for file modes, so the kernel adds the import module `slicc_fs`, whose functions return a preview1 errno (kernels without them answer `ENOSYS`):
429
+
430
+ | Import | Does |
431
+ | --- | --- |
432
+ | `fd_chmod(fd, mode)` | `fchmod` on an open file or directory |
433
+ | `path_chmod(dirfd, path, path_len, mode, flags)` | `fchmodat`; with `flags & 1` (`AT_SYMLINK_NOFOLLOW`) a symlink answers `ENOTSUP` and is never followed |
434
+ | `umask(mask, old_out)` | sets the process's umask and writes the previous one |
435
+ | `fd_mode(fd, mode_out)` | the permission bits of an open file or directory |
436
+ | `path_mode(dirfd, path, path_len, flags, mode_out)` | those of a path, of the link itself with `flags & 1` |
437
+
438
+ Lowering a soft resource limit (`ulimit -S -n 64`) still fails with `EPERM` in Emscripten programs: their libc's `setrlimit` is a stub compiled into the program, not a call to the kernel.
439
+
424
440
  ### `/proc`
425
441
 
426
442
  Every process of the kernel has `/proc/<pid>/` with `cmdline`, `comm`, `stat`, `statm` and `status`, in the formats procps reads, and `/proc/self` is a link to the `/proc/<pid>` of the process that reads it, as on Linux (`readlink /proc/self` is its pid, the one it was exec'd under included). They come from the kernel's process table when they are opened, so a terminal sees the processes of every client; an exec'd program shows under the pid its parent knows. `/proc/uptime`, `/proc/loadavg`, `/proc/stat` and `/proc/meminfo` are there too, and `/proc/mounts`. A process with no parent in the kernel (one a page, a client or the Node entry started) has parent 1, as children of init do, in `getppid()`, `/proc/<pid>/stat` and `ps` alike, and so does a process whose parent has ended, waited for or not (`getppid()` asks the kernel each time; a shell's `$PPID` keeps the value it started with, as on Linux). Pids, parents, process groups, sessions, command lines, terminals, start and boot times are real, and so is each process's memory: the size of its wasm memory (`VSZ` and `RSS` alike, as wasm has no paging), which `/proc/meminfo` counts as used. CPU times, load and `MemTotal` (from `navigator.deviceMemory`, else 4 GiB) are placeholders. Only Emscripten processes see this `/proc`.
@@ -7857,10 +7857,12 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
7857
7857
  "fd-cloexec",
7858
7858
  "fd-list",
7859
7859
  "proc-alarm",
7860
+ "proc-umask",
7860
7861
  "dl-log",
7861
7862
  "fd-renumber",
7862
7863
  "fd-promote",
7863
7864
  "fd-open-tty",
7865
+ "fd-tty-names",
7864
7866
  "tty-get",
7865
7867
  "tty-set",
7866
7868
  "tty-winsz",
@@ -7894,6 +7896,7 @@ function isWasmSyscall(req) {
7894
7896
  return typeof op === "string" && SYSCALL_OPS.has(op);
7895
7897
  }
7896
7898
  var MAX_READ = 1024 * 1024;
7899
+ var DEFAULT_UMASK = 18;
7897
7900
  var DYING = { ok: false, errno: "EINTR", message: "the process is being killed" };
7898
7901
  var HOSTS_ONLY = new Resolver({ routes: new Routes() });
7899
7902
  var WasmProcess = class {
@@ -7904,6 +7907,7 @@ var WasmProcess = class {
7904
7907
  inherited = 0;
7905
7908
  interrupt = new AbortController();
7906
7909
  dying = false;
7910
+ umask = DEFAULT_UMASK;
7907
7911
  execChild;
7908
7912
  execTermsig;
7909
7913
  stopped = 0;
@@ -7921,6 +7925,7 @@ var WasmProcess = class {
7921
7925
  this.fds = fds;
7922
7926
  this.options = options;
7923
7927
  this.inherited = options.ignored ?? 0;
7928
+ this.umask = options.umask ?? DEFAULT_UMASK;
7924
7929
  this.children = new ChildTable(fds, options.spawner, options.forker);
7925
7930
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
7926
7931
  if (!options.nodes) options.openFiles?.add(this.nodes);
@@ -7993,6 +7998,11 @@ var WasmProcess = class {
7993
7998
  return result;
7994
7999
  }
7995
8000
  }
8001
+ setUmask(mask) {
8002
+ const old = this.umask;
8003
+ if (mask !== void 0) this.umask = mask & 511;
8004
+ return old;
8005
+ }
7996
8006
  die() {
7997
8007
  if (this.dying) return;
7998
8008
  this.dying = true;
@@ -8157,6 +8167,8 @@ var WasmProcess = class {
8157
8167
  if (!tty) throw new KernelError("ENXIO");
8158
8168
  return { ok: true, kind: "json", json: this.fds.install(tty.file(), 3) };
8159
8169
  }
8170
+ case "fd-tty-names":
8171
+ return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
8160
8172
  case "fd-select": {
8161
8173
  const { read, write, timeoutMs } = req;
8162
8174
  const signal = this.blockingSignal();
@@ -8388,6 +8400,8 @@ var WasmProcess = class {
8388
8400
  case "proc-alarm":
8389
8401
  this.setAlarm(req.sig, req.firstMs ?? req.ms, req.repeat ? req.ms : 0, req.timer);
8390
8402
  return { ok: true, kind: "void" };
8403
+ case "proc-umask":
8404
+ return { ok: true, kind: "json", json: this.setUmask(req.mask) };
8391
8405
  case "dl-log":
8392
8406
  if (req.append) this.dlLog.push(req.append);
8393
8407
  return { ok: true, kind: "json", json: this.dlLog.slice(req.from) };
@@ -8537,7 +8551,7 @@ var Dying = class {
8537
8551
  return [typeof reported === "number" ? reported : CRASHED, void 0];
8538
8552
  }
8539
8553
  };
8540
- function processInit(opts, sab) {
8554
+ function processInit(opts, sab, umask) {
8541
8555
  const base = {
8542
8556
  type: WASM_PROCESS_INIT,
8543
8557
  pid: opts.shownPid ?? opts.pid,
@@ -8546,6 +8560,7 @@ function processInit(opts, sab) {
8546
8560
  env: opts.env,
8547
8561
  cwd: opts.cwd,
8548
8562
  sab,
8563
+ umask,
8549
8564
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {}
8550
8565
  };
8551
8566
  if (opts.program.abi === "js") {
@@ -8557,11 +8572,17 @@ function processInit(opts, sab) {
8557
8572
  ...opts.fork ? { fork: opts.fork } : { fds: inheritedFds(opts.fds) }
8558
8573
  };
8559
8574
  }
8575
+ function inherited(opts) {
8576
+ return {
8577
+ ...opts.ignored ? { ignored: opts.ignored } : {},
8578
+ ...opts.umask !== void 0 ? { umask: opts.umask } : {}
8579
+ };
8580
+ }
8560
8581
  function spawnWasmProcess(opts) {
8561
8582
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
8562
8583
  const header2 = new Int32Array(sab, 0, SAB_HEADER_I32);
8563
8584
  const process = new WasmProcess(opts.pid, opts.fds, {
8564
- ...opts.ignored ? { ignored: opts.ignored } : {},
8585
+ ...inherited(opts),
8565
8586
  ...opts.identity ? { identity: opts.identity } : {},
8566
8587
  ...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
8567
8588
  spawner: opts.spawner,
@@ -8695,7 +8716,7 @@ function spawnWasmProcess(opts) {
8695
8716
  };
8696
8717
  tw.postMessage(tinit);
8697
8718
  };
8698
- const init = processInit(opts, sab);
8719
+ const init = processInit(opts, sab, process.umask);
8699
8720
  worker.postMessage(init, opts.fork ? [opts.fork.memory.buffer] : []);
8700
8721
  const signal = (sig) => {
8701
8722
  if (process.signal(sig) !== "terminate") return;
@@ -8713,6 +8734,7 @@ function spawnWasmProcess(opts) {
8713
8734
  onState: (listener) => process.onState(listener),
8714
8735
  memory: () => Atomics.load(header2, SAB_I_MEMORY) * 65536,
8715
8736
  ignoredSignals: (fork) => process.inheritable(fork),
8737
+ umask: () => process.umask,
8716
8738
  writesBack: () => opts.program.abi === "wasi" && !done4
8717
8739
  };
8718
8740
  }
@@ -8818,8 +8840,13 @@ var JobTable = class {
8818
8840
  return id;
8819
8841
  }
8820
8842
  terminalNamed(name2) {
8821
- const known = /* @__PURE__ */ new Set([...this.terminals.values(), ...this.foreground.keys()]);
8822
- return [...known].find((tty) => tty.name === name2);
8843
+ return this.knownTerminals().find((tty) => tty.name === name2);
8844
+ }
8845
+ terminalNames() {
8846
+ return this.knownTerminals().flatMap((tty) => tty.name ? [tty.name] : []);
8847
+ }
8848
+ knownTerminals() {
8849
+ return [.../* @__PURE__ */ new Set([...this.terminals.values(), ...this.foreground.keys()])];
8823
8850
  }
8824
8851
  controllingTerminal(pid) {
8825
8852
  const member = this.members.get(pid);
@@ -10453,6 +10480,7 @@ var Launcher = class {
10453
10480
  this.settling.syscall(pid, call.op);
10454
10481
  },
10455
10482
  ...req.ignored ? { ignored: req.ignored } : {},
10483
+ ...req.umask !== void 0 ? { umask: req.umask } : {},
10456
10484
  program: req.program,
10457
10485
  argv0: req.argv0,
10458
10486
  args: req.args,
@@ -10544,7 +10572,8 @@ var Launcher = class {
10544
10572
  ...described,
10545
10573
  started: (this.described.get(root.pid) ?? described).started,
10546
10574
  state: this.processes.has(member.pid) ? "S" : "Z",
10547
- memory: this.processes.get(member.pid)?.memory() ?? 0
10575
+ memory: this.processes.get(member.pid)?.memory() ?? 0,
10576
+ umask: this.umaskOf(member.pid)
10548
10577
  });
10549
10578
  }
10550
10579
  return listed;
@@ -10566,6 +10595,7 @@ var Launcher = class {
10566
10595
  report,
10567
10596
  ppid,
10568
10597
  ignored: this.ignoredBy(ppid),
10598
+ umask: this.umaskOf(ppid),
10569
10599
  ...exec
10570
10600
  });
10571
10601
  deciding?.attach();
@@ -10610,7 +10640,8 @@ var Launcher = class {
10610
10640
  fds,
10611
10641
  ppid,
10612
10642
  fork: state,
10613
- ignored: this.ignoredBy(ppid, true)
10643
+ ignored: this.ignoredBy(ppid, true),
10644
+ umask: this.umaskOf(ppid)
10614
10645
  })
10615
10646
  );
10616
10647
  }
@@ -10619,6 +10650,9 @@ var Launcher = class {
10619
10650
  const foreground = () => this.jobs.tcgetpgrp(tty, fallback);
10620
10651
  this.settling.deliver(sig, pgid, send, tty.reading ? void 0 : foreground);
10621
10652
  }
10653
+ umaskOf(pid) {
10654
+ return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
10655
+ }
10622
10656
  ignoredBy(pid, fork = false) {
10623
10657
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
10624
10658
  }