diff --git a/README.md b/README.md index 89ec53d..48de759 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ ecr [OPTIONS] [-- COMMAND...] | `--no-cache` | Force a fresh download, bypassing the cache | | `-v, --verbose` | Print diagnostic output (URLs, layer info, extraction steps) | | `-a, --arch ` | Target architecture (`amd64`, `arm64`, `armhf`, `riscv64`, …) | -| `--kernel [PATH]` | Boot with QEMU system emulation. Downloads Alpine's `linux-virt` kernel if no path provided | +| `--kernel[=PATH]` | Boot with QEMU system emulation. Downloads Alpine's `linux-virt` kernel if no `=PATH` given | | `-m, --memory ` | Memory for QEMU VM (default: 2G, only with `--kernel`) | ## Examples @@ -62,8 +62,8 @@ ecr --no-cache fedora # Boot with QEMU system emulation (auto-downloads default kernel) ecr --kernel alpine -# Boot with your own kernel -ecr --kernel /boot/vmlinuz ubuntu +# Boot with your own kernel (note the `=` — a space would parse the path as the distro) +ecr --kernel=/boot/vmlinuz ubuntu # Boot with custom memory ecr --kernel --memory 4G alpine @@ -78,16 +78,18 @@ When `--kernel` is specified, ecr boots the rootfs in a full QEMU virtual machin ecr --kernel alpine # Use your own kernel -ecr --kernel /boot/vmlinuz ubuntu +ecr --kernel=/boot/vmlinuz ubuntu ``` This mode: -- Creates a gzipped CPIO initramfs from the rootfs +- Creates an uncompressed CPIO initramfs from the rootfs (streamed to disk) - Boots QEMU with your kernel (or auto-downloads Alpine's `linux-virt` kernel) - Provides full VM isolation - Works for any architecture (no binfmt_misc needed) - Caches the default kernel in `~/.cache/ecr/` +Host bind mounts (`--bind`, `--bind-rw`) are not applied in this mode. + Requirements: - `qemu-system-` installed - For custom kernels: kernel must have serial console support diff --git a/SPEC.md b/SPEC.md index 5009c2c..30bc4aa 100644 --- a/SPEC.md +++ b/SPEC.md @@ -22,7 +22,7 @@ ecr [OPTIONS] -- [COMMAND]... | `--bind-rw ` | none | Read-write bind mount at `/mnt/` (can be specified multiple times, overrides `--bind` for same path) | | `--no-cache` | false | Download fresh tarball, ignore cache | | `--no-bind` | false | Skip mounting any directory | -| `--kernel ` | none | Boot with QEMU system emulation using specified kernel (triggers disk image creation) | +| `--kernel[=PATH]` | none | Boot with QEMU system emulation; downloads the default Alpine `linux-virt` kernel when no `=PATH` is given | | `-m, --memory ` | 2G | Memory size for QEMU VM (only used with `--kernel`) | | `-v, --verbose` | false | Print diagnostic messages | | `-h, --help` | - | Show help | @@ -187,35 +187,38 @@ No action required. Modern qemu-user-static packages register binfmt_misc with t ## QEMU System Emulation Mode -When `--kernel` is specified, ecr switches from namespace/chroot mode to QEMU system emulation. The extracted rootfs is converted to a gzipped CPIO initramfs and booted with the provided kernel. +When `--kernel` is specified, ecr switches from namespace/chroot mode to QEMU system emulation. The extracted rootfs is converted to an uncompressed CPIO initramfs and booted with the provided kernel. ### Usage +The kernel path uses `=` syntax (`--kernel=PATH`); `--kernel` without a value downloads the default kernel. Without `=`, a following path would be parsed as the DISTRO argument. + ```sh -ecr --kernel /boot/vmlinuz ubuntu:noble -ecr --kernel /boot/vmlinuz --memory 4G alpine -ecr --kernel /boot/vmlinuz debian -- /bin/sh -c "echo hello" +ecr --kernel ubuntu:noble +ecr --kernel=/boot/vmlinuz ubuntu:noble +ecr --kernel=/boot/vmlinuz --memory 4G alpine +ecr --kernel=/boot/vmlinuz debian -- /bin/sh -c "echo hello" ``` ### Execution Flow 1. Download/cache rootfs tarball (same as namespace mode) 2. Extract tarball to temporary directory -3. Create gzipped CPIO initramfs from rootfs +3. Create uncompressed CPIO initramfs from rootfs (streamed to disk), including essential device nodes (/dev/ttyS0, /dev/null, /dev/tty) and an `/init` script that mounts proc/sys/dev, sets the hostname, execs the requested command argv verbatim (each argv element base64-encoded in the cmdline as `ECR_ARGV`), and powers off on exit 4. Launch QEMU with: - - `-kernel ` - provided kernel - - `-initrd initramfs.cpio.gz` - rootfs as initramfs - - `-append "console=ttyS0 quiet rdinit=/bin/sh -- -c \"setsid sh -c 'exec sh /dev/ttyS0 2>&1'\""` - kernel command line -7. Essential device nodes (/dev/ttyS0, /dev/null, /dev/tty) are added to initramfs for proper console support + - `-kernel ` - provided (or downloaded) kernel + - `-initrd initramfs.cpio` - rootfs as initramfs + - `-append "console=ttyS0 [quiet] ECR_SHELL=... [ECR_ARGV=...] ECR_HOSTNAME=..."` - kernel command line (`quiet` unless `-v`) - `-m ` - memory size (default 2G) - `-display none -serial mon:stdio` - console on stdio - - `-netdev user,id=net0 -device virtio-net-pci,netdev=net0` - network -5. Wait for QEMU to exit + - `-netdev user,id=net0 -device virtio-net-pci,netdev=net0` - network NIC + - `-enable-kvm -cpu host` - when the host supports KVM and the target matches the host architecture +5. Wait for QEMU to exit (init powers the VM off when the command/shell exits; `-no-reboot` makes QEMU terminate) 6. Cleanup temporary files ### Initramfs Creation -The rootfs directory is converted to a gzipped CPIO archive (newc format) using the `cpio` crate. +The rootfs directory is converted to an uncompressed CPIO archive (newc format) using the `cpio` crate, streamed entry by entry so large rootfs images never need to fit in memory. Hard links are preserved: the first occurrence of a (device, inode) pair carries the data with a synthetic inode, subsequent occurrences are zero-size entries sharing that inode, which the kernel's initramfs loader turns into real hard links. ### Architecture Support @@ -238,11 +241,11 @@ The rootfs directory is converted to a gzipped CPIO archive (newc format) using | Feature | Namespace Mode | QEMU Mode | |---------|---------------|-----------| | Isolation | User namespace | Full VM | -| Performance | Near-native | Emulated (slower) | -| Root access | No | No | +| Performance | Near-native | Emulated (KVM-accelerated when available) | +| Root access | No | Yes (inside the VM) | | Foreign arch | binfmt_misc required | Built-in emulation | -| Bind mounts | Overlay/bind | Not supported | -| Network | Host network | User-mode network | +| Bind mounts | Overlay/bind | Not supported (flags are ignored with a warning) | +| Network | Host network | User-mode NIC (not configured inside the guest) | ## File Handling