@ai-ecoverse/slicc-kernel 1.110.7 → 1.111.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 +18 -2
- package/dist/kernel-worker.js +1031 -826
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +106 -23
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +615 -410
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +103 -20
- package/dist/process-worker.js.map +3 -3
- package/dist/types/kernel/fd-table.d.ts +1 -1
- package/dist/types/kernel/net/datagram-wire.d.ts +1 -0
- package/dist/types/kernel/socket-syscalls.d.ts +1 -0
- package/dist/types/kernel/socket.d.ts +6 -0
- package/dist/types/kernel/unix-dgram.d.ts +47 -0
- package/dist/types/process/wasi/wasi-host.d.ts +4 -0
- package/package.json +3 -3
package/dist/kernel-worker.js
CHANGED
|
@@ -4674,6 +4674,7 @@ var LOCAL_ONLY = new Routes();
|
|
|
4674
4674
|
var LoopbackNet = class {
|
|
4675
4675
|
bound = /* @__PURE__ */ new Map();
|
|
4676
4676
|
dgrams = /* @__PURE__ */ new Map();
|
|
4677
|
+
unixDgrams = /* @__PURE__ */ new Map();
|
|
4677
4678
|
activators = /* @__PURE__ */ new Map();
|
|
4678
4679
|
internal = /* @__PURE__ */ new Set();
|
|
4679
4680
|
reserved = /* @__PURE__ */ new Set();
|
|
@@ -4699,6 +4700,18 @@ var LoopbackNet = class {
|
|
|
4699
4700
|
this.dgrams.set(port, socket);
|
|
4700
4701
|
return { ...addr, port };
|
|
4701
4702
|
}
|
|
4703
|
+
bindUnixDgram(socket, key) {
|
|
4704
|
+
if (this.bound.has(key) || this.unixDgrams.has(key)) throw new KernelError("EADDRINUSE");
|
|
4705
|
+
this.unixDgrams.set(key, socket);
|
|
4706
|
+
}
|
|
4707
|
+
releaseUnixDgram(key, socket) {
|
|
4708
|
+
if (this.unixDgrams.get(key) === socket) this.unixDgrams.delete(key);
|
|
4709
|
+
}
|
|
4710
|
+
unixDgramAt(key) {
|
|
4711
|
+
const socket = this.unixDgrams.get(key);
|
|
4712
|
+
if (socket) return socket;
|
|
4713
|
+
throw new KernelError(this.bound.has(key) ? "EPROTOTYPE" : "ECONNREFUSED");
|
|
4714
|
+
}
|
|
4702
4715
|
releaseDgram(addr, socket) {
|
|
4703
4716
|
if (this.dgrams.get(addr.port) === socket) this.dgrams.delete(addr.port);
|
|
4704
4717
|
}
|
|
@@ -4752,7 +4765,9 @@ var LoopbackNet = class {
|
|
|
4752
4765
|
} else this.bindable(addr, kernel);
|
|
4753
4766
|
const named = addr.family === "inet" && addr.port === 0 ? { ...addr, port: this.ephemeral() } : addr;
|
|
4754
4767
|
const key = keyOf(named, name2);
|
|
4755
|
-
if (this.bound.has(key) && !name2
|
|
4768
|
+
if (this.bound.has(key) && !name2 || this.unixDgrams.has(key)) {
|
|
4769
|
+
throw new KernelError("EADDRINUSE");
|
|
4770
|
+
}
|
|
4756
4771
|
this.bound.set(key, socket);
|
|
4757
4772
|
socket.named = name2;
|
|
4758
4773
|
return named;
|
|
@@ -4766,6 +4781,7 @@ var LoopbackNet = class {
|
|
|
4766
4781
|
throw new KernelError("ENETUNREACH");
|
|
4767
4782
|
}
|
|
4768
4783
|
const key = keyOf(addr, name2);
|
|
4784
|
+
if (this.unixDgrams.has(key)) throw new KernelError("EPROTOTYPE");
|
|
4769
4785
|
if (!this.bound.has(key)) this.activators.get(key)?.();
|
|
4770
4786
|
const socket = this.bound.get(key);
|
|
4771
4787
|
if (!socket?.listening) throw new KernelError("ECONNREFUSED");
|
|
@@ -6176,7 +6192,7 @@ function satisfies(version, range2) {
|
|
|
6176
6192
|
}
|
|
6177
6193
|
|
|
6178
6194
|
// src/version.ts
|
|
6179
|
-
var KERNEL_VERSION = true ? "1.
|
|
6195
|
+
var KERNEL_VERSION = true ? "1.111.0" : "0.0.0-dev";
|
|
6180
6196
|
var OS_RELEASE = "6.0.0-slicc";
|
|
6181
6197
|
function procVersion() {
|
|
6182
6198
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -11764,594 +11780,208 @@ var ProcessClocks = class {
|
|
|
11764
11780
|
}
|
|
11765
11781
|
};
|
|
11766
11782
|
|
|
11767
|
-
// src/
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
|
|
11778
|
-
|
|
11779
|
-
|
|
11780
|
-
|
|
11781
|
-
|
|
11782
|
-
|
|
11783
|
-
|
|
11784
|
-
waiting = /* @__PURE__ */ new Map();
|
|
11785
|
-
nextId = 0;
|
|
11786
|
-
constructor(port, traits, routes) {
|
|
11787
|
-
this.port = port;
|
|
11788
|
-
this.traits = traits;
|
|
11789
|
-
this.routes = routes;
|
|
11790
|
-
}
|
|
11791
|
-
receive(reply) {
|
|
11792
|
-
const waiter = this.waiting.get(reply.uid);
|
|
11793
|
-
if (!waiter) return;
|
|
11794
|
-
this.waiting.delete(reply.uid);
|
|
11795
|
-
if (reply.uplink === "error") {
|
|
11796
|
-
waiter.reject(Object.assign(new Error(reply.message), { code: reply.code }));
|
|
11797
|
-
} else waiter.resolve(reply);
|
|
11798
|
-
}
|
|
11799
|
-
ask(call, signal, transfer) {
|
|
11800
|
-
const waiter = Promise.withResolvers();
|
|
11801
|
-
this.waiting.set(call.uid, waiter);
|
|
11802
|
-
const abort = () => {
|
|
11803
|
-
this.waiting.delete(call.uid);
|
|
11804
|
-
this.port.postMessage({ uplink: "cancel", uid: call.uid });
|
|
11805
|
-
waiter.reject(signal?.reason);
|
|
11806
|
-
};
|
|
11807
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
11808
|
-
const settled = () => signal?.removeEventListener("abort", abort);
|
|
11809
|
-
waiter.promise.then(settled, settled);
|
|
11810
|
-
this.port.postMessage(call, transfer);
|
|
11811
|
-
return waiter.promise;
|
|
11783
|
+
// src/fs/pages.ts
|
|
11784
|
+
var PAGE = 64 * 1024;
|
|
11785
|
+
var READAHEAD = 1024 * 1024;
|
|
11786
|
+
var CLEAN_LIMIT = 4 * 1024 * 1024;
|
|
11787
|
+
var WRITEBACK = 8 * 1024 * 1024;
|
|
11788
|
+
var RUN = 1024 * 1024;
|
|
11789
|
+
var Pages = class {
|
|
11790
|
+
length;
|
|
11791
|
+
stored;
|
|
11792
|
+
shrink;
|
|
11793
|
+
pages = /* @__PURE__ */ new Map();
|
|
11794
|
+
dirty = /* @__PURE__ */ new Set();
|
|
11795
|
+
free = [];
|
|
11796
|
+
pinned = false;
|
|
11797
|
+
constructor(size) {
|
|
11798
|
+
this.length = size;
|
|
11799
|
+
this.stored = size;
|
|
11812
11800
|
}
|
|
11813
|
-
|
|
11814
|
-
|
|
11815
|
-
{ uplink: "resolve", uid: ++this.nextId, name: name2, family },
|
|
11816
|
-
signal
|
|
11817
|
-
);
|
|
11818
|
-
return answerOf(reply);
|
|
11801
|
+
get dirtyBytes() {
|
|
11802
|
+
return this.dirty.size * PAGE;
|
|
11819
11803
|
}
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
const { host, port } = req;
|
|
11823
|
-
const opened2 = await this.ask(
|
|
11824
|
-
{ uplink: "dial", uid: ++this.nextId, host, port },
|
|
11825
|
-
req.signal
|
|
11826
|
-
);
|
|
11827
|
-
const { cid, localAddr, remoteAddr } = opened2;
|
|
11828
|
-
return {
|
|
11829
|
-
localAddr,
|
|
11830
|
-
remoteAddr,
|
|
11831
|
-
read: async () => {
|
|
11832
|
-
const got = await this.ask({
|
|
11833
|
-
uplink: "read",
|
|
11834
|
-
uid: ++this.nextId,
|
|
11835
|
-
cid
|
|
11836
|
-
});
|
|
11837
|
-
return got.bytes;
|
|
11838
|
-
},
|
|
11839
|
-
write: async (bytes) => {
|
|
11840
|
-
const copy = bytes.slice();
|
|
11841
|
-
const call = { uplink: "write", uid: ++this.nextId, cid, bytes: copy };
|
|
11842
|
-
const done4 = await this.ask(call, void 0, [
|
|
11843
|
-
copy.buffer
|
|
11844
|
-
]);
|
|
11845
|
-
return done4.n;
|
|
11846
|
-
},
|
|
11847
|
-
closeWrite: () => this.port.postMessage({ uplink: "close-write", cid }),
|
|
11848
|
-
close: () => this.port.postMessage({ uplink: "close", cid })
|
|
11849
|
-
};
|
|
11804
|
+
get changed() {
|
|
11805
|
+
return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
|
|
11850
11806
|
}
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
const
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
return
|
|
11864
|
-
localAddr,
|
|
11865
|
-
send: async (bytes, host, port) => {
|
|
11866
|
-
const copy = bytes.slice();
|
|
11867
|
-
const call = {
|
|
11868
|
-
uplink: "udp-send",
|
|
11869
|
-
uid: ++this.nextId,
|
|
11870
|
-
cid,
|
|
11871
|
-
bytes: copy,
|
|
11872
|
-
host,
|
|
11873
|
-
port
|
|
11874
|
-
};
|
|
11875
|
-
await this.ask(call, void 0, [copy.buffer]);
|
|
11876
|
-
},
|
|
11877
|
-
messages: {
|
|
11878
|
-
[Symbol.asyncIterator]: async function* () {
|
|
11879
|
-
for (; ; ) {
|
|
11880
|
-
const got = await recv();
|
|
11881
|
-
if (!got.message) return;
|
|
11882
|
-
yield got.message;
|
|
11883
|
-
}
|
|
11884
|
-
}
|
|
11885
|
-
},
|
|
11886
|
-
close: () => this.port.postMessage({ uplink: "udp-close", cid })
|
|
11887
|
-
};
|
|
11807
|
+
missing(at, length2) {
|
|
11808
|
+
this.evict();
|
|
11809
|
+
const end = Math.min(this.stored, at + length2, this.length);
|
|
11810
|
+
const runs = [];
|
|
11811
|
+
for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
|
|
11812
|
+
if (this.pages.has(page)) continue;
|
|
11813
|
+
const last2 = runs[runs.length - 1];
|
|
11814
|
+
if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
|
|
11815
|
+
else runs.push([page * PAGE, PAGE]);
|
|
11816
|
+
}
|
|
11817
|
+
const last = runs[runs.length - 1];
|
|
11818
|
+
if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
|
|
11819
|
+
return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
|
|
11888
11820
|
}
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
var SO_BROADCAST = 6;
|
|
11902
|
-
var SO_SNDBUF2 = 7;
|
|
11903
|
-
var SO_RCVBUF2 = 8;
|
|
11904
|
-
var SO_DOMAIN2 = 39;
|
|
11905
|
-
var SOCK_DGRAM = 2;
|
|
11906
|
-
var AF_INET2 = 2;
|
|
11907
|
-
var IP_MULTICAST_FIRST = 32;
|
|
11908
|
-
var IP_MULTICAST_LAST = 36;
|
|
11909
|
-
var SEND_ERRORS = ["ENETUNREACH", "EHOSTUNREACH", "EACCES", "EMSGSIZE"];
|
|
11910
|
-
var UNREACHABLE = ["EACCES", "ENETUNREACH", "EHOSTUNREACH"];
|
|
11911
|
-
function sendError(e) {
|
|
11912
|
-
if (e instanceof KernelError) return e;
|
|
11913
|
-
const code = e?.code;
|
|
11914
|
-
return new KernelError(SEND_ERRORS.find((errno2) => errno2 === code) ?? "EHOSTUNREACH");
|
|
11915
|
-
}
|
|
11916
|
-
function sameAddr(a, b) {
|
|
11917
|
-
return a.port === b.port && canonicalHost(a.host) === canonicalHost(b.host);
|
|
11918
|
-
}
|
|
11919
|
-
function familyOf(host) {
|
|
11920
|
-
return host.includes(":") ? 6 : 4;
|
|
11921
|
-
}
|
|
11922
|
-
var KernelDgram = class {
|
|
11923
|
-
local;
|
|
11924
|
-
peer;
|
|
11925
|
-
net;
|
|
11926
|
-
queue = [];
|
|
11927
|
-
queued = 0;
|
|
11928
|
-
pending;
|
|
11929
|
-
closed = false;
|
|
11930
|
-
sending = 0;
|
|
11931
|
-
changes = new Changes();
|
|
11932
|
-
options = /* @__PURE__ */ new Map();
|
|
11933
|
-
uplinks = /* @__PURE__ */ new Map();
|
|
11934
|
-
stop = new AbortController();
|
|
11935
|
-
constructor(net) {
|
|
11936
|
-
this.net = net;
|
|
11821
|
+
partial(at, length2) {
|
|
11822
|
+
if (length2 === 0) return [];
|
|
11823
|
+
this.evict();
|
|
11824
|
+
const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
|
|
11825
|
+
const runs = [];
|
|
11826
|
+
for (const page of edges) {
|
|
11827
|
+
const start2 = page * PAGE;
|
|
11828
|
+
const covered = at <= start2 && at + length2 >= start2 + PAGE;
|
|
11829
|
+
if (covered || this.pages.has(page) || start2 >= this.stored) continue;
|
|
11830
|
+
runs.push([start2, Math.min(PAGE, this.stored - start2)]);
|
|
11831
|
+
}
|
|
11832
|
+
return runs;
|
|
11937
11833
|
}
|
|
11938
|
-
|
|
11939
|
-
|
|
11940
|
-
|
|
11834
|
+
fill(start2, bytes) {
|
|
11835
|
+
for (let at = 0; at < bytes.length; at += PAGE) {
|
|
11836
|
+
const page = (start2 + at) / PAGE;
|
|
11837
|
+
if (this.pages.has(page)) continue;
|
|
11838
|
+
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
11839
|
+
const fresh2 = this.take(part.length);
|
|
11840
|
+
fresh2.set(part);
|
|
11841
|
+
this.pages.set(page, fresh2);
|
|
11842
|
+
}
|
|
11941
11843
|
}
|
|
11942
|
-
|
|
11943
|
-
|
|
11944
|
-
|
|
11844
|
+
read(at, length2) {
|
|
11845
|
+
const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
|
|
11846
|
+
this.readInto(at, out);
|
|
11847
|
+
return out;
|
|
11945
11848
|
}
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11849
|
+
readInto(at, out) {
|
|
11850
|
+
const end = Math.min(this.length, at + out.length);
|
|
11851
|
+
for (let pos = at; pos < end; ) {
|
|
11852
|
+
const page = Math.floor(pos / PAGE);
|
|
11853
|
+
const from = pos - page * PAGE;
|
|
11854
|
+
const n = Math.min(PAGE - from, end - pos);
|
|
11855
|
+
const bytes = this.pages.get(page);
|
|
11856
|
+
if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
|
|
11857
|
+
else out.fill(0, pos - at, pos - at + n);
|
|
11858
|
+
pos += n;
|
|
11859
|
+
}
|
|
11860
|
+
return Math.max(0, end - at);
|
|
11953
11861
|
}
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
}
|
|
11969
|
-
async send(bytes, to) {
|
|
11970
|
-
if (this.closed) throw new KernelError("EBADF");
|
|
11971
|
-
const pending = this.takeError();
|
|
11972
|
-
if (pending) throw new KernelError(pending);
|
|
11973
|
-
const target = to ?? this.peer;
|
|
11974
|
-
if (!target) throw new KernelError("EDESTADDRREQ");
|
|
11975
|
-
if (target.family !== "inet") throw new KernelError("EAFNOSUPPORT");
|
|
11976
|
-
const max = familyOf(target.host) === 6 ? MAX_DATAGRAM_V6 : MAX_DATAGRAM_V4;
|
|
11977
|
-
if (bytes.length > max) throw new KernelError("EMSGSIZE");
|
|
11978
|
-
const where = this.route(target.host);
|
|
11979
|
-
const local = this.bound();
|
|
11980
|
-
if (where === "uplink") return this.sendUp(bytes, target);
|
|
11981
|
-
const from = {
|
|
11982
|
-
family: "inet",
|
|
11983
|
-
host: local.host === "0.0.0.0" ? "127.0.0.1" : local.host,
|
|
11984
|
-
port: local.port
|
|
11985
|
-
};
|
|
11986
|
-
const delivered = this.net.deliverDgram(target.port, { bytes: bytes.slice(), from });
|
|
11987
|
-
if (!delivered && this.peer && sameAddr(this.peer, target)) {
|
|
11988
|
-
this.pending = "ECONNREFUSED";
|
|
11989
|
-
this.changes.wake();
|
|
11862
|
+
write(at, bytes) {
|
|
11863
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
11864
|
+
const pos = at + done4;
|
|
11865
|
+
const page = Math.floor(pos / PAGE);
|
|
11866
|
+
const from = pos - page * PAGE;
|
|
11867
|
+
const n = Math.min(PAGE - from, bytes.length - done4);
|
|
11868
|
+
let target = this.pages.get(page);
|
|
11869
|
+
if (!target) {
|
|
11870
|
+
target = this.take(0);
|
|
11871
|
+
this.pages.set(page, target);
|
|
11872
|
+
}
|
|
11873
|
+
target.set(bytes.subarray(done4, done4 + n), from);
|
|
11874
|
+
this.dirty.add(page);
|
|
11875
|
+
done4 += n;
|
|
11990
11876
|
}
|
|
11991
|
-
|
|
11877
|
+
this.length = Math.max(this.length, at + bytes.length);
|
|
11992
11878
|
}
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
try {
|
|
11998
|
-
const socket = await this.uplinkSocket(uplink, familyOf(to.host));
|
|
11999
|
-
await socket.send(bytes.slice(), to.host, to.port);
|
|
12000
|
-
} catch (e) {
|
|
12001
|
-
throw sendError(e);
|
|
12002
|
-
} finally {
|
|
12003
|
-
this.sending -= bytes.length;
|
|
12004
|
-
this.changes.wake();
|
|
11879
|
+
truncate(size) {
|
|
11880
|
+
if (size < this.stored) {
|
|
11881
|
+
this.stored = size;
|
|
11882
|
+
this.shrink = Math.min(this.shrink ?? size, size);
|
|
12005
11883
|
}
|
|
12006
|
-
|
|
12007
|
-
|
|
12008
|
-
|
|
12009
|
-
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
});
|
|
12015
|
-
this.uplinks.set(family, opening2);
|
|
12016
|
-
opening2.then(
|
|
12017
|
-
(socket) => {
|
|
12018
|
-
if (this.closed) {
|
|
12019
|
-
socket.close();
|
|
12020
|
-
return;
|
|
12021
|
-
}
|
|
12022
|
-
void this.pump(socket, family);
|
|
12023
|
-
},
|
|
12024
|
-
() => this.uplinks.delete(family)
|
|
12025
|
-
);
|
|
12026
|
-
return opening2;
|
|
11884
|
+
if (size < this.length) {
|
|
11885
|
+
const edge = Math.floor(size / PAGE);
|
|
11886
|
+
for (const page of [...this.pages.keys()]) {
|
|
11887
|
+
if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
|
|
11888
|
+
}
|
|
11889
|
+
this.pages.get(edge)?.fill(0, size - edge * PAGE);
|
|
11890
|
+
}
|
|
11891
|
+
this.length = size;
|
|
12027
11892
|
}
|
|
12028
|
-
|
|
12029
|
-
const
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
11893
|
+
plan() {
|
|
11894
|
+
const runs = [];
|
|
11895
|
+
const sorted = [...this.dirty].sort((a, b) => a - b);
|
|
11896
|
+
for (let i = 0; i < sorted.length; ) {
|
|
11897
|
+
let j = i + 1;
|
|
11898
|
+
const first = sorted[i];
|
|
11899
|
+
while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
|
|
11900
|
+
j++;
|
|
11901
|
+
}
|
|
11902
|
+
const start2 = first * PAGE;
|
|
11903
|
+
const end = Math.min(this.length, (first + (j - i)) * PAGE);
|
|
11904
|
+
const bytes = new Uint8Array(end - start2);
|
|
11905
|
+
for (let k = i; k < j; k++) {
|
|
11906
|
+
const page = this.pages.get(sorted[k]);
|
|
11907
|
+
bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
|
|
12035
11908
|
}
|
|
11909
|
+
runs.push({ offset: start2, bytes });
|
|
11910
|
+
i = j;
|
|
11911
|
+
}
|
|
11912
|
+
const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
|
|
11913
|
+
return {
|
|
11914
|
+
...this.shrink !== void 0 ? { shrink: this.shrink } : {},
|
|
11915
|
+
runs,
|
|
11916
|
+
...this.length > written2 ? { extend: this.length } : {}
|
|
12036
11917
|
};
|
|
12037
|
-
await iterate().catch(() => void 0);
|
|
12038
|
-
if (!this.closed) this.uplinks.delete(family);
|
|
12039
|
-
socket.close();
|
|
12040
11918
|
}
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
this.
|
|
12044
|
-
this.
|
|
11919
|
+
flushed() {
|
|
11920
|
+
this.dirty.clear();
|
|
11921
|
+
this.shrink = void 0;
|
|
11922
|
+
this.stored = this.length;
|
|
11923
|
+
this.evict();
|
|
12045
11924
|
}
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
if (this.peer && !sameAddr(this.peer, datagram2.from)) return true;
|
|
12049
|
-
const full = this.queue.length >= DGRAM_QUEUE_COUNT || this.queued + datagram2.bytes.length > DGRAM_QUEUE_BYTES;
|
|
12050
|
-
if (full) return true;
|
|
12051
|
-
this.queue.push(datagram2);
|
|
12052
|
-
this.queued += datagram2.bytes.length;
|
|
12053
|
-
this.changes.wake();
|
|
12054
|
-
return true;
|
|
11925
|
+
pin() {
|
|
11926
|
+
this.pinned = true;
|
|
12055
11927
|
}
|
|
12056
|
-
|
|
12057
|
-
|
|
12058
|
-
|
|
12059
|
-
|
|
12060
|
-
|
|
12061
|
-
|
|
12062
|
-
if (next3) {
|
|
12063
|
-
if (!peek) {
|
|
12064
|
-
this.queue.shift();
|
|
12065
|
-
this.queued -= next3.bytes.length;
|
|
12066
|
-
this.changes.wake();
|
|
12067
|
-
}
|
|
12068
|
-
const size = Math.max(0, max);
|
|
12069
|
-
return {
|
|
12070
|
-
bytes: next3.bytes.slice(0, size),
|
|
12071
|
-
from: next3.from,
|
|
12072
|
-
truncated: next3.bytes.length > size
|
|
12073
|
-
};
|
|
12074
|
-
}
|
|
12075
|
-
await this.changes.wait(signal);
|
|
12076
|
-
}
|
|
11928
|
+
reset(size) {
|
|
11929
|
+
if (this.pinned) return;
|
|
11930
|
+
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
11931
|
+
if (this.changed) return;
|
|
11932
|
+
this.length = size;
|
|
11933
|
+
this.stored = size;
|
|
12077
11934
|
}
|
|
12078
|
-
|
|
12079
|
-
|
|
11935
|
+
refresh(size) {
|
|
11936
|
+
if (this.pinned) return;
|
|
11937
|
+
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
11938
|
+
const resized2 = this.length !== this.stored || this.shrink !== void 0;
|
|
11939
|
+
if (this.shrink === void 0) this.stored = size;
|
|
11940
|
+
this.length = resized2 ? Math.max(this.length, this.stored) : size;
|
|
12080
11941
|
}
|
|
12081
|
-
|
|
12082
|
-
|
|
11942
|
+
evict() {
|
|
11943
|
+
if (this.pinned) return;
|
|
11944
|
+
let clean = (this.pages.size - this.dirty.size) * PAGE;
|
|
11945
|
+
for (const page of this.pages.keys()) {
|
|
11946
|
+
if (clean <= CLEAN_LIMIT) return;
|
|
11947
|
+
if (this.dirty.has(page)) continue;
|
|
11948
|
+
this.drop(page);
|
|
11949
|
+
clean -= PAGE;
|
|
11950
|
+
}
|
|
12083
11951
|
}
|
|
12084
|
-
|
|
12085
|
-
|
|
11952
|
+
take(used) {
|
|
11953
|
+
const page = this.free.pop();
|
|
11954
|
+
if (!page) return new Uint8Array(PAGE);
|
|
11955
|
+
page.fill(0, used);
|
|
11956
|
+
return page;
|
|
12086
11957
|
}
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
...this.pending !== void 0 ? { error: true } : {}
|
|
12093
|
-
};
|
|
11958
|
+
drop(page) {
|
|
11959
|
+
const bytes = this.pages.get(page);
|
|
11960
|
+
this.pages.delete(page);
|
|
11961
|
+
this.dirty.delete(page);
|
|
11962
|
+
if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
|
|
12094
11963
|
}
|
|
12095
|
-
|
|
12096
|
-
|
|
11964
|
+
};
|
|
11965
|
+
|
|
11966
|
+
// src/fs/ranged.ts
|
|
11967
|
+
var AsyncRangedFile = class {
|
|
11968
|
+
pages;
|
|
11969
|
+
io;
|
|
11970
|
+
pinned = false;
|
|
11971
|
+
version;
|
|
11972
|
+
path;
|
|
11973
|
+
constructor(io, path, size, version) {
|
|
11974
|
+
this.io = io;
|
|
11975
|
+
this.path = path;
|
|
11976
|
+
this.version = version;
|
|
11977
|
+
this.pages = new Pages(size);
|
|
12097
11978
|
}
|
|
12098
|
-
|
|
12099
|
-
|
|
12100
|
-
if (how < 0 || how > 2) throw new KernelError("EINVAL");
|
|
11979
|
+
size() {
|
|
11980
|
+
return this.pages.length;
|
|
12101
11981
|
}
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
case SO_TYPE2:
|
|
12106
|
-
return SOCK_DGRAM;
|
|
12107
|
-
case SO_ERROR2: {
|
|
12108
|
-
const pending = this.takeError();
|
|
12109
|
-
return pending ? wasiErrno(pending) : 0;
|
|
12110
|
-
}
|
|
12111
|
-
case SO_DOMAIN2:
|
|
12112
|
-
return AF_INET2;
|
|
12113
|
-
case SO_BROADCAST:
|
|
12114
|
-
return 0;
|
|
12115
|
-
case SO_SNDBUF2:
|
|
12116
|
-
return this.options.get(`${level}:${name2}`) ?? DGRAM_SEND_ROOM;
|
|
12117
|
-
case SO_RCVBUF2:
|
|
12118
|
-
return this.options.get(`${level}:${name2}`) ?? DGRAM_QUEUE_BYTES;
|
|
12119
|
-
}
|
|
12120
|
-
}
|
|
12121
|
-
if (level === IPPROTO_IP && name2 >= IP_MULTICAST_FIRST && name2 <= IP_MULTICAST_LAST) {
|
|
12122
|
-
throw new KernelError("ENOPROTOOPT");
|
|
12123
|
-
}
|
|
12124
|
-
return this.options.get(`${level}:${name2}`) ?? 0;
|
|
12125
|
-
}
|
|
12126
|
-
setOption(level, name2, value2) {
|
|
12127
|
-
if (level === SOL_SOCKET2 && name2 === SO_BROADCAST && value2 !== 0) {
|
|
12128
|
-
throw new KernelError("EOPNOTSUPP");
|
|
12129
|
-
}
|
|
12130
|
-
if (level === IPPROTO_IP && name2 >= IP_MULTICAST_FIRST && name2 <= IP_MULTICAST_LAST) {
|
|
12131
|
-
throw new KernelError("ENOPROTOOPT");
|
|
12132
|
-
}
|
|
12133
|
-
this.options.set(`${level}:${name2}`, value2);
|
|
12134
|
-
}
|
|
12135
|
-
close() {
|
|
12136
|
-
if (this.closed) return;
|
|
12137
|
-
this.closed = true;
|
|
12138
|
-
this.stop.abort();
|
|
12139
|
-
for (const opening2 of this.uplinks.values()) {
|
|
12140
|
-
opening2.then(
|
|
12141
|
-
(socket) => socket.close(),
|
|
12142
|
-
() => void 0
|
|
12143
|
-
);
|
|
12144
|
-
}
|
|
12145
|
-
this.uplinks.clear();
|
|
12146
|
-
if (this.local) this.net.releaseDgram(this.local, this);
|
|
12147
|
-
this.queue.length = 0;
|
|
12148
|
-
this.queued = 0;
|
|
12149
|
-
this.changes.wake();
|
|
12150
|
-
}
|
|
12151
|
-
};
|
|
12152
|
-
|
|
12153
|
-
// src/fs/pages.ts
|
|
12154
|
-
var PAGE = 64 * 1024;
|
|
12155
|
-
var READAHEAD = 1024 * 1024;
|
|
12156
|
-
var CLEAN_LIMIT = 4 * 1024 * 1024;
|
|
12157
|
-
var WRITEBACK = 8 * 1024 * 1024;
|
|
12158
|
-
var RUN = 1024 * 1024;
|
|
12159
|
-
var Pages = class {
|
|
12160
|
-
length;
|
|
12161
|
-
stored;
|
|
12162
|
-
shrink;
|
|
12163
|
-
pages = /* @__PURE__ */ new Map();
|
|
12164
|
-
dirty = /* @__PURE__ */ new Set();
|
|
12165
|
-
free = [];
|
|
12166
|
-
pinned = false;
|
|
12167
|
-
constructor(size) {
|
|
12168
|
-
this.length = size;
|
|
12169
|
-
this.stored = size;
|
|
12170
|
-
}
|
|
12171
|
-
get dirtyBytes() {
|
|
12172
|
-
return this.dirty.size * PAGE;
|
|
12173
|
-
}
|
|
12174
|
-
get changed() {
|
|
12175
|
-
return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
|
|
12176
|
-
}
|
|
12177
|
-
missing(at, length2) {
|
|
12178
|
-
this.evict();
|
|
12179
|
-
const end = Math.min(this.stored, at + length2, this.length);
|
|
12180
|
-
const runs = [];
|
|
12181
|
-
for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
|
|
12182
|
-
if (this.pages.has(page)) continue;
|
|
12183
|
-
const last2 = runs[runs.length - 1];
|
|
12184
|
-
if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
|
|
12185
|
-
else runs.push([page * PAGE, PAGE]);
|
|
12186
|
-
}
|
|
12187
|
-
const last = runs[runs.length - 1];
|
|
12188
|
-
if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
|
|
12189
|
-
return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
|
|
12190
|
-
}
|
|
12191
|
-
partial(at, length2) {
|
|
12192
|
-
if (length2 === 0) return [];
|
|
12193
|
-
this.evict();
|
|
12194
|
-
const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
|
|
12195
|
-
const runs = [];
|
|
12196
|
-
for (const page of edges) {
|
|
12197
|
-
const start2 = page * PAGE;
|
|
12198
|
-
const covered = at <= start2 && at + length2 >= start2 + PAGE;
|
|
12199
|
-
if (covered || this.pages.has(page) || start2 >= this.stored) continue;
|
|
12200
|
-
runs.push([start2, Math.min(PAGE, this.stored - start2)]);
|
|
12201
|
-
}
|
|
12202
|
-
return runs;
|
|
12203
|
-
}
|
|
12204
|
-
fill(start2, bytes) {
|
|
12205
|
-
for (let at = 0; at < bytes.length; at += PAGE) {
|
|
12206
|
-
const page = (start2 + at) / PAGE;
|
|
12207
|
-
if (this.pages.has(page)) continue;
|
|
12208
|
-
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
12209
|
-
const fresh2 = this.take(part.length);
|
|
12210
|
-
fresh2.set(part);
|
|
12211
|
-
this.pages.set(page, fresh2);
|
|
12212
|
-
}
|
|
12213
|
-
}
|
|
12214
|
-
read(at, length2) {
|
|
12215
|
-
const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
|
|
12216
|
-
this.readInto(at, out);
|
|
12217
|
-
return out;
|
|
12218
|
-
}
|
|
12219
|
-
readInto(at, out) {
|
|
12220
|
-
const end = Math.min(this.length, at + out.length);
|
|
12221
|
-
for (let pos = at; pos < end; ) {
|
|
12222
|
-
const page = Math.floor(pos / PAGE);
|
|
12223
|
-
const from = pos - page * PAGE;
|
|
12224
|
-
const n = Math.min(PAGE - from, end - pos);
|
|
12225
|
-
const bytes = this.pages.get(page);
|
|
12226
|
-
if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
|
|
12227
|
-
else out.fill(0, pos - at, pos - at + n);
|
|
12228
|
-
pos += n;
|
|
12229
|
-
}
|
|
12230
|
-
return Math.max(0, end - at);
|
|
12231
|
-
}
|
|
12232
|
-
write(at, bytes) {
|
|
12233
|
-
for (let done4 = 0; done4 < bytes.length; ) {
|
|
12234
|
-
const pos = at + done4;
|
|
12235
|
-
const page = Math.floor(pos / PAGE);
|
|
12236
|
-
const from = pos - page * PAGE;
|
|
12237
|
-
const n = Math.min(PAGE - from, bytes.length - done4);
|
|
12238
|
-
let target = this.pages.get(page);
|
|
12239
|
-
if (!target) {
|
|
12240
|
-
target = this.take(0);
|
|
12241
|
-
this.pages.set(page, target);
|
|
12242
|
-
}
|
|
12243
|
-
target.set(bytes.subarray(done4, done4 + n), from);
|
|
12244
|
-
this.dirty.add(page);
|
|
12245
|
-
done4 += n;
|
|
12246
|
-
}
|
|
12247
|
-
this.length = Math.max(this.length, at + bytes.length);
|
|
12248
|
-
}
|
|
12249
|
-
truncate(size) {
|
|
12250
|
-
if (size < this.stored) {
|
|
12251
|
-
this.stored = size;
|
|
12252
|
-
this.shrink = Math.min(this.shrink ?? size, size);
|
|
12253
|
-
}
|
|
12254
|
-
if (size < this.length) {
|
|
12255
|
-
const edge = Math.floor(size / PAGE);
|
|
12256
|
-
for (const page of [...this.pages.keys()]) {
|
|
12257
|
-
if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
|
|
12258
|
-
}
|
|
12259
|
-
this.pages.get(edge)?.fill(0, size - edge * PAGE);
|
|
12260
|
-
}
|
|
12261
|
-
this.length = size;
|
|
12262
|
-
}
|
|
12263
|
-
plan() {
|
|
12264
|
-
const runs = [];
|
|
12265
|
-
const sorted = [...this.dirty].sort((a, b) => a - b);
|
|
12266
|
-
for (let i = 0; i < sorted.length; ) {
|
|
12267
|
-
let j = i + 1;
|
|
12268
|
-
const first = sorted[i];
|
|
12269
|
-
while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
|
|
12270
|
-
j++;
|
|
12271
|
-
}
|
|
12272
|
-
const start2 = first * PAGE;
|
|
12273
|
-
const end = Math.min(this.length, (first + (j - i)) * PAGE);
|
|
12274
|
-
const bytes = new Uint8Array(end - start2);
|
|
12275
|
-
for (let k = i; k < j; k++) {
|
|
12276
|
-
const page = this.pages.get(sorted[k]);
|
|
12277
|
-
bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
|
|
12278
|
-
}
|
|
12279
|
-
runs.push({ offset: start2, bytes });
|
|
12280
|
-
i = j;
|
|
12281
|
-
}
|
|
12282
|
-
const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
|
|
12283
|
-
return {
|
|
12284
|
-
...this.shrink !== void 0 ? { shrink: this.shrink } : {},
|
|
12285
|
-
runs,
|
|
12286
|
-
...this.length > written2 ? { extend: this.length } : {}
|
|
12287
|
-
};
|
|
12288
|
-
}
|
|
12289
|
-
flushed() {
|
|
12290
|
-
this.dirty.clear();
|
|
12291
|
-
this.shrink = void 0;
|
|
12292
|
-
this.stored = this.length;
|
|
12293
|
-
this.evict();
|
|
12294
|
-
}
|
|
12295
|
-
pin() {
|
|
12296
|
-
this.pinned = true;
|
|
12297
|
-
}
|
|
12298
|
-
reset(size) {
|
|
12299
|
-
if (this.pinned) return;
|
|
12300
|
-
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
12301
|
-
if (this.changed) return;
|
|
12302
|
-
this.length = size;
|
|
12303
|
-
this.stored = size;
|
|
12304
|
-
}
|
|
12305
|
-
refresh(size) {
|
|
12306
|
-
if (this.pinned) return;
|
|
12307
|
-
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
12308
|
-
const resized2 = this.length !== this.stored || this.shrink !== void 0;
|
|
12309
|
-
if (this.shrink === void 0) this.stored = size;
|
|
12310
|
-
this.length = resized2 ? Math.max(this.length, this.stored) : size;
|
|
12311
|
-
}
|
|
12312
|
-
evict() {
|
|
12313
|
-
if (this.pinned) return;
|
|
12314
|
-
let clean = (this.pages.size - this.dirty.size) * PAGE;
|
|
12315
|
-
for (const page of this.pages.keys()) {
|
|
12316
|
-
if (clean <= CLEAN_LIMIT) return;
|
|
12317
|
-
if (this.dirty.has(page)) continue;
|
|
12318
|
-
this.drop(page);
|
|
12319
|
-
clean -= PAGE;
|
|
12320
|
-
}
|
|
12321
|
-
}
|
|
12322
|
-
take(used) {
|
|
12323
|
-
const page = this.free.pop();
|
|
12324
|
-
if (!page) return new Uint8Array(PAGE);
|
|
12325
|
-
page.fill(0, used);
|
|
12326
|
-
return page;
|
|
12327
|
-
}
|
|
12328
|
-
drop(page) {
|
|
12329
|
-
const bytes = this.pages.get(page);
|
|
12330
|
-
this.pages.delete(page);
|
|
12331
|
-
this.dirty.delete(page);
|
|
12332
|
-
if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
|
|
12333
|
-
}
|
|
12334
|
-
};
|
|
12335
|
-
|
|
12336
|
-
// src/fs/ranged.ts
|
|
12337
|
-
var AsyncRangedFile = class {
|
|
12338
|
-
pages;
|
|
12339
|
-
io;
|
|
12340
|
-
pinned = false;
|
|
12341
|
-
version;
|
|
12342
|
-
path;
|
|
12343
|
-
constructor(io, path, size, version) {
|
|
12344
|
-
this.io = io;
|
|
12345
|
-
this.path = path;
|
|
12346
|
-
this.version = version;
|
|
12347
|
-
this.pages = new Pages(size);
|
|
12348
|
-
}
|
|
12349
|
-
size() {
|
|
12350
|
-
return this.pages.length;
|
|
12351
|
-
}
|
|
12352
|
-
async fetch(runs) {
|
|
12353
|
-
for (const [start2, n] of runs) {
|
|
12354
|
-
this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
|
|
11982
|
+
async fetch(runs) {
|
|
11983
|
+
for (const [start2, n] of runs) {
|
|
11984
|
+
this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
|
|
12355
11985
|
}
|
|
12356
11986
|
}
|
|
12357
11987
|
async read(at, length2) {
|
|
@@ -14929,19 +14559,143 @@ var HttpHandles = class {
|
|
|
14929
14559
|
}
|
|
14930
14560
|
};
|
|
14931
14561
|
|
|
14932
|
-
// src/kernel/net/
|
|
14933
|
-
|
|
14934
|
-
|
|
14935
|
-
var RESOLVE_CACHE_MAX = 256;
|
|
14936
|
-
var LOOPBACK_NAMES2 = /* @__PURE__ */ new Set(["localhost", "localhost.localdomain", "ip6-localhost", "::1"]);
|
|
14937
|
-
function loopbackName(name2, own) {
|
|
14938
|
-
return LOOPBACK_NAMES2.has(name2) || name2.endsWith(".localhost") || own.includes(name2);
|
|
14562
|
+
// src/kernel/net/uplink.ts
|
|
14563
|
+
function carriesUdp(uplink) {
|
|
14564
|
+
return uplink.traits.udp === true && typeof uplink.udp === "function";
|
|
14939
14565
|
}
|
|
14940
|
-
function
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
|
|
14944
|
-
|
|
14566
|
+
function answerOf(result) {
|
|
14567
|
+
const answer = Array.isArray(result) ? { addresses: result } : result;
|
|
14568
|
+
const addresses = Array.isArray(answer?.addresses) ? answer.addresses.filter((a) => typeof a === "string") : [];
|
|
14569
|
+
return {
|
|
14570
|
+
addresses,
|
|
14571
|
+
...typeof answer?.ttl === "number" && answer.ttl >= 0 ? { ttl: answer.ttl } : {}
|
|
14572
|
+
};
|
|
14573
|
+
}
|
|
14574
|
+
var NEVER = new Routes(true);
|
|
14575
|
+
var RemoteUplink = class {
|
|
14576
|
+
traits;
|
|
14577
|
+
routes;
|
|
14578
|
+
port;
|
|
14579
|
+
waiting = /* @__PURE__ */ new Map();
|
|
14580
|
+
nextId = 0;
|
|
14581
|
+
constructor(port, traits, routes) {
|
|
14582
|
+
this.port = port;
|
|
14583
|
+
this.traits = traits;
|
|
14584
|
+
this.routes = routes;
|
|
14585
|
+
}
|
|
14586
|
+
receive(reply) {
|
|
14587
|
+
const waiter = this.waiting.get(reply.uid);
|
|
14588
|
+
if (!waiter) return;
|
|
14589
|
+
this.waiting.delete(reply.uid);
|
|
14590
|
+
if (reply.uplink === "error") {
|
|
14591
|
+
waiter.reject(Object.assign(new Error(reply.message), { code: reply.code }));
|
|
14592
|
+
} else waiter.resolve(reply);
|
|
14593
|
+
}
|
|
14594
|
+
ask(call, signal, transfer) {
|
|
14595
|
+
const waiter = Promise.withResolvers();
|
|
14596
|
+
this.waiting.set(call.uid, waiter);
|
|
14597
|
+
const abort = () => {
|
|
14598
|
+
this.waiting.delete(call.uid);
|
|
14599
|
+
this.port.postMessage({ uplink: "cancel", uid: call.uid });
|
|
14600
|
+
waiter.reject(signal?.reason);
|
|
14601
|
+
};
|
|
14602
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
14603
|
+
const settled = () => signal?.removeEventListener("abort", abort);
|
|
14604
|
+
waiter.promise.then(settled, settled);
|
|
14605
|
+
this.port.postMessage(call, transfer);
|
|
14606
|
+
return waiter.promise;
|
|
14607
|
+
}
|
|
14608
|
+
async resolve(name2, family, signal) {
|
|
14609
|
+
const reply = await this.ask(
|
|
14610
|
+
{ uplink: "resolve", uid: ++this.nextId, name: name2, family },
|
|
14611
|
+
signal
|
|
14612
|
+
);
|
|
14613
|
+
return answerOf(reply);
|
|
14614
|
+
}
|
|
14615
|
+
async dial(req) {
|
|
14616
|
+
req.signal.throwIfAborted();
|
|
14617
|
+
const { host, port } = req;
|
|
14618
|
+
const opened2 = await this.ask(
|
|
14619
|
+
{ uplink: "dial", uid: ++this.nextId, host, port },
|
|
14620
|
+
req.signal
|
|
14621
|
+
);
|
|
14622
|
+
const { cid, localAddr, remoteAddr } = opened2;
|
|
14623
|
+
return {
|
|
14624
|
+
localAddr,
|
|
14625
|
+
remoteAddr,
|
|
14626
|
+
read: async () => {
|
|
14627
|
+
const got = await this.ask({
|
|
14628
|
+
uplink: "read",
|
|
14629
|
+
uid: ++this.nextId,
|
|
14630
|
+
cid
|
|
14631
|
+
});
|
|
14632
|
+
return got.bytes;
|
|
14633
|
+
},
|
|
14634
|
+
write: async (bytes) => {
|
|
14635
|
+
const copy = bytes.slice();
|
|
14636
|
+
const call = { uplink: "write", uid: ++this.nextId, cid, bytes: copy };
|
|
14637
|
+
const done4 = await this.ask(call, void 0, [
|
|
14638
|
+
copy.buffer
|
|
14639
|
+
]);
|
|
14640
|
+
return done4.n;
|
|
14641
|
+
},
|
|
14642
|
+
closeWrite: () => this.port.postMessage({ uplink: "close-write", cid }),
|
|
14643
|
+
close: () => this.port.postMessage({ uplink: "close", cid })
|
|
14644
|
+
};
|
|
14645
|
+
}
|
|
14646
|
+
async udp(req) {
|
|
14647
|
+
req.signal.throwIfAborted();
|
|
14648
|
+
const opened2 = await this.ask(
|
|
14649
|
+
{ uplink: "udp-open", uid: ++this.nextId, family: req.family },
|
|
14650
|
+
req.signal
|
|
14651
|
+
);
|
|
14652
|
+
const { cid, localAddr } = opened2;
|
|
14653
|
+
const recv = () => this.ask({
|
|
14654
|
+
uplink: "udp-recv",
|
|
14655
|
+
uid: ++this.nextId,
|
|
14656
|
+
cid
|
|
14657
|
+
});
|
|
14658
|
+
return {
|
|
14659
|
+
localAddr,
|
|
14660
|
+
send: async (bytes, host, port) => {
|
|
14661
|
+
const copy = bytes.slice();
|
|
14662
|
+
const call = {
|
|
14663
|
+
uplink: "udp-send",
|
|
14664
|
+
uid: ++this.nextId,
|
|
14665
|
+
cid,
|
|
14666
|
+
bytes: copy,
|
|
14667
|
+
host,
|
|
14668
|
+
port
|
|
14669
|
+
};
|
|
14670
|
+
await this.ask(call, void 0, [copy.buffer]);
|
|
14671
|
+
},
|
|
14672
|
+
messages: {
|
|
14673
|
+
[Symbol.asyncIterator]: async function* () {
|
|
14674
|
+
for (; ; ) {
|
|
14675
|
+
const got = await recv();
|
|
14676
|
+
if (!got.message) return;
|
|
14677
|
+
yield got.message;
|
|
14678
|
+
}
|
|
14679
|
+
}
|
|
14680
|
+
},
|
|
14681
|
+
close: () => this.port.postMessage({ uplink: "udp-close", cid })
|
|
14682
|
+
};
|
|
14683
|
+
}
|
|
14684
|
+
};
|
|
14685
|
+
|
|
14686
|
+
// src/kernel/net/resolver.ts
|
|
14687
|
+
var RESOLVE_TTL_MAX = 60;
|
|
14688
|
+
var RESOLVE_TIMEOUT_MS = 1e4;
|
|
14689
|
+
var RESOLVE_CACHE_MAX = 256;
|
|
14690
|
+
var LOOPBACK_NAMES2 = /* @__PURE__ */ new Set(["localhost", "localhost.localdomain", "ip6-localhost", "::1"]);
|
|
14691
|
+
function loopbackName(name2, own) {
|
|
14692
|
+
return LOOPBACK_NAMES2.has(name2) || name2.endsWith(".localhost") || own.includes(name2);
|
|
14693
|
+
}
|
|
14694
|
+
function hostsEntry(name2, own = ownNames(DEFAULT_HOSTNAME)) {
|
|
14695
|
+
if (own.includes(name2)) return HOSTNAME_ADDRESS;
|
|
14696
|
+
if (LOOPBACK_NAMES2.has(name2) || name2.endsWith(".localhost")) return "127.0.0.1";
|
|
14697
|
+
if (name2 === HOST_LOOPBACK) return HOST_LOOPBACK_ADDRESS.join(".");
|
|
14698
|
+
return ipv4Bytes(name2) ? name2 : void 0;
|
|
14945
14699
|
}
|
|
14946
14700
|
var Resolver = class {
|
|
14947
14701
|
uplink;
|
|
@@ -15652,274 +15406,536 @@ async function selectFds(fds, read, write, timeoutMs, interrupt) {
|
|
|
15652
15406
|
} finally {
|
|
15653
15407
|
interrupted3.done();
|
|
15654
15408
|
}
|
|
15655
|
-
}
|
|
15656
|
-
async function waitReady(fds, read, write, deadline, interrupted3) {
|
|
15657
|
-
for (; ; ) {
|
|
15658
|
-
const now = ready(fds, read, write);
|
|
15659
|
-
if (now.read.length > 0 || now.write.length > 0 || performance.now() >= deadline) return now;
|
|
15660
|
-
const round = new AbortController();
|
|
15661
|
-
const changes = [.../* @__PURE__ */ new Set([...read, ...write])].flatMap((fd) => {
|
|
15662
|
-
const file = fds.get(fd).file;
|
|
15663
|
-
return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
|
|
15664
|
-
});
|
|
15665
|
-
let cancel = () => {
|
|
15409
|
+
}
|
|
15410
|
+
async function waitReady(fds, read, write, deadline, interrupted3) {
|
|
15411
|
+
for (; ; ) {
|
|
15412
|
+
const now = ready(fds, read, write);
|
|
15413
|
+
if (now.read.length > 0 || now.write.length > 0 || performance.now() >= deadline) return now;
|
|
15414
|
+
const round = new AbortController();
|
|
15415
|
+
const changes = [.../* @__PURE__ */ new Set([...read, ...write])].flatMap((fd) => {
|
|
15416
|
+
const file = fds.get(fd).file;
|
|
15417
|
+
return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
|
|
15418
|
+
});
|
|
15419
|
+
let cancel = () => {
|
|
15420
|
+
};
|
|
15421
|
+
const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve2) => cancel = timerAt(deadline, resolve2))];
|
|
15422
|
+
try {
|
|
15423
|
+
await Promise.race([...changes, ...expiry, interrupted3]);
|
|
15424
|
+
} finally {
|
|
15425
|
+
round.abort();
|
|
15426
|
+
cancel();
|
|
15427
|
+
}
|
|
15428
|
+
}
|
|
15429
|
+
}
|
|
15430
|
+
|
|
15431
|
+
// src/kernel/sem.ts
|
|
15432
|
+
var SEM_VALUE_MAX = 2147483647;
|
|
15433
|
+
var SEM_NAMES_MAX = 65536;
|
|
15434
|
+
var SEM_HANDLES_MAX = 65536;
|
|
15435
|
+
var NAME_MAX = 251;
|
|
15436
|
+
var SEM_OPS = [
|
|
15437
|
+
"sem-open",
|
|
15438
|
+
"sem-close",
|
|
15439
|
+
"sem-unlink",
|
|
15440
|
+
"sem-post",
|
|
15441
|
+
"sem-wait",
|
|
15442
|
+
"sem-getvalue"
|
|
15443
|
+
];
|
|
15444
|
+
var Semaphore = class {
|
|
15445
|
+
value;
|
|
15446
|
+
uid;
|
|
15447
|
+
gid;
|
|
15448
|
+
mode;
|
|
15449
|
+
waiters = [];
|
|
15450
|
+
constructor(value2, uid, gid, mode) {
|
|
15451
|
+
this.value = value2;
|
|
15452
|
+
this.uid = uid;
|
|
15453
|
+
this.gid = gid;
|
|
15454
|
+
this.mode = mode;
|
|
15455
|
+
}
|
|
15456
|
+
post() {
|
|
15457
|
+
const next3 = this.waiters.shift();
|
|
15458
|
+
if (next3) next3.grant();
|
|
15459
|
+
else if (this.value >= SEM_VALUE_MAX) throw new KernelError("EOVERFLOW");
|
|
15460
|
+
else this.value++;
|
|
15461
|
+
}
|
|
15462
|
+
take() {
|
|
15463
|
+
if (this.value === 0) return false;
|
|
15464
|
+
this.value--;
|
|
15465
|
+
return true;
|
|
15466
|
+
}
|
|
15467
|
+
wait(signal, timeoutMs) {
|
|
15468
|
+
if (this.take()) return Promise.resolve(true);
|
|
15469
|
+
if (signal.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
15470
|
+
return new Promise((resolve2, reject) => {
|
|
15471
|
+
const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : performance.now() + timeoutMs;
|
|
15472
|
+
let cancel = () => {
|
|
15473
|
+
};
|
|
15474
|
+
const leave = () => {
|
|
15475
|
+
const at = this.waiters.indexOf(waiter);
|
|
15476
|
+
if (at >= 0) this.waiters.splice(at, 1);
|
|
15477
|
+
cancel();
|
|
15478
|
+
signal.removeEventListener("abort", abort);
|
|
15479
|
+
};
|
|
15480
|
+
const waiter = {
|
|
15481
|
+
grant: () => {
|
|
15482
|
+
leave();
|
|
15483
|
+
resolve2(true);
|
|
15484
|
+
}
|
|
15485
|
+
};
|
|
15486
|
+
const abort = () => {
|
|
15487
|
+
leave();
|
|
15488
|
+
reject(new KernelError("EINTR"));
|
|
15489
|
+
};
|
|
15490
|
+
const expire = () => {
|
|
15491
|
+
leave();
|
|
15492
|
+
resolve2(false);
|
|
15493
|
+
};
|
|
15494
|
+
this.waiters.push(waiter);
|
|
15495
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
15496
|
+
if (deadline !== Number.POSITIVE_INFINITY) {
|
|
15497
|
+
cancel = timerAt(deadline, expire);
|
|
15498
|
+
}
|
|
15499
|
+
});
|
|
15500
|
+
}
|
|
15501
|
+
};
|
|
15502
|
+
var SemTable = class {
|
|
15503
|
+
names = /* @__PURE__ */ new Map();
|
|
15504
|
+
limit;
|
|
15505
|
+
constructor(limit = SEM_NAMES_MAX) {
|
|
15506
|
+
this.limit = limit;
|
|
15507
|
+
}
|
|
15508
|
+
open(name2, create, exclusive, make, check, adopt) {
|
|
15509
|
+
const found = this.names.get(name2);
|
|
15510
|
+
if (found) {
|
|
15511
|
+
if (create && exclusive) throw new KernelError("EEXIST");
|
|
15512
|
+
check(found);
|
|
15513
|
+
return adopt(found);
|
|
15514
|
+
}
|
|
15515
|
+
if (!create) throw new KernelError("ENOENT");
|
|
15516
|
+
if (this.names.size >= this.limit) throw new KernelError("ENOSPC");
|
|
15517
|
+
const sem = make();
|
|
15518
|
+
const handle = adopt(sem);
|
|
15519
|
+
this.names.set(name2, sem);
|
|
15520
|
+
return handle;
|
|
15521
|
+
}
|
|
15522
|
+
unlink(name2, check) {
|
|
15523
|
+
const found = this.names.get(name2);
|
|
15524
|
+
if (!found) throw new KernelError("ENOENT");
|
|
15525
|
+
check(found);
|
|
15526
|
+
this.names.delete(name2);
|
|
15527
|
+
}
|
|
15528
|
+
};
|
|
15529
|
+
var SemHandles = class _SemHandles {
|
|
15530
|
+
byHandle = /* @__PURE__ */ new Map();
|
|
15531
|
+
next = 1;
|
|
15532
|
+
limit;
|
|
15533
|
+
constructor(limit = SEM_HANDLES_MAX) {
|
|
15534
|
+
this.limit = limit;
|
|
15535
|
+
}
|
|
15536
|
+
add(sem) {
|
|
15537
|
+
for (const [handle2, entry] of this.byHandle) {
|
|
15538
|
+
if (entry.sem === sem) {
|
|
15539
|
+
entry.opens++;
|
|
15540
|
+
return handle2;
|
|
15541
|
+
}
|
|
15542
|
+
}
|
|
15543
|
+
if (this.byHandle.size >= this.limit) throw new KernelError("EMFILE");
|
|
15544
|
+
const handle = this.next++;
|
|
15545
|
+
this.byHandle.set(handle, { sem, opens: 1 });
|
|
15546
|
+
return handle;
|
|
15547
|
+
}
|
|
15548
|
+
get(handle) {
|
|
15549
|
+
const entry = this.byHandle.get(handle);
|
|
15550
|
+
if (!entry) throw new KernelError("EINVAL");
|
|
15551
|
+
return entry.sem;
|
|
15552
|
+
}
|
|
15553
|
+
close(handle) {
|
|
15554
|
+
const entry = this.byHandle.get(handle);
|
|
15555
|
+
if (!entry) throw new KernelError("EINVAL");
|
|
15556
|
+
if (--entry.opens === 0) this.byHandle.delete(handle);
|
|
15557
|
+
}
|
|
15558
|
+
fork() {
|
|
15559
|
+
const child = new _SemHandles(this.limit);
|
|
15560
|
+
child.next = this.next;
|
|
15561
|
+
for (const [handle, entry] of this.byHandle) child.byHandle.set(handle, { ...entry });
|
|
15562
|
+
return child;
|
|
15563
|
+
}
|
|
15564
|
+
clear() {
|
|
15565
|
+
this.byHandle.clear();
|
|
15566
|
+
}
|
|
15567
|
+
};
|
|
15568
|
+
function semName(raw) {
|
|
15569
|
+
const name2 = raw.replace(/^\/+/, "");
|
|
15570
|
+
if (name2 === "" || name2.includes("/") || name2 === "." || name2 === "..") {
|
|
15571
|
+
throw new KernelError("EINVAL");
|
|
15572
|
+
}
|
|
15573
|
+
if (new TextEncoder().encode(name2).length > NAME_MAX) throw new KernelError("ENAMETOOLONG");
|
|
15574
|
+
return name2;
|
|
15575
|
+
}
|
|
15576
|
+
var ok2 = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
|
|
15577
|
+
async function semSyscall(req, proc) {
|
|
15578
|
+
const { table, handles, cred } = proc;
|
|
15579
|
+
if (!table) throw new KernelError("ENOSYS");
|
|
15580
|
+
const { denied: denied2 } = proc;
|
|
15581
|
+
const shown = (name2) => `/dev/shm/sem.${name2}`;
|
|
15582
|
+
const opens = (name2) => (sem) => {
|
|
15583
|
+
const perm = { uid: sem.uid, gid: sem.gid, mode: sem.mode };
|
|
15584
|
+
if (denied2 && !permits(cred, perm, R_OK | W_OK, false)) denied2("sem_open", shown(name2));
|
|
15585
|
+
};
|
|
15586
|
+
const unlinks = (name2) => (sem) => {
|
|
15587
|
+
if (denied2 && cred.euid !== 0 && cred.euid !== sem.uid) denied2("sem_unlink", shown(name2));
|
|
15588
|
+
};
|
|
15589
|
+
switch (req.op) {
|
|
15590
|
+
case "sem-open": {
|
|
15591
|
+
if (req.value < 0 || req.value > SEM_VALUE_MAX) throw new KernelError("EINVAL");
|
|
15592
|
+
const name2 = semName(req.name);
|
|
15593
|
+
const mode = req.mode & 511 & ~proc.umask;
|
|
15594
|
+
return ok2(
|
|
15595
|
+
table.open(
|
|
15596
|
+
name2,
|
|
15597
|
+
req.create,
|
|
15598
|
+
req.exclusive,
|
|
15599
|
+
() => new Semaphore(req.value, cred.euid, cred.egid, mode),
|
|
15600
|
+
opens(name2),
|
|
15601
|
+
(sem) => handles.add(sem)
|
|
15602
|
+
)
|
|
15603
|
+
);
|
|
15604
|
+
}
|
|
15605
|
+
case "sem-close":
|
|
15606
|
+
handles.close(req.handle);
|
|
15607
|
+
return ok2();
|
|
15608
|
+
case "sem-unlink": {
|
|
15609
|
+
const name2 = semName(req.name);
|
|
15610
|
+
table.unlink(name2, unlinks(name2));
|
|
15611
|
+
return ok2();
|
|
15612
|
+
}
|
|
15613
|
+
case "sem-post":
|
|
15614
|
+
handles.get(req.handle).post();
|
|
15615
|
+
return ok2();
|
|
15616
|
+
case "sem-getvalue":
|
|
15617
|
+
return ok2(handles.get(req.handle).value);
|
|
15618
|
+
case "sem-wait": {
|
|
15619
|
+
const sem = handles.get(req.handle);
|
|
15620
|
+
if (sem.take()) return ok2();
|
|
15621
|
+
if (req.how === "try") throw new KernelError("EAGAIN");
|
|
15622
|
+
if (req.how === "timed" && req.timeoutMs === 0) throw new KernelError("ETIMEDOUT");
|
|
15623
|
+
const got = await sem.wait(proc.blocking(), req.how === "block" ? -1 : req.timeoutMs);
|
|
15624
|
+
if (!got) throw new KernelError("ETIMEDOUT");
|
|
15625
|
+
return ok2();
|
|
15626
|
+
}
|
|
15627
|
+
}
|
|
15628
|
+
}
|
|
15629
|
+
|
|
15630
|
+
// src/kernel/socket-nodes.ts
|
|
15631
|
+
function filePath(addr) {
|
|
15632
|
+
if (addr.family !== "unix" || addr.path === "" || addr.path.startsWith("\0")) return void 0;
|
|
15633
|
+
return normalizePath(addr.path);
|
|
15634
|
+
}
|
|
15635
|
+
function nameOf(st) {
|
|
15636
|
+
return { key: `${st.dev ?? 0}:${st.ino}`, typed: st.isSocket === true };
|
|
15637
|
+
}
|
|
15638
|
+
function codeOf3(err) {
|
|
15639
|
+
return err?.code;
|
|
15640
|
+
}
|
|
15641
|
+
function socketNames(deps) {
|
|
15642
|
+
const { fs } = deps;
|
|
15643
|
+
return {
|
|
15644
|
+
async make(path) {
|
|
15645
|
+
const g = deps.guard();
|
|
15646
|
+
if (g) await inParent(g, "bind", path);
|
|
15647
|
+
const owner = deps.owner();
|
|
15648
|
+
try {
|
|
15649
|
+
await deps.create(path, owner);
|
|
15650
|
+
} catch (err) {
|
|
15651
|
+
if (codeOf3(err) === "EEXIST") throw new KernelError("EADDRINUSE");
|
|
15652
|
+
throw err;
|
|
15653
|
+
}
|
|
15654
|
+
const remove = () => fs.rm(path, { file: true }).catch(() => void 0);
|
|
15655
|
+
try {
|
|
15656
|
+
if (!deps.owners && owner.mode !== void 0) await fs.chmod(path, owner.mode);
|
|
15657
|
+
return { ...nameOf(await fs.lstat(path)), remove };
|
|
15658
|
+
} catch (err) {
|
|
15659
|
+
await remove();
|
|
15660
|
+
throw err;
|
|
15661
|
+
}
|
|
15662
|
+
},
|
|
15663
|
+
async find(path) {
|
|
15664
|
+
const g = deps.guard();
|
|
15665
|
+
if (g) await need(g, "connect", path, W_OK, { directory: false });
|
|
15666
|
+
return nameOf(await fs.stat(path));
|
|
15667
|
+
}
|
|
15668
|
+
};
|
|
15669
|
+
}
|
|
15670
|
+
|
|
15671
|
+
// src/kernel/dgram.ts
|
|
15672
|
+
var DGRAM_QUEUE_BYTES = 256 * 1024;
|
|
15673
|
+
var DGRAM_QUEUE_COUNT = 512;
|
|
15674
|
+
var DGRAM_SEND_ROOM = 256 * 1024;
|
|
15675
|
+
var MAX_DATAGRAM_V4 = 65507;
|
|
15676
|
+
var MAX_DATAGRAM_V6 = 65527;
|
|
15677
|
+
var SOL_SOCKET2 = 1;
|
|
15678
|
+
var IPPROTO_IP = 0;
|
|
15679
|
+
var SO_TYPE2 = 3;
|
|
15680
|
+
var SO_ERROR2 = 4;
|
|
15681
|
+
var SO_BROADCAST = 6;
|
|
15682
|
+
var SO_SNDBUF2 = 7;
|
|
15683
|
+
var SO_RCVBUF2 = 8;
|
|
15684
|
+
var SO_DOMAIN2 = 39;
|
|
15685
|
+
var SOCK_DGRAM = 2;
|
|
15686
|
+
var AF_INET2 = 2;
|
|
15687
|
+
var IP_MULTICAST_FIRST = 32;
|
|
15688
|
+
var IP_MULTICAST_LAST = 36;
|
|
15689
|
+
var SEND_ERRORS = ["ENETUNREACH", "EHOSTUNREACH", "EACCES", "EMSGSIZE"];
|
|
15690
|
+
var UNREACHABLE = ["EACCES", "ENETUNREACH", "EHOSTUNREACH"];
|
|
15691
|
+
function sendError(e) {
|
|
15692
|
+
if (e instanceof KernelError) return e;
|
|
15693
|
+
const code = e?.code;
|
|
15694
|
+
return new KernelError(SEND_ERRORS.find((errno2) => errno2 === code) ?? "EHOSTUNREACH");
|
|
15695
|
+
}
|
|
15696
|
+
function sameAddr(a, b) {
|
|
15697
|
+
return a.port === b.port && canonicalHost(a.host) === canonicalHost(b.host);
|
|
15698
|
+
}
|
|
15699
|
+
function familyOf(host) {
|
|
15700
|
+
return host.includes(":") ? 6 : 4;
|
|
15701
|
+
}
|
|
15702
|
+
var KernelDgram = class {
|
|
15703
|
+
local;
|
|
15704
|
+
peer;
|
|
15705
|
+
net;
|
|
15706
|
+
queue = [];
|
|
15707
|
+
queued = 0;
|
|
15708
|
+
pending;
|
|
15709
|
+
closed = false;
|
|
15710
|
+
sending = 0;
|
|
15711
|
+
changes = new Changes();
|
|
15712
|
+
options = /* @__PURE__ */ new Map();
|
|
15713
|
+
uplinks = /* @__PURE__ */ new Map();
|
|
15714
|
+
stop = new AbortController();
|
|
15715
|
+
constructor(net) {
|
|
15716
|
+
this.net = net;
|
|
15717
|
+
}
|
|
15718
|
+
bind(addr) {
|
|
15719
|
+
if (this.local) throw new KernelError("EINVAL");
|
|
15720
|
+
this.local = this.net.bindDgram(this, addr);
|
|
15721
|
+
}
|
|
15722
|
+
bound() {
|
|
15723
|
+
this.local ??= this.net.bindDgram(this, { family: "inet", host: "0.0.0.0", port: 0 });
|
|
15724
|
+
return this.local;
|
|
15725
|
+
}
|
|
15726
|
+
connectTo(addr) {
|
|
15727
|
+
if (addr.family !== "inet") throw new KernelError("EAFNOSUPPORT");
|
|
15728
|
+
this.route(addr.host);
|
|
15729
|
+
this.bound();
|
|
15730
|
+
this.peer = { family: "inet", host: canonicalHost(addr.host), port: addr.port };
|
|
15731
|
+
this.pending = void 0;
|
|
15732
|
+
this.drain((d) => sameAddr(d.from, addr));
|
|
15733
|
+
}
|
|
15734
|
+
drain(keep2) {
|
|
15735
|
+
const kept = this.queue.filter(keep2);
|
|
15736
|
+
this.queue.splice(0, this.queue.length, ...kept);
|
|
15737
|
+
this.queued = kept.reduce((sum, d) => sum + d.bytes.length, 0);
|
|
15738
|
+
}
|
|
15739
|
+
route(host) {
|
|
15740
|
+
const where = this.net.destination(host);
|
|
15741
|
+
if (where === "loopback" || where === "uplink") return where;
|
|
15742
|
+
throw new KernelError("ENETUNREACH");
|
|
15743
|
+
}
|
|
15744
|
+
takeError() {
|
|
15745
|
+
const pending = this.pending;
|
|
15746
|
+
this.pending = void 0;
|
|
15747
|
+
return pending;
|
|
15748
|
+
}
|
|
15749
|
+
async send(bytes, to) {
|
|
15750
|
+
if (this.closed) throw new KernelError("EBADF");
|
|
15751
|
+
const pending = this.takeError();
|
|
15752
|
+
if (pending) throw new KernelError(pending);
|
|
15753
|
+
const target = to ?? this.peer;
|
|
15754
|
+
if (!target) throw new KernelError("EDESTADDRREQ");
|
|
15755
|
+
if (target.family !== "inet") throw new KernelError("EAFNOSUPPORT");
|
|
15756
|
+
const max = familyOf(target.host) === 6 ? MAX_DATAGRAM_V6 : MAX_DATAGRAM_V4;
|
|
15757
|
+
if (bytes.length > max) throw new KernelError("EMSGSIZE");
|
|
15758
|
+
const where = this.route(target.host);
|
|
15759
|
+
const local = this.bound();
|
|
15760
|
+
if (where === "uplink") return this.sendUp(bytes, target);
|
|
15761
|
+
const from = {
|
|
15762
|
+
family: "inet",
|
|
15763
|
+
host: local.host === "0.0.0.0" ? "127.0.0.1" : local.host,
|
|
15764
|
+
port: local.port
|
|
15666
15765
|
};
|
|
15667
|
-
const
|
|
15766
|
+
const delivered = this.net.deliverDgram(target.port, { bytes: bytes.slice(), from });
|
|
15767
|
+
if (!delivered && this.peer && sameAddr(this.peer, target)) {
|
|
15768
|
+
this.pending = "ECONNREFUSED";
|
|
15769
|
+
this.changes.wake();
|
|
15770
|
+
}
|
|
15771
|
+
return bytes.length;
|
|
15772
|
+
}
|
|
15773
|
+
async sendUp(bytes, to) {
|
|
15774
|
+
const uplink = this.net.uplinkFor(to.host)?.uplink;
|
|
15775
|
+
if (!uplink || !carriesUdp(uplink)) throw new KernelError("ENETUNREACH");
|
|
15776
|
+
this.sending += bytes.length;
|
|
15668
15777
|
try {
|
|
15669
|
-
await
|
|
15778
|
+
const socket = await this.uplinkSocket(uplink, familyOf(to.host));
|
|
15779
|
+
await socket.send(bytes.slice(), to.host, to.port);
|
|
15780
|
+
} catch (e) {
|
|
15781
|
+
throw sendError(e);
|
|
15670
15782
|
} finally {
|
|
15671
|
-
|
|
15672
|
-
|
|
15783
|
+
this.sending -= bytes.length;
|
|
15784
|
+
this.changes.wake();
|
|
15673
15785
|
}
|
|
15786
|
+
return bytes.length;
|
|
15674
15787
|
}
|
|
15675
|
-
|
|
15676
|
-
|
|
15677
|
-
|
|
15678
|
-
|
|
15679
|
-
|
|
15680
|
-
|
|
15681
|
-
|
|
15682
|
-
|
|
15683
|
-
|
|
15684
|
-
|
|
15685
|
-
|
|
15686
|
-
|
|
15687
|
-
|
|
15688
|
-
|
|
15689
|
-
|
|
15690
|
-
|
|
15691
|
-
|
|
15692
|
-
|
|
15693
|
-
|
|
15694
|
-
mode;
|
|
15695
|
-
waiters = [];
|
|
15696
|
-
constructor(value2, uid, gid, mode) {
|
|
15697
|
-
this.value = value2;
|
|
15698
|
-
this.uid = uid;
|
|
15699
|
-
this.gid = gid;
|
|
15700
|
-
this.mode = mode;
|
|
15788
|
+
uplinkSocket(uplink, family) {
|
|
15789
|
+
const known = this.uplinks.get(family);
|
|
15790
|
+
if (known !== void 0) return known;
|
|
15791
|
+
const opening2 = Promise.resolve().then(() => {
|
|
15792
|
+
if (!uplink.udp) throw new KernelError("ENETUNREACH");
|
|
15793
|
+
return uplink.udp({ family, signal: this.stop.signal });
|
|
15794
|
+
});
|
|
15795
|
+
this.uplinks.set(family, opening2);
|
|
15796
|
+
opening2.then(
|
|
15797
|
+
(socket) => {
|
|
15798
|
+
if (this.closed) {
|
|
15799
|
+
socket.close();
|
|
15800
|
+
return;
|
|
15801
|
+
}
|
|
15802
|
+
void this.pump(socket, family);
|
|
15803
|
+
},
|
|
15804
|
+
() => this.uplinks.delete(family)
|
|
15805
|
+
);
|
|
15806
|
+
return opening2;
|
|
15701
15807
|
}
|
|
15702
|
-
|
|
15703
|
-
const
|
|
15704
|
-
|
|
15705
|
-
|
|
15706
|
-
|
|
15808
|
+
async pump(socket, family) {
|
|
15809
|
+
const iterate = async () => {
|
|
15810
|
+
for await (const message3 of socket.messages) {
|
|
15811
|
+
if (this.closed) return;
|
|
15812
|
+
const from = { family: "inet", host: message3.host, port: message3.port };
|
|
15813
|
+
if ("bytes" in message3) this.deliver({ bytes: message3.bytes, from });
|
|
15814
|
+
else this.unreachable(message3.error, from);
|
|
15815
|
+
}
|
|
15816
|
+
};
|
|
15817
|
+
await iterate().catch(() => void 0);
|
|
15818
|
+
if (!this.closed) this.uplinks.delete(family);
|
|
15819
|
+
socket.close();
|
|
15707
15820
|
}
|
|
15708
|
-
|
|
15709
|
-
if (this.
|
|
15710
|
-
this.
|
|
15821
|
+
unreachable(error, from) {
|
|
15822
|
+
if (!this.peer || !sameAddr(this.peer, from)) return;
|
|
15823
|
+
this.pending = UNREACHABLE.find((errno2) => errno2 === error) ?? "EHOSTUNREACH";
|
|
15824
|
+
this.changes.wake();
|
|
15825
|
+
}
|
|
15826
|
+
deliver(datagram2) {
|
|
15827
|
+
if (this.closed) return false;
|
|
15828
|
+
if (this.peer && !sameAddr(this.peer, datagram2.from)) return true;
|
|
15829
|
+
const full = this.queue.length >= DGRAM_QUEUE_COUNT || this.queued + datagram2.bytes.length > DGRAM_QUEUE_BYTES;
|
|
15830
|
+
if (full) return true;
|
|
15831
|
+
this.queue.push(datagram2);
|
|
15832
|
+
this.queued += datagram2.bytes.length;
|
|
15833
|
+
this.changes.wake();
|
|
15711
15834
|
return true;
|
|
15712
15835
|
}
|
|
15713
|
-
|
|
15714
|
-
|
|
15715
|
-
|
|
15716
|
-
|
|
15717
|
-
|
|
15718
|
-
|
|
15719
|
-
|
|
15720
|
-
|
|
15721
|
-
|
|
15722
|
-
|
|
15723
|
-
|
|
15724
|
-
signal.removeEventListener("abort", abort);
|
|
15725
|
-
};
|
|
15726
|
-
const waiter = {
|
|
15727
|
-
grant: () => {
|
|
15728
|
-
leave();
|
|
15729
|
-
resolve2(true);
|
|
15836
|
+
async receive(max, peek, signal) {
|
|
15837
|
+
for (; ; ) {
|
|
15838
|
+
const pending = this.takeError();
|
|
15839
|
+
if (pending) throw new KernelError(pending);
|
|
15840
|
+
if (this.closed) throw new KernelError("EBADF");
|
|
15841
|
+
const next3 = this.queue[0];
|
|
15842
|
+
if (next3) {
|
|
15843
|
+
if (!peek) {
|
|
15844
|
+
this.queue.shift();
|
|
15845
|
+
this.queued -= next3.bytes.length;
|
|
15846
|
+
this.changes.wake();
|
|
15730
15847
|
}
|
|
15731
|
-
|
|
15732
|
-
|
|
15733
|
-
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
leave();
|
|
15738
|
-
resolve2(false);
|
|
15739
|
-
};
|
|
15740
|
-
this.waiters.push(waiter);
|
|
15741
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
15742
|
-
if (deadline !== Number.POSITIVE_INFINITY) {
|
|
15743
|
-
cancel = timerAt(deadline, expire);
|
|
15848
|
+
const size = Math.max(0, max);
|
|
15849
|
+
return {
|
|
15850
|
+
bytes: next3.bytes.slice(0, size),
|
|
15851
|
+
from: next3.from,
|
|
15852
|
+
truncated: next3.bytes.length > size
|
|
15853
|
+
};
|
|
15744
15854
|
}
|
|
15745
|
-
|
|
15746
|
-
}
|
|
15747
|
-
};
|
|
15748
|
-
var SemTable = class {
|
|
15749
|
-
names = /* @__PURE__ */ new Map();
|
|
15750
|
-
limit;
|
|
15751
|
-
constructor(limit = SEM_NAMES_MAX) {
|
|
15752
|
-
this.limit = limit;
|
|
15753
|
-
}
|
|
15754
|
-
open(name2, create, exclusive, make, check, adopt) {
|
|
15755
|
-
const found = this.names.get(name2);
|
|
15756
|
-
if (found) {
|
|
15757
|
-
if (create && exclusive) throw new KernelError("EEXIST");
|
|
15758
|
-
check(found);
|
|
15759
|
-
return adopt(found);
|
|
15855
|
+
await this.changes.wait(signal);
|
|
15760
15856
|
}
|
|
15761
|
-
if (!create) throw new KernelError("ENOENT");
|
|
15762
|
-
if (this.names.size >= this.limit) throw new KernelError("ENOSPC");
|
|
15763
|
-
const sem = make();
|
|
15764
|
-
const handle = adopt(sem);
|
|
15765
|
-
this.names.set(name2, sem);
|
|
15766
|
-
return handle;
|
|
15767
|
-
}
|
|
15768
|
-
unlink(name2, check) {
|
|
15769
|
-
const found = this.names.get(name2);
|
|
15770
|
-
if (!found) throw new KernelError("ENOENT");
|
|
15771
|
-
check(found);
|
|
15772
|
-
this.names.delete(name2);
|
|
15773
15857
|
}
|
|
15774
|
-
|
|
15775
|
-
|
|
15776
|
-
byHandle = /* @__PURE__ */ new Map();
|
|
15777
|
-
next = 1;
|
|
15778
|
-
limit;
|
|
15779
|
-
constructor(limit = SEM_HANDLES_MAX) {
|
|
15780
|
-
this.limit = limit;
|
|
15781
|
-
}
|
|
15782
|
-
add(sem) {
|
|
15783
|
-
for (const [handle2, entry] of this.byHandle) {
|
|
15784
|
-
if (entry.sem === sem) {
|
|
15785
|
-
entry.opens++;
|
|
15786
|
-
return handle2;
|
|
15787
|
-
}
|
|
15788
|
-
}
|
|
15789
|
-
if (this.byHandle.size >= this.limit) throw new KernelError("EMFILE");
|
|
15790
|
-
const handle = this.next++;
|
|
15791
|
-
this.byHandle.set(handle, { sem, opens: 1 });
|
|
15792
|
-
return handle;
|
|
15858
|
+
async read(max, signal) {
|
|
15859
|
+
return (await this.receive(max, false, signal)).bytes;
|
|
15793
15860
|
}
|
|
15794
|
-
|
|
15795
|
-
|
|
15796
|
-
if (!entry) throw new KernelError("EINVAL");
|
|
15797
|
-
return entry.sem;
|
|
15861
|
+
async peek(max, signal) {
|
|
15862
|
+
return (await this.receive(max, true, signal)).bytes;
|
|
15798
15863
|
}
|
|
15799
|
-
|
|
15800
|
-
|
|
15801
|
-
if (!entry) throw new KernelError("EINVAL");
|
|
15802
|
-
if (--entry.opens === 0) this.byHandle.delete(handle);
|
|
15864
|
+
write(bytes) {
|
|
15865
|
+
return this.send(bytes);
|
|
15803
15866
|
}
|
|
15804
|
-
|
|
15805
|
-
|
|
15806
|
-
|
|
15807
|
-
|
|
15808
|
-
|
|
15867
|
+
poll() {
|
|
15868
|
+
return {
|
|
15869
|
+
readable: this.queue.length > 0 || this.pending !== void 0 || this.closed,
|
|
15870
|
+
writable: this.sending < DGRAM_SEND_ROOM,
|
|
15871
|
+
hangup: this.closed,
|
|
15872
|
+
...this.pending !== void 0 ? { error: true } : {}
|
|
15873
|
+
};
|
|
15809
15874
|
}
|
|
15810
|
-
|
|
15811
|
-
this.
|
|
15875
|
+
changed(signal) {
|
|
15876
|
+
return this.changes.wait(signal);
|
|
15812
15877
|
}
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
if (name2 === "" || name2.includes("/") || name2 === "." || name2 === "..") {
|
|
15817
|
-
throw new KernelError("EINVAL");
|
|
15878
|
+
shutdown(how) {
|
|
15879
|
+
if (!this.peer) throw new KernelError("ENOTCONN");
|
|
15880
|
+
if (how < 0 || how > 2) throw new KernelError("EINVAL");
|
|
15818
15881
|
}
|
|
15819
|
-
|
|
15820
|
-
|
|
15821
|
-
|
|
15822
|
-
|
|
15823
|
-
|
|
15824
|
-
|
|
15825
|
-
|
|
15826
|
-
|
|
15827
|
-
|
|
15828
|
-
|
|
15829
|
-
|
|
15830
|
-
|
|
15831
|
-
|
|
15832
|
-
|
|
15833
|
-
|
|
15834
|
-
|
|
15835
|
-
|
|
15836
|
-
|
|
15837
|
-
if (req.value < 0 || req.value > SEM_VALUE_MAX) throw new KernelError("EINVAL");
|
|
15838
|
-
const name2 = semName(req.name);
|
|
15839
|
-
const mode = req.mode & 511 & ~proc.umask;
|
|
15840
|
-
return ok2(
|
|
15841
|
-
table.open(
|
|
15842
|
-
name2,
|
|
15843
|
-
req.create,
|
|
15844
|
-
req.exclusive,
|
|
15845
|
-
() => new Semaphore(req.value, cred.euid, cred.egid, mode),
|
|
15846
|
-
opens(name2),
|
|
15847
|
-
(sem) => handles.add(sem)
|
|
15848
|
-
)
|
|
15849
|
-
);
|
|
15882
|
+
getOption(level, name2) {
|
|
15883
|
+
if (level === SOL_SOCKET2) {
|
|
15884
|
+
switch (name2) {
|
|
15885
|
+
case SO_TYPE2:
|
|
15886
|
+
return SOCK_DGRAM;
|
|
15887
|
+
case SO_ERROR2: {
|
|
15888
|
+
const pending = this.takeError();
|
|
15889
|
+
return pending ? wasiErrno(pending) : 0;
|
|
15890
|
+
}
|
|
15891
|
+
case SO_DOMAIN2:
|
|
15892
|
+
return AF_INET2;
|
|
15893
|
+
case SO_BROADCAST:
|
|
15894
|
+
return 0;
|
|
15895
|
+
case SO_SNDBUF2:
|
|
15896
|
+
return this.options.get(`${level}:${name2}`) ?? DGRAM_SEND_ROOM;
|
|
15897
|
+
case SO_RCVBUF2:
|
|
15898
|
+
return this.options.get(`${level}:${name2}`) ?? DGRAM_QUEUE_BYTES;
|
|
15899
|
+
}
|
|
15850
15900
|
}
|
|
15851
|
-
|
|
15852
|
-
|
|
15853
|
-
return ok2();
|
|
15854
|
-
case "sem-unlink": {
|
|
15855
|
-
const name2 = semName(req.name);
|
|
15856
|
-
table.unlink(name2, unlinks(name2));
|
|
15857
|
-
return ok2();
|
|
15901
|
+
if (level === IPPROTO_IP && name2 >= IP_MULTICAST_FIRST && name2 <= IP_MULTICAST_LAST) {
|
|
15902
|
+
throw new KernelError("ENOPROTOOPT");
|
|
15858
15903
|
}
|
|
15859
|
-
|
|
15860
|
-
|
|
15861
|
-
|
|
15862
|
-
|
|
15863
|
-
|
|
15864
|
-
|
|
15865
|
-
|
|
15866
|
-
|
|
15867
|
-
if (req.how === "try") throw new KernelError("EAGAIN");
|
|
15868
|
-
if (req.how === "timed" && req.timeoutMs === 0) throw new KernelError("ETIMEDOUT");
|
|
15869
|
-
const got = await sem.wait(proc.blocking(), req.how === "block" ? -1 : req.timeoutMs);
|
|
15870
|
-
if (!got) throw new KernelError("ETIMEDOUT");
|
|
15871
|
-
return ok2();
|
|
15904
|
+
return this.options.get(`${level}:${name2}`) ?? 0;
|
|
15905
|
+
}
|
|
15906
|
+
setOption(level, name2, value2) {
|
|
15907
|
+
if (level === SOL_SOCKET2 && name2 === SO_BROADCAST && value2 !== 0) {
|
|
15908
|
+
throw new KernelError("EOPNOTSUPP");
|
|
15909
|
+
}
|
|
15910
|
+
if (level === IPPROTO_IP && name2 >= IP_MULTICAST_FIRST && name2 <= IP_MULTICAST_LAST) {
|
|
15911
|
+
throw new KernelError("ENOPROTOOPT");
|
|
15872
15912
|
}
|
|
15913
|
+
this.options.set(`${level}:${name2}`, value2);
|
|
15873
15914
|
}
|
|
15874
|
-
|
|
15875
|
-
|
|
15876
|
-
|
|
15877
|
-
|
|
15878
|
-
|
|
15879
|
-
|
|
15880
|
-
|
|
15881
|
-
|
|
15882
|
-
|
|
15883
|
-
}
|
|
15884
|
-
function codeOf3(err) {
|
|
15885
|
-
return err?.code;
|
|
15886
|
-
}
|
|
15887
|
-
function socketNames(deps) {
|
|
15888
|
-
const { fs } = deps;
|
|
15889
|
-
return {
|
|
15890
|
-
async make(path) {
|
|
15891
|
-
const g = deps.guard();
|
|
15892
|
-
if (g) await inParent(g, "bind", path);
|
|
15893
|
-
const owner = deps.owner();
|
|
15894
|
-
try {
|
|
15895
|
-
await deps.create(path, owner);
|
|
15896
|
-
} catch (err) {
|
|
15897
|
-
if (codeOf3(err) === "EEXIST") throw new KernelError("EADDRINUSE");
|
|
15898
|
-
throw err;
|
|
15899
|
-
}
|
|
15900
|
-
const remove = () => fs.rm(path, { file: true }).catch(() => void 0);
|
|
15901
|
-
try {
|
|
15902
|
-
if (!deps.owners && owner.mode !== void 0) await fs.chmod(path, owner.mode);
|
|
15903
|
-
return { ...nameOf(await fs.lstat(path)), remove };
|
|
15904
|
-
} catch (err) {
|
|
15905
|
-
await remove();
|
|
15906
|
-
throw err;
|
|
15907
|
-
}
|
|
15908
|
-
},
|
|
15909
|
-
async find(path) {
|
|
15910
|
-
const g = deps.guard();
|
|
15911
|
-
if (g) await need(g, "connect", path, W_OK, { directory: false });
|
|
15912
|
-
return nameOf(await fs.stat(path));
|
|
15915
|
+
close() {
|
|
15916
|
+
if (this.closed) return;
|
|
15917
|
+
this.closed = true;
|
|
15918
|
+
this.stop.abort();
|
|
15919
|
+
for (const opening2 of this.uplinks.values()) {
|
|
15920
|
+
opening2.then(
|
|
15921
|
+
(socket) => socket.close(),
|
|
15922
|
+
() => void 0
|
|
15923
|
+
);
|
|
15913
15924
|
}
|
|
15914
|
-
|
|
15915
|
-
|
|
15925
|
+
this.uplinks.clear();
|
|
15926
|
+
if (this.local) this.net.releaseDgram(this.local, this);
|
|
15927
|
+
this.queue.length = 0;
|
|
15928
|
+
this.queued = 0;
|
|
15929
|
+
this.changes.wake();
|
|
15930
|
+
}
|
|
15931
|
+
};
|
|
15916
15932
|
|
|
15917
15933
|
// src/kernel/net/datagram-wire.ts
|
|
15918
15934
|
function encodeReceived(got) {
|
|
15919
15935
|
const host = new TextEncoder().encode(got.host);
|
|
15920
15936
|
const out = new Uint8Array(4 + host.length + got.bytes.length);
|
|
15921
15937
|
const view2 = new DataView(out.buffer);
|
|
15922
|
-
view2.setUint8(0, got.truncated ? 1 : 0);
|
|
15938
|
+
view2.setUint8(0, (got.truncated ? 1 : 0) | (got.unix ? 2 : 0));
|
|
15923
15939
|
view2.setUint16(1, got.port);
|
|
15924
15940
|
view2.setUint8(3, host.length);
|
|
15925
15941
|
out.set(host, 4);
|
|
@@ -15927,6 +15943,152 @@ function encodeReceived(got) {
|
|
|
15927
15943
|
return out;
|
|
15928
15944
|
}
|
|
15929
15945
|
|
|
15946
|
+
// src/kernel/unix-dgram.ts
|
|
15947
|
+
var SOL_SOCKET3 = 1;
|
|
15948
|
+
var SO_TYPE3 = 3;
|
|
15949
|
+
var SO_ERROR3 = 4;
|
|
15950
|
+
var SO_SNDBUF3 = 7;
|
|
15951
|
+
var SO_RCVBUF3 = 8;
|
|
15952
|
+
var SO_DOMAIN3 = 39;
|
|
15953
|
+
var SOCK_DGRAM2 = 2;
|
|
15954
|
+
var AF_UNIX2 = 1;
|
|
15955
|
+
var UNNAMED = { family: "unix", path: "" };
|
|
15956
|
+
var UnixDgram = class _UnixDgram {
|
|
15957
|
+
local;
|
|
15958
|
+
peer;
|
|
15959
|
+
key;
|
|
15960
|
+
peerCred;
|
|
15961
|
+
target;
|
|
15962
|
+
net;
|
|
15963
|
+
queue = [];
|
|
15964
|
+
queued = 0;
|
|
15965
|
+
closed = false;
|
|
15966
|
+
changes = new Changes();
|
|
15967
|
+
options = /* @__PURE__ */ new Map();
|
|
15968
|
+
constructor(net) {
|
|
15969
|
+
this.net = net;
|
|
15970
|
+
}
|
|
15971
|
+
static pair(net, cred) {
|
|
15972
|
+
const a = new _UnixDgram(net);
|
|
15973
|
+
const b = new _UnixDgram(net);
|
|
15974
|
+
a.target = b;
|
|
15975
|
+
b.target = a;
|
|
15976
|
+
a.peer = b.peer = UNNAMED;
|
|
15977
|
+
a.peerCred = b.peerCred = cred;
|
|
15978
|
+
return [a, b];
|
|
15979
|
+
}
|
|
15980
|
+
bind(addr, key) {
|
|
15981
|
+
if (addr.family !== "unix") throw new KernelError("EINVAL");
|
|
15982
|
+
if (this.local) throw new KernelError("EINVAL");
|
|
15983
|
+
if (addr.path === "") throw new KernelError("EINVAL");
|
|
15984
|
+
this.net.bindUnixDgram(this, key);
|
|
15985
|
+
this.local = { family: "unix", path: addr.path };
|
|
15986
|
+
this.key = key;
|
|
15987
|
+
}
|
|
15988
|
+
connectTo(addr, target) {
|
|
15989
|
+
this.target = target;
|
|
15990
|
+
this.peer = addr;
|
|
15991
|
+
const kept = this.queue.filter((d) => d.sender === target);
|
|
15992
|
+
this.queue.splice(0, this.queue.length, ...kept);
|
|
15993
|
+
this.queued = kept.reduce((sum, d) => sum + d.bytes.length, 0);
|
|
15994
|
+
}
|
|
15995
|
+
destination() {
|
|
15996
|
+
if (!this.target) throw new KernelError("EDESTADDRREQ");
|
|
15997
|
+
return this.target;
|
|
15998
|
+
}
|
|
15999
|
+
room(bytes) {
|
|
16000
|
+
return this.closed || this.queue.length < DGRAM_QUEUE_COUNT && this.queued + bytes <= DGRAM_QUEUE_BYTES;
|
|
16001
|
+
}
|
|
16002
|
+
async send(bytes, to, nonblock, signal) {
|
|
16003
|
+
if (this.closed) throw new KernelError("EBADF");
|
|
16004
|
+
if (bytes.length > DGRAM_QUEUE_BYTES) throw new KernelError("EMSGSIZE");
|
|
16005
|
+
if (to.target && to.target !== this) throw new KernelError("EPERM");
|
|
16006
|
+
while (!to.room(bytes.length)) {
|
|
16007
|
+
if (nonblock) throw new KernelError("EAGAIN");
|
|
16008
|
+
await to.changed(signal);
|
|
16009
|
+
}
|
|
16010
|
+
if (to.closed) throw new KernelError("ECONNREFUSED");
|
|
16011
|
+
to.queue.push({ bytes: bytes.slice(), from: this.local ?? UNNAMED, sender: this });
|
|
16012
|
+
to.queued += bytes.length;
|
|
16013
|
+
to.changes.wake();
|
|
16014
|
+
return bytes.length;
|
|
16015
|
+
}
|
|
16016
|
+
async receive(max, peek, signal) {
|
|
16017
|
+
for (; ; ) {
|
|
16018
|
+
if (this.closed) throw new KernelError("EBADF");
|
|
16019
|
+
const next3 = this.queue[0];
|
|
16020
|
+
if (next3) {
|
|
16021
|
+
if (!peek) {
|
|
16022
|
+
this.queue.shift();
|
|
16023
|
+
this.queued -= next3.bytes.length;
|
|
16024
|
+
this.changes.wake();
|
|
16025
|
+
}
|
|
16026
|
+
const size = Math.max(0, max);
|
|
16027
|
+
return {
|
|
16028
|
+
bytes: next3.bytes.slice(0, size),
|
|
16029
|
+
from: next3.from,
|
|
16030
|
+
truncated: next3.bytes.length > size
|
|
16031
|
+
};
|
|
16032
|
+
}
|
|
16033
|
+
await this.changes.wait(signal);
|
|
16034
|
+
}
|
|
16035
|
+
}
|
|
16036
|
+
async read(max, signal) {
|
|
16037
|
+
return (await this.receive(max, false, signal)).bytes;
|
|
16038
|
+
}
|
|
16039
|
+
async peek(max, signal) {
|
|
16040
|
+
return (await this.receive(max, true, signal)).bytes;
|
|
16041
|
+
}
|
|
16042
|
+
write(bytes, signal) {
|
|
16043
|
+
return this.send(bytes, this.destination(), false, signal);
|
|
16044
|
+
}
|
|
16045
|
+
poll() {
|
|
16046
|
+
const target = this.target;
|
|
16047
|
+
return {
|
|
16048
|
+
readable: this.queue.length > 0 || this.closed,
|
|
16049
|
+
writable: !target || target.room(1),
|
|
16050
|
+
hangup: this.closed
|
|
16051
|
+
};
|
|
16052
|
+
}
|
|
16053
|
+
changed(signal) {
|
|
16054
|
+
return this.changes.wait(signal);
|
|
16055
|
+
}
|
|
16056
|
+
shutdown(how) {
|
|
16057
|
+
if (!this.peer) throw new KernelError("ENOTCONN");
|
|
16058
|
+
if (how < 0 || how > 2) throw new KernelError("EINVAL");
|
|
16059
|
+
}
|
|
16060
|
+
getOption(level, name2) {
|
|
16061
|
+
if (level === SOL_SOCKET3) {
|
|
16062
|
+
switch (name2) {
|
|
16063
|
+
case SO_TYPE3:
|
|
16064
|
+
return SOCK_DGRAM2;
|
|
16065
|
+
case SO_ERROR3:
|
|
16066
|
+
return 0;
|
|
16067
|
+
case SO_DOMAIN3:
|
|
16068
|
+
return AF_UNIX2;
|
|
16069
|
+
case SO_SNDBUF3:
|
|
16070
|
+
case SO_RCVBUF3:
|
|
16071
|
+
return this.options.get(`${level}:${name2}`) ?? DGRAM_QUEUE_BYTES;
|
|
16072
|
+
}
|
|
16073
|
+
}
|
|
16074
|
+
return this.options.get(`${level}:${name2}`) ?? 0;
|
|
16075
|
+
}
|
|
16076
|
+
setOption(level, name2, value2) {
|
|
16077
|
+
this.options.set(`${level}:${name2}`, value2);
|
|
16078
|
+
}
|
|
16079
|
+
close() {
|
|
16080
|
+
if (this.closed) return;
|
|
16081
|
+
this.closed = true;
|
|
16082
|
+
if (this.key !== void 0) this.net.releaseUnixDgram(this.key, this);
|
|
16083
|
+
this.queue.length = 0;
|
|
16084
|
+
this.queued = 0;
|
|
16085
|
+
this.changes.wake();
|
|
16086
|
+
}
|
|
16087
|
+
};
|
|
16088
|
+
function isDatagram(file) {
|
|
16089
|
+
return file instanceof KernelDgram || file instanceof UnixDgram;
|
|
16090
|
+
}
|
|
16091
|
+
|
|
15930
16092
|
// src/kernel/socket-syscalls.ts
|
|
15931
16093
|
var SOCKET_OPS = [
|
|
15932
16094
|
"sock-open",
|
|
@@ -15945,32 +16107,37 @@ var SOCKET_OPS = [
|
|
|
15945
16107
|
];
|
|
15946
16108
|
function socketAt(fds, fd) {
|
|
15947
16109
|
const file = anySocketAt(fds, fd);
|
|
15948
|
-
if (file instanceof
|
|
16110
|
+
if (!(file instanceof KernelSocket)) throw new KernelError("EOPNOTSUPP");
|
|
15949
16111
|
return file;
|
|
15950
16112
|
}
|
|
15951
16113
|
function anySocketAt(fds, fd) {
|
|
15952
16114
|
const file = fds.get(fd).file;
|
|
15953
|
-
if (!(file instanceof KernelSocket) && !(file instanceof KernelDgram)) {
|
|
16115
|
+
if (!(file instanceof KernelSocket) && !(file instanceof KernelDgram) && !(file instanceof UnixDgram)) {
|
|
15954
16116
|
throw new KernelError("ENOTSOCK");
|
|
15955
16117
|
}
|
|
15956
16118
|
return file;
|
|
15957
16119
|
}
|
|
15958
16120
|
function dgramAt(fds, fd) {
|
|
15959
16121
|
const file = anySocketAt(fds, fd);
|
|
15960
|
-
if (
|
|
16122
|
+
if (file instanceof KernelSocket) throw new KernelError("EOPNOTSUPP");
|
|
15961
16123
|
return file;
|
|
15962
16124
|
}
|
|
16125
|
+
async function unixTarget(proc, addr) {
|
|
16126
|
+
if (addr.family !== "unix") throw new KernelError("EAFNOSUPPORT");
|
|
16127
|
+
const path = filePath(addr);
|
|
16128
|
+
const name2 = path !== void 0 ? await proc.names?.find(path) : void 0;
|
|
16129
|
+
return proc.net.unixDgramAt(keyOf(path !== void 0 ? { family: "unix", path } : addr, name2));
|
|
16130
|
+
}
|
|
15963
16131
|
var ok3 = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
|
|
15964
16132
|
async function socketSyscall(req, proc) {
|
|
15965
16133
|
const { fds, net } = proc;
|
|
15966
16134
|
switch (req.op) {
|
|
15967
16135
|
case "sock-open": {
|
|
15968
|
-
|
|
15969
|
-
const file = req.type === "dgram" ? new KernelDgram(net) : net.socket(req.domain);
|
|
16136
|
+
const file = req.type !== "dgram" ? net.socket(req.domain) : req.domain === "unix" ? new UnixDgram(net) : new KernelDgram(net);
|
|
15970
16137
|
return ok3(fds.install(new OpenFile(file), 3));
|
|
15971
16138
|
}
|
|
15972
16139
|
case "sock-pair": {
|
|
15973
|
-
const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
|
|
16140
|
+
const [a, b] = req.type === "dgram" && req.domain === "unix" ? UnixDgram.pair(net, proc.cred?.()) : KernelSocket.pair(net, req.domain, proc.cred?.());
|
|
15974
16141
|
const first = fds.install(new OpenFile(a), 3);
|
|
15975
16142
|
try {
|
|
15976
16143
|
return ok3([first, fds.install(new OpenFile(b), 3)]);
|
|
@@ -15996,7 +16163,7 @@ async function socketSyscall(req, proc) {
|
|
|
15996
16163
|
const socket = anySocketAt(fds, req.fd);
|
|
15997
16164
|
const addr = req.peer ? socket.peer : socket.local;
|
|
15998
16165
|
if (req.peer && !addr) throw new KernelError("ENOTCONN");
|
|
15999
|
-
return ok3(addr ?? unnamed(socket instanceof
|
|
16166
|
+
return ok3(addr ?? unnamed(socket instanceof KernelSocket ? socket.domain : domainOf(socket)));
|
|
16000
16167
|
}
|
|
16001
16168
|
case "sock-getopt":
|
|
16002
16169
|
return ok3(anySocketAt(fds, req.fd).getOption(req.level, req.name));
|
|
@@ -16018,6 +16185,10 @@ async function connect(req, proc) {
|
|
|
16018
16185
|
target.connectTo(req.addr);
|
|
16019
16186
|
return ok3();
|
|
16020
16187
|
}
|
|
16188
|
+
if (target instanceof UnixDgram) {
|
|
16189
|
+
target.connectTo(req.addr, await unixTarget(proc, req.addr));
|
|
16190
|
+
return ok3();
|
|
16191
|
+
}
|
|
16021
16192
|
const path = filePath(req.addr);
|
|
16022
16193
|
const name2 = path !== void 0 ? await proc.names?.find(path) : void 0;
|
|
16023
16194
|
const stamp = proc.net.facadeAt(req.addr) && await proc.stamp?.();
|
|
@@ -16029,8 +16200,23 @@ async function connect(req, proc) {
|
|
|
16029
16200
|
if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
|
|
16030
16201
|
return ok3();
|
|
16031
16202
|
}
|
|
16203
|
+
async function bindUnixDgram(proc, socket, addr) {
|
|
16204
|
+
const path = filePath(addr);
|
|
16205
|
+
if (path === void 0 || !proc.names) {
|
|
16206
|
+
socket.bind(addr, keyOf(addr));
|
|
16207
|
+
return;
|
|
16208
|
+
}
|
|
16209
|
+
const name2 = await proc.names.make(path);
|
|
16210
|
+
try {
|
|
16211
|
+
socket.bind({ family: "unix", path }, keyOf({ family: "unix", path }, name2));
|
|
16212
|
+
} catch (err) {
|
|
16213
|
+
await name2.remove();
|
|
16214
|
+
throw err;
|
|
16215
|
+
}
|
|
16216
|
+
}
|
|
16032
16217
|
async function bind(proc, fd, addr) {
|
|
16033
16218
|
const socket = anySocketAt(proc.fds, fd);
|
|
16219
|
+
if (socket instanceof UnixDgram) return bindUnixDgram(proc, socket, addr);
|
|
16034
16220
|
const path = filePath(addr);
|
|
16035
16221
|
if (!(socket instanceof KernelSocket) || path === void 0 || !proc.names) {
|
|
16036
16222
|
socket.bind(addr);
|
|
@@ -16047,6 +16233,7 @@ async function bind(proc, fd, addr) {
|
|
|
16047
16233
|
}
|
|
16048
16234
|
async function datagram(req, proc) {
|
|
16049
16235
|
const dgram = dgramAt(proc.fds, req.fd);
|
|
16236
|
+
if (dgram instanceof UnixDgram) return unixDatagram(req, dgram, proc);
|
|
16050
16237
|
if (req.op === "sock-sendto") {
|
|
16051
16238
|
if (req.nonblock && !pollFile(dgram).writable) throw new KernelError("EAGAIN");
|
|
16052
16239
|
return ok3(await dgram.send(req.body, req.addr));
|
|
@@ -16057,6 +16244,24 @@ async function datagram(req, proc) {
|
|
|
16057
16244
|
const { bytes, from, truncated } = got;
|
|
16058
16245
|
return { ok: true, kind: "bytes", bytes: encodeReceived({ bytes, truncated, ...from }) };
|
|
16059
16246
|
}
|
|
16247
|
+
async function unixDatagram(req, dgram, proc) {
|
|
16248
|
+
if (req.op === "sock-sendto") {
|
|
16249
|
+
const to = req.addr ? await unixTarget(proc, req.addr) : dgram.destination();
|
|
16250
|
+
return ok3(await dgram.send(req.body, to, req.nonblock, proc.blocking()));
|
|
16251
|
+
}
|
|
16252
|
+
const ready2 = pollFile(dgram).readable;
|
|
16253
|
+
if (req.nonblock && !ready2) throw new KernelError("EAGAIN");
|
|
16254
|
+
const got = await dgram.receive(req.max, req.peek, ready2 ? void 0 : proc.blocking());
|
|
16255
|
+
const { bytes, from, truncated } = got;
|
|
16256
|
+
return {
|
|
16257
|
+
ok: true,
|
|
16258
|
+
kind: "bytes",
|
|
16259
|
+
bytes: encodeReceived({ bytes, truncated, host: from.path, port: 0, unix: true })
|
|
16260
|
+
};
|
|
16261
|
+
}
|
|
16262
|
+
function domainOf(socket) {
|
|
16263
|
+
return socket instanceof UnixDgram ? "unix" : "inet";
|
|
16264
|
+
}
|
|
16060
16265
|
function interruptible2(dialing, signal) {
|
|
16061
16266
|
return new Promise((resolve2, reject) => {
|
|
16062
16267
|
const abort = () => reject(new KernelError("EINTR"));
|
|
@@ -17874,8 +18079,8 @@ var WasmProcess = class {
|
|
|
17874
18079
|
const terminal = (file.tty ?? file.pty?.slave)?.name;
|
|
17875
18080
|
return {
|
|
17876
18081
|
tty: file.tty !== void 0,
|
|
17877
|
-
kind: file instanceof KernelSocket || file
|
|
17878
|
-
...file
|
|
18082
|
+
kind: file instanceof KernelSocket || isDatagram(file) ? "socket" : kernelFdKind(file),
|
|
18083
|
+
...isDatagram(file) ? { dgram: true } : {},
|
|
17879
18084
|
...file.heldMeta ? { meta: file.heldMeta } : {},
|
|
17880
18085
|
...flags2 !== void 0 ? { flags: flags2 } : {},
|
|
17881
18086
|
...this.fds.closesOnExec(fd) ? { cloexec: true } : {},
|
|
@@ -18311,7 +18516,7 @@ function inheritedFds(fds) {
|
|
|
18311
18516
|
return fds.numbers().filter((fd) => fd > 2 && !fds.get(fd).file.held).map((fd) => {
|
|
18312
18517
|
const open = fds.get(fd);
|
|
18313
18518
|
const flags2 = fds.statusFlags(fd);
|
|
18314
|
-
const kind = open.file instanceof KernelSocket || open.file
|
|
18519
|
+
const kind = open.file instanceof KernelSocket || isDatagram(open.file) ? "socket" : kernelFdKind(open.file);
|
|
18315
18520
|
const meta = open.file.heldMeta;
|
|
18316
18521
|
return {
|
|
18317
18522
|
fd,
|