Commit Graph
39 Commits
Author SHA1 Message Date
vhaudiquet ba90e0f367 fix(qemu): select the rva23s64 CPU for riscv64 under TCG
Ubuntu builds its riscv64 port against the RVA23 profile (since
25.10), and QEMU's default rv64 CPU does not implement all profile
extensions: Ubuntu binaries die with SIGILL during init and the
kernel panics with 'Attempted to kill init'. Select the rva23s64
profile CPU (QEMU 10.1+) for riscv64 under TCG; profiles are
supersets, so baseline rv64gc rootfses run unchanged on it.

KVM mode keeps -cpu host.
2026-09-21 01:24:11 +02:00
vhaudiquet 12f771d326 docs: document riscv64 kernel flavor and virt machine 2026-09-21 01:03:21 +02:00
vhaudiquet 810bf50814 fix(qemu): select the virt machine for riscv64
qemu-system-riscv64's default machine is spike, not virt: spike has no
PCI bus, so virtio-net-pci failed with "No 'PCI' bus found", and no
16550 UART, so console=ttyS0 output went nowhere. Pass -machine virt
explicitly for riscv64; other architectures keep their emulator's
default.
2026-09-21 01:03:21 +02:00
vhaudiquet 5dd9dff76a fix(kernel): fall back to linux-lts where linux-virt is absent
Alpine does not build the linux-virt flavor for riscv64, so --kernel
without a path failed there with 'linux-virt package not found'. Scan
the APKINDEX once for both flavors and prefer linux-virt, taking
linux-lts when the virt flavor is missing.

Also store the cached kernel decompressed: riscv64 and aarch64 ship
their image gzipped (Image.gz), and QEMU's riscv -kernel loader
understands only ELF, uImage and raw images, so the gzipped image hung
at boot after the OpenSBI banner.
2026-09-21 01:03:18 +02:00
vhaudiquet de507682c1 fix(chroot): resolve bare command names through PATH before exec
CI / Check (push) Successful in 1m11s
CI / Format (push) Successful in 15s
CI / Clippy (push) Successful in 1m8s
CI / Test (push) Successful in 1m23s
execve(2) does not search PATH, so `ecr alpine -- echo hi` failed with
ENOENT: the old code only used PATH as an existence check and then
still exec'd the bare name, which the kernel resolved relative to the
working directory.  Pre-dates the crate split, but bare names are the
natural CLI usage so it needs to work.

Programs are now resolved execvp-style: a bare name is looked up in
the caller-composed PATH (empty components skipped rather than treated
as the cwd), paths containing '/' are used as-is with a friendlier
error than a raw ENOENT.
2026-09-21 00:16:25 +02:00
vhaudiquet af06264e60 docs: document library crate, exec and rootfs cache apis
README gains a Library section covering prepare, the exec options
(envp, bind targets, arch) and the provisioned-rootfs hot-cell flow.
SPEC documents the workspace layout, the library API, the cache
sidecars (.digest, .provisioned) and the updated execution flow.
AGENTS.md scope paths follow the new crates/ layout.
2026-09-21 00:08:49 +02:00
vhaudiquet b6ddd85525 feat(rootfs): cache-aware rootfs preparation with persist hook
Add ecr::rootfs with the full rootfs lifecycle behind the library:

- RootfsCache::prepare resolves an image reference, downloads through
  the tarball cache (with the OCI :latest digest freshness check) and
  extracts into a scratch directory tracked by PreparedRootfs.
- PreparedRootfs::persist packs the current rootfs back into its cache
  entry (compressed to match the entry's extension, symlinks and
  permissions preserved) and marks it with a .provisioned sidecar.
- RootfsCache::prepare_provisioned composes both into the hot-cell
  flow: provision once, and every later call sharing the cache skips
  the download and the provisioning step.

extract: an "oci-" cache entry without a layers.manifest is a
persisted provisioned rootfs; extract it as a plain archive.

The CLI now drives prepare and drops its inline cache/orchestration
code and the dirs/tempfile dependencies.  The binfmt check moves ahead
of the download so foreign-arch runs fail before pulling an image.
2026-09-21 00:06:20 +02:00
vhaudiquet 4f669fb5ec feat(exec): exec API with caller envp, bind targets and arch
Add ecr::exec: run a command inside a prepared rootfs in fresh
user/PID/mount/UTS namespaces, with the caller composing the full
environment (ExecOptions::env), the bind targets (BindTarget, with
explicit absolute mount points inside the rootfs) and the target
architecture (binfmt_misc is verified for foreign arches).

