@ai-ecoverse/slicc-kernel 1.94.3 → 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.
- package/README.md +85 -3
- package/dist/kernel-worker.js +417 -59
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +108 -21
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +416 -58
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +108 -21
- package/dist/process-worker.js.map +4 -4
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -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
|
|
2585
|
-
if (!
|
|
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
|
|
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) {
|
|
@@ -4245,6 +4256,8 @@ var UserDb = class {
|
|
|
4245
4256
|
owners;
|
|
4246
4257
|
umasks = /* @__PURE__ */ new Map();
|
|
4247
4258
|
writing = Promise.resolve();
|
|
4259
|
+
stable;
|
|
4260
|
+
commits = 0;
|
|
4248
4261
|
constructor(fs, owners = false) {
|
|
4249
4262
|
this.fs = fs;
|
|
4250
4263
|
this.owners = owners;
|
|
@@ -4280,11 +4293,18 @@ var UserDb = class {
|
|
|
4280
4293
|
throw err;
|
|
4281
4294
|
}
|
|
4282
4295
|
}
|
|
4296
|
+
async texts() {
|
|
4297
|
+
for (; ; ) {
|
|
4298
|
+
if (this.stable) return this.stable;
|
|
4299
|
+
const commits = this.commits;
|
|
4300
|
+
const passwd = await this.read(PASSWD, DEFAULT_PASSWD);
|
|
4301
|
+
const group = await this.read(GROUP, DEFAULT_GROUP);
|
|
4302
|
+
if (this.commits === commits && !this.stable) return [passwd, group];
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4283
4305
|
async tables() {
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
group: parseGroup(await this.read(GROUP, DEFAULT_GROUP))
|
|
4287
|
-
};
|
|
4306
|
+
const [passwd, group] = await this.texts();
|
|
4307
|
+
return { passwd: parsePasswd(passwd), group: parseGroup(group) };
|
|
4288
4308
|
}
|
|
4289
4309
|
async provision(row, umask, shared) {
|
|
4290
4310
|
const dirs = 511 & ~umask | (shared ? 1024 : 0);
|
|
@@ -4303,15 +4323,25 @@ var UserDb = class {
|
|
|
4303
4323
|
}
|
|
4304
4324
|
}
|
|
4305
4325
|
async commit(passwd, group, then) {
|
|
4306
|
-
const before = [
|
|
4326
|
+
const before = [
|
|
4327
|
+
await this.read(PASSWD, DEFAULT_PASSWD),
|
|
4328
|
+
await this.read(GROUP, DEFAULT_GROUP)
|
|
4329
|
+
];
|
|
4330
|
+
this.stable = before;
|
|
4331
|
+
this.commits++;
|
|
4307
4332
|
try {
|
|
4308
4333
|
await this.fs.writeFile(GROUP, group);
|
|
4309
4334
|
await this.fs.writeFile(PASSWD, passwd);
|
|
4335
|
+
this.stable = void 0;
|
|
4310
4336
|
await then?.();
|
|
4311
4337
|
} catch (err) {
|
|
4338
|
+
this.stable = before;
|
|
4312
4339
|
await this.fs.writeFile(GROUP, before[1]);
|
|
4313
4340
|
await this.fs.writeFile(PASSWD, before[0]);
|
|
4314
4341
|
throw err;
|
|
4342
|
+
} finally {
|
|
4343
|
+
this.stable = void 0;
|
|
4344
|
+
this.commits++;
|
|
4315
4345
|
}
|
|
4316
4346
|
}
|
|
4317
4347
|
serial(work) {
|
|
@@ -5120,7 +5150,7 @@ function clipboardAsker(port) {
|
|
|
5120
5150
|
function integrityAsker(port) {
|
|
5121
5151
|
const asks = /* @__PURE__ */ new Map();
|
|
5122
5152
|
let next2 = 0;
|
|
5123
|
-
const
|
|
5153
|
+
const lookup2 = (path) => {
|
|
5124
5154
|
const id2 = ++next2;
|
|
5125
5155
|
const waiting = Promise.withResolvers();
|
|
5126
5156
|
asks.set(id2, waiting);
|
|
@@ -5134,7 +5164,7 @@ function integrityAsker(port) {
|
|
|
5134
5164
|
if (error !== void 0) waiting.reject(new Error(error));
|
|
5135
5165
|
else waiting.resolve(hash);
|
|
5136
5166
|
};
|
|
5137
|
-
return { lookup, answer };
|
|
5167
|
+
return { lookup: lookup2, answer };
|
|
5138
5168
|
}
|
|
5139
5169
|
|
|
5140
5170
|
// src/kernel/net/loopback-names.ts
|
|
@@ -11693,7 +11723,7 @@ function mountTable(mounts = []) {
|
|
|
11693
11723
|
}
|
|
11694
11724
|
|
|
11695
11725
|
// src/version.ts
|
|
11696
|
-
var KERNEL_VERSION = true ? "1.
|
|
11726
|
+
var KERNEL_VERSION = true ? "1.95.0" : "0.0.0-dev";
|
|
11697
11727
|
var OS_RELEASE = "6.0.0-slicc";
|
|
11698
11728
|
function procVersion() {
|
|
11699
11729
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -12080,6 +12110,295 @@ async function procAlias(fs, path, follow2) {
|
|
|
12080
12110
|
return step.value;
|
|
12081
12111
|
}
|
|
12082
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
|
+
|
|
12083
12402
|
// src/kernel/host-ops.ts
|
|
12084
12403
|
var HOST_OPS = [
|
|
12085
12404
|
"lock",
|
|
@@ -12970,14 +13289,14 @@ var Resolver = class {
|
|
|
12970
13289
|
if (hit && hit.expires > this.now()) return hit.addresses;
|
|
12971
13290
|
const signal = blocking?.();
|
|
12972
13291
|
if (signal?.aborted) throw new KernelError("EINTR");
|
|
12973
|
-
let
|
|
12974
|
-
if (!
|
|
13292
|
+
let lookup2 = this.pending.get(id2);
|
|
13293
|
+
if (!lookup2) {
|
|
12975
13294
|
const abort = new AbortController();
|
|
12976
|
-
|
|
12977
|
-
this.pending.set(id2,
|
|
12978
|
-
void
|
|
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));
|
|
12979
13298
|
}
|
|
12980
|
-
return waitFor(
|
|
13299
|
+
return waitFor(lookup2, signal);
|
|
12981
13300
|
}
|
|
12982
13301
|
async lookup(name2, family, id2, abandoned) {
|
|
12983
13302
|
const uplink = this.uplink;
|
|
@@ -13018,17 +13337,17 @@ var Resolver = class {
|
|
|
13018
13337
|
}
|
|
13019
13338
|
}
|
|
13020
13339
|
};
|
|
13021
|
-
function waitFor(
|
|
13022
|
-
|
|
13340
|
+
function waitFor(lookup2, signal) {
|
|
13341
|
+
lookup2.waiters++;
|
|
13023
13342
|
return new Promise((resolve2, reject) => {
|
|
13024
13343
|
const abort = () => {
|
|
13025
|
-
if (--
|
|
13344
|
+
if (--lookup2.waiters === 0) lookup2.abort.abort(new KernelError("EINTR"));
|
|
13026
13345
|
reject(new KernelError("EINTR"));
|
|
13027
13346
|
};
|
|
13028
13347
|
signal?.addEventListener("abort", abort, { once: true });
|
|
13029
|
-
void
|
|
13348
|
+
void lookup2.work.then((value) => {
|
|
13030
13349
|
if (signal?.aborted) return;
|
|
13031
|
-
|
|
13350
|
+
lookup2.waiters--;
|
|
13032
13351
|
signal?.removeEventListener("abort", abort);
|
|
13033
13352
|
resolve2(value);
|
|
13034
13353
|
});
|
|
@@ -13675,17 +13994,17 @@ function socketAt(fds, fd) {
|
|
|
13675
13994
|
if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
|
|
13676
13995
|
return file;
|
|
13677
13996
|
}
|
|
13678
|
-
var
|
|
13997
|
+
var ok2 = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
|
|
13679
13998
|
async function socketSyscall(req, proc) {
|
|
13680
13999
|
const { fds, net } = proc;
|
|
13681
14000
|
switch (req.op) {
|
|
13682
14001
|
case "sock-open":
|
|
13683
|
-
return
|
|
14002
|
+
return ok2(fds.install(new OpenFile(net.socket(req.domain)), 3));
|
|
13684
14003
|
case "sock-pair": {
|
|
13685
14004
|
const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
|
|
13686
14005
|
const first = fds.install(new OpenFile(a), 3);
|
|
13687
14006
|
try {
|
|
13688
|
-
return
|
|
14007
|
+
return ok2([first, fds.install(new OpenFile(b), 3)]);
|
|
13689
14008
|
} catch (e) {
|
|
13690
14009
|
await Promise.resolve(fds.close(first));
|
|
13691
14010
|
throw e;
|
|
@@ -13693,12 +14012,12 @@ async function socketSyscall(req, proc) {
|
|
|
13693
14012
|
}
|
|
13694
14013
|
case "sock-bind":
|
|
13695
14014
|
socketAt(fds, req.fd).bind(req.addr);
|
|
13696
|
-
return
|
|
14015
|
+
return ok2();
|
|
13697
14016
|
case "sock-listen":
|
|
13698
14017
|
socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
|
|
13699
|
-
return
|
|
14018
|
+
return ok2();
|
|
13700
14019
|
case "sock-accept":
|
|
13701
|
-
return
|
|
14020
|
+
return ok2(await accept(proc, req.fd, req.nonblock));
|
|
13702
14021
|
case "sock-connect": {
|
|
13703
14022
|
const socket = socketAt(fds, req.fd);
|
|
13704
14023
|
const stamp = net.facadeAt(req.addr) && await proc.stamp?.();
|
|
@@ -13708,26 +14027,26 @@ async function socketSyscall(req, proc) {
|
|
|
13708
14027
|
throw new KernelError("EINPROGRESS");
|
|
13709
14028
|
}
|
|
13710
14029
|
if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
|
|
13711
|
-
return
|
|
14030
|
+
return ok2();
|
|
13712
14031
|
}
|
|
13713
14032
|
case "sock-shutdown":
|
|
13714
14033
|
socketAt(fds, req.fd).shutdown(req.how);
|
|
13715
|
-
return
|
|
14034
|
+
return ok2();
|
|
13716
14035
|
case "sock-name": {
|
|
13717
14036
|
const socket = socketAt(fds, req.fd);
|
|
13718
14037
|
const addr = req.peer ? socket.peer : socket.local;
|
|
13719
14038
|
if (req.peer && !addr) throw new KernelError("ENOTCONN");
|
|
13720
|
-
return
|
|
14039
|
+
return ok2(addr ?? unnamed(socket.domain));
|
|
13721
14040
|
}
|
|
13722
14041
|
case "sock-getopt":
|
|
13723
|
-
return
|
|
14042
|
+
return ok2(socketAt(fds, req.fd).getOption(req.level, req.name));
|
|
13724
14043
|
case "sock-setopt":
|
|
13725
14044
|
socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
|
|
13726
|
-
return
|
|
14045
|
+
return ok2();
|
|
13727
14046
|
case "sock-resolve": {
|
|
13728
14047
|
const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
|
|
13729
14048
|
if (addresses.length === 0) throw new KernelError("ENOENT");
|
|
13730
|
-
return
|
|
14049
|
+
return ok2(addresses);
|
|
13731
14050
|
}
|
|
13732
14051
|
}
|
|
13733
14052
|
}
|
|
@@ -14428,6 +14747,9 @@ function isHttpSyscall(req) {
|
|
|
14428
14747
|
function isSocketSyscall(req) {
|
|
14429
14748
|
return req.op.startsWith("sock-");
|
|
14430
14749
|
}
|
|
14750
|
+
function isEpollSyscall(req) {
|
|
14751
|
+
return EPOLL_OPS.includes(req.op);
|
|
14752
|
+
}
|
|
14431
14753
|
function isHostSyscall(req) {
|
|
14432
14754
|
return HOST_OPS.includes(req.op);
|
|
14433
14755
|
}
|
|
@@ -14518,6 +14840,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
14518
14840
|
"sig-mask",
|
|
14519
14841
|
"sig-pause",
|
|
14520
14842
|
...SOCKET_OPS,
|
|
14843
|
+
...EPOLL_OPS,
|
|
14521
14844
|
...HTTP_OPS,
|
|
14522
14845
|
...PTY_OPS,
|
|
14523
14846
|
...HOST_OPS
|
|
@@ -14632,9 +14955,9 @@ var WasmProcess = class {
|
|
|
14632
14955
|
this.options.onPending?.(sig);
|
|
14633
14956
|
if (this.options.jobs?.keyboard && KEYBOARD.has(sig) && this.waiting.size === 0)
|
|
14634
14957
|
this.owed = true;
|
|
14635
|
-
const
|
|
14958
|
+
const blocked2 = this.interrupt;
|
|
14636
14959
|
this.interrupt = new AbortController();
|
|
14637
|
-
|
|
14960
|
+
blocked2.abort();
|
|
14638
14961
|
return "deliver";
|
|
14639
14962
|
}
|
|
14640
14963
|
async spawn(req) {
|
|
@@ -14693,9 +15016,9 @@ var WasmProcess = class {
|
|
|
14693
15016
|
this.stopped = sig;
|
|
14694
15017
|
this.stops++;
|
|
14695
15018
|
this.resumed = new Promise((resolve2) => this.wake = resolve2);
|
|
14696
|
-
const
|
|
15019
|
+
const blocked2 = this.interrupt;
|
|
14697
15020
|
this.interrupt = new AbortController();
|
|
14698
|
-
|
|
15021
|
+
blocked2.abort();
|
|
14699
15022
|
for (const listener of this.stateListeners) listener("stopped", sig);
|
|
14700
15023
|
return "stop";
|
|
14701
15024
|
}
|
|
@@ -14745,6 +15068,13 @@ var WasmProcess = class {
|
|
|
14745
15068
|
if (isTtySyscall(req)) return this.ttySyscall(req);
|
|
14746
15069
|
if (isJobSyscall(req)) return this.jobSyscall(req);
|
|
14747
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
|
+
}
|
|
14748
15078
|
if (isHttpSyscall(req)) return await this.httpSyscall(req);
|
|
14749
15079
|
if (isHostSyscall(req)) {
|
|
14750
15080
|
return await hostSyscall(req, {
|
|
@@ -14811,6 +15141,7 @@ var WasmProcess = class {
|
|
|
14811
15141
|
if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
|
|
14812
15142
|
if (file.tty) this.checkForeground(file.tty);
|
|
14813
15143
|
if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
|
|
15144
|
+
if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
|
|
14814
15145
|
if (req.max <= 0) return new Uint8Array(0);
|
|
14815
15146
|
if (file instanceof KernelSocket && file.unconnected) throw new KernelError("ENOTCONN");
|
|
14816
15147
|
const ready2 = pollFile(file).readable;
|
|
@@ -15658,9 +15989,9 @@ var WasmProcess = class {
|
|
|
15658
15989
|
}
|
|
15659
15990
|
timerExpired(which) {
|
|
15660
15991
|
this.options.onTimer?.(which);
|
|
15661
|
-
const
|
|
15992
|
+
const blocked2 = this.interrupt;
|
|
15662
15993
|
this.interrupt = new AbortController();
|
|
15663
|
-
|
|
15994
|
+
blocked2.abort();
|
|
15664
15995
|
}
|
|
15665
15996
|
clearAlarm() {
|
|
15666
15997
|
clearTimeout(this.alarm);
|
|
@@ -15764,9 +16095,9 @@ function killThread(thread, caught, sig) {
|
|
|
15764
16095
|
Atomics.or(thread.header, SAB_I_SIGNALS, sigbit(sig));
|
|
15765
16096
|
if (held) return true;
|
|
15766
16097
|
Atomics.notify(thread.header, SAB_I_SIGNALS);
|
|
15767
|
-
const
|
|
16098
|
+
const blocked2 = thread.interrupt;
|
|
15768
16099
|
thread.interrupt = new AbortController();
|
|
15769
|
-
|
|
16100
|
+
blocked2.abort();
|
|
15770
16101
|
return true;
|
|
15771
16102
|
}
|
|
15772
16103
|
function unblocked(header3) {
|
|
@@ -15945,8 +16276,8 @@ function threadInit(opts, program, sab, cred, thread, main) {
|
|
|
15945
16276
|
function spawnWasmProcess(opts) {
|
|
15946
16277
|
const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
15947
16278
|
const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
|
|
15948
|
-
const
|
|
15949
|
-
if (
|
|
16279
|
+
const blocked2 = opts.fork?.wasi?.blocked ?? opts.blocked;
|
|
16280
|
+
if (blocked2 && anyBlocked(blocked2) && opts.program.abi === "wasi") storeBlocked(header3, blocked2);
|
|
15950
16281
|
const threadSignals = /* @__PURE__ */ new Map();
|
|
15951
16282
|
const created3 = new Created();
|
|
15952
16283
|
const process = new WasmProcess(
|
|
@@ -19801,6 +20132,8 @@ var Launcher = class {
|
|
|
19801
20132
|
const head = await this.head(glue);
|
|
19802
20133
|
if (!head) return void 0;
|
|
19803
20134
|
if (isWasm(head)) {
|
|
20135
|
+
const owner2 = await this.wasmOwner(glue, baseName(file).replace(/\.wasm$/, ""), scope);
|
|
20136
|
+
if (owner2) return { ...targetOf(owner2), ...asked };
|
|
19804
20137
|
return {
|
|
19805
20138
|
abi: "wasi",
|
|
19806
20139
|
glue,
|
|
@@ -19823,6 +20156,17 @@ var Launcher = class {
|
|
|
19823
20156
|
);
|
|
19824
20157
|
return owners.find((c) => c.name === name2) ?? owners[0];
|
|
19825
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
|
+
}
|
|
19826
20170
|
async head(path) {
|
|
19827
20171
|
try {
|
|
19828
20172
|
return (await this.base.readFileBuffer(path)).subarray(0, SHEBANG_MAX);
|
|
@@ -19910,16 +20254,29 @@ var Launcher = class {
|
|
|
19910
20254
|
for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
|
|
19911
20255
|
}
|
|
19912
20256
|
async checkedPlan(req, { ppid, cred, scope }, fds) {
|
|
19913
|
-
const
|
|
19914
|
-
try {
|
|
19915
|
-
await this.mayExec(cred, { ...req, pid: ppid }, planned);
|
|
19916
|
-
} catch (err) {
|
|
20257
|
+
const refused2 = async (err) => {
|
|
19917
20258
|
await fds.closeAll();
|
|
19918
20259
|
throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
|
|
19919
|
-
}
|
|
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);
|
|
19920
20264
|
if (planned) return planned;
|
|
19921
20265
|
await fds.closeAll();
|
|
19922
|
-
throw new SpawnError(await this.unrunnable(
|
|
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));
|
|
19923
20280
|
}
|
|
19924
20281
|
async ticketFile(req, fds) {
|
|
19925
20282
|
if (req.file.startsWith("/")) return physicalPathAsync(this.base, req.file);
|
|
@@ -20018,7 +20375,8 @@ var Launcher = class {
|
|
|
20018
20375
|
}
|
|
20019
20376
|
return manifests;
|
|
20020
20377
|
}
|
|
20021
|
-
async backing(
|
|
20378
|
+
async backing(asked, cwd) {
|
|
20379
|
+
const file = await this.execFile(asked, cwd);
|
|
20022
20380
|
const planned = await this.plan(file, [file], cwd, "");
|
|
20023
20381
|
return planned && this.backingOf(planned, file, cwd, "");
|
|
20024
20382
|
}
|
|
@@ -20655,8 +21013,8 @@ var Launcher = class {
|
|
|
20655
21013
|
}
|
|
20656
21014
|
signalMasksOf(pid) {
|
|
20657
21015
|
const masks = this.processes.get(pid)?.signalMasks?.();
|
|
20658
|
-
const [
|
|
20659
|
-
const sigBlk =
|
|
21016
|
+
const [blocked2] = this.blockedBy(pid);
|
|
21017
|
+
const sigBlk = blocked2 ? { sigBlk: blocked2 } : {};
|
|
20660
21018
|
return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught, ...sigBlk } : sigBlk;
|
|
20661
21019
|
}
|
|
20662
21020
|
controlsJobs(pid) {
|
|
@@ -21104,7 +21462,7 @@ var Launcher = class {
|
|
|
21104
21462
|
const scope = account.uid === 0 ? "" : pnpmHomeOf(account, this.pnpmHome);
|
|
21105
21463
|
const start2 = dir ?? "/";
|
|
21106
21464
|
const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
|
|
21107
|
-
const
|
|
21465
|
+
const file = await this.execFile(argv[0] ?? "", cwd, this.execGuard(credOf(account), 0, tag));
|
|
21108
21466
|
if (!await this.base.exists(cwd)) await this.stackFor(tag).fs.mkdir(cwd, { recursive: true });
|
|
21109
21467
|
const planned = await this.plan(file, argv, cwd, scope);
|
|
21110
21468
|
await this.mayExec(credOf(account), { file, cwd, pid: 0, tag }, planned);
|
|
@@ -21397,19 +21755,19 @@ function usersOp(l, req) {
|
|
|
21397
21755
|
if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
|
|
21398
21756
|
return l.users.list();
|
|
21399
21757
|
}
|
|
21400
|
-
function hostOptions(req, port, sealed, clipboard,
|
|
21758
|
+
function hostOptions(req, port, sealed, clipboard, lookup2) {
|
|
21401
21759
|
return {
|
|
21402
21760
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
21403
21761
|
...req.users !== void 0 ? { users: req.users } : {},
|
|
21404
21762
|
...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
|
|
21405
|
-
...req.protect ? { protect: hostProtect(req.protect,
|
|
21763
|
+
...req.protect ? { protect: hostProtect(req.protect, lookup2) } : {},
|
|
21406
21764
|
...req.sealed ? { sealed } : {},
|
|
21407
21765
|
...req.clipboard ? { clipboard } : {}
|
|
21408
21766
|
};
|
|
21409
21767
|
}
|
|
21410
|
-
function hostProtect(init,
|
|
21768
|
+
function hostProtect(init, lookup2) {
|
|
21411
21769
|
const { integrity, ...rest2 } = protectInit(init);
|
|
21412
|
-
return { ...rest2, ...integrity ? { integrity: { ...integrity, lookup } } : {} };
|
|
21770
|
+
return { ...rest2, ...integrity ? { integrity: { ...integrity, lookup: lookup2 } } : {} };
|
|
21413
21771
|
}
|
|
21414
21772
|
function policyAsker(port) {
|
|
21415
21773
|
const policies = /* @__PURE__ */ new Map();
|