RustForge

foundry

Foundry OS: the operating system built on the Rustbelt kernel

foundry / ports / busybox / README.md
5572 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# busybox 1.36.1 (M5)

[busybox]https://busybox.net is one program that contains ~300 small
Unix tools ("applets"): `awk`, `sed`, `vi`, `tar`, `gzip`, `wget`, `ps`,
`adduser`, a shell (`ash`)... Which applet runs depends on the name it was
started under (`argv[0]`), or on the first argument: `busybox awk ...`.

## Version

**1.36.1** (2023-05-18), the newest release that busybox.net calls
"stable". 1.37.0 and 1.38.0 exist but are marked "unstable" on the
download page, so we wait. The tarball is pinned by SHA256 in
`busybox.sha256` and downloaded once into `cache/` by `ports/fetch.sh`.

## Where it goes

Contract v2 only, on the ext2 root:

```
/usr/lib/busybox/busybox      the program (static-PIE, ~1 MB)
/usr/lib/busybox/awk -> busybox   one symlink per applet (320 of them)
/usr/lib/busybox/LICENSE      GPL v2
```

The directory is **off** the default `PATH`, so the Foundry OS Rust tools
(`/bin/ls`, ...) and dash (`/bin/sh`) stay the default. Use busybox as

```sh
/usr/lib/busybox/busybox awk -F: '{print $1}' /etc/passwd
/usr/lib/busybox/awk -F: '{print $1}' /etc/passwd    # same thing
PATH=/usr/lib/busybox:$PATH                           # all applets, this shell only
/usr/lib/busybox/busybox ash                          # busybox's shell
```

## Configuration (`busybox.config`)

`busybox.config` is the complete `.config`, committed, and the build uses it
as is (it fails if busybox would have to ask about a new option). It is made
by `mkconfig.sh` from busybox's own `make defconfig` (nearly everything on),
with these changes. `mkconfig.sh` lists every option with a comment, so the
list there is the reference; in short:

**Switched off (83 applets): things Rustbelt cannot do**, so they could only
ever fail:

| Group | Applets | Why not |
|---|---|---|
| Kernel modules | `insmod`, `rmmod`, `lsmod`, `modprobe`, `depmod`, `modinfo` | Rustbelt has no loadable modules |
| Traffic control | `tc` | netlink; and 1.36.1's `tc` doesn't compile against current Linux headers |
| Linux network setup | `ip` (+ `ipaddr`, `iplink`, `iproute`, ...), `brctl`, `udhcpc`, `udhcpd`, `arping`, `ether-wake`, `zcip`, `ifplugd`, `nameif`, `tunctl`, `vconfig`, `ifenslave`, `nbd-client`, ... | netlink or raw packet sockets; Rustbelt sets up its network itself (DHCP in the kernel) |
| Hardware Rustbelt doesn't expose | flash/UBI tools, `i2c*`, `lspci`, `lsusb`, `hwclock`, `watchdog`, `devmem`, `acpid`, `beep`, `setserial` | no `/dev/mtd*`, `/dev/i2c-*`, `/dev/rtc`, `/sys` |
| Linux console and framebuffer | `loadfont`, `setfont`, `kbd_mode`, `loadkmap`, `dumpkmap`, `setkeycodes`, `fbset`, `fbsplash`, `conspy` | Linux console ioctls; Rustbelt's console works differently |
| Other kernel features | `mdev`, `uevent`, swap (`mkswap`, `swapon`, `swapoff`), `losetup`, `raidautorun`, floppy tools, `freeramdisk`, `eject`, `blkdiscard`, `fsfreeze`, `rdev`, `pivot_root`, `switch_root`, `nsenter`, `unshare`, `setarch`/`linux32`/`linux64`, `adjtimex` | hot-plug, swap, loop devices, RAID, namespaces, switching the root |

SELinux and PAM are already off in defconfig, and nothing is built as a
loadable module (busybox has none of its own anyway).

**Kept on purpose**, even though they fail today: `ps`, `top`, `free`,
`mount`, `df`, `mkfifo`, `chroot`, `dmesg`, `ping` and friends. They need
kernel features that are planned (K7, K14, K15 in docs/kernel-requests.md),
and the test runs them as expected failures, so we see when they start to
work. `login`, `su`, `passwd`, `adduser`, `getty` stay for M6 (users).

**Switched on (1 option): `FEATURE_PREFER_APPLETS`.** When one applet runs
another (`xargs echo`, `find -exec`, `adduser` running `addgroup`), busybox
normally looks the command up on `PATH`, where our busybox isn't. With
this option it runs its own applet (through `/proc/self/exe`) instead.
One exception, by busybox's design: `tar -z`/`-j` always run an external
`gzip`/`bzip2` from `PATH`; put `/usr/lib/busybox` on `PATH` for those (or
use a pipe: `tar -cf - dir | busybox gzip > dir.tgz`).

## Building

`make busybox` (also part of `make image`): `ports/fetch.sh busybox cache`,
then `build.sh cache out/ports/busybox-build out/ports/busybox`. It runs
busybox's Makefile with

```
make CC=toolchain/foundry-cc LD=ld HOSTCC=gcc EXTRA_CFLAGS="-isystem <kernel headers>"
```

- `CC=foundry-cc`: our gcc + musl wrapper, so the result is a static-PIE.
- `LD=ld`: busybox links each directory with `ld -r` first, and that can't
  be combined with foundry-cc's `-static-pie`. The final link still uses CC.
- `HOSTCC=gcc`: the little helper programs busybox runs during the build
  are for the build machine, not for Rustbelt.
- Kernel headers: busybox includes a few Linux headers (`<linux/fs.h>`...)
  that musl doesn't have. `build.sh` copies the build machine's
  (`/usr/include/linux`, `asm`, `asm-generic`: Debian/Ubuntu package
  `linux-libc-dev`) into the build directory.
- No `bzip2` program on the build machine? `build.sh` unpacks the tarball
  with Python (`untar.py`) and gives the build a tiny Python stand-in
  (`bzip2-shim.py`) for the one place it pipes through `bzip2`.

To change the configuration: edit the lists in `mkconfig.sh`, run
`ports/busybox/mkconfig.sh cache /tmp/bbcfg`, check `git diff
ports/busybox/busybox.config`, then `make busybox`.

## Speed note

Rustbelt reads the whole program file on every `execve` (no page cache
yet), so starting the 1 MB busybox costs about 0.2 s, against about 0.04 s
for a 16 KB sbase tool. Scripts that start hundreds of applets feel it.
See K15 in docs/kernel-requests.md.