mount::setup_mounts now takes &[BindTarget] instead of parallel
read-only/read-write path lists; chroot::run_chroot takes the envp and
a resolved working directory, and chroot::default_env composes the
previous hardcoded environment as a starting point for callers.

The CLI maps its flags onto the new API; behavior is unchanged
(overlay at /root/<basename>, rw bind at /mnt/<basename>, cwd default).
2026-09-20 23:34:54 +02:00
vhaudiquet b6e5b4f006 refactor: split ecr into library and cli crates
The root package becomes a virtual workspace: `crates/ecr` holds the
library (package name `ecr`) and `crates/ecr-cli` the command line
front-end, which keeps installing the `ecr` binary. No behavior change.

The library must not depend on CLI types, so mount::setup_mounts now
takes the `no_bind` flag instead of a `&Args`.
2026-09-20 23:29:03 +02:00
vhaudiquet 7137aa15c5 docs: add AGENTS.md with commit and code conventions 2026-09-20 23:08:28 +02:00
vhaudiquet 2c47a5c662 docs: update QEMU mode docs for =PATH syntax and uncompressed initramfs
CI / Check (push) Successful in 1m7s
CI / Format (push) Successful in 14s
CI / Clippy (push) Successful in 1m8s
CI / Test (push) Successful in 1m24s
2026-09-20 22:37:33 +02:00
vhaudiquet 240a66c532 fix: initramfs hard links, streaming archive, verbatim argv transport
- hard links in the rootfs were written as zero-size entries with no
  inode set (all zero), so the kernel could link them to the wrong file;
  assign a synthetic inode per (device, inode) group.
- build the cpio archive by streaming entries to disk instead of holding
  the whole archive in memory.
- the VM command was joined into a single string and shell-expanded in
  the guest, mangling quotes and nested commands; pass the argv
  base64-encoded (ECR_ARGV) and exec it verbatim.
- parse ecr parameters from /proc/cmdline in the init script, set the
  hostname via procfs (arch has no hostname binary), and give the serial
  console a moment to drain before poweroff.
2026-09-20 22:37:33 +02:00
vhaudiquet d64da0671b fix: --kernel =PATH syntax, exit-code cleanup, bind warning in VM mode
- --kernel took an optional value greedily, swallowing the DISTRO
  positional: 'ecr --kernel alpine' failed with a missing-argument error
  and 'ecr --kernel alpine -- cmd' pulled a garbage OCI ref. require
  =PATH syntax for the kernel path instead.
- setup_namespaces returns the child's exit code (or 128+signal) instead
  of calling process::exit, so the extracted rootfs tempdir is cleaned
  up before exiting instead of leaking into /tmp.
- warn that --bind/--bind-rw are ignored when booting with --kernel.
2026-09-20 22:37:11 +02:00
vhaudiquet ef00776414 fix: return Ok after extracting kernel from APK
extract_kernel_from_apk fell through to an unconditional error after a
successful extraction, so every first --kernel run failed after
downloading the 45MB package; the second run worked from cache. Also
remove the downloaded APK even when extraction fails.
2026-09-20 22:36:49 +02:00
vhaudiquet e1d69eaed6 fix: make kernel verbose with --kernel -v 2026-06-19 16:51:24 +02:00
vhaudiquet 3188566b6e feat: download alpine linux-virt kernel for single --kernel 2026-06-19 16:46:23 +02:00
vhaudiquet 1d2031b3ca fix: fmt 2026-06-17 17:42:56 +02:00
vhaudiquet 49343e5811 fix: clippy 2026-06-17 17:42:41 +02:00
vhaudiquet f3aec10618 test: add unit tests for namespace and chroot modules
- Add tests for hostname format and uniqueness
- Add tests for chroot environment setup
- Add tests for environment isolation and PATH configuration
- Verify all 34 tests pass
2026-06-17 17:28:50 +02:00
vhaudiquet 5834630d60 refactor: multiple fixes
- Create unified Arch enum in utils.rs with methods for all naming conventions
- Replace 5 duplicate architecture mapping functions with single source of truth
- Add memory string validation for QEMU
- Add KVM capability verification via ioctl
- Fix TOCTOU race in resolv.conf writing using O_EXCL
- Extract magic numbers to named constants
- Use constants for stack size, buffer sizes, hostname entropy
2026-06-17 17:24:41 +02:00
vhaudiquet 6bd6f2cf77 feat: enable kvm with --kernel if relevant 2026-06-17 16:42:34 +02:00
vhaudiquet 09661ec9e0 feat: use uncompressed initramfs 2026-06-17 16:35:28 +02:00
vhaudiquet 503578d648 feat: terminate when init process exits (--kernel)
Add an init script to the initramfs that traps shell exit and triggers
poweroff via /proc/sysrq-trigger. This ensures QEMU terminates cleanly
when the user exits the shell, rather than hanging indefinitely.

