@ai-ecoverse/slicc-kernel 1.94.4 → 1.95.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.
@@ -2581,8 +2581,8 @@ async function inParent(g, op, path, old = false) {
2581
2581
  const dir = await access(g.fs, g.cred, parentOf4(path), W_OK | X_OK, linked(g, DIR));
2582
2582
  if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
2583
2583
  if (dir.link) return deny(g, op, dir.denied);
2584
- const lookup = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
2585
- if (!lookup || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
2584
+ const lookup2 = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
2585
+ if (!lookup2 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
2586
2586
  }
2587
2587
  async function makeIn(g, op, path) {
2588
2588
  for (let dir = parentOf4(path); ; dir = parentOf4(dir)) {
@@ -2902,6 +2902,7 @@ function pollFile(file) {
2902
2902
  }
2903
2903
  var OpenFile = class {
2904
2904
  refs = 1;
2905
+ closers;
2905
2906
  file;
2906
2907
  constructor(file) {
2907
2908
  this.file = file;
@@ -2910,9 +2911,18 @@ var OpenFile = class {
2910
2911
  this.refs += 1;
2911
2912
  return this;
2912
2913
  }
2914
+ onClose(fn) {
2915
+ this.closers ??= /* @__PURE__ */ new Set();
2916
+ this.closers.add(fn);
2917
+ return () => void this.closers?.delete(fn);
2918
+ }
2913
2919
  release() {
2914
2920
  this.refs -= 1;
2915
- if (this.refs === 0) return this.file.close();
2921
+ if (this.refs !== 0) return;
2922
+ const closers = this.closers;
2923
+ this.closers = void 0;
2924
+ for (const fn of closers ?? []) fn();
2925
+ return this.file.close();
2916
2926
  }
2917
2927
  };
2918
2928
  function openPipe(capacity) {
@@ -3460,7 +3470,8 @@ var KernelSocket = class _KernelSocket {
3460
3470
  return {
3461
3471
  readable: this.readShut || rx.readReady,
3462
3472
  writable: this.writeShut || tx.writeReady,
3463
- hangup: rx.writersGone && tx.readersGone
3473
+ hangup: rx.writersGone && tx.readersGone,
3474
+ rdhup: rx.writersGone
3464
3475
  };
3465
3476
  }
3466
3477
  changed(signal) {
@@ -5139,7 +5150,7 @@ function clipboardAsker(port) {
5139
5150
  function integrityAsker(port) {
5140
5151
  const asks = /* @__PURE__ */ new Map();
5141
5152
  let next2 = 0;
5142
- const lookup = (path) => {
5153
+ const lookup2 = (path) => {
5143
5154
  const id2 = ++next2;
5144
5155
  const waiting = Promise.withResolvers();
5145
5156
  asks.set(id2, waiting);
@@ -5153,7 +5164,7 @@ function integrityAsker(port) {
5153
5164
  if (error !== void 0) waiting.reject(new Error(error));
5154
5165
  else waiting.resolve(hash);
5155
5166
  };
5156
- return { lookup, answer };
5167
+ return { lookup: lookup2, answer };
5157
5168
  }
5158
5169
 
5159
5170
  // src/kernel/net/loopback-names.ts
@@ -11712,7 +11723,7 @@ function mountTable(mounts = []) {
11712
11723
  }
11713
11724
 
11714
11725
  // src/version.ts
11715
- var KERNEL_VERSION = true ? "1.94.4" : "0.0.0-dev";
11726
+ var KERNEL_VERSION = true ? "1.95.0" : "0.0.0-dev";
11716
11727
  var OS_RELEASE = "6.0.0-slicc";
11717
11728
  function procVersion() {
11718
11729
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -12099,6 +12110,295 @@ async function procAlias(fs, path, follow2) {
12099
12110
  return step.value;
12100
12111
  }
12101
12112
 
12113
+ // src/kernel/eventfd.ts
12114
+ var EVENTFD_MAX = 0xfffffffffffffffen;
12115
+ var Notifier = class {
12116
+ waiters = [];
12117
+ wait(signal) {
12118
+ if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
12119
+ return new Promise((resolve2, reject) => {
12120
+ const onAbort = () => {
12121
+ this.waiters = this.waiters.filter((w) => w !== waiter);
12122
+ reject(new KernelError("EINTR"));
12123
+ };
12124
+ const waiter = () => {
12125
+ signal?.removeEventListener("abort", onAbort);
12126
+ resolve2();
12127
+ };
12128
+ this.waiters.push(waiter);
12129
+ signal?.addEventListener("abort", onAbort, { once: true });
12130
+ });
12131
+ }
12132
+ wake() {
12133
+ const waiters = this.waiters;
12134
+ this.waiters = [];
12135
+ for (const resolve2 of waiters) resolve2();
12136
+ }
12137
+ };
12138
+ function blocked(signal) {
12139
+ if (signal?.aborted) throw new KernelError("EAGAIN");
12140
+ }
12141
+ var EventFd = class _EventFd {
12142
+ static SIZE = 8;
12143
+ count;
12144
+ semaphore;
12145
+ changes = new Notifier();
12146
+ constructor(initial, semaphore) {
12147
+ this.count = initial;
12148
+ this.semaphore = semaphore;
12149
+ }
12150
+ async read(max, signal) {
12151
+ if (max < _EventFd.SIZE) throw new KernelError("EINVAL");
12152
+ while (this.count === 0n) {
12153
+ blocked(signal);
12154
+ await this.changes.wait(signal);
12155
+ }
12156
+ const value = this.semaphore ? 1n : this.count;
12157
+ this.count -= value;
12158
+ this.changes.wake();
12159
+ const out = new Uint8Array(_EventFd.SIZE);
12160
+ new DataView(out.buffer).setBigUint64(0, value, true);
12161
+ return out;
12162
+ }
12163
+ async write(bytes, signal) {
12164
+ if (bytes.length < _EventFd.SIZE) throw new KernelError("EINVAL");
12165
+ const value = new DataView(bytes.buffer, bytes.byteOffset, _EventFd.SIZE).getBigUint64(0, true);
12166
+ if (value > EVENTFD_MAX) throw new KernelError("EINVAL");
12167
+ while (EVENTFD_MAX - this.count < value) {
12168
+ blocked(signal);
12169
+ await this.changes.wait(signal);
12170
+ }
12171
+ this.count += value;
12172
+ if (value > 0n) this.changes.wake();
12173
+ return _EventFd.SIZE;
12174
+ }
12175
+ poll() {
12176
+ return { readable: this.count > 0n, writable: this.count < EVENTFD_MAX, hangup: false };
12177
+ }
12178
+ changed(signal) {
12179
+ return this.changes.wait(signal);
12180
+ }
12181
+ close() {
12182
+ this.changes.wake();
12183
+ }
12184
+ };
12185
+
12186
+ // src/kernel/epoll.ts
12187
+ var EPOLL = {
12188
+ IN: 1,
12189
+ OUT: 2,
12190
+ RDHUP: 4,
12191
+ PRI: 8,
12192
+ ERR: 16,
12193
+ HUP: 32,
12194
+ ET: 64,
12195
+ ONESHOT: 128
12196
+ };
12197
+ var ALWAYS = EPOLL.ERR | EPOLL.HUP;
12198
+ var ASKED = EPOLL.IN | EPOLL.OUT | EPOLL.RDHUP;
12199
+ var RETRY_MS = 50;
12200
+ var UNPOLLABLE = /* @__PURE__ */ new Set(["file", "held", "device"]);
12201
+ var EPOLL_OPS = [
12202
+ "epoll-create",
12203
+ "epoll-ctl",
12204
+ "epoll-wait",
12205
+ "eventfd-open"
12206
+ ];
12207
+ function levelOf(file) {
12208
+ const state = pollFile(file);
12209
+ let bits2 = 0;
12210
+ if (state.readable) bits2 |= EPOLL.IN;
12211
+ if (state.writable) bits2 |= EPOLL.OUT;
12212
+ if (state.rdhup) bits2 |= EPOLL.RDHUP;
12213
+ if (state.hangup) bits2 |= file.read ? EPOLL.HUP : EPOLL.ERR;
12214
+ return bits2;
12215
+ }
12216
+ function due(interest, level) {
12217
+ if (!interest.armed) return 0;
12218
+ const ready2 = level & (interest.events & ASKED | ALWAYS);
12219
+ if (ready2 === 0) return 0;
12220
+ if ((interest.events & EPOLL.ET) !== 0 && interest.reported === interest.gen) return 0;
12221
+ return ready2;
12222
+ }
12223
+ function consume(interest) {
12224
+ interest.reported = interest.gen;
12225
+ if ((interest.events & EPOLL.ONESHOT) !== 0) interest.armed = false;
12226
+ }
12227
+ function lookup(fds, fd) {
12228
+ try {
12229
+ return fds.get(fd);
12230
+ } catch {
12231
+ return void 0;
12232
+ }
12233
+ }
12234
+ function pause(ms, signal) {
12235
+ return new Promise((resolve2) => {
12236
+ const timer = setTimeout(resolve2, ms);
12237
+ signal.addEventListener(
12238
+ "abort",
12239
+ () => {
12240
+ clearTimeout(timer);
12241
+ resolve2();
12242
+ },
12243
+ { once: true }
12244
+ );
12245
+ });
12246
+ }
12247
+ var EpollFile = class _EpollFile {
12248
+ regs = /* @__PURE__ */ new Map();
12249
+ changes = new Notifier();
12250
+ cursor = 0;
12251
+ ctl(fds, how, fd, events, data) {
12252
+ const open = fds.get(fd);
12253
+ if (open.file === this) throw new KernelError("EINVAL");
12254
+ const known = this.regs.get(fd);
12255
+ const current = known?.open === open ? known : void 0;
12256
+ if (how === "add" || how === "set" && !current) {
12257
+ if (current) throw new KernelError("EEXIST");
12258
+ if (UNPOLLABLE.has(kernelFdKind(open.file))) throw new KernelError("EPERM");
12259
+ if (open.file instanceof _EpollFile && open.file.reaches(this)) throw new KernelError("ELOOP");
12260
+ if (known) this.drop(known);
12261
+ this.register(fd, open, events, data);
12262
+ } else if (!current) {
12263
+ throw new KernelError("ENOENT");
12264
+ } else if (how === "del") {
12265
+ this.drop(current);
12266
+ } else {
12267
+ current.events = events;
12268
+ current.data = data;
12269
+ current.armed = true;
12270
+ current.gen++;
12271
+ }
12272
+ this.changes.wake();
12273
+ }
12274
+ reaches(target) {
12275
+ if (this === target) return true;
12276
+ return [...this.regs.values()].some(
12277
+ (reg) => reg.open.file instanceof _EpollFile && reg.open.file.reaches(target)
12278
+ );
12279
+ }
12280
+ collect(fds, max) {
12281
+ const list = [...this.regs.values()];
12282
+ const out = [];
12283
+ const start2 = list.length === 0 ? 0 : this.cursor % list.length;
12284
+ for (let i = 0; i < list.length && out.length < max; i++) {
12285
+ const reg = list[(start2 + i) % list.length];
12286
+ if (lookup(fds, reg.fd) !== reg.open) {
12287
+ this.drop(reg);
12288
+ continue;
12289
+ }
12290
+ const bits2 = due(reg, levelOf(reg.open.file));
12291
+ if (bits2 === 0) continue;
12292
+ consume(reg);
12293
+ out.push({ events: bits2, data: reg.data });
12294
+ this.cursor = start2 + i + 1;
12295
+ }
12296
+ return out;
12297
+ }
12298
+ async wait(fds, max, timeoutMs, signal) {
12299
+ const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : Date.now() + timeoutMs;
12300
+ for (; ; ) {
12301
+ const events = this.collect(fds, max);
12302
+ if (events.length > 0 || Date.now() >= deadline) return events;
12303
+ const round = new AbortController();
12304
+ const woken = this.changes.wait(AbortSignal.any([round.signal, signal]));
12305
+ woken.catch(() => void 0);
12306
+ let timer;
12307
+ const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve2) => timer = setTimeout(resolve2, deadline - Date.now()))];
12308
+ try {
12309
+ await Promise.race([woken, ...expiry]);
12310
+ } finally {
12311
+ round.abort();
12312
+ clearTimeout(timer);
12313
+ }
12314
+ }
12315
+ }
12316
+ poll() {
12317
+ const ready2 = [...this.regs.values()].some((reg) => due(reg, levelOf(reg.open.file)) !== 0);
12318
+ return { readable: ready2, writable: false, hangup: false };
12319
+ }
12320
+ changed(signal) {
12321
+ return this.changes.wait(signal);
12322
+ }
12323
+ close() {
12324
+ for (const reg of [...this.regs.values()]) this.drop(reg);
12325
+ this.changes.wake();
12326
+ }
12327
+ register(fd, open, events, data) {
12328
+ const stop = new AbortController();
12329
+ const unhook = open.onClose(() => {
12330
+ this.drop(reg);
12331
+ this.changes.wake();
12332
+ });
12333
+ const reg = {
12334
+ fd,
12335
+ open,
12336
+ events,
12337
+ data,
12338
+ gen: 1,
12339
+ reported: 0,
12340
+ armed: true,
12341
+ stop,
12342
+ unhook
12343
+ };
12344
+ this.regs.set(fd, reg);
12345
+ void this.watch(reg);
12346
+ }
12347
+ drop(reg) {
12348
+ reg.stop.abort();
12349
+ reg.unhook();
12350
+ if (this.regs.get(reg.fd) === reg) this.regs.delete(reg.fd);
12351
+ }
12352
+ async watch(reg) {
12353
+ const file = reg.open.file;
12354
+ const { signal } = reg.stop;
12355
+ while (file.changed && !signal.aborted) {
12356
+ const round = new AbortController();
12357
+ const stop = () => round.abort();
12358
+ signal.addEventListener("abort", stop, { once: true });
12359
+ let failed3 = false;
12360
+ try {
12361
+ await file.changed(round.signal);
12362
+ } catch {
12363
+ failed3 = true;
12364
+ } finally {
12365
+ round.abort();
12366
+ signal.removeEventListener("abort", stop);
12367
+ }
12368
+ if (signal.aborted) return;
12369
+ reg.gen++;
12370
+ this.changes.wake();
12371
+ if (failed3) await pause(RETRY_MS, signal);
12372
+ }
12373
+ }
12374
+ };
12375
+ function epollAt(fds, fd) {
12376
+ const file = fds.get(fd).file;
12377
+ if (!(file instanceof EpollFile)) throw new KernelError("EINVAL");
12378
+ return file;
12379
+ }
12380
+ var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
12381
+ async function epollSyscall(req, proc) {
12382
+ const { fds } = proc;
12383
+ switch (req.op) {
12384
+ case "epoll-create":
12385
+ return ok(fds.install(new OpenFile(new EpollFile()), 3));
12386
+ case "eventfd-open":
12387
+ return ok(fds.install(new OpenFile(new EventFd(BigInt(req.initial), req.semaphore)), 3));
12388
+ case "epoll-ctl":
12389
+ epollAt(fds, req.epfd).ctl(fds, req.how, req.fd, req.events, req.data);
12390
+ return ok();
12391
+ case "epoll-wait": {
12392
+ const ep = epollAt(fds, req.epfd);
12393
+ if (req.timeoutMs === 0 || proc.owed()) {
12394
+ const now = ep.collect(fds, req.max);
12395
+ if (req.timeoutMs === 0 || now.length > 0) return ok(now);
12396
+ }
12397
+ return ok(await ep.wait(fds, req.max, req.timeoutMs, proc.blocking()));
12398
+ }
12399
+ }
12400
+ }
12401
+
12102
12402
  // src/kernel/host-ops.ts
12103
12403
  var HOST_OPS = [
12104
12404
  "lock",
@@ -12989,14 +13289,14 @@ var Resolver = class {
12989
13289
  if (hit && hit.expires > this.now()) return hit.addresses;
12990
13290
  const signal = blocking?.();
12991
13291
  if (signal?.aborted) throw new KernelError("EINTR");
12992
- let lookup = this.pending.get(id2);
12993
- if (!lookup) {
13292
+ let lookup2 = this.pending.get(id2);
13293
+ if (!lookup2) {
12994
13294
  const abort = new AbortController();
12995
- lookup = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
12996
- this.pending.set(id2, lookup);
12997
- void lookup.work.then(() => this.pending.delete(id2));
13295
+ lookup2 = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
13296
+ this.pending.set(id2, lookup2);
13297
+ void lookup2.work.then(() => this.pending.delete(id2));
12998
13298
  }
12999
- return waitFor(lookup, signal);
13299
+ return waitFor(lookup2, signal);
13000
13300
  }
13001
13301
  async lookup(name2, family, id2, abandoned) {
13002
13302
  const uplink = this.uplink;
@@ -13037,17 +13337,17 @@ var Resolver = class {
13037
13337
  }
13038
13338
  }
13039
13339
  };
13040
- function waitFor(lookup, signal) {
13041
- lookup.waiters++;
13340
+ function waitFor(lookup2, signal) {
13341
+ lookup2.waiters++;
13042
13342
  return new Promise((resolve2, reject) => {
13043
13343
  const abort = () => {
13044
- if (--lookup.waiters === 0) lookup.abort.abort(new KernelError("EINTR"));
13344
+ if (--lookup2.waiters === 0) lookup2.abort.abort(new KernelError("EINTR"));
13045
13345
  reject(new KernelError("EINTR"));
13046
13346
  };
13047
13347
  signal?.addEventListener("abort", abort, { once: true });
13048
- void lookup.work.then((value) => {
13348
+ void lookup2.work.then((value) => {
13049
13349
  if (signal?.aborted) return;
13050
- lookup.waiters--;
13350
+ lookup2.waiters--;
13051
13351
  signal?.removeEventListener("abort", abort);
13052
13352
  resolve2(value);
13053
13353
  });
@@ -13694,17 +13994,17 @@ function socketAt(fds, fd) {
13694
13994
  if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
13695
13995
  return file;
13696
13996
  }
13697
- var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
13997
+ var ok2 = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
13698
13998
  async function socketSyscall(req, proc) {
13699
13999
  const { fds, net } = proc;
13700
14000
  switch (req.op) {
13701
14001
  case "sock-open":
13702
- return ok(fds.install(new OpenFile(net.socket(req.domain)), 3));
14002
+ return ok2(fds.install(new OpenFile(net.socket(req.domain)), 3));
13703
14003
  case "sock-pair": {
13704
14004
  const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
13705
14005
  const first = fds.install(new OpenFile(a), 3);
13706
14006
  try {
13707
- return ok([first, fds.install(new OpenFile(b), 3)]);
14007
+ return ok2([first, fds.install(new OpenFile(b), 3)]);
13708
14008
  } catch (e) {
13709
14009
  await Promise.resolve(fds.close(first));
13710
14010
  throw e;
@@ -13712,12 +14012,12 @@ async function socketSyscall(req, proc) {
13712
14012
  }
13713
14013
  case "sock-bind":
13714
14014
  socketAt(fds, req.fd).bind(req.addr);
13715
- return ok();
14015
+ return ok2();
13716
14016
  case "sock-listen":
13717
14017
  socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
13718
- return ok();
14018
+ return ok2();
13719
14019
  case "sock-accept":
13720
- return ok(await accept(proc, req.fd, req.nonblock));
14020
+ return ok2(await accept(proc, req.fd, req.nonblock));
13721
14021
  case "sock-connect": {
13722
14022
  const socket = socketAt(fds, req.fd);
13723
14023
  const stamp = net.facadeAt(req.addr) && await proc.stamp?.();
@@ -13727,26 +14027,26 @@ async function socketSyscall(req, proc) {
13727
14027
  throw new KernelError("EINPROGRESS");
13728
14028
  }
13729
14029
  if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
13730
- return ok();
14030
+ return ok2();
13731
14031
  }
13732
14032
  case "sock-shutdown":
13733
14033
  socketAt(fds, req.fd).shutdown(req.how);
13734
- return ok();
14034
+ return ok2();
13735
14035
  case "sock-name": {
13736
14036
  const socket = socketAt(fds, req.fd);
13737
14037
  const addr = req.peer ? socket.peer : socket.local;
13738
14038
  if (req.peer && !addr) throw new KernelError("ENOTCONN");
13739
- return ok(addr ?? unnamed(socket.domain));
14039
+ return ok2(addr ?? unnamed(socket.domain));
13740
14040
  }
13741
14041
  case "sock-getopt":
13742
- return ok(socketAt(fds, req.fd).getOption(req.level, req.name));
14042
+ return ok2(socketAt(fds, req.fd).getOption(req.level, req.name));
13743
14043
  case "sock-setopt":
13744
14044
  socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
13745
- return ok();
14045
+ return ok2();
13746
14046
  case "sock-resolve": {
13747
14047
  const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
13748
14048
  if (addresses.length === 0) throw new KernelError("ENOENT");
13749
- return ok(addresses);
14049
+ return ok2(addresses);
13750
14050
  }
13751
14051
  }
13752
14052
  }
@@ -14447,6 +14747,9 @@ function isHttpSyscall(req) {
14447
14747
  function isSocketSyscall(req) {
14448
14748
  return req.op.startsWith("sock-");
14449
14749
  }
14750
+ function isEpollSyscall(req) {
14751
+ return EPOLL_OPS.includes(req.op);
14752
+ }
14450
14753
  function isHostSyscall(req) {
14451
14754
  return HOST_OPS.includes(req.op);
14452
14755
  }
@@ -14537,6 +14840,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
14537
14840
  "sig-mask",
14538
14841
  "sig-pause",
14539
14842
  ...SOCKET_OPS,
14843
+ ...EPOLL_OPS,
14540
14844
  ...HTTP_OPS,
14541
14845
  ...PTY_OPS,
14542
14846
  ...HOST_OPS
@@ -14651,9 +14955,9 @@ var WasmProcess = class {
14651
14955
  this.options.onPending?.(sig);
14652
14956
  if (this.options.jobs?.keyboard && KEYBOARD.has(sig) && this.waiting.size === 0)
14653
14957
  this.owed = true;
14654
- const blocked = this.interrupt;
14958
+ const blocked2 = this.interrupt;
14655
14959
  this.interrupt = new AbortController();
14656
- blocked.abort();
14960
+ blocked2.abort();
14657
14961
  return "deliver";
14658
14962
  }
14659
14963
  async spawn(req) {
@@ -14712,9 +15016,9 @@ var WasmProcess = class {
14712
15016
  this.stopped = sig;
14713
15017
  this.stops++;
14714
15018
  this.resumed = new Promise((resolve2) => this.wake = resolve2);
14715
- const blocked = this.interrupt;
15019
+ const blocked2 = this.interrupt;
14716
15020
  this.interrupt = new AbortController();
14717
- blocked.abort();
15021
+ blocked2.abort();
14718
15022
  for (const listener of this.stateListeners) listener("stopped", sig);
14719
15023
  return "stop";
14720
15024
  }
@@ -14764,6 +15068,13 @@ var WasmProcess = class {
14764
15068
  if (isTtySyscall(req)) return this.ttySyscall(req);
14765
15069
  if (isJobSyscall(req)) return this.jobSyscall(req);
14766
15070
  if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
15071
+ if (isEpollSyscall(req)) {
15072
+ return await epollSyscall(req, {
15073
+ fds: this.fds,
15074
+ blocking: () => this.blockingSignal(cancelled),
15075
+ owed: () => this.owed
15076
+ });
15077
+ }
14767
15078
  if (isHttpSyscall(req)) return await this.httpSyscall(req);
14768
15079
  if (isHostSyscall(req)) {
14769
15080
  return await hostSyscall(req, {
@@ -14830,6 +15141,7 @@ var WasmProcess = class {
14830
15141
  if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
14831
15142
  if (file.tty) this.checkForeground(file.tty);
14832
15143
  if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
15144
+ if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
14833
15145
  if (req.max <= 0) return new Uint8Array(0);
14834
15146
  if (file instanceof KernelSocket && file.unconnected) throw new KernelError("ENOTCONN");
14835
15147
  const ready2 = pollFile(file).readable;
@@ -15677,9 +15989,9 @@ var WasmProcess = class {
15677
15989
  }
15678
15990
  timerExpired(which) {
15679
15991
  this.options.onTimer?.(which);
15680
- const blocked = this.interrupt;
15992
+ const blocked2 = this.interrupt;
15681
15993
  this.interrupt = new AbortController();
15682
- blocked.abort();
15994
+ blocked2.abort();
15683
15995
  }
15684
15996
  clearAlarm() {
15685
15997
  clearTimeout(this.alarm);
@@ -15783,9 +16095,9 @@ function killThread(thread, caught, sig) {
15783
16095
  Atomics.or(thread.header, SAB_I_SIGNALS, sigbit(sig));
15784
16096
  if (held) return true;
15785
16097
  Atomics.notify(thread.header, SAB_I_SIGNALS);
15786
- const blocked = thread.interrupt;
16098
+ const blocked2 = thread.interrupt;
15787
16099
  thread.interrupt = new AbortController();
15788
- blocked.abort();
16100
+ blocked2.abort();
15789
16101
  return true;
15790
16102
  }
15791
16103
  function unblocked(header3) {
@@ -15964,8 +16276,8 @@ function threadInit(opts, program, sab, cred, thread, main) {
15964
16276
  function spawnWasmProcess(opts) {
15965
16277
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
15966
16278
  const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
15967
- const blocked = opts.fork?.wasi?.blocked ?? opts.blocked;
15968
- if (blocked && anyBlocked(blocked) && opts.program.abi === "wasi") storeBlocked(header3, blocked);
16279
+ const blocked2 = opts.fork?.wasi?.blocked ?? opts.blocked;
16280
+ if (blocked2 && anyBlocked(blocked2) && opts.program.abi === "wasi") storeBlocked(header3, blocked2);
15969
16281
  const threadSignals = /* @__PURE__ */ new Map();
15970
16282
  const created3 = new Created();
15971
16283
  const process = new WasmProcess(
@@ -19820,6 +20132,8 @@ var Launcher = class {
19820
20132
  const head = await this.head(glue);
19821
20133
  if (!head) return void 0;
19822
20134
  if (isWasm(head)) {
20135
+ const owner2 = await this.wasmOwner(glue, baseName(file).replace(/\.wasm$/, ""), scope);
20136
+ if (owner2) return { ...targetOf(owner2), ...asked };
19823
20137
  return {
19824
20138
  abi: "wasi",
19825
20139
  glue,
@@ -19842,6 +20156,17 @@ var Launcher = class {
19842
20156
  );
19843
20157
  return owners.find((c) => c.name === name2) ?? owners[0];
19844
20158
  }
20159
+ async wasmOwner(wasm, name2, scope) {
20160
+ const leaf = baseName(wasm);
20161
+ const real = await physicalPathAsync(this.base, wasm);
20162
+ const owners = [];
20163
+ for (const command of (await this.commands(scope)).values()) {
20164
+ if (command.abi !== "wasi" || command.script || baseName(command.wasm) !== leaf) continue;
20165
+ if (command.wasm === wasm || await physicalPathAsync(this.base, command.wasm) === real)
20166
+ owners.push(command);
20167
+ }
20168
+ return owners.find((c) => c.name === name2) ?? owners[0];
20169
+ }
19845
20170
  async head(path) {
19846
20171
  try {
19847
20172
  return (await this.base.readFileBuffer(path)).subarray(0, SHEBANG_MAX);
@@ -19929,16 +20254,29 @@ var Launcher = class {
19929
20254
  for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
19930
20255
  }
19931
20256
  async checkedPlan(req, { ppid, cred, scope }, fds) {
19932
- const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
19933
- try {
19934
- await this.mayExec(cred, { ...req, pid: ppid }, planned);
19935
- } catch (err) {
20257
+ const refused2 = async (err) => {
19936
20258
  await fds.closeAll();
19937
20259
  throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
19938
- }
20260
+ };
20261
+ const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(refused2);
20262
+ const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0);
20263
+ await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused2);
19939
20264
  if (planned) return planned;
19940
20265
  await fds.closeAll();
19941
- throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
20266
+ throw new SpawnError(await this.unrunnable(file, req.cwd, scope));
20267
+ }
20268
+ execGuard(cred, pid, tag) {
20269
+ const { dac } = this;
20270
+ return dac && { fs: this.fs, cred, pid, dac, tag, links: this.links.get(pid) };
20271
+ }
20272
+ async execFile(file, cwd, g) {
20273
+ if (!file.split("/").includes("..")) return file;
20274
+ const path = file.startsWith("/") ? file : `${cwd}/${file}`;
20275
+ const slash = path.lastIndexOf("/");
20276
+ const dir = path.slice(0, slash);
20277
+ const walked = g && await need(g, "exec", dir, X_OK, { directory: true });
20278
+ const real = walked && walked.denied === void 0 ? walked.path : await physicalPathAsync(this.base, dir);
20279
+ return joinName(real, path.slice(slash + 1));
19942
20280
  }
19943
20281
  async ticketFile(req, fds) {
19944
20282
  if (req.file.startsWith("/")) return physicalPathAsync(this.base, req.file);
@@ -20037,7 +20375,8 @@ var Launcher = class {
20037
20375
  }
20038
20376
  return manifests;
20039
20377
  }
20040
- async backing(file, cwd) {
20378
+ async backing(asked, cwd) {
20379
+ const file = await this.execFile(asked, cwd);
20041
20380
  const planned = await this.plan(file, [file], cwd, "");
20042
20381
  return planned && this.backingOf(planned, file, cwd, "");
20043
20382
  }
@@ -20674,8 +21013,8 @@ var Launcher = class {
20674
21013
  }
20675
21014
  signalMasksOf(pid) {
20676
21015
  const masks = this.processes.get(pid)?.signalMasks?.();
20677
- const [blocked] = this.blockedBy(pid);
20678
- const sigBlk = blocked ? { sigBlk: blocked } : {};
21016
+ const [blocked2] = this.blockedBy(pid);
21017
+ const sigBlk = blocked2 ? { sigBlk: blocked2 } : {};
20679
21018
  return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught, ...sigBlk } : sigBlk;
20680
21019
  }
20681
21020
  controlsJobs(pid) {
@@ -21123,7 +21462,7 @@ var Launcher = class {
21123
21462
  const scope = account.uid === 0 ? "" : pnpmHomeOf(account, this.pnpmHome);
21124
21463
  const start2 = dir ?? "/";
21125
21464
  const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
21126
- const [file = ""] = argv;
21465
+ const file = await this.execFile(argv[0] ?? "", cwd, this.execGuard(credOf(account), 0, tag));
21127
21466
  if (!await this.base.exists(cwd)) await this.stackFor(tag).fs.mkdir(cwd, { recursive: true });
21128
21467
  const planned = await this.plan(file, argv, cwd, scope);
21129
21468
  await this.mayExec(credOf(account), { file, cwd, pid: 0, tag }, planned);
@@ -21416,19 +21755,19 @@ function usersOp(l, req) {
21416
21755
  if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
21417
21756
  return l.users.list();
21418
21757
  }
21419
- function hostOptions(req, port, sealed, clipboard, lookup) {
21758
+ function hostOptions(req, port, sealed, clipboard, lookup2) {
21420
21759
  return {
21421
21760
  ...req.hostname !== void 0 ? { hostname: req.hostname } : {},
21422
21761
  ...req.users !== void 0 ? { users: req.users } : {},
21423
21762
  ...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
21424
- ...req.protect ? { protect: hostProtect(req.protect, lookup) } : {},
21763
+ ...req.protect ? { protect: hostProtect(req.protect, lookup2) } : {},
21425
21764
  ...req.sealed ? { sealed } : {},
21426
21765
  ...req.clipboard ? { clipboard } : {}
21427
21766
  };
21428
21767
  }
21429
- function hostProtect(init, lookup) {
21768
+ function hostProtect(init, lookup2) {
21430
21769
  const { integrity, ...rest2 } = protectInit(init);
21431
- return { ...rest2, ...integrity ? { integrity: { ...integrity, lookup } } : {} };
21770
+ return { ...rest2, ...integrity ? { integrity: { ...integrity, lookup: lookup2 } } : {} };
21432
21771
  }
21433
21772
  function policyAsker(port) {
21434
21773
  const policies = /* @__PURE__ */ new Map();