buninu-linux 0.11.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 +53 -50
- package/README.md +50 -19
- package/fetch-alpine.sh +1 -1
- package/initramfs/bin/buninu-land +2 -0
- package/initramfs/bin/dhclient +8 -0
- package/initramfs/bin/ping +8 -0
- package/initramfs/buninu/apps/jsgotty/gotty.js +4 -2
- package/initramfs/init.js +8 -7
- package/initramfs/lib/browser-resources.js +11 -0
- package/initramfs/lib/bunterm/images.js +2 -1
- package/initramfs/lib/bunterm/renderer.js +1 -1
- package/initramfs/lib/bunterm/terminal.js +2 -2
- 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 +11 -11
- package/scripts/stage-npm.js +8 -1
- package/scripts/stage-npm.sh +9 -1
- package/test/bunterm-image-bytes.test.js +55 -0
- 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
- package/test/stage-npm.test.js +23 -3
package/NOTICE.md
CHANGED
|
@@ -139,83 +139,83 @@ the built image redistributes them.
|
|
|
139
139
|
- License: GPL-2.0-only WITH Linux-syscall-note
|
|
140
140
|
- See: `LICENSES/GPL-2.0.txt` and `LICENSES/Linux-syscall-note.txt`
|
|
141
141
|
|
|
142
|
-
Linux 6.18.
|
|
143
|
-
The default build uses package `linux-virt`, version 6.18.
|
|
144
|
-
`--linux-lts` and `--real` use package `linux-lts`, version 6.18.
|
|
142
|
+
Linux 6.18.57 as packaged by Alpine Linux 3.24 from the `linux-lts` aport.
|
|
143
|
+
The default build uses package `linux-virt`, version 6.18.57-r0.
|
|
144
|
+
`--linux-lts` and `--real` use package `linux-lts`, version 6.18.57-r0.
|
|
145
145
|
Each comes from the x86_64 or aarch64 repository to match `--arch`. All
|
|
146
146
|
listed modules are the selected package's `.ko.gz` files decompressed without
|
|
147
147
|
modification.
|
|
148
148
|
|
|
149
|
-
- Upstream source: https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.
|
|
149
|
+
- Upstream source: https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.57.tar.xz
|
|
150
150
|
- Alpine build recipe, configuration and patches (Corresponding Source),
|
|
151
|
-
pinned to the commit shipping 6.18.
|
|
152
|
-
https://gitlab.alpinelinux.org/alpine/aports/-/blob/
|
|
151
|
+
pinned to the commit shipping 6.18.57-r0 (the same for both architectures):
|
|
152
|
+
https://gitlab.alpinelinux.org/alpine/aports/-/blob/0c40eb4fea009af7de457fa20e186f43a88e0954/main/linux-lts/APKBUILD
|
|
153
153
|
- Packages:
|
|
154
|
-
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/x86_64/linux-virt-6.18.
|
|
155
|
-
SHA-256 `
|
|
156
|
-
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/x86_64/linux-lts-6.18.
|
|
157
|
-
SHA-256 `
|
|
158
|
-
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/aarch64/linux-virt-6.18.
|
|
159
|
-
SHA-256 `
|
|
160
|
-
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/aarch64/linux-lts-6.18.
|
|
161
|
-
SHA-256 `
|
|
154
|
+
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/x86_64/linux-virt-6.18.57-r0.apk
|
|
155
|
+
SHA-256 `414aa2572ef88164b1af9905fa6dfbe863bf50f6130d895d57282dea3e15f7b9`
|
|
156
|
+
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/x86_64/linux-lts-6.18.57-r0.apk
|
|
157
|
+
SHA-256 `7614556471859956a436b95813daf409b444aa849ec899178b404e828c5a9d98`
|
|
158
|
+
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/aarch64/linux-virt-6.18.57-r0.apk
|
|
159
|
+
SHA-256 `b3820fee22e0366a15f7c139e5bc00348e333f3e7f77802271df7ded795aee4f`
|
|
160
|
+
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/aarch64/linux-lts-6.18.57-r0.apk
|
|
161
|
+
SHA-256 `67b8f61c5cfb6ac9b5a65cb1610b67d467e6c4ffc17e7d29dbea4bd8c447ff32`
|
|
162
162
|
|
|
163
163
|
SHA-256 of files in the default x86_64 `linux-virt` image:
|
|
164
164
|
|
|
165
165
|
```text
|
|
166
|
-
kernel/x86_64/vmlinuz-virt
|
|
167
|
-
failover.ko
|
|
168
|
-
net_failover.ko
|
|
169
|
-
virtio_net.ko
|
|
166
|
+
kernel/x86_64/vmlinuz-virt 3725536e1c015345d781430b728849c2f090cf2040af00d7fe825920a106a08e
|
|
167
|
+
failover.ko f373917790e5e17b8c0c278efdd6fef7545ee8362510efaa80efb821e553f0b3
|
|
168
|
+
net_failover.ko 78fce63b3625b11bc65d4f0eb8f844796cad472ef14302016458ac60f898ac95
|
|
169
|
+
virtio_net.ko 18addca2d17e0b01b9adc64111b0409c08da6a196215e68bfda067309ff323a8
|
|
170
170
|
```
|
|
171
171
|
|
|
172
172
|
SHA-256 of files in an x86_64 `--linux-lts` image:
|
|
173
173
|
|
|
174
174
|
```text
|
|
175
|
-
kernel/x86_64/vmlinuz-lts
|
|
176
|
-
failover.ko
|
|
177
|
-
net_failover.ko
|
|
178
|
-
virtio_net.ko
|
|
179
|
-
virtio_ring.ko
|
|
180
|
-
virtio.ko
|
|
181
|
-
virtio_pci_legacy_dev.ko
|
|
182
|
-
virtio_pci_modern_dev.ko
|
|
183
|
-
virtio_pci.ko
|
|
175
|
+
kernel/x86_64/vmlinuz-lts 674c615f1f0887621e6609becd11a7db40cc71b7616644fcc260b4b226a52103
|
|
176
|
+
failover.ko 00d409746a8033e5acf0d4d0b8db4589d48168f2965e4de0c05374bc4e3a4025
|
|
177
|
+
net_failover.ko afbe1a6cd279381ade52943158c62931d79f38dcd293e31ef522878ff82c17ed
|
|
178
|
+
virtio_net.ko 84b658a64416094f3bc793ac43ccd987e087898d0c0510ddf5f9e53e0f9b36ff
|
|
179
|
+
virtio_ring.ko db497b3c23133f48b5a48800695b9f7068244dfa9d52cd1d10b5d4618e68fb60
|
|
180
|
+
virtio.ko c867c11e61db4c9c4a7af54f29ba9d127759706854ba84bf42ca6f2ae8b4f71a
|
|
181
|
+
virtio_pci_legacy_dev.ko 8825df8124c86800b3bf3d3403f6aba47d527d02565f91baddebe40ea0f66f88
|
|
182
|
+
virtio_pci_modern_dev.ko 3ba582ad90247b04fe2afe289d2b51a39e3efc1cc40e58f55e7701062141e061
|
|
183
|
+
virtio_pci.ko 5f6baef35e1536d735628e14fa3172c27228cd26f7c80b7465d2afbed8ee3876
|
|
184
184
|
```
|
|
185
185
|
|
|
186
186
|
`--real` additionally includes the following modules from that same
|
|
187
187
|
x86_64 `linux-lts` package for physical xHCI controllers and USB HID keyboards:
|
|
188
188
|
|
|
189
189
|
```text
|
|
190
|
-
usb-common.ko
|
|
191
|
-
usbcore.ko
|
|
192
|
-
xhci-hcd.ko
|
|
193
|
-
xhci-pci.ko
|
|
194
|
-
xhci-pci-renesas.ko
|
|
195
|
-
hid.ko
|
|
196
|
-
hid-generic.ko
|
|
197
|
-
usbhid.ko
|
|
190
|
+
usb-common.ko eb9d7b52b5c8f74b062ea3efbd8112c1460e3d34c0338e538602c648b958a8ef
|
|
191
|
+
usbcore.ko 30286b4e42c985e56903f41fd35233b3b35700b96ab99b088b1d91735c8e6e94
|
|
192
|
+
xhci-hcd.ko f0988c511d5692265f8c5221d77c468c9650151f3d69b5b68988eba9d1fab7f1
|
|
193
|
+
xhci-pci.ko a9145f467b6bf9b099e52eb5ac41b0c1164d9400e5b2f5f9d595ff58b4d776fb
|
|
194
|
+
xhci-pci-renesas.ko 2d88a4e72512944b62a4394172702db8dc211ccc7b99a93778f2d42ed8a23caf
|
|
195
|
+
hid.ko 7994f6b42f1df68a62312cf4de50a22b1a9756c3da91966a634630a62b18ae10
|
|
196
|
+
hid-generic.ko ea2eb7491e30bef769d2c419a2ccc9d55db030edddfe531c0d4e4723e170a4c3
|
|
197
|
+
usbhid.ko 899f357db5ba74182cf9161a577b080f8a6a9b0b015f6a322a5c31bf8754188b
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
SHA-256 of files in the default aarch64 `linux-virt` image:
|
|
201
201
|
|
|
202
202
|
```text
|
|
203
|
-
kernel/aarch64/vmlinuz-virt
|
|
204
|
-
failover.ko
|
|
205
|
-
net_failover.ko
|
|
206
|
-
virtio_net.ko
|
|
203
|
+
kernel/aarch64/vmlinuz-virt 60603568b5589c730e9183e5d6d4f0b8575ddac31e9187222decc92416329b26
|
|
204
|
+
failover.ko 792943de0975a4111d2040ab35cc214e5b1b4ec8c12dbc9725fd0602a56f1576
|
|
205
|
+
net_failover.ko ba3ab71ef38aa3a957c5b0fe7ddd908e31635e408f5937bccb50f36bb78761ba
|
|
206
|
+
virtio_net.ko 716e32b4425aa8dd4ebfeb6808e58c125e6b125c71b00ba8288851363f72fa60
|
|
207
207
|
```
|
|
208
208
|
|
|
209
209
|
SHA-256 of files in an aarch64 `--linux-lts` image:
|
|
210
210
|
|
|
211
211
|
```text
|
|
212
|
-
kernel/aarch64/vmlinuz-lts
|
|
213
|
-
failover.ko
|
|
214
|
-
net_failover.ko
|
|
215
|
-
virtio_net.ko
|
|
216
|
-
virtio_pci_legacy_dev.ko
|
|
217
|
-
virtio_pci_modern_dev.ko
|
|
218
|
-
virtio_pci.ko
|
|
212
|
+
kernel/aarch64/vmlinuz-lts 19fc9f974bb3cb666b8a216f18ca9f9df8e1658da4e9e55ed464c3fcaf87a303
|
|
213
|
+
failover.ko d243de2d9c2b2c34094c125719fa784b2e476986bfcd5766b96e7f7fbcb658ac
|
|
214
|
+
net_failover.ko 675cdf971cbb2ab15f8db6295afadcfc852172f38d289e9935ed28386e3e38f6
|
|
215
|
+
virtio_net.ko 34ea985ebf0bef82731f398aa3aca9030d6d7882d4bcb1542fd17d8637cad70e
|
|
216
|
+
virtio_pci_legacy_dev.ko 17f1487bf62ac3b2961f5f959fb28a2e1135db565e8a77296372c5101e00aae1
|
|
217
|
+
virtio_pci_modern_dev.ko e7a76945ae0b9c75af78a77950a4f22c95758fe507db2b77fbc168a4d6f842d8
|
|
218
|
+
virtio_pci.ko 76efc4e0428eb0413727247f41972d72c3f2ed1373f7e94f88052357a097b6a8
|
|
219
219
|
```
|
|
220
220
|
|
|
221
221
|
### linuxx64.efi.stub and linuxaa64.efi.stub
|
|
@@ -285,8 +285,8 @@ committed source files, but their contents are redistributed in the image.
|
|
|
285
285
|
| Package | Version | Package license | Project / package page | Installed license and notices (relative to `/usr/lib/node_modules/`) |
|
|
286
286
|
|---|---|---|---|---|
|
|
287
287
|
| `buninu-browser` | 0.2.3 | MIT for the browser's own code; bundled components retain their own terms | https://github.com/jjtseng93/buninu-browser | `buninu-browser/LICENSE`, `THIRD_PARTY_NOTICES.md`, `LICENSES/` and vendor notices within that package |
|
|
288
|
-
| `buninu-land` | 0.1.
|
|
289
|
-
| `@drxiaozhi/casty` | 1.3.
|
|
288
|
+
| `buninu-land` | 0.1.3 | MIT for its own code; bundled components retain their own terms | https://github.com/jjtseng93/buninu-land | `buninu-land/LICENSE`, `THIRD_PARTY_NOTICES.md`, `LICENSES/`, `SOURCES.md`, protocol XML notices and vendor notices |
|
|
289
|
+
| `@drxiaozhi/casty` | 1.3.7 | MIT | https://github.com/jjtseng93/casty | `@drxiaozhi/casty/LICENSE` |
|
|
290
290
|
| `@drxiaozhi/jspulse` | 0.2.1 | LGPL-2.1-or-later, with additional component notices | https://github.com/jjtseng93/jspulse | `@drxiaozhi/jspulse/LICENSE`, `NOTICE.md`, `LICENSES/` and source-file notices within that package |
|
|
291
291
|
| `@drxiaozhi/jspinyin` | 0.1.1 | MIT; hanzipinyin material under the Unlicense | https://www.npmjs.com/package/@drxiaozhi/jspinyin | `@drxiaozhi/jspinyin/LICENSE` and `@drxiaozhi/jspinyin/LICENSE-hanzipinyin.md` |
|
|
292
292
|
|
|
@@ -320,8 +320,11 @@ the source archive or rebuild material merely to reduce the image size.
|
|
|
320
320
|
|
|
321
321
|
### Buninu Land
|
|
322
322
|
|
|
323
|
-
The compositor package
|
|
324
|
-
|
|
323
|
+
The compositor package retains its loader, protocol XML, source provenance,
|
|
324
|
+
patches and license collection. When its CanvasKit WASM is byte-identical to
|
|
325
|
+
the system copy, packing omits the duplicate and the distribution launcher
|
|
326
|
+
creates a relative symlink to `/lib/canvaskit/canvaskit.wasm`. Differing WASM
|
|
327
|
+
versions remain bundled. All associated licenses and notices are retained.
|
|
325
328
|
Retain `THIRD_PARTY_NOTICES.md`, `SOURCES.md`, `vendor-manifest.json`,
|
|
326
329
|
`LICENSES/`, `vendor/canvaskit/LICENSE`, and the notices embedded in the
|
|
327
330
|
Wayland protocol XML files. These cover Buninu Browser-derived code,
|
package/README.md
CHANGED
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
* Bun reaches its interactive REPL
|
|
36
36
|
* `start` starts the Buninu userspace shell
|
|
37
37
|
* `cfg.disk` + shell `mount` mounts local disks
|
|
38
|
-
* `cfg.net` loads common wired NIC and Android USB-tethering drivers
|
|
38
|
+
* `cfg.net` loads common wired NIC and Android USB-tethering drivers and automatically attempts DHCP configuration
|
|
39
39
|
* `cfg.sound` loads every packaged ALSA sound module
|
|
40
40
|
* Android phone USB tethering over RNDIS has been tested successfully, allowing Buninu Linux to access the Internet through an Android phone
|
|
41
|
-
*
|
|
41
|
+
* After a DHCP lease expires or when you need fresh network settings, run `dhclient usb0` (using your interface name). Manual configuration with `ip` remains available.
|
|
42
42
|
* The bundled `jmi` editor, jsmdcui App runtime, and local JavaScript execution also work.
|
|
43
43
|
* Multi-tasking with kernel VT or jsmdcui
|
|
44
44
|
+ See [Multitasking modes](#multitasking-modes)
|
|
@@ -227,8 +227,7 @@ It supports Chinese text, emoji and the browser's terminal graphics. Use
|
|
|
227
227
|
`Ctrl+Alt+F1` to return to the original console and `Ctrl+Alt+F2` to return
|
|
228
228
|
to this terminal.
|
|
229
229
|
|
|
230
|
-
|
|
231
|
-
described under [Basic configuration](#basic-configuration). Run `poweroff` for a synced
|
|
230
|
+
Run `poweroff` for a synced
|
|
232
231
|
shutdown. After editing the initramfs, `bun ./index.js -b --real --export` rebuilds the
|
|
233
232
|
image; `-h` lists all flags.
|
|
234
233
|
|
|
@@ -384,9 +383,16 @@ cfg.power
|
|
|
384
383
|
cfg.sound
|
|
385
384
|
```
|
|
386
385
|
|
|
387
|
-
`cfg.net` loads the packaged wired-network and Android USB-tethering drivers
|
|
388
|
-
interfaces and MAC addresses
|
|
389
|
-
|
|
386
|
+
`cfg.net` loads the packaged wired-network and Android USB-tethering drivers,
|
|
387
|
+
prints detected interfaces and MAC addresses, and automatically attempts DHCP
|
|
388
|
+
configuration. If it succeeds, no additional DHCP command is needed.
|
|
389
|
+
|
|
390
|
+
After a lease expires or when you need to obtain network settings again,
|
|
391
|
+
enter the Buninu shell with `start()` and run `dhclient usb0` (replace `usb0`
|
|
392
|
+
with your interface name). It configures the interface once and exits;
|
|
393
|
+
there is no automatic renewal.
|
|
394
|
+
|
|
395
|
+
For a manually configured static address, use:
|
|
390
396
|
|
|
391
397
|
```sh
|
|
392
398
|
ip link set eth0 up
|
|
@@ -695,6 +701,8 @@ If there is no sound, check step 4's terminal for errors, and make sure
|
|
|
695
701
|
| `mount` | `mount(2)` with type detection, `-o` parsing, `LABEL=`/`UUID=`, bind/move/remount; loads required filesystem and disk modules | `mount --help` → `/usr/share/doc/buninu-linux/mount.md` |
|
|
696
702
|
| `umount` | `umount2(2)` with `-l`, `-f`, `-R` | `umount --help` |
|
|
697
703
|
| `ip` | iproute2 grammar over `SIOC*` ioctls and `/proc/net`: `link`, `addr`, `route`, `neigh` | `ip --help` |
|
|
704
|
+
| `dhclient` | Reacquire DHCPv4 settings after expiry or when needed (`dhclient usb0`), then exit; also supports release | `dhclient --help` |
|
|
705
|
+
| `ping` | IPv4 ICMP / IPv6 ICMPv6 echo via Bun FFI; count, interval, timeout, deadline, payload size, TTL and source selection | `ping --help` |
|
|
698
706
|
| `ps` | composable process fields from `/proc`; also exports its snapshot and single-row formatter for other commands | `ps --help` |
|
|
699
707
|
| `top` | periodically refreshes the process snapshot and rows supplied by `ps` | `top --help` |
|
|
700
708
|
| `poweroff` | sync pending writes and power off through the Linux reboot system call | `poweroff --help` |
|
|
@@ -717,6 +725,14 @@ above, implemented as Bun scripts or thin launchers for bundled packages.
|
|
|
717
725
|
For Debian/Alpine rootfs and Chromium examples, see
|
|
718
726
|
[Optional chroot and native binaries](#optional-chroot-and-native-binaries).
|
|
719
727
|
|
|
728
|
+
`ping -c 4 127.0.0.1` checks IPv4 connectivity; `ping -6 -c 4 ::1` checks
|
|
729
|
+
IPv6. `ping -c 10 -i 0.2 -W 1 HOST` adjusts count, interval and reply timeout.
|
|
730
|
+
The command uses ICMP sockets directly, with a raw-socket fallback when needed.
|
|
731
|
+
It supports Linux glibc/musl and native Android bionic through the shared libc
|
|
732
|
+
loader; `BUNINU_LIBC` remains an explicit override. Run
|
|
733
|
+
`bun test test/ping.test.js test/libc.test.js` to compare against the installed
|
|
734
|
+
system ping. See `ping --help` for permission requirements and supported flags.
|
|
735
|
+
|
|
720
736
|
### Buninu Browser (early-stage, experimental)
|
|
721
737
|
|
|
722
738
|
`buninu-browser` is still in an early stage. Websites may render incorrectly
|
|
@@ -763,7 +779,8 @@ The `/bin/buninu-land` launcher sets `CASTY_BROWSER` to
|
|
|
763
779
|
`/usr/lib/node_modules/buninu-land/compositor.js` and runs the bundled Casty.
|
|
764
780
|
`--casty` forwards arguments to Casty; `--headless`, `--help`, `--version`,
|
|
765
781
|
README options and reserved native frontends go through the package entry.
|
|
766
|
-
The package's CanvasKit WASM
|
|
782
|
+
The package's CanvasKit WASM shares the system copy when byte-identical;
|
|
783
|
+
its loader and license files are retained.
|
|
767
784
|
|
|
768
785
|
### Archive tools and other examples
|
|
769
786
|
|
|
@@ -1178,7 +1195,7 @@ initramfs/ init.js, commands, userspace: the same for every archi
|
|
|
1178
1195
|
native/<arch>/ musl and the GCC runtime (committed); bun and modules (fetched)
|
|
1179
1196
|
```
|
|
1180
1197
|
|
|
1181
|
-
The `--real` inputs are Alpine v3.24 `linux-lts-6.18.
|
|
1198
|
+
The `--real` inputs are Alpine v3.24 `linux-lts-6.18.57-r0`, musl `1.2.6-r2`,
|
|
1182
1199
|
`systemd-efistub-260.2-r0`, `libstdc++`/`libgcc` `15.2.0-r5`, and Bun 1.4.2
|
|
1183
1200
|
`linux-x64-musl-baseline`. The aarch64 guest uses the same versions from
|
|
1184
1201
|
Alpine's aarch64 repository and Bun's `linux-aarch64-musl`. There is no BusyBox
|
|
@@ -1193,11 +1210,11 @@ extracts from it. Bumping a version changes both the file name and the hash, so
|
|
|
1193
1210
|
it always refetches.
|
|
1194
1211
|
|
|
1195
1212
|
The five application packages are pinned separately in
|
|
1196
|
-
`scripts/npm-packages.md`: `buninu-browser@0.2.
|
|
1197
|
-
`@drxiaozhi/casty@1.3.
|
|
1213
|
+
`scripts/npm-packages.md`: `buninu-browser@0.2.4`, `buninu-land@0.1.4`,
|
|
1214
|
+
`@drxiaozhi/casty@1.3.7`, `@drxiaozhi/jspulse@0.2.1`, and
|
|
1198
1215
|
`@drxiaozhi/jspinyin@0.1.1`. These were npm's `latest` versions when checked
|
|
1199
1216
|
on 2026-10-11, with buninu-browser updated on 2026-10-12, and
|
|
1200
|
-
casty 1.3.
|
|
1217
|
+
casty 1.3.7 and buninu-land 0.1.3 verified on 2026-10-16;
|
|
1201
1218
|
fetch never resolves `latest` or a version range at runtime.
|
|
1202
1219
|
Each registry tarball was checked against its published SHA-512 integrity
|
|
1203
1220
|
before its SHA-256 was recorded in the shared pins file.
|
|
@@ -1342,7 +1359,7 @@ The physical boot chain is firmware → the UKI stub → its embedded
|
|
|
1342
1359
|
and, with the currently pinned kernel, embeds this complete command line:
|
|
1343
1360
|
|
|
1344
1361
|
```text
|
|
1345
|
-
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.
|
|
1362
|
+
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.57-0-lts rdinit=/bin/bun -- -e import('/init.js')
|
|
1346
1363
|
```
|
|
1347
1364
|
|
|
1348
1365
|
Serial kernel logging is retained, while the final console makes
|
|
@@ -1366,7 +1383,7 @@ the same way). Build without `--real`; this selects Alpine `linux-virt`, omits t
|
|
|
1366
1383
|
module set, and puts `ttyS0` last so serial stdio owns `/dev/console`:
|
|
1367
1384
|
|
|
1368
1385
|
```text
|
|
1369
|
-
console=tty0 console=ttyS0,115200 panic=0 PATH=/bin KERNEL_RELEASE=6.18.
|
|
1386
|
+
console=tty0 console=ttyS0,115200 panic=0 PATH=/bin KERNEL_RELEASE=6.18.57-0-virt rdinit=/bin/bun -- -e import('/init.js')
|
|
1370
1387
|
```
|
|
1371
1388
|
|
|
1372
1389
|
Build and boot it with:
|
|
@@ -1483,7 +1500,7 @@ The default `--real` image embeds this kernel command line (shown for the
|
|
|
1483
1500
|
currently pinned LTS release):
|
|
1484
1501
|
|
|
1485
1502
|
```text
|
|
1486
|
-
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.
|
|
1503
|
+
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.57-0-lts rdinit=/bin/bun -- -e import('/init.js')
|
|
1487
1504
|
```
|
|
1488
1505
|
|
|
1489
1506
|
The physical display (`tty0`) is the final console and therefore owns PID 1's
|
|
@@ -1562,8 +1579,22 @@ custom `libinit.so` is needed.
|
|
|
1562
1579
|
On a `--real` boot, init starts `/etc/resolv.conf` with `1.1.1.1` and
|
|
1563
1580
|
`8.8.8.8`; these are initial public resolvers until the user replaces them
|
|
1564
1581
|
with DNS appropriate for the local network. Hardware networking remains down
|
|
1565
|
-
until `cfg.net` loads the packaged drivers and
|
|
1566
|
-
route.
|
|
1582
|
+
until `cfg.net` loads the packaged drivers and attempts DHCP configuration.
|
|
1583
|
+
Manual address and route configuration with `ip` remains available.
|
|
1584
|
+
|
|
1585
|
+
`cfg.net` starts DHCP in the background for every detected non-loopback interface.
|
|
1586
|
+
It returns immediately; failures and acquisition timeouts are logged without
|
|
1587
|
+
interrupting init. Each client exits after configuring its lease; no renewal process stays running.
|
|
1588
|
+
Repeated `cfg.net` calls do not launch duplicate active clients. Route metrics
|
|
1589
|
+
start at 100; only the first interface updates DNS, leaving the init fallback
|
|
1590
|
+
resolver available if it cannot acquire a lease.
|
|
1591
|
+
|
|
1592
|
+
After a lease expires or when network settings need to be obtained again,
|
|
1593
|
+
run `dhclient usb0` (or `dhclient eth0`, depending on the interface name).
|
|
1594
|
+
This is not an extra step after successful automatic configuration by `cfg.net`.
|
|
1595
|
+
`dhclient` exits after configuration; `-timeout 20` limits acquisition to 20
|
|
1596
|
+
seconds. Leases do not renew automatically. For manual static configuration,
|
|
1597
|
+
see [Basic configuration](#basic-configuration).
|
|
1567
1598
|
|
|
1568
1599
|
On a `--real` image the REPL also offers the getter-based `cfg` namespace:
|
|
1569
1600
|
`cfg.net`, `cfg.disk`, `cfg.power`, `cfg.sound` and `cfg.all` run as soon as the property is read, without
|
|
@@ -1696,9 +1727,9 @@ when redistributing the image.
|
|
|
1696
1727
|
| systemd EFI stub | LGPL-2.1-or-later | image only |
|
|
1697
1728
|
| Bun | MIT, plus the licenses of what it statically links | image only |
|
|
1698
1729
|
| buninu-browser 0.2.3 | MIT for its own code; bundled components have their own licenses | npm download → image |
|
|
1699
|
-
| buninu-land 0.1.
|
|
1730
|
+
| buninu-land 0.1.3 | MIT for its own code; bundled components retain their own licenses and notices | npm download → image |
|
|
1700
1731
|
| wasmpeg / FFmpeg (`@wasmpeg/cpu` 1.0.1, bundled in buninu-browser) | LGPL-2.1-or-later, plus component notices; source archive and rebuild documentation retained | browser package → image |
|
|
1701
|
-
| `@drxiaozhi/casty` 1.3.
|
|
1732
|
+
| `@drxiaozhi/casty` 1.3.7 | MIT | npm download → image |
|
|
1702
1733
|
| `@drxiaozhi/jspulse` 0.2.1 | LGPL-2.1-or-later, plus component notices | npm download → image |
|
|
1703
1734
|
| `@drxiaozhi/jspinyin` 0.1.1 | MIT; hanzipinyin material under the Unlicense | npm download → image |
|
|
1704
1735
|
| CanvasKit 0.41.1 (`initramfs/lib/canvaskit/`, Skia compiled to WebAssembly) | BSD-3-Clause (`LICENSES/BSD-3-Clause-Skia.txt`) | committed |
|
package/fetch-alpine.sh
CHANGED
|
@@ -63,7 +63,7 @@ select_optional_module() {
|
|
|
63
63
|
# Network: virtio-net for QEMU (init.js loads these at boot). virtio_pci is
|
|
64
64
|
# the PCI transport for every virtio device, disk included; linux-virt builds
|
|
65
65
|
# it in, linux-lts has it as a module that nothing in modules.dep pulls in.
|
|
66
|
-
for module in failover net_failover virtio_net; do select_module "$module"; done
|
|
66
|
+
for module in failover net_failover virtio_net af_packet; do select_module "$module"; done
|
|
67
67
|
select_optional_module virtio_pci
|
|
68
68
|
# Storage: what /bin/mount loads on demand. ext4 brings jbd2, mbcache and
|
|
69
69
|
# crc16; FAT, exFAT and NTFS ask the kernel for the NLS tables at mount
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
// preserve the distribution entry point.
|
|
4
4
|
import { resolve, join } from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { ensureLandResources } from "../lib/browser-resources.js";
|
|
6
7
|
|
|
7
8
|
const root = resolve(import.meta.dir, "..");
|
|
8
9
|
const modules = join(root, "usr/lib/node_modules");
|
|
9
10
|
const browser = join(modules, "buninu-land");
|
|
11
|
+
ensureLandResources(browser, root);
|
|
10
12
|
const entry = join(browser, "index.js");
|
|
11
13
|
const { chooseFrontend } = await import(pathToFileURL(entry).href);
|
|
12
14
|
const args = process.argv.slice(2);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/bin/bun
|
|
2
|
+
import {parseDhclient,runDhclient} from '../lib/dhclient.js';
|
|
3
|
+
import {showDocument} from '../lib/document.js';
|
|
4
|
+
try {
|
|
5
|
+
const options=parseDhclient(Bun.argv.slice(2));
|
|
6
|
+
if(options.help)await showDocument('dhclient');
|
|
7
|
+
else process.exitCode=await runDhclient(options);
|
|
8
|
+
}catch(error){console.error(`dhclient: ${error.message}`);process.exitCode=1;}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/bin/bun
|
|
2
|
+
import {parsePing,runPing} from '../lib/ping.js';
|
|
3
|
+
import {showDocument} from '../lib/document.js';
|
|
4
|
+
try {
|
|
5
|
+
const options=parsePing(Bun.argv.slice(2));
|
|
6
|
+
if(options.help)await showDocument('ping');
|
|
7
|
+
else process.exitCode=await runPing(options);
|
|
8
|
+
} catch(error) {console.error(`ping: ${error.message}`);process.exitCode=2;}
|
|
@@ -1001,7 +1001,9 @@ class CursorStateTracker {
|
|
|
1001
1001
|
}
|
|
1002
1002
|
|
|
1003
1003
|
class KittyGraphicsParser {
|
|
1004
|
-
constructor() {
|
|
1004
|
+
constructor({ binaryData = false } = {}) {
|
|
1005
|
+
// Local renderers can consume bytes directly; browser clients retain base64.
|
|
1006
|
+
this.binaryData = binaryData;
|
|
1005
1007
|
this.pending = Buffer.alloc(0);
|
|
1006
1008
|
this.transfers = new Map();
|
|
1007
1009
|
this.images = new Map();
|
|
@@ -1220,7 +1222,7 @@ class KittyGraphicsParser {
|
|
|
1220
1222
|
width: Number.parseInt(existing.control.s || "0", 10),
|
|
1221
1223
|
height: Number.parseInt(existing.control.v || "0", 10),
|
|
1222
1224
|
mime: existing.control.U || existing.control.mime || null,
|
|
1223
|
-
data: binary.toString("base64")
|
|
1225
|
+
data: this.binaryData ? binary : binary.toString("base64")
|
|
1224
1226
|
};
|
|
1225
1227
|
|
|
1226
1228
|
this.images.set(imageId, image);
|
package/initramfs/init.js
CHANGED
|
@@ -495,7 +495,7 @@ globalThis.start = launchBuninu;
|
|
|
495
495
|
// cfg.net loads every packaged network module, including PHY and bus support
|
|
496
496
|
// that a kernel userspace modprobe helper would normally load on demand. It
|
|
497
497
|
// then reports the device matches and lists the resulting interfaces. The
|
|
498
|
-
//
|
|
498
|
+
// DHCP runs independently afterward; errors and timeouts never block init.
|
|
499
499
|
const configureEthernet = () => {
|
|
500
500
|
const { autoload, modprobeAll } = require("/lib/modprobe.js");
|
|
501
501
|
const { readdirSync, readFileSync } = require("node:fs");
|
|
@@ -529,12 +529,13 @@ const configureEthernet = () => {
|
|
|
529
529
|
const carrier = read(`/sys/class/net/${name}/carrier`);
|
|
530
530
|
console.log(`eth: ${name} ${read(`/sys/class/net/${name}/address`)} ${read(`/sys/class/net/${name}/operstate`)}${carrier === "1" ? " carrier" : ""}`);
|
|
531
531
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
532
|
+
try {
|
|
533
|
+
const { startDhcp } = require("/lib/dhcp-start.js");
|
|
534
|
+
startDhcp(interfaces);
|
|
535
|
+
} catch (error) {
|
|
536
|
+
console.log(`[dhcp setup] unavailable: ${error?.message ?? error}; init continues`);
|
|
537
|
+
}
|
|
538
|
+
console.log("[dhcp] If automatic DHCP fails, run start() to enter the shell, then try dhclient usb0 (replace usb0 with your interface) or configure the network manually with ip commands. Use ip link to list network interfaces.");
|
|
538
539
|
return interfaces;
|
|
539
540
|
};
|
|
540
541
|
|
|
@@ -23,3 +23,14 @@ export function ensureBrowserResources(browser, systemRoot = "/") {
|
|
|
23
23
|
catch (error) { if (error.code !== "EEXIST") throw error; }
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
// Land keeps its own loader and notices; only the identical WASM is shared.
|
|
28
|
+
export function ensureLandResources(land, systemRoot = "/") {
|
|
29
|
+
const path = join(land, "vendor/canvaskit/canvaskit.wasm");
|
|
30
|
+
if (lstat(path)) return;
|
|
31
|
+
const target = resolve(systemRoot, "lib/canvaskit/canvaskit.wasm");
|
|
32
|
+
if (!statSync(target).isFile()) throw new Error(`missing compositor resource: ${target}`);
|
|
33
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
34
|
+
try { symlinkSync(relative(dirname(path), target), path); }
|
|
35
|
+
catch (error) { if (error.code !== "EEXIST") throw error; }
|
|
36
|
+
}
|
|
@@ -16,7 +16,8 @@ const number = (value, fallback = 0) => {
|
|
|
16
16
|
// encoded format Skia knows); 24 and 32 are raw RGB / RGBA. Bun.Image, when
|
|
17
17
|
// present, transcodes encodings Skia cannot read on its own.
|
|
18
18
|
export const decodeImage = async (CanvasKit, image) => {
|
|
19
|
-
|
|
19
|
+
// The local parser supplies bytes; retain compatibility with base64 callers.
|
|
20
|
+
const bytes = typeof image.data === "string" ? Buffer.from(image.data, "base64") : image.data;
|
|
20
21
|
if (image.format === 24 || image.format === 32) {
|
|
21
22
|
const { width, height } = image;
|
|
22
23
|
if (!width || !height) throw new Error("raw image without dimensions");
|
|
@@ -58,7 +58,7 @@ const isSingleCodePoint = (text) => {
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
export class Renderer {
|
|
61
|
-
constructor({ CanvasKit, screen, fonts, fontSize =
|
|
61
|
+
constructor({ CanvasKit, screen, fonts, fontSize = 13, lineHeight = 1, theme = defaultTheme }) {
|
|
62
62
|
this.CanvasKit = CanvasKit;
|
|
63
63
|
this.screen = screen;
|
|
64
64
|
this.canvas = screen.canvas;
|
|
@@ -49,7 +49,7 @@ export const createSession = ({
|
|
|
49
49
|
fonts,
|
|
50
50
|
command = "/bin/sh",
|
|
51
51
|
argv = [],
|
|
52
|
-
fontSize =
|
|
52
|
+
fontSize = 13,
|
|
53
53
|
lineHeight = 1,
|
|
54
54
|
theme = defaultTheme,
|
|
55
55
|
scrollback = 1000,
|
|
@@ -70,7 +70,7 @@ export const createSession = ({
|
|
|
70
70
|
term.loadAddon(new UnicodeGraphemesAddon());
|
|
71
71
|
term.unicode.activeVersion = "15-graphemes";
|
|
72
72
|
const images = new ImageStore({ CanvasKit, term, renderer });
|
|
73
|
-
const kitty = new KittyGraphicsParser();
|
|
73
|
+
const kitty = new KittyGraphicsParser({ binaryData: true });
|
|
74
74
|
|
|
75
75
|
const backend = new BunPtyBackend({
|
|
76
76
|
command,
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {readFileSync,writeFileSync,mkdirSync,renameSync,unlinkSync,openSync,closeSync} from 'node:fs';
|
|
2
|
+
import {dirname,resolve} from 'node:path';
|
|
3
|
+
import {DhcpClient} from './dhcp.js';
|
|
4
|
+
import {interfaceInfo,openTransport} from './dhcp-transport.js';
|
|
5
|
+
export function parseDhclient(args) {
|
|
6
|
+
const o={timeout:60,dns:true,resolv:'/etc/resolv.conf',once:true,release:false,verbose:false};
|
|
7
|
+
for(let i=0;i<args.length;i++){
|
|
8
|
+
const a=args[i];
|
|
9
|
+
if(['--help','-h','-help'].includes(a))return {...o,help:true};
|
|
10
|
+
if(a==='-4'||a==='-d')continue;
|
|
11
|
+
if(a==='-1')o.once=true;
|
|
12
|
+
else if(a==='-r')o.release=true;
|
|
13
|
+
else if(a==='-v')o.verbose=true;
|
|
14
|
+
else if(a==='-q')o.quiet=true;
|
|
15
|
+
else if(a==='--no-dns')o.dns=false;
|
|
16
|
+
else if(['-timeout','-lf','-H','--resolv-conf','--metric'].includes(a)){
|
|
17
|
+
const value=args[++i];if(!value||value.startsWith('-'))throw new Error(`${a} requires a value`);
|
|
18
|
+
if(a==='-timeout'){o.timeout=Number(value);if(!Number.isFinite(o.timeout)||o.timeout<=0)throw new Error('timeout must be positive');}
|
|
19
|
+
if(a==='--metric'){o.metric=Number(value);if(!Number.isInteger(o.metric)||o.metric<0||o.metric>32766)throw new Error('metric must be between 0 and 32766');}
|
|
20
|
+
if(a==='-lf')o.leaseFile=resolve(value);
|
|
21
|
+
if(a==='-H'){if(!/^[a-zA-Z0-9][a-zA-Z0-9.-]{0,252}$/.test(value))throw new Error('invalid hostname');o.hostname=value;}
|
|
22
|
+
if(a==='--resolv-conf')o.resolv=resolve(value);
|
|
23
|
+
}else if(a.startsWith('-'))throw new Error(`unsupported option: ${a} (DHCPv4 only; see --help)`);
|
|
24
|
+
else if(o.interface)throw new Error('specify one interface per process');
|
|
25
|
+
else o.interface=a;
|
|
26
|
+
}
|
|
27
|
+
if(!o.interface||!/^[a-zA-Z0-9_.:-]{1,15}$/.test(o.interface))throw new Error('specify an Ethernet interface, e.g. dhclient usb0. Use ip link to list network interfaces; use dhclient -h or dhclient --help for help.');
|
|
28
|
+
o.leaseFile??=`/var/lib/dhclient/${o.interface}.json`;
|
|
29
|
+
if(args.includes('-1')&&o.release)throw new Error('-1 and -r cannot be combined');
|
|
30
|
+
return o;
|
|
31
|
+
}
|
|
32
|
+
export async function runIp(args) {
|
|
33
|
+
const p=Bun.spawn([process.execPath,`${import.meta.dir}/../bin/ip`,...args],{stdout:'pipe',stderr:'pipe'});
|
|
34
|
+
const [status,out,err]=await Promise.all([p.exited,new Response(p.stdout).text(),new Response(p.stderr).text()]);
|
|
35
|
+
if(status)throw new Error(`ip ${args.join(' ')}: ${err.trim()||out.trim()}`);
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
const readOptional=path=>{try{return readFileSync(path,'utf8');}catch(e){if(e.code==='ENOENT')return null;throw e;}};
|
|
39
|
+
export function saveLease(path,lease) {
|
|
40
|
+
mkdirSync(dirname(path),{recursive:true});const tmp=`${path}.${process.pid}.tmp`;
|
|
41
|
+
writeFileSync(tmp,JSON.stringify(lease,null,2)+'\n',{mode:0o600});renameSync(tmp,path);
|
|
42
|
+
}
|
|
43
|
+
export class Configuration {
|
|
44
|
+
constructor(options,ip=runIp){this.options=options;this.ip=ip;}
|
|
45
|
+
metric(lease){return lease.metric===undefined?[]:['metric',String(lease.metric)];}
|
|
46
|
+
async apply(lease,previous) {
|
|
47
|
+
const o=this.options;
|
|
48
|
+
lease.metric=o.metric;
|
|
49
|
+
if(previous&&previous.routers[0]!==lease.routers[0])await this.removeRoute(previous);
|
|
50
|
+
await this.ip(['addr','replace',`${lease.address}/${lease.prefix}`,'brd','+','dev',o.interface]);
|
|
51
|
+
if(lease.routers[0])await this.ip(['route','replace','default','via',lease.routers[0],'dev',o.interface,...this.metric(lease)]);
|
|
52
|
+
if(previous?.resolver)lease.resolver=previous.resolver;
|
|
53
|
+
if(o.dns&&lease.dns.length){
|
|
54
|
+
const content=`# dhclient ${o.interface}\n`+lease.dns.map(ip=>`nameserver ${ip}\n`).join('');
|
|
55
|
+
const old=lease.resolver;
|
|
56
|
+
// Keep the original resolver across repeated invocations, unless another writer took over.
|
|
57
|
+
const current=readOptional(o.resolv);
|
|
58
|
+
lease.resolver={path:o.resolv,before:old?.path===o.resolv&¤t===old.written?old.before:current,written:content};
|
|
59
|
+
writeFileSync(o.resolv,content);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async removeRoute(lease) {
|
|
63
|
+
if(!lease.routers[0])return;
|
|
64
|
+
try{await this.ip(['route','del','default','via',lease.routers[0],'dev',this.options.interface,...this.metric(lease)]);}
|
|
65
|
+
catch(e){if(!/No such process|No such device|Cannot find device/.test(e.message))throw e;}
|
|
66
|
+
}
|
|
67
|
+
async clear(lease) {
|
|
68
|
+
const errors=[];
|
|
69
|
+
try{await this.removeRoute(lease);}catch(e){errors.push(e);}
|
|
70
|
+
// ip addr del checks the current address before deleting it.
|
|
71
|
+
try{await this.ip(['addr','del',`${lease.address}/${lease.prefix}`,'dev',this.options.interface]);}
|
|
72
|
+
catch(e){if(!/Cannot assign requested address|Cannot find device/.test(e.message))errors.push(e);}
|
|
73
|
+
try{
|
|
74
|
+
const r=lease.resolver;
|
|
75
|
+
if(r&&readOptional(r.path)===r.written){if(r.before===null)unlinkSync(r.path);else writeFileSync(r.path,r.before);}
|
|
76
|
+
}catch(e){errors.push(e);}
|
|
77
|
+
if(errors.length)throw new AggregateError(errors,errors.map(e=>e.message).join('; '));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// A command invocation acquires once and exits. There is no renewal daemon.
|
|
81
|
+
export async function manageLease(client,config,{previous,signal,log=console.log,save=()=>{},forget=()=>{}}={}) {
|
|
82
|
+
let lease,kept=false;
|
|
83
|
+
try {
|
|
84
|
+
lease=await client.acquire();
|
|
85
|
+
if(!lease){if(signal?.aborted)return 0;throw new Error('no DHCP lease received before timeout');}
|
|
86
|
+
if(signal?.aborted)return 0;
|
|
87
|
+
await config.apply(lease,previous);await save(lease);
|
|
88
|
+
log(`bound to ${lease.address}/${lease.prefix}, lease ${lease.seconds}s; configured and exiting (no automatic renewal)`);
|
|
89
|
+
kept=true;return 0;
|
|
90
|
+
}finally{
|
|
91
|
+
if(lease&&!kept){
|
|
92
|
+
try{client.release(lease);}catch(e){log(`release: ${e.message}`);}
|
|
93
|
+
await config.clear(lease);await forget();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export async function runDhclient(o) {
|
|
98
|
+
if(!['linux','android'].includes(process.platform))throw new Error('dhclient requires Linux/Android networking');
|
|
99
|
+
// Serialize this interface even when callers choose different lease files.
|
|
100
|
+
const lock=`/run/dhclient-${o.interface}.lock`;mkdirSync(dirname(lock),{recursive:true});
|
|
101
|
+
let handle;
|
|
102
|
+
try{handle=openSync(lock,'wx',0o600);}catch(e){if(e.code==='EEXIST')throw new Error(`${lock} exists; stop the other client, or remove this lock if its process died`);throw e;}
|
|
103
|
+
writeFileSync(handle,`${process.pid}\n`);
|
|
104
|
+
let transport;
|
|
105
|
+
const controller=new AbortController(),stop=()=>controller.abort();
|
|
106
|
+
process.on('SIGINT',stop);process.on('SIGTERM',stop);
|
|
107
|
+
const log=message=>{if(!o.quiet)console.log(`dhclient: ${message}`);};
|
|
108
|
+
const forget=()=>{try{unlinkSync(o.leaseFile);}catch(e){if(e.code!=='ENOENT')throw e;}};
|
|
109
|
+
try{
|
|
110
|
+
const info=interfaceInfo(o.interface);
|
|
111
|
+
await runIp(['link','set','dev',o.interface,'up']);
|
|
112
|
+
try { transport=openTransport(o.interface,info); }
|
|
113
|
+
catch(error) {
|
|
114
|
+
if(error.errno!==97)throw error; // EAFNOSUPPORT: Alpine ships AF_PACKET as a module.
|
|
115
|
+
const {modprobe}=await import('./modprobe.js');
|
|
116
|
+
modprobe('af_packet');transport=openTransport(o.interface,info);
|
|
117
|
+
}
|
|
118
|
+
const client=new DhcpClient({transport,...info,...o,signal:controller.signal,log:o.verbose?log:()=>{}});
|
|
119
|
+
const config=new Configuration(o);
|
|
120
|
+
if(o.release){
|
|
121
|
+
const text=readOptional(o.leaseFile);if(!text)throw new Error(`no saved lease: ${o.leaseFile}`);
|
|
122
|
+
const lease=JSON.parse(text);if(lease.interface!==o.interface||lease.mac!==info.mac.toString('hex'))throw new Error('saved lease belongs to another interface');
|
|
123
|
+
try{client.release(lease);}finally{await config.clear(lease);forget();}log(`released ${lease.address}`);return 0;
|
|
124
|
+
}
|
|
125
|
+
let previous;
|
|
126
|
+
const saved=readOptional(o.leaseFile);
|
|
127
|
+
if(saved){
|
|
128
|
+
const candidate=JSON.parse(saved);
|
|
129
|
+
if(candidate.interface===o.interface&&candidate.mac===info.mac.toString('hex'))previous=candidate;
|
|
130
|
+
}
|
|
131
|
+
return await manageLease(client,config,{...o,previous,signal:controller.signal,log,save:lease=>saveLease(o.leaseFile,{...lease,interface:o.interface,mac:info.mac.toString('hex')}),forget});
|
|
132
|
+
}finally{
|
|
133
|
+
transport?.close();process.off('SIGINT',stop);process.off('SIGTERM',stop);closeSync(handle);unlinkSync(lock);
|
|
134
|
+
}
|
|
135
|
+
}
|