@ai-ecoverse/slicc-kernel 1.64.0 → 1.64.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.
@@ -1484,6 +1484,11 @@ var Created = class {
1484
1484
  function deny(g, op, path, errno = "EACCES") {
1485
1485
  g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno });
1486
1486
  }
1487
+ var TTY_MODE = 400;
1488
+ function ttyOpenable(g, tty) {
1489
+ if (!g || !tty.owner || permits(g.cred, tty.owner, R_OK | W_OK, false)) return;
1490
+ deny(g, "open", tty.name ?? "/dev/tty");
1491
+ }
1487
1492
  async function need(g, op, path, want, options = {}, shown = path) {
1488
1493
  const walked = await access(g.fs, g.cred, path, want, options);
1489
1494
  if (walked.denied !== void 0) deny(g, op, shown);
@@ -3196,10 +3201,18 @@ var UserDb = class {
3196
3201
  };
3197
3202
  }
3198
3203
  async lookup(user) {
3204
+ return (await this.lookupTerminal(user))?.account;
3205
+ }
3206
+ async ttyGroup() {
3207
+ return parsed(parseGroup(await this.read(GROUP, DEFAULT_GROUP))).find((g) => g.name === "tty")?.gid;
3208
+ }
3209
+ async lookupTerminal(user) {
3199
3210
  const { passwd, group } = await this.tables();
3200
3211
  const key = typeof user === "number" ? "uid" : "name";
3201
3212
  const row = parsed(passwd).find((r) => r[key] === user);
3202
- return row && this.account(row, parsed(group));
3213
+ const groups = parsed(group);
3214
+ const tty = groups.find((g) => g.name === "tty")?.gid;
3215
+ return row && { account: this.account(row, groups), tty };
3203
3216
  }
