buninu-linux 0.13.56 → 0.14.57
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/NOTICE.md +46 -46
- package/README.md +44 -14
- package/fetch-alpine.sh +1 -1
- package/initramfs/bin/dhclient +8 -0
- package/initramfs/bin/ping +8 -0
- package/initramfs/init.js +8 -7
- package/initramfs/lib/bunterm/renderer.js +1 -1
- package/initramfs/lib/bunterm/terminal.js +1 -1
- package/initramfs/lib/dhclient.js +135 -0
- package/initramfs/lib/dhcp-start.js +85 -0
- package/initramfs/lib/dhcp-transport.js +44 -0
- package/initramfs/lib/dhcp.js +124 -0
- package/initramfs/lib/dlopen.js +58 -11
- package/initramfs/lib/ping.js +110 -0
- package/initramfs/usr/share/doc/buninu-linux/bunterm.md +1 -1
- package/initramfs/usr/share/doc/buninu-linux/dhclient.md +82 -0
- package/initramfs/usr/share/doc/buninu-linux/ping.md +91 -0
- package/package.json +2 -2
- package/scripts/arch.js +5 -5
- package/scripts/arch.sh +5 -5
- package/scripts/modules.js +1 -0
- package/scripts/npm-packages.md +8 -8
- package/test/dhcp-start.test.js +67 -0
- package/test/dhcp.test.js +99 -0
- package/test/fetch-npm.test.js +1 -1
- package/test/libc.test.js +41 -0
- package/test/ping.test.js +80 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// DHCPv4 codec and lease state machine. Network/configuration adapters are
|
|
2
|
+
// separate so protocol tests never change the host's addresses or resolver.
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
export const TYPE = { DISCOVER:1, OFFER:2, REQUEST:3, DECLINE:4, ACK:5, NAK:6, RELEASE:7 };
|
|
5
|
+
export const ipv4 = text => {
|
|
6
|
+
const parts=String(text).split('.');
|
|
7
|
+
if(parts.length!==4 || parts.some(p=>!/^\d{1,3}$/.test(p)||Number(p)>255))throw new Error(`invalid IPv4 address: ${text}`);
|
|
8
|
+
return Buffer.from(parts.map(Number));
|
|
9
|
+
};
|
|
10
|
+
const address=b=>[...b].join('.');
|
|
11
|
+
export function checksum(b) {let n=0;for(let i=0;i<b.length;i+=2)n+=(b[i]<<8)|(b[i+1]??0);while(n>>>16)n=(n&65535)+(n>>>16);return (~n)&65535;}
|
|
12
|
+
export function encode({type,xid,mac,requested,server,ciaddr='0.0.0.0',hostname,secs=0,broadcast=true}) {
|
|
13
|
+
const b=Buffer.alloc(240);b[0]=1;b[1]=1;b[2]=6;b.writeUInt32BE(xid,4);b.writeUInt16BE(Math.min(65535,secs),8);
|
|
14
|
+
b.writeUInt16BE(broadcast?0x8000:0,10);ipv4(ciaddr).copy(b,12);Buffer.from(mac).copy(b,28,0,6);b.writeUInt32BE(0x63825363,236);
|
|
15
|
+
const options=[];
|
|
16
|
+
const option=(code,value)=>{value=Buffer.from(value);if(value.length>255)throw new Error('DHCP option too long');options.push(Buffer.from([code,value.length]),value);};
|
|
17
|
+
option(53,[type]);option(61,[1,...mac]);
|
|
18
|
+
if(requested)option(50,ipv4(requested));if(server)option(54,ipv4(server));
|
|
19
|
+
if(hostname)option(12,Buffer.from(hostname));
|
|
20
|
+
if(type!==TYPE.RELEASE){option(55,[1,3,6,15,28,51,58,59]);option(57,[5,220]);}
|
|
21
|
+
const packet=Buffer.concat([b,...options,Buffer.from([255])]);
|
|
22
|
+
return packet.length<300?Buffer.concat([packet,Buffer.alloc(300-packet.length)]):packet;
|
|
23
|
+
}
|
|
24
|
+
export function decode(b,{xid,mac}={}) {
|
|
25
|
+
if(b.length<240||b[0]!==2||b[1]!==1||b[2]!==6||b.readUInt32BE(236)!==0x63825363)return null;
|
|
26
|
+
if(xid!==undefined&&b.readUInt32BE(4)!==xid)return null;
|
|
27
|
+
if(mac&&!b.subarray(28,34).equals(Buffer.from(mac)))return null;
|
|
28
|
+
const options=new Map();
|
|
29
|
+
function parse(start,end){for(let i=start;i<end;){const code=b[i++];if(code===255)return true;if(!code)continue;if(i>=end)return false;const len=b[i++];if(i+len>end)return false;options.set(code,Buffer.concat([options.get(code)??Buffer.alloc(0),b.subarray(i,i+len)]));i+=len;}return true;}
|
|
30
|
+
if(!parse(240,b.length))return null;
|
|
31
|
+
const overload=options.get(52);if(overload&&(overload.length!==1||overload[0]>3))return null;
|
|
32
|
+
if(overload?.[0]&1)if(!parse(108,236))return null;
|
|
33
|
+
if(overload?.[0]&2)if(!parse(44,108))return null;
|
|
34
|
+
if(options.get(53)?.length!==1)return null;
|
|
35
|
+
for(const code of [1,28,51,54,58,59])if(options.has(code)&&options.get(code).length!==4)return null;
|
|
36
|
+
for(const code of [3,6])if(options.has(code)&&(!options.get(code).length||options.get(code).length%4))return null;
|
|
37
|
+
const ip=code=>options.has(code)?address(options.get(code)):undefined;
|
|
38
|
+
const ips=code=>{const v=options.get(code),out=[];if(v)for(let i=0;i<v.length;i+=4)out.push(address(v.subarray(i,i+4)));return out;};
|
|
39
|
+
const number=code=>options.get(code)?.readUInt32BE(0);
|
|
40
|
+
return {type:options.get(53)[0],xid:b.readUInt32BE(4),address:address(b.subarray(16,20)),server:ip(54),mask:ip(1),routers:options.has(3)?ips(3):undefined,dns:options.has(6)?ips(6):undefined,lease:number(51),t1:number(58),t2:number(59)};
|
|
41
|
+
}
|
|
42
|
+
export function prefixOf(mask) {
|
|
43
|
+
const n=ipv4(mask).readUInt32BE(),inverse=(~n)>>>0;
|
|
44
|
+
if((inverse&(inverse+1))!==0)throw new Error('DHCP supplied a non-contiguous subnet mask');
|
|
45
|
+
return 32-Math.log2(inverse+1);
|
|
46
|
+
}
|
|
47
|
+
export function leaseFrom(reply,previous,now) {
|
|
48
|
+
const ip=reply.address==='0.0.0.0'?previous?.address:reply.address;
|
|
49
|
+
const server=reply.server,mask=reply.mask??previous?.mask;
|
|
50
|
+
if(!ip||ip==='0.0.0.0'||!server||!mask||!reply.lease)throw new Error('DHCP ACK is missing address, server, mask or lease duration');
|
|
51
|
+
const prefix=prefixOf(mask),seconds=reply.lease;
|
|
52
|
+
let t1=reply.t1??seconds*.5,t2=reply.t2??seconds*.875;
|
|
53
|
+
if(!(t1>0&&t1<t2&&t2<seconds)){t1=seconds*.5;t2=seconds*.875;}
|
|
54
|
+
return {address:ip,server,mask,prefix,routers:reply.routers??previous?.routers??[],dns:reply.dns??previous?.dns??[],seconds,
|
|
55
|
+
acquiredAt:now,renewAt:now+t1*1000,rebindAt:now+t2*1000,expiresAt:now+seconds*1000};
|
|
56
|
+
}
|
|
57
|
+
// SOCK_DGRAM AF_PACKET supplies/consumes the Ethernet header itself.
|
|
58
|
+
export function wrapIPv4(payload,source='0.0.0.0',destination='255.255.255.255') {
|
|
59
|
+
const b=Buffer.alloc(28+payload.length);b[0]=0x45;b.writeUInt16BE(b.length,2);b[8]=64;b[9]=17;
|
|
60
|
+
ipv4(source).copy(b,12);ipv4(destination).copy(b,16);b.writeUInt16BE(checksum(b.subarray(0,20)),10);
|
|
61
|
+
b.writeUInt16BE(68,20);b.writeUInt16BE(67,22);b.writeUInt16BE(8+payload.length,24);payload.copy(b,28);
|
|
62
|
+
// IPv4 allows zero UDP checksum; the IPv4 header is checksummed above.
|
|
63
|
+
return b;
|
|
64
|
+
}
|
|
65
|
+
export function unwrapIPv4(b) {
|
|
66
|
+
if(b.length<28||b[0]>>>4!==4||b[9]!==17)return null;
|
|
67
|
+
const h=(b[0]&15)*4,n=b.readUInt16BE(2);
|
|
68
|
+
if(h<20||n>b.length||n<h+8||(b.readUInt16BE(6)&0x3fff)||checksum(b.subarray(0,h)))return null;
|
|
69
|
+
const len=b.readUInt16BE(h+4);
|
|
70
|
+
if(b.readUInt16BE(h)!==67||b.readUInt16BE(h+2)!==68||len<8||h+len>n)return null;
|
|
71
|
+
if(b.readUInt16BE(h+6)){
|
|
72
|
+
const pseudo=Buffer.alloc(12);b.copy(pseudo,0,12,20);pseudo[9]=17;pseudo.writeUInt16BE(len,10);
|
|
73
|
+
if(checksum(Buffer.concat([pseudo,b.subarray(h,h+len)])))return null;
|
|
74
|
+
}
|
|
75
|
+
return b.subarray(h+8,h+len);
|
|
76
|
+
}
|
|
77
|
+
export class NakError extends Error {}
|
|
78
|
+
export class DhcpClient {
|
|
79
|
+
constructor({transport,mac,hostname,timeout=60,signal,log=()=>{},now=Date.now,sleep=ms=>Bun.sleep(ms),random=Math.random}) {
|
|
80
|
+
Object.assign(this,{transport,mac,hostname,timeout,signal,log,now,sleep,random});
|
|
81
|
+
}
|
|
82
|
+
get stopped(){return this.signal?.aborted;}
|
|
83
|
+
async exchange(fields,types,deadline,server) {
|
|
84
|
+
const xid=randomBytes(4).readUInt32BE(),start=this.now();let retry=0,next=start;
|
|
85
|
+
while(!this.stopped&&this.now()<deadline){
|
|
86
|
+
if(this.now()>=next){
|
|
87
|
+
const packet=encode({...fields,xid,mac:this.mac,hostname:this.hostname,secs:Math.floor((this.now()-start)/1000)});
|
|
88
|
+
this.transport.send(packet,fields.broadcast===false?server:undefined);
|
|
89
|
+
next=this.now()+Math.min(64000,4000*2**Math.min(retry++,4))*(.75+.5*this.random());
|
|
90
|
+
}
|
|
91
|
+
// Bound the work per iteration so unrelated traffic cannot starve timers.
|
|
92
|
+
for(let i=0;i<64;i++){
|
|
93
|
+
const packet=this.transport.receive();if(!packet)break;
|
|
94
|
+
const reply=decode(packet,{xid,mac:this.mac});
|
|
95
|
+
if(!reply||!types.includes(reply.type)||!reply.server||(server&&reply.server!==server))continue;
|
|
96
|
+
if(reply.type===TYPE.NAK)throw new NakError('DHCP server rejected the lease');
|
|
97
|
+
return {reply,sentAt:start};
|
|
98
|
+
}
|
|
99
|
+
await this.sleep(Math.min(20,Math.max(1,deadline-this.now())));
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
async acquire() {
|
|
104
|
+
const deadline=this.now()+this.timeout*1000;
|
|
105
|
+
while(!this.stopped&&this.now()<deadline){
|
|
106
|
+
this.log('DHCPDISCOVER');
|
|
107
|
+
const offer=await this.exchange({type:TYPE.DISCOVER},[TYPE.OFFER],deadline);
|
|
108
|
+
if(!offer)return null;
|
|
109
|
+
if(offer.reply.address==='0.0.0.0')continue;
|
|
110
|
+
this.log(`DHCPOFFER ${offer.reply.address} from ${offer.reply.server}`);
|
|
111
|
+
try {
|
|
112
|
+
const result=await this.exchange({type:TYPE.REQUEST,requested:offer.reply.address,server:offer.reply.server},[TYPE.ACK,TYPE.NAK],Math.min(deadline,this.now()+16000),offer.reply.server);
|
|
113
|
+
if(result){if(result.reply.address!==offer.reply.address)continue;return leaseFrom(result.reply,offer.reply,result.sentAt);}
|
|
114
|
+
}catch(error){if(!(error instanceof NakError))throw error;await this.sleep(1000);}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
async renew(lease,rebind=false) {
|
|
119
|
+
this.log(rebind?'DHCPREQUEST broadcast (rebinding)':'DHCPREQUEST unicast (renewing)');
|
|
120
|
+
const result=await this.exchange({type:TYPE.REQUEST,ciaddr:lease.address,broadcast:rebind},[TYPE.ACK,TYPE.NAK],rebind?lease.expiresAt:lease.rebindAt,rebind?undefined:lease.server);
|
|
121
|
+
return result?leaseFrom(result.reply,lease,result.sentAt):null;
|
|
122
|
+
}
|
|
123
|
+
release(lease){this.transport.send(encode({type:TYPE.RELEASE,xid:randomBytes(4).readUInt32BE(),mac:this.mac,ciaddr:lease.address,server:lease.server,broadcast:false}),lease.server);}
|
|
124
|
+
}
|
package/initramfs/lib/dlopen.js
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
// init.js opens musl on its own because it runs before /proc exists and must
|
|
4
4
|
// not import anything. Every other command imports this module instead, so
|
|
5
5
|
// the symbol table, the errno plumbing and the error messages live in one
|
|
6
|
-
// place.
|
|
7
|
-
// which also lets the commands run from a checkout for testing.
|
|
6
|
+
// place. Automatic lookup follows the runtime ABI; BUNINU_LIBC overrides it.
|
|
8
7
|
|
|
9
8
|
import { dlopen, FFIType, ptr, read, CString, toArrayBuffer } from "bun:ffi";
|
|
10
9
|
|
|
@@ -20,11 +19,13 @@ const architectures = {
|
|
|
20
19
|
export const arch = architectures[process.arch]
|
|
21
20
|
?? (() => { throw new Error(`unsupported CPU architecture ${process.arch}`); })();
|
|
22
21
|
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
export const
|
|
22
|
+
// Automatic lookup must not mix bionic, glibc and musl runtime ABIs.
|
|
23
|
+
// An explicit override is authoritative: report its error instead of hiding it.
|
|
24
|
+
const android = process.platform === "android";
|
|
25
|
+
const glibc = !android && !!process.report?.getReport?.().header?.glibcVersionRuntime;
|
|
26
|
+
export const libcFamily = android ? "bionic" : glibc ? "glibc" : "musl";
|
|
27
|
+
const errnoSymbol = android ? "__errno" : "__errno_location";
|
|
28
|
+
const definitions = {
|
|
28
29
|
mount: {
|
|
29
30
|
args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.u64, FFIType.ptr],
|
|
30
31
|
returns: FFIType.i32,
|
|
@@ -41,7 +42,7 @@ export const libc = dlopen(libcPath, {
|
|
|
41
42
|
args: [],
|
|
42
43
|
returns: FFIType.void,
|
|
43
44
|
},
|
|
44
|
-
|
|
45
|
+
[errnoSymbol]: {
|
|
45
46
|
args: [],
|
|
46
47
|
returns: FFIType.ptr,
|
|
47
48
|
},
|
|
@@ -77,6 +78,14 @@ export const libc = dlopen(libcPath, {
|
|
|
77
78
|
args: [FFIType.i32, FFIType.i32, FFIType.i32],
|
|
78
79
|
returns: FFIType.i32,
|
|
79
80
|
},
|
|
81
|
+
bind: { args: [FFIType.i32, FFIType.ptr, FFIType.u32], returns: FFIType.i32 },
|
|
82
|
+
inet_pton: { args: [FFIType.i32, FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
|
|
83
|
+
connect: { args: [FFIType.i32, FFIType.ptr, FFIType.u32], returns: FFIType.i32 },
|
|
84
|
+
getsockname: { args: [FFIType.i32, FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
|
|
85
|
+
setsockopt: { args: [FFIType.i32, FFIType.i32, FFIType.i32, FFIType.ptr, FFIType.u32], returns: FFIType.i32 },
|
|
86
|
+
send: { args: [FFIType.i32, FFIType.ptr, FFIType.u64, FFIType.i32], returns: FFIType.i64 },
|
|
87
|
+
sendto: { args: [FFIType.i32, FFIType.ptr, FFIType.u64, FFIType.i32, FFIType.ptr, FFIType.u32], returns: FFIType.i64 },
|
|
88
|
+
recv: { args: [FFIType.i32, FFIType.ptr, FFIType.u64, FFIType.i32], returns: FFIType.i64 },
|
|
80
89
|
syscall: {
|
|
81
90
|
args: [FFIType.i64, FFIType.i64, FFIType.ptr, FFIType.i64],
|
|
82
91
|
returns: FFIType.i32,
|
|
@@ -93,13 +102,51 @@ export const libc = dlopen(libcPath, {
|
|
|
93
102
|
args: [FFIType.ptr],
|
|
94
103
|
returns: FFIType.i32,
|
|
95
104
|
},
|
|
96
|
-
}
|
|
105
|
+
};
|
|
106
|
+
const override = process.env.BUNINU_LIBC;
|
|
107
|
+
const candidates = override !== undefined ? [override]
|
|
108
|
+
: android ? ["libc.so", "/apex/com.android.runtime/lib64/bionic/libc.so", "/system/lib64/libc.so"]
|
|
109
|
+
: glibc ? ["libc.so.6", `/lib/${process.arch === "x64" ? "x86_64" : "aarch64"}-linux-gnu/libc.so.6`, `/usr/lib/${process.arch === "x64" ? "x86_64" : "aarch64"}-linux-gnu/libc.so.6`]
|
|
110
|
+
: [`${import.meta.dir}/${arch.libc}`, arch.libc, `/lib/${arch.libc}`, `/lib/ld-musl-${process.arch === "x64" ? "x86_64" : "aarch64"}.so.1`, "libc.so"];
|
|
111
|
+
// Bun requires at least one symbol per dlopen call. Defer opening until a
|
|
112
|
+
// symbol is requested; unrelated missing symbols never prevent an import.
|
|
113
|
+
export let libcPath;
|
|
114
|
+
const handles = [], bound = new Map();
|
|
115
|
+
let closed = false;
|
|
116
|
+
function resolve(name) {
|
|
117
|
+
if (bound.has(name)) return bound.get(name);
|
|
118
|
+
const failures = [];
|
|
119
|
+
for (const path of libcPath === undefined ? candidates : [libcPath]) {
|
|
120
|
+
try {
|
|
121
|
+
const handle = dlopen(path, { [name]: definitions[name] });
|
|
122
|
+
libcPath = path; handles.push(handle); bound.set(name, handle.symbols[name]);
|
|
123
|
+
return handle.symbols[name];
|
|
124
|
+
} catch (error) { failures.push(`${path}: ${error.message}`); }
|
|
125
|
+
}
|
|
126
|
+
throw new Error(`Cannot load libc symbol ${name}:\n${failures.join("\n")}`);
|
|
127
|
+
}
|
|
128
|
+
export const libc = {
|
|
129
|
+
symbols: new Proxy({}, {
|
|
130
|
+
get(_target, name) {
|
|
131
|
+
if (closed) throw new Error("libc is closed");
|
|
132
|
+
if (!Object.hasOwn(definitions, name)) return undefined;
|
|
133
|
+
const symbol = resolve(name);
|
|
134
|
+
// Compile the errno accessor before the native call, so first-use FFI
|
|
135
|
+
// compilation cannot clobber the errno that the caller wants to inspect.
|
|
136
|
+
// An absent accessor only fails when errno() is actually requested.
|
|
137
|
+
if (name !== errnoSymbol && !bound.has(errnoSymbol)) {
|
|
138
|
+
try { resolve(errnoSymbol); } catch {}
|
|
139
|
+
}
|
|
140
|
+
return symbol;
|
|
141
|
+
},
|
|
142
|
+
}),
|
|
143
|
+
close() { if (closed) return; closed = true; for (const handle of handles) handle.close(); },
|
|
144
|
+
};
|
|
97
145
|
|
|
98
146
|
export const cString = (value) => new TextEncoder().encode(`${value}\0`);
|
|
99
147
|
|
|
100
148
|
// musl reports failure as -1 plus errno; the raw syscall() wrapper does too.
|
|
101
|
-
const
|
|
102
|
-
export const errno = () => read.i32(errnoLocation);
|
|
149
|
+
export const errno = () => read.i32(libc.symbols[errnoSymbol]());
|
|
103
150
|
export const strerror = (code) => new CString(libc.symbols.strerror(code)).toString();
|
|
104
151
|
|
|
105
152
|
export const Errno = {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { isIP } from 'node:net';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
|
|
4
|
+
export function parsePing(args) {
|
|
5
|
+
const o={count:4,interval:1,timeout:2,deadline:Infinity,size:56,ttl:64,family:0,quiet:false,timestamp:false};
|
|
6
|
+
const values={c:'count',i:'interval',W:'timeout',w:'deadline',s:'size',t:'ttl',I:'source'};
|
|
7
|
+
let positional=false;
|
|
8
|
+
for(let i=0;i<args.length;i++){
|
|
9
|
+
const arg=args[i];
|
|
10
|
+
if(!positional&&arg==='--'){positional=true;continue;}
|
|
11
|
+
if(!positional&&(arg==='--help'||arg==='-h')){o.help=true;continue;}
|
|
12
|
+
if(!positional&&arg.startsWith('-')&&arg!=='-'){
|
|
13
|
+
for(let j=1;j<arg.length;j++){
|
|
14
|
+
const flag=arg[j];
|
|
15
|
+
if(values[flag]){const value=arg.slice(j+1)||args[++i];if(value===undefined||value==='')throw Error(`-${flag} requires a value`);if(flag!=='I'&&(!Number.isFinite(Number(value))||!value.trim()))throw Error(`invalid ${values[flag]}`);o[values[flag]]=flag==='I'?value:Number(value);break;}
|
|
16
|
+
if(flag==='4'||flag==='6')o.family=Number(flag);
|
|
17
|
+
else if(flag==='q')o.quiet=true;
|
|
18
|
+
else if(flag==='D')o.timestamp=true;
|
|
19
|
+
else if(flag!=='n')throw Error(`unknown option -${flag}`);
|
|
20
|
+
}
|
|
21
|
+
}else{if(o.host)throw Error('only one destination is supported');o.host=arg;}
|
|
22
|
+
}
|
|
23
|
+
if(o.help)return o;
|
|
24
|
+
if(!o.host)throw Error('destination required (see ping --help)');
|
|
25
|
+
for(const key of ['count','size','ttl'])if((key!=='count'||o.count!==Infinity)&&(!Number.isInteger(o[key])||o[key]<(key==='size'?0:1)))throw Error(`invalid ${key}`);
|
|
26
|
+
if(o.size>65507||o.ttl>255||o.count>Number.MAX_SAFE_INTEGER&&o.count!==Infinity)throw Error('count, packet size or TTL out of range');
|
|
27
|
+
for(const key of ['interval','timeout','deadline'])if(!(o[key]>0)||(!Number.isFinite(o[key])&&!(key==='deadline'&&o[key]===Infinity)))throw Error(`invalid ${key}`);
|
|
28
|
+
if(o.interval<.002)throw Error('minimum interval is 0.002 seconds');
|
|
29
|
+
return o;
|
|
30
|
+
}
|
|
31
|
+
export function checksum(bytes) {
|
|
32
|
+
let sum=0;for(let i=0;i<bytes.length;i+=2)sum+=(bytes[i]<<8)|(bytes[i+1]??0);
|
|
33
|
+
while(sum>>>16)sum=(sum&65535)+(sum>>>16);return (~sum)&65535;
|
|
34
|
+
}
|
|
35
|
+
export function echoPacket(family,id,seq,size) {
|
|
36
|
+
const b=Buffer.alloc(size+8);b[0]=family===6?128:8;b.writeUInt16BE(id,4);b.writeUInt16BE(seq,6);randomBytes(size).copy(b,8);
|
|
37
|
+
if(family===4)b.writeUInt16BE(checksum(b),2);return b;
|
|
38
|
+
}
|
|
39
|
+
export function isEchoReply(bytes,{family,raw,id,seq,packet}) {
|
|
40
|
+
let offset=0;
|
|
41
|
+
if(raw&&family===4){if(bytes.length<20||bytes[0]>>>4!==4||bytes[9]!==1)return false;offset=(bytes[0]&15)*4;if(offset<20||offset>bytes.length-8)return false;}
|
|
42
|
+
const b=bytes.subarray(offset);
|
|
43
|
+
return b.length===packet.length&&b[0]===(family===6?129:0)&&b[1]===0&&b.readUInt16BE(4)===id&&b.readUInt16BE(6)===seq&&b.subarray(8).equals(packet.subarray(8))&&(family===6||checksum(b)===0);
|
|
44
|
+
}
|
|
45
|
+
export async function runPing(o,{log=console.log,error=console.error,forceRaw=false}={}) {
|
|
46
|
+
if(!['linux','android'].includes(process.platform))throw Error('ping currently supports Linux and Android; Windows needs an ICMP API backend');
|
|
47
|
+
const {libc,ptr,cString,errno,SysError,check}=await import('./dlopen.js');
|
|
48
|
+
const c=libc.symbols;
|
|
49
|
+
let family=o.family||isIP(o.host)||4,address=o.host;
|
|
50
|
+
if(isIP(address)&&isIP(address)!==family)throw Error('destination does not match selected address family');
|
|
51
|
+
if(!isIP(address)){
|
|
52
|
+
const records=await Bun.dns.lookup(address,{family});address=records.find(r=>r.family===family)?.address;
|
|
53
|
+
if(!address)throw Error(`no IPv${family} address for ${o.host}`);
|
|
54
|
+
}
|
|
55
|
+
const af=family===6?10:2;
|
|
56
|
+
const sockaddr=value=>{
|
|
57
|
+
const b=Buffer.alloc(family===6?28:16);b.writeUInt16LE(af,0);
|
|
58
|
+
if(c.inet_pton(af,ptr(cString(value)),ptr(b.subarray(family===6?8:4)))!==1)throw Error(`invalid IPv${family} address: ${value}`);
|
|
59
|
+
return b;
|
|
60
|
+
};
|
|
61
|
+
let raw=forceRaw,fd=c.socket(af,(raw?3:2)|0x80800,family===6?58:1);
|
|
62
|
+
if(fd<0&&!raw){const code=errno();if(![1,13,93,97].includes(code))throw new SysError('ICMP socket',code);raw=true;fd=c.socket(af,3|0x80800,family===6?58:1);}
|
|
63
|
+
if(fd<0)throw new SysError('ICMP socket (allow ping_group_range or CAP_NET_RAW)');
|
|
64
|
+
let stop=false;const interrupt=()=>{stop=true};let transmitted=0,received=0,min=Infinity,max=0,sum=0,sumSquares=0;
|
|
65
|
+
const started=performance.now(),pending=new Map();let next=started;
|
|
66
|
+
const prefix=()=>o.timestamp?`[${(Date.now()/1000).toFixed(6)}] `:'';
|
|
67
|
+
try{
|
|
68
|
+
if(o.source){
|
|
69
|
+
if(isIP(o.source)){const src=sockaddr(o.source);check('bind source address',c.bind(fd,ptr(src),src.length));}
|
|
70
|
+
else{const name=cString(o.source);check('bind interface',c.setsockopt(fd,1,25,ptr(name),name.length));}
|
|
71
|
+
}
|
|
72
|
+
const ttl=Buffer.alloc(4);ttl.writeInt32LE(o.ttl);check('set TTL',c.setsockopt(fd,family===6?41:0,family===6?16:2,ptr(ttl),4));
|
|
73
|
+
const remote=sockaddr(address);check('connect',c.connect(fd,ptr(remote),remote.length));
|
|
74
|
+
const local=Buffer.alloc(28),length=Buffer.alloc(4);length.writeUInt32LE(local.length);check('getsockname',c.getsockname(fd,ptr(local),ptr(length)));
|
|
75
|
+
const id=raw?randomBytes(2).readUInt16BE():local.readUInt16BE(2);
|
|
76
|
+
log(`PING ${o.host} (${address}) ${o.size} data bytes`);
|
|
77
|
+
process.on('SIGINT',interrupt);process.on('SIGTERM',interrupt);
|
|
78
|
+
const buffer=Buffer.alloc(65536);
|
|
79
|
+
while(!stop&&performance.now()-started<o.deadline*1000){
|
|
80
|
+
let now=performance.now();
|
|
81
|
+
if(transmitted<o.count&&now>=next){
|
|
82
|
+
const seq=(transmitted+1)&65535;
|
|
83
|
+
// Avoid confusing a sequence that wrapped while an old probe is pending.
|
|
84
|
+
if(!pending.has(seq)){
|
|
85
|
+
const packet=echoPacket(family,id,seq,o.size);const sent=Number(c.send(fd,ptr(packet),packet.length,0));
|
|
86
|
+
if(sent!==packet.length)throw new SysError('send');
|
|
87
|
+
transmitted++;pending.set(seq,{packet,start:now});
|
|
88
|
+
}
|
|
89
|
+
next=now+o.interval*1000;
|
|
90
|
+
}
|
|
91
|
+
for(let n=0;n<64;n++){
|
|
92
|
+
const size=Number(c.recv(fd,ptr(buffer),buffer.length,0));
|
|
93
|
+
if(size<0){const code=errno();if(code===11)break;if(code===4)continue;if([101,111,113].includes(code)){if(!o.quiet)error(`${prefix()}ping: ${new SysError('receive',code).message}`);break;}throw new SysError('receive',code);}
|
|
94
|
+
const b=buffer.subarray(0,size),offset=raw&&family===4?(b[0]&15)*4:0;
|
|
95
|
+
if(size<offset+8)continue;const seq=b.readUInt16BE(offset+6),p=pending.get(seq);
|
|
96
|
+
if(!p||!isEchoReply(b,{family,raw,id,seq,packet:p.packet}))continue;
|
|
97
|
+
const ms=performance.now()-p.start;pending.delete(seq);received++;min=Math.min(min,ms);max=Math.max(max,ms);sum+=ms;sumSquares+=ms*ms;
|
|
98
|
+
if(!o.quiet)log(`${prefix()}${p.packet.length} bytes from ${address}: icmp_seq=${seq}${raw&&family===4?` ttl=${b[8]}`:''} time=${ms.toFixed(3)} ms`);
|
|
99
|
+
}
|
|
100
|
+
now=performance.now();for(const [seq,p] of pending)if(now-p.start>=o.timeout*1000){pending.delete(seq);if(!o.quiet)log(`${prefix()}Request timeout for icmp_seq=${seq}`);}
|
|
101
|
+
if(transmitted>=o.count&&!pending.size)break;
|
|
102
|
+
const sendDelay=transmitted<o.count?next-now:10;
|
|
103
|
+
await Bun.sleep(Math.max(1,Math.min(10,sendDelay,started+o.deadline*1000-now)));
|
|
104
|
+
}
|
|
105
|
+
}finally{process.removeListener('SIGINT',interrupt);process.removeListener('SIGTERM',interrupt);c.close(fd);}
|
|
106
|
+
log(`\n--- ${o.host} ping statistics ---`);
|
|
107
|
+
log(`${transmitted} packets transmitted, ${received} received, ${transmitted?((transmitted-received)/transmitted*100).toFixed(0):0}% packet loss, time ${Math.round(performance.now()-started)}ms`);
|
|
108
|
+
if(received)log(`rtt min/avg/max/mdev = ${min.toFixed(3)}/${(sum/received).toFixed(3)}/${max.toFixed(3)}/${Math.sqrt(Math.max(0,sumSquares/received-(sum/received)**2)).toFixed(3)} ms`);
|
|
109
|
+
return received>0&&!(Number.isFinite(o.deadline)&&Number.isFinite(o.count)&&received<o.count)?0:1;
|
|
110
|
+
}
|
|
@@ -35,7 +35,7 @@ identified from the process task name and its open console descriptor under
|
|
|
35
35
|
| --- | --- |
|
|
36
36
|
| `N`, `/dev/ttyN` | The virtual console to use for graphics and the keyboard; for example, `2` means `/dev/tty2`. Naming a VT other than the active one starts a detached bunterm there, switches the display to it, and immediately returns control to the calling shell. Without this argument, bunterm uses the active VT in the foreground. |
|
|
37
37
|
| `-e command [argument...]` | The program to run; everything after `-e` belongs to it. Default `/bin/sh`. |
|
|
38
|
-
| `-s N`, `--font-size N` | Font size in pixels (default
|
|
38
|
+
| `-s N`, `--font-size N` | Font size in pixels (default 13). The cell is 0.6 × N wide and 1.16 × N tall with DejaVu Sans Mono, so 1024×768 at 16 px gives 106 × 41 cells. |
|
|
39
39
|
| `--line-height F` | Multiplies the cell height (default 1). |
|
|
40
40
|
| `--fb /dev/fbN` | The framebuffer device (default `/dev/fb0`). |
|
|
41
41
|
| `--no-blink` | A steady cursor. |
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# dhclient — DHCPv4 client
|
|
2
|
+
|
|
3
|
+
Use `ip link` to list available network interfaces, then choose the interface
|
|
4
|
+
name for the command, for example `dhclient usb0`.
|
|
5
|
+
|
|
6
|
+
Acquire an IPv4 lease on an Ethernet-compatible interface. DHCP packets are
|
|
7
|
+
implemented in JavaScript; Bun FFI calls the runtime libc for packet/UDP
|
|
8
|
+
sockets and interface identification. Address and route changes invoke the
|
|
9
|
+
existing Buninu `ip` command, without an external DHCP binary or dependencies.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
dhclient eth0 # acquire, configure, then exit
|
|
13
|
+
dhclient -v -timeout 20 eth0 # show progress, fail after 20 seconds
|
|
14
|
+
dhclient -r eth0 # release a saved lease after the client stops
|
|
15
|
+
dhclient --no-dns eth0 # leave the resolver unchanged
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
| Option | Meaning |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| `-4` | DHCPv4 (default) |
|
|
21
|
+
| `-d` | Accepted for compatibility; all invocations exit after configuration |
|
|
22
|
+
| `-1` | Acquire once (already the default) |
|
|
23
|
+
| `-r` | Send DHCPRELEASE and remove the saved lease's configuration |
|
|
24
|
+
| `-timeout SECONDS` | Initial acquisition deadline; default 60 seconds |
|
|
25
|
+
| `-v`, `-q` | Verbose protocol progress, or quiet output |
|
|
26
|
+
| `-H HOSTNAME` | Send DHCP hostname option |
|
|
27
|
+
| `-lf PATH` | JSON lease file; default `/var/lib/dhclient/INTERFACE.json` |
|
|
28
|
+
| `--metric NUMBER` | Default route metric, 0–32766 |
|
|
29
|
+
| `--no-dns` | Do not write DNS configuration |
|
|
30
|
+
| `--resolv-conf PATH` | Resolver file; default `/etc/resolv.conf` |
|
|
31
|
+
| `-h`, `--help` | This manual |
|
|
32
|
+
|
|
33
|
+
Specify one interface per process. The interface is brought up first. This
|
|
34
|
+
client replaces its primary IPv4 address and installs the first offered
|
|
35
|
+
router as its default gateway. DNS server addresses replace the resolver
|
|
36
|
+
file; its previous content is saved and restored on explicit release only if
|
|
37
|
+
another writer has not modified it. `--no-dns` is useful for secondary links.
|
|
38
|
+
Avoid simultaneous resolver management by multiple clients.
|
|
39
|
+
|
|
40
|
+
Each invocation acquires and configures one lease, then exits. No background
|
|
41
|
+
renewal process is started. `-1` is an optional alias for this default behavior.
|
|
42
|
+
A DHCP lease is still time-limited unless the server grants an infinite lease:
|
|
43
|
+
run the command again before expiry, or use an appropriate static address.
|
|
44
|
+
After exit this command cannot renew or remove an expired address. The reported
|
|
45
|
+
lease duration and JSON timestamps show the server's validity period.
|
|
46
|
+
Ctrl-C/SIGTERM during acquisition stops the attempt; successful configuration
|
|
47
|
+
remains after normal exit. Use `-r` to release it explicitly. Saved JSON leases
|
|
48
|
+
support release and resolver restoration; their addresses are never assumed
|
|
49
|
+
valid on a later acquisition.
|
|
50
|
+
|
|
51
|
+
The per-interface `/run/dhclient-INTERFACE.lock` prevents duplicate Buninu
|
|
52
|
+
clients, including callers using different lease files. Stop the running
|
|
53
|
+
client before `-r`. After an unclean kill, verify it is gone before removing
|
|
54
|
+
the stale lock. UDP port 68 is reserved exclusively to detect other clients.
|
|
55
|
+
|
|
56
|
+
Linux/Android on x86-64 or aarch64 is supported, subject to privileges:
|
|
57
|
+
CAP_NET_RAW for packet sockets, CAP_NET_ADMIN for interface/route changes,
|
|
58
|
+
and permission to bind UDP 68 and write lease/resolver files. Ordinary
|
|
59
|
+
unprivileged Termux cannot configure Android networking. Root in Buninu's
|
|
60
|
+
VM can. Library selection follows the shared `dlopen.js` runtime ABI rules.
|
|
61
|
+
|
|
62
|
+
This is a focused client, not an ISC dhclient CLI clone. DHCPv6, ISC config/
|
|
63
|
+
script hooks, address-conflict ARP probing/DECLINE, classless static routes,
|
|
64
|
+
and automatic renewal/daemonization are not implemented. Wi-Fi must already be
|
|
65
|
+
associated. A failed acquisition exits nonzero; successful one-shot/release
|
|
66
|
+
or clean signal termination returns zero.
|
|
67
|
+
|
|
68
|
+
Protocol: [RFC 2131](https://www.rfc-editor.org/rfc/rfc2131) and
|
|
69
|
+
[DHCP options, RFC 2132](https://www.rfc-editor.org/rfc/rfc2132).
|
|
70
|
+
Run `bun test test/dhcp.test.js test/libc.test.js` for codec, state-machine,
|
|
71
|
+
configuration and libc regression tests; tests do not reconfigure host links.
|
|
72
|
+
|
|
73
|
+
`cfg.net` launches foreground clients as background child processes. Each has
|
|
74
|
+
a 20-second acquisition deadline and an independent 25-second startup watchdog
|
|
75
|
+
(SIGTERM, then SIGKILL after 2 seconds if necessary). Successful clients exit after configuration; none remain to renew. All child and output failures are caught and logged. Metrics start
|
|
76
|
+
at 100 per interface; only the first interface may write DNS. Repeated calls
|
|
77
|
+
reuse active clients; a later call can retry clients which have exited.
|
|
78
|
+
|
|
79
|
+
Automatic calls report one final result per interface, prefixed with
|
|
80
|
+
`[dhcp INTERFACE]`. A failed interface does not change another interface's
|
|
81
|
+
success result or configuration; output is collected before reporting exit
|
|
82
|
+
status so a delayed stderr message cannot appear after its final result.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# ping — send ICMP echo requests
|
|
2
|
+
|
|
3
|
+
`ping` sends real IPv4 ICMP or IPv6 ICMPv6 echo requests through Bun FFI.
|
|
4
|
+
It does not execute a system ping, open a TCP connection, or use an HTTP probe.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
ping [-4|-6] [-nqD] [-c COUNT] [-i SECONDS] [-W SECONDS]
|
|
8
|
+
[-w SECONDS] [-s BYTES] [-t TTL] [-I ADDRESS|INTERFACE] DESTINATION
|
|
9
|
+
ping --help
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| Option | Meaning |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `-c COUNT` | Stop after COUNT requests and their replies/timeouts. Default: 4 requests, then exit. |
|
|
15
|
+
| `-i SECONDS` | Send interval, default 1 second; fractional values supported, minimum 0.002 seconds. |
|
|
16
|
+
| `-W SECONDS` | Per-request reply timeout, default 2 seconds; fractional values supported. |
|
|
17
|
+
| `-w SECONDS` | Overall probing deadline, including outstanding requests. DNS resolution happens first. |
|
|
18
|
+
| `-s BYTES` | Payload size, default 56; 0–65507 bytes. The ICMP header adds 8 bytes. |
|
|
19
|
+
| `-t TTL` | IPv4 TTL or IPv6 hop limit, 1–255; default 64. |
|
|
20
|
+
| `-I ADDRESS` | Bind a source IPv4/IPv6 address. |
|
|
21
|
+
| `-I INTERFACE` | Bind an interface such as `eth0` or `wlan0`; OS permissions may be required. |
|
|
22
|
+
| `-4`, `-6` | Select IPv4 or IPv6. Numeric destinations infer their family; hostnames default to IPv4. |
|
|
23
|
+
| `-n` | Numeric reply addresses (currently always used; no reverse DNS). |
|
|
24
|
+
| `-q` | Print only the initial line and final statistics. |
|
|
25
|
+
| `-D` | Add Unix timestamps to per-packet output. |
|
|
26
|
+
| `-h`, `--help` | Show this document. |
|
|
27
|
+
|
|
28
|
+
Short options can be combined (`-nq`) and values attached (`-c3`). Use `--`
|
|
29
|
+
to end option parsing. Unknown options are rejected rather than ignored.
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
ping -c 4 1.1.1.1
|
|
33
|
+
ping -c 3 localhost
|
|
34
|
+
ping -6 -c 3 ::1
|
|
35
|
+
ping -n -c 10 -i 0.2 -W 1 -s 1200 192.168.1.1
|
|
36
|
+
ping -q -w 5 192.168.1.1
|
|
37
|
+
ping -I eth0 -c 3 192.168.1.1
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Statistics include transmitted/received counts, loss and RTT min/average/max/
|
|
41
|
+
standard deviation. Ctrl-C prints statistics and closes the socket. Exit status
|
|
42
|
+
is 0 if any reply arrived, 1 if none arrived (or a combined `-c`/`-w` deadline
|
|
43
|
+
prevented receiving the requested count), and 2 for argument/setup/system errors.
|
|
44
|
+
RTT is measured by the Bun process, so event-loop scheduling affects it.
|
|
45
|
+
|
|
46
|
+
The preferred socket is an unprivileged ICMP datagram (ping) socket. If the OS
|
|
47
|
+
refuses that socket, a raw ICMP socket is attempted; this normally requires root
|
|
48
|
+
or `CAP_NET_RAW`. Linux controls datagram access with `ping_group_range`, and
|
|
49
|
+
Android may impose additional restrictions. The command does not change system
|
|
50
|
+
permissions. Replies are matched by identifier, sequence and payload; IPv4
|
|
51
|
+
checksums are checked. Concurrent outstanding probes are supported. Datagram
|
|
52
|
+
replies do not expose their received TTL through this implementation, so it is
|
|
53
|
+
not printed; raw IPv4 replies do include TTL.
|
|
54
|
+
|
|
55
|
+
Supported runtimes: 64-bit Linux (glibc or musl) and native Android/Termux
|
|
56
|
+
(bionic), on x86_64 or aarch64. `/lib/dlopen.js` chooses libc automatically:
|
|
57
|
+
|
|
58
|
+
- Android: `libc.so`, then the APEX and `/system/lib64` bionic paths; `__errno`.
|
|
59
|
+
- Linux glibc runtime: `libc.so.6`, then architecture-specific `/lib` and
|
|
60
|
+
`/usr/lib` glibc paths; `__errno_location`.
|
|
61
|
+
- Linux musl runtime: the adjacent `libc.musl-<arch>.so.1`, its soname,
|
|
62
|
+
`/lib/libc.musl-<arch>.so.1`, `/lib/ld-musl-<arch>.so.1`, then `libc.so`;
|
|
63
|
+
`__errno_location`.
|
|
64
|
+
- Search stays within the runtime ABI: loading musl into glibc can crash on
|
|
65
|
+
errno/TLS access.
|
|
66
|
+
- `BUNINU_LIBC=/absolute/path` overrides the search. A bad override fails
|
|
67
|
+
explicitly rather than silently selecting a different library. Symbols resolve
|
|
68
|
+
on first use: an unavailable mount symbol does not prevent ping from using
|
|
69
|
+
the socket symbols. An explicit override is not rejected based on its filename;
|
|
70
|
+
matching the runtime ABI remains the caller's responsibility.
|
|
71
|
+
|
|
72
|
+
Windows requires a separate `iphlpapi.dll` ICMP backend and is not implemented.
|
|
73
|
+
Flood/adaptive modes, IPv4 record-route/timestamps, reverse DNS, broadcast ping,
|
|
74
|
+
and IPv6 scoped address notation (`fe80::1%eth0`) are not implemented.
|
|
75
|
+
|
|
76
|
+
## Tests
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
bun test test/ping.test.js test/libc.test.js
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
These compare local IPv4/IPv6 echo replies, payload sizes, counts, quiet output,
|
|
83
|
+
source selection, deadlines and exit codes against the installed system ping.
|
|
84
|
+
RTT values are not expected to match. A missing system ping or unavailable
|
|
85
|
+
loopback ICMP support skips the corresponding network comparisons. Older
|
|
86
|
+
Android systems have a separate `ping6` and a 200 ms interval restriction; the
|
|
87
|
+
reference tests accommodate those without limiting this command's options.
|
|
88
|
+
Packet validation, argument errors, fast intervals and Ctrl-C also have tests.
|
|
89
|
+
|
|
90
|
+
References: [Linux ICMP sockets](https://man7.org/linux/man-pages/man7/icmp.7.html),
|
|
91
|
+
[Android errno](https://android.googlesource.com/platform/bionic/+/master/libc/include/errno.h).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "buninu-linux",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.57",
|
|
4
4
|
"description": "A Linux distribution running Bun as PID 1 with the Buninu userspace.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -37,6 +37,6 @@
|
|
|
37
37
|
"fetch": "./index.js -f",
|
|
38
38
|
"pack": "./index.js -b",
|
|
39
39
|
"start": "./index.js -r",
|
|
40
|
-
"check": "for f in ./index.js ./initramfs/init.js ./initramfs/lib/document.js ./initramfs/lib/dlopen.js ./initramfs/lib/fbdev.js ./initramfs/lib/canvas.js ./initramfs/lib/bunterm/glyphs.js ./initramfs/lib/bunterm/renderer.js ./initramfs/lib/bunterm/images.js ./initramfs/lib/bunterm/input.js ./initramfs/lib/bunterm/mouse.js ./initramfs/lib/bunterm/terminal.js ./initramfs/lib/modprobe.js ./initramfs/lib/power.js ./initramfs/bin/bunterm ./initramfs/bin/chroot ./initramfs/bin/mount ./initramfs/bin/umount ./initramfs/bin/ip ./initramfs/bin/ps ./initramfs/bin/top ./initramfs/bin/poweroff ./initramfs/bin/reboot ./initramfs/bin/tar ./initramfs/bin/stripansi; do node --check \"$f\" || exit 1; done"
|
|
40
|
+
"check": "for f in ./index.js ./initramfs/init.js ./initramfs/lib/document.js ./initramfs/lib/dlopen.js ./initramfs/lib/ping.js ./initramfs/lib/dhcp.js ./initramfs/lib/dhcp-transport.js ./initramfs/lib/dhclient.js ./initramfs/lib/dhcp-start.js ./initramfs/lib/fbdev.js ./initramfs/lib/canvas.js ./initramfs/lib/bunterm/glyphs.js ./initramfs/lib/bunterm/renderer.js ./initramfs/lib/bunterm/images.js ./initramfs/lib/bunterm/input.js ./initramfs/lib/bunterm/mouse.js ./initramfs/lib/bunterm/terminal.js ./initramfs/lib/modprobe.js ./initramfs/lib/power.js ./initramfs/bin/bunterm ./initramfs/bin/chroot ./initramfs/bin/mount ./initramfs/bin/umount ./initramfs/bin/ip ./initramfs/bin/ping ./initramfs/bin/dhclient ./initramfs/bin/ps ./initramfs/bin/top ./initramfs/bin/poweroff ./initramfs/bin/reboot ./initramfs/bin/tar ./initramfs/bin/stripansi; do node --check \"$f\" || exit 1; done"
|
|
41
41
|
}
|
|
42
42
|
}
|
package/scripts/arch.js
CHANGED
|
@@ -16,8 +16,8 @@ export const rootDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
16
16
|
|
|
17
17
|
const pins = {
|
|
18
18
|
x86_64: {
|
|
19
|
-
linuxVirtSha256: "
|
|
20
|
-
linuxLtsSha256: "
|
|
19
|
+
linuxVirtSha256: "414aa2572ef88164b1af9905fa6dfbe863bf50f6130d895d57282dea3e15f7b9",
|
|
20
|
+
linuxLtsSha256: "7614556471859956a436b95813daf409b444aa849ec899178b404e828c5a9d98",
|
|
21
21
|
muslSha256: "573712e2f49c15bfc20a2699f204acdfc74c772722b15e7353d768057fae0e71",
|
|
22
22
|
stubSha256: "8e64a5a3afee5f930e6e6716be726dc6d405530ac7f8fa5be6251dae68671ec9",
|
|
23
23
|
libstdcppSha256: "14c987b556f5385a5db18376e788c75f37d85321b8dc1920d926ea7daac1d6f6",
|
|
@@ -32,8 +32,8 @@ const pins = {
|
|
|
32
32
|
serialMajor: 4,
|
|
33
33
|
},
|
|
34
34
|
aarch64: {
|
|
35
|
-
linuxVirtSha256: "
|
|
36
|
-
linuxLtsSha256: "
|
|
35
|
+
linuxVirtSha256: "b3820fee22e0366a15f7c139e5bc00348e333f3e7f77802271df7ded795aee4f",
|
|
36
|
+
linuxLtsSha256: "67b8f61c5cfb6ac9b5a65cb1610b67d467e6c4ffc17e7d29dbea4bd8c447ff32",
|
|
37
37
|
muslSha256: "5e9674b7f41152fe2119093b5cb4c13eaaadb19c2d5422b2d7267913e663ee6e",
|
|
38
38
|
stubSha256: "a1823d2d7082db555d528f82c1809f276c818aeb5b40f198f4565f104e055c38",
|
|
39
39
|
libstdcppSha256: "2302e766d4e4926038ec166ecb85837ee884576115236ddb565e3a5fca4a11d7",
|
|
@@ -67,7 +67,7 @@ export const config = ({
|
|
|
67
67
|
// The Alpine repository keeps only the newest build of a package, so a pin
|
|
68
68
|
// stops downloading when Alpine bumps it; the hashes then fail loudly.
|
|
69
69
|
const alpineBase = `${mirror}/v3.24/main/${arch}`;
|
|
70
|
-
const kernelVersion = "6.18.
|
|
70
|
+
const kernelVersion = "6.18.57";
|
|
71
71
|
const kernelRelease = `${kernelVersion}-0-${flavor}`;
|
|
72
72
|
const bunVersion = "1.4.2";
|
|
73
73
|
const downloadsDir = join(rootDir, "downloads", arch);
|
package/scripts/arch.sh
CHANGED
|
@@ -44,7 +44,7 @@ fi
|
|
|
44
44
|
alpine_mirror=${ALPINE_MIRROR:-https://dl-cdn.alpinelinux.org/alpine}
|
|
45
45
|
alpine_base="$alpine_mirror/v3.24/main/$arch"
|
|
46
46
|
|
|
47
|
-
kernel_version=6.18.
|
|
47
|
+
kernel_version=6.18.57
|
|
48
48
|
kernel_release="$kernel_version-0-$linux_flavor"
|
|
49
49
|
linux_package="linux-$linux_flavor-$kernel_version-r0.apk"
|
|
50
50
|
musl_package=musl-1.2.6-r2.apk
|
|
@@ -63,8 +63,8 @@ musl_libc="libc.musl-$arch.so.1"
|
|
|
63
63
|
|
|
64
64
|
case $arch in
|
|
65
65
|
x86_64)
|
|
66
|
-
linux_virt_sha256=
|
|
67
|
-
linux_lts_sha256=
|
|
66
|
+
linux_virt_sha256=414aa2572ef88164b1af9905fa6dfbe863bf50f6130d895d57282dea3e15f7b9
|
|
67
|
+
linux_lts_sha256=7614556471859956a436b95813daf409b444aa849ec899178b404e828c5a9d98
|
|
68
68
|
musl_sha256=573712e2f49c15bfc20a2699f204acdfc74c772722b15e7353d768057fae0e71
|
|
69
69
|
stub_sha256=8e64a5a3afee5f930e6e6716be726dc6d405530ac7f8fa5be6251dae68671ec9
|
|
70
70
|
libstdcpp_sha256=14c987b556f5385a5db18376e788c75f37d85321b8dc1920d926ea7daac1d6f6
|
|
@@ -82,8 +82,8 @@ case $arch in
|
|
|
82
82
|
serial_major=4
|
|
83
83
|
;;
|
|
84
84
|
aarch64)
|
|
85
|
-
linux_virt_sha256=
|
|
86
|
-
linux_lts_sha256=
|
|
85
|
+
linux_virt_sha256=b3820fee22e0366a15f7c139e5bc00348e333f3e7f77802271df7ded795aee4f
|
|
86
|
+
linux_lts_sha256=67b8f61c5cfb6ac9b5a65cb1610b67d467e6c4ffc17e7d29dbea4bd8c447ff32
|
|
87
87
|
musl_sha256=5e9674b7f41152fe2119093b5cb4c13eaaadb19c2d5422b2d7267913e663ee6e
|
|
88
88
|
stub_sha256=a1823d2d7082db555d528f82c1809f276c818aeb5b40f198f4565f104e055c38
|
|
89
89
|
libstdcpp_sha256=2302e766d4e4926038ec166ecb85837ee884576115236ddb565e3a5fca4a11d7
|
package/scripts/modules.js
CHANGED
package/scripts/npm-packages.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# npm packages
|
|
2
2
|
# Latest versions checked on 2026-10-11; SHA-256 pins verified against registry SHA-512 integrity.
|
|
3
|
-
# buninu-browser
|
|
4
|
-
# casty 1.3.7
|
|
3
|
+
# buninu-browser 0.2.4 and buninu-land 0.1.4 verified on 2026-10-18.
|
|
4
|
+
# casty 1.3.7 verified on 2026-10-16.
|
|
5
5
|
|
|
6
|
-
"buninu-browser@0.2.
|
|
7
|
-
- https://registry.npmjs.org/buninu-browser/-/buninu-browser-0.2.
|
|
8
|
-
- sha256:
|
|
6
|
+
"buninu-browser@0.2.4":
|
|
7
|
+
- https://registry.npmjs.org/buninu-browser/-/buninu-browser-0.2.4.tgz
|
|
8
|
+
- sha256:247c1ae9644b5016d1dc3f74d1c6c18a986e3961b1d86d2497862e4007bc50aa
|
|
9
9
|
|
|
10
|
-
"buninu-land@0.1.
|
|
11
|
-
- https://registry.npmjs.org/buninu-land/-/buninu-land-0.1.
|
|
12
|
-
- sha256:
|
|
10
|
+
"buninu-land@0.1.4":
|
|
11
|
+
- https://registry.npmjs.org/buninu-land/-/buninu-land-0.1.4.tgz
|
|
12
|
+
- sha256:5cda65c60a7af8c7a05ec519a254e0af3f9913ea94f6ae493e5ddc93a8d87407
|
|
13
13
|
|
|
14
14
|
"@drxiaozhi/casty@1.3.7":
|
|
15
15
|
- https://registry.npmjs.org/@drxiaozhi/casty/-/casty-1.3.7.tgz
|