Changes:
- Add /init script to initramfs that runs as PID 1
- Use -no-reboot QEMU flag to exit on guest poweroff
- Simplify kernel command line using environment variables
- Init script handles hostname, device creation, and poweroff on exit
2026-06-17 14:03:04 +02:00
vhaudiquet 7a37f99030 feat: detect shell for --kernel
- Move detect_shell() to new src/utils.rs for reuse
- Use detected shell in QEMU VM mode (bash when available)
- Update chroot.rs and qemu_vm.rs to use shared function
2026-06-17 11:13:05 +02:00
vhaudiquet d52310c0f4 feat: set unique hostname in QEMU VM mode
Generate hostname like "ecr-vm-<hex>" at boot time via kernel command
line, leaving initramfs cacheable. Set via echo to /etc/hostname and
hostname command before spawning shell.
2026-06-17 10:52:00 +02:00
vhaudiquet b3ffa89faa fix: handle hard links in initramfs creation
- Track inodes to avoid duplicating data for hard-linked files
- Use fast compression level for faster initramfs creation
2026-06-17 10:40:10 +02:00
vhaudiquet 3e3af3dab8 feat: add progressbar for initramfs compression 2026-06-17 10:11:04 +02:00
vhaudiquet 4475dff141 feat: add progressbar for extraction and initramfs creation 2026-06-17 00:11:12 +02:00
vhaudiquet a81e699619 fix: enable job control for shell with --kernel 2026-06-16 23:38:09 +02:00
vhaudiquet 8875bcc92a feat: do not display kernel logs with --kernel 2026-06-16 22:07:28 +02:00
vhaudiquet 931a6dcfd5 feat: use cpio crate for initramfs creation
Add the `cpio` crate as dependency, removing e2fsprogs external dependency.
2026-06-16 20:31:25 +02:00
vhaudiquet 4f44af4449 feat: add --kernel flag for QEMU system emulation mode
Add --kernel <PATH> option to boot extracted rootfs in a QEMU virtual
machine instead of namespace/chroot mode. The rootfs is converted to an
ext4 disk image using mke2fs and booted with the provided kernel.
2026-06-16 19:16:34 +02:00
vhaudiquet c163f89cb2 readme: add animated GIF with usage
CI / Format (push) Successful in 9s
CI / Check (push) Successful in 40s
CI / Clippy (push) Successful in 19s
CI / Test (push) Successful in 34s
2026-06-15 17:17:29 +02:00
vhaudiquet 3a0d78b048 fix: correctly propagate exit codes without printing them
CI / Clippy (push) Successful in 23s
CI / Format (push) Failing after 10m51s
CI / Check (push) Failing after 12m5s
CI / Test (push) Failing after 13m28s
2026-06-15 17:01:59 +02:00
vhaudiquet 91d6225946 ci: add ci pipeline
CI / Check (push) Failing after 11m45s
CI / Test (push) Failing after 11m59s
CI / Clippy (push) Failing after 13m13s
CI / Format (push) Failing after 14m14s
2026-06-09 19:47:23 +02:00
vhaudiquet b12a8db34b readme: README.md 2026-06-09 19:15:38 +02:00
vhaudiquet 04ed1f25ae feat: basic functionality 2026-06-09 19:09:57 +02:00
vhaudiquet a3339f9d34 feat: hello, world! 2026-06-08 18:44:10 +02:00
vhaudiquet 5cb0428876 initial commit: SPEC.md, implementation spec 2026-06-08 18:40:31 +02:00