3204
3217
  async list() {
3205
3218
  const { passwd, group } = await this.tables();
@@ -8840,8 +8853,10 @@ var EARLY_WHOLE_FILE = 64 * 1024;
8840
8853
 
8841
8854
  // src/process/process-fds.ts
8842
8855
  var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
8856
+ var pinnedRoot;
8857
+ var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
8843
8858
  function mountTable(mounts = []) {
8844
- const root = globalThis.navigator?.storage ? "opfs" : "memory";
8859
+ const root = pinnedRoot ?? rootKind();
8845
8860
  const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
8846
8861
  for (const m of mounts) {
8847
8862
  const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
@@ -9816,6 +9831,7 @@ var KernelTty = class {
9816
9831
  waiters = [];
9817
9832
  hungUp = false;
9818
9833
  name;
9834
+ owner;
9819
9835
  onRead;
9820
9836
  readers = 0;
9821
9837
  screen;
@@ -10159,10 +10175,11 @@ var PtyTable = class {
10159
10175
  this.signal = signal;
10160
10176
  this.onRead = onRead;
10161
10177
  }
10162
- open() {
10178
+ open(owner) {
10163
10179
  let index = 0;
10164
10180
  while (this.pairs.has(index)) index += 1;
10165
10181
  const pair = new PtyPair(index, this.signal, (p) => this.pairs.delete(p.index), this.onRead);
10182
+ if (owner) pair.slave.owner = { uid: owner.uid, gid: owner.gid, mode: TTY_MODE };
10166
10183
  this.pairs.set(index, pair);
10167
10184
  return { pair, master: pair.master() };
10168
10185
  }
@@ -10175,6 +10192,10 @@ var PtyTable = class {
10175
10192
  return [...this.pairs.keys()].sort((a, b) => a - b);
10176
10193
  }
10177
10194
  };
10195
+ function ptsNumber(path) {
10196
+ const m = /^\/dev\/pts\/(\d+)$/.exec(path);
10197
+ return m ? Number(m[1]) : void 0;
10198
+ }
10178
10199
  var PTY_OPS = [
10179
10200
  "pty-open",
10180
10201
  "pty-slave-open",
@@ -10202,11 +10223,12 @@ function ptySyscall(req, ctx) {
10202
10223
  switch (req.op) {
10203
10224
  case "pty-open": {
10204
10225
  if (!ctx.ptys) throw new KernelError("ENOENT");
10205
- return json4(ctx.fds.install(ctx.ptys.open().master, 3));
10226
+ return json4(ctx.fds.install(ctx.ptys.open(ctx.owner).master, 3));
10206
10227
  }
10207
10228
  case "pty-slave-open": {
10208
10229
  if (!ctx.ptys) throw new KernelError("ENOENT");
10209
10230
  const pair = ctx.ptys.get(req.n);
10231
+ ttyOpenable(ctx.guard, pair.slave);
10210
10232
  const fd = ctx.fds.install(pair.openSlave(), 3);
10211
10233
  if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
10212
10234
  return json4(fd);
@@ -11022,6 +11044,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
11022
11044
  "fd-promote",
11023
11045
  "fd-open-tty",
11024
11046
  "fd-tty-names",
11047
+ "fd-tty-owner",
11025
11048
  "tty-get",
11026
11049
  "tty-set",
11027
11050
  "tty-winsz",
@@ -11223,7 +11246,9 @@ var WasmProcess = class {
11223
11246
  }
11224
11247
  if (isPtySyscall(req)) {
11225
11248
  const { ptys, jobs } = this.options;
11226
- return ptySyscall(req, { pid: this.pid, fds: this.fds, ptys, jobs });
11249
+ const owner = this.options.owners ? this.ttyOwner() : void 0;
11250
+ const guard2 = this.guarded();
11251
+ return ptySyscall(req, { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner });
11227
11252
  }
11228
11253
  return await this.procSyscall(req, cancelled);
11229
11254
  } catch (e) {
@@ -11355,6 +11380,25 @@ var WasmProcess = class {
11355
11380
  const want = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
11356
11381
  await openable(g, "open", req.path, want, req.create === true);
11357
11382
  }
11383
+ openTty(name2) {
11384
+ const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
11385
+ if (!tty) throw new KernelError("ENXIO");
11386
+ if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
11387
+ return this.fds.install(tty.file(), 3);
11388
+ }
11389
+ guarded() {
11390
+ const { guard: guard2 } = this.options;
11391
+ return guard2 && { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid };
11392
+ }
11393
+ ttyOwner() {
11394
+ return { uid: this.cred.euid, gid: this.cred.egid, mode: TTY_MODE };
11395
+ }
11396
+ terminalByName(name2) {
11397
+ const n = ptsNumber(name2);
11398
+ const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) : this.options.ptys?.get(n).slave;
11399
+ if (!tty) throw new KernelError("ENOENT");
11400
+ return tty;
11401
+ }
11358
11402
  newOwner() {
11359
11403
  if (!this.options.owners) return void 0;
11360
11404
  return { uid: this.cred.euid, gid: this.cred.egid, mode: 438 & ~this.umask };
@@ -11458,13 +11502,12 @@ var WasmProcess = class {
11458
11502
  if (!req.keep) await Promise.resolve(this.fds.close(req.from));
11459
11503
  }
11460
11504
  return { ok: true, kind: "void" };
11461
- case "fd-open-tty": {
11462
- const tty = req.name === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(req.name);
11463
- if (!tty) throw new KernelError("ENXIO");
11464
- return { ok: true, kind: "json", json: this.fds.install(tty.file(), 3) };
11465
- }
11505
+ case "fd-open-tty":
11506
+ return { ok: true, kind: "json", json: this.openTty(req.name) };
11466
11507
  case "fd-tty-names":
11467
11508
  return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
11509
+ case "fd-tty-owner":
11510
+ return { ok: true, kind: "json", json: this.terminalByName(req.name).owner ?? null };
11468
11511
  case "fd-select": {
11469
11512
  const { read, write, timeoutMs } = req;
11470
11513
  if (this.owed && timeoutMs !== 0) {
@@ -12145,7 +12188,7 @@ function spawnWasmProcess(opts) {
12145
12188
  finish(...dying.status(data.code));
12146
12189
  } else if (data?.type === WASM_PROCESS_ERROR) {
12147
12190
  if (!dying.ending) opts.onError?.(String(data.message));
12148
- finish(...dying.status(CRASHED));
12191
+ finish(...dying.status(data.code));
12149
12192
  } else if (data?.type === WASM_THREAD_SPAWN && data.thread) {
12150
12193
  spawnThread(data.thread);
12151
12194
  }
@@ -15086,7 +15129,12 @@ var Launcher = class {
15086
15129
  }
15087
15130
  async account(user) {
15088
15131
  if (typeof user === "object") return user;
15089
- const found = await this.users.lookup(user ?? 0);
15132
+ return (await this.terminalAccount(user)).account;
15133
+ }
15134
+ async terminalAccount(user) {
15135
+ if (typeof user === "object")
15136
+ return { account: user, tty: this.owners ? await this.users.ttyGroup() : void 0 };
15137
+ const found = await this.users.lookupTerminal(user ?? 0);
15090
15138
  if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
15091
15139
  return found;
15092
15140
  }
@@ -15108,7 +15156,7 @@ var Launcher = class {
15108
15156
  return { cwd, file, planned };
15109
15157
  }
15110
15158
  async openTerminal(argv, options) {
15111
- const account = await this.account(options.user);
15159
+ const { account, tty: ttyGroup } = await this.terminalAccount(options.user);
15112
15160
  const { cwd, file, planned } = await this.starting(argv, options.cwd, account);
15113
15161
  if (!planned) throw new Error(`${file}: command not found`);
15114
15162
  let leader = 0;
@@ -15118,6 +15166,7 @@ var Launcher = class {
15118
15166
  );
15119
15167
  tty.onRead = () => this.settling.reading(this.jobs.tcgetpgrp(tty, leader));
15120
15168
  tty.name = `/dev/tty${++this.terminals}`;
15169
+ if (this.owners) tty.owner = { uid: account.uid, gid: ttyGroup ?? account.gid, mode: TTY_MODE };
15121
15170
  tty.setSize(options.cols ?? 80, options.rows ?? 24);
15122
15171
  const fds = new FdTable();
15123
15172
  const stdio = tty.file();