fix: enable job control for shell with --kernel
This commit is contained in:
@@ -205,7 +205,8 @@ ecr --kernel /boot/vmlinuz debian -- /bin/sh -c "echo hello"
|
||||
4. Launch QEMU with:
|
||||
- `-kernel <path>` - provided kernel
|
||||
- `-initrd initramfs.cpio.gz` - rootfs as initramfs
|
||||
- `-append "console=ttyS0 quiet rdinit=/bin/sh"` - kernel command line
|
||||
- `-append "console=ttyS0 quiet rdinit=/bin/sh -- -c \"setsid sh -c 'exec sh </dev/ttyS0 >/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
|
||||
- `-m <memory>` - memory size (default 2G)
|
||||
- `-display none -serial mon:stdio` - console on stdio
|
||||
- `-netdev user,id=net0 -device virtio-net-pci,netdev=net0` - network
|
||||
|
||||
+46
-9
@@ -57,15 +57,17 @@ pub fn launch_qemu(config: QemuConfig) -> Result<()> {
|
||||
// For initramfs boot, use rdinit= instead of init=
|
||||
// No root= needed as initramfs becomes the rootfs
|
||||
// 'quiet' suppresses kernel log messages for a cleaner console
|
||||
// Use setsid with redirected stdio to get proper job control
|
||||
// /dev/ttyS0 is created in the initramfs for serial console
|
||||
let kernel_append = if let Some(ref cmd) = config.command {
|
||||
let cmd_str = cmd.join(" ");
|
||||
format!(
|
||||
"console=ttyS0 quiet rdinit=/bin/sh -- -c \"{}\"",
|
||||
"console=ttyS0 quiet rdinit=/bin/sh -- -c \"setsid sh -c 'exec sh </dev/ttyS0 >/dev/ttyS0 2>&1 -c {}'\"",
|
||||
cmd_str
|
||||
)
|
||||
} else {
|
||||
// Default to interactive shell
|
||||
"console=ttyS0 quiet rdinit=/bin/sh".to_string()
|
||||
// Default to interactive shell with proper job control
|
||||
"console=ttyS0 quiet rdinit=/bin/sh -- -c \"setsid sh -c 'exec sh </dev/ttyS0 >/dev/ttyS0 2>&1'\"".to_string()
|
||||
};
|
||||
|
||||
veprintln!("Launching QEMU: {}", qemu_bin);
|
||||
@@ -175,22 +177,25 @@ fn create_cpio_archive(rootfs: &Path) -> Result<Vec<u8>> {
|
||||
// Collect all entries with their data
|
||||
let entries = collect_entries(rootfs, rootfs)?;
|
||||
|
||||
// Collect entry names for checking existence later
|
||||
let entry_names: Vec<&str> = entries.iter().map(|(n, _, _, _, _)| n.as_str()).collect();
|
||||
|
||||
// Write each entry using the cpio crate
|
||||
for (name, mode, mtime, nlink, data) in entries {
|
||||
for (name, mode, mtime, nlink, data) in &entries {
|
||||
let file_size = data.len() as u32;
|
||||
|
||||
let builder = NewcBuilder::new(&name)
|
||||
.mode(mode)
|
||||
let builder = NewcBuilder::new(name)
|
||||
.mode(*mode)
|
||||
.uid(0)
|
||||
.gid(0)
|
||||
.nlink(nlink)
|
||||
.mtime(mtime);
|
||||
.nlink(*nlink)
|
||||
.mtime(*mtime);
|
||||
|
||||
// Write header and get a writer
|
||||
let mut writer = builder.write(&mut archive, file_size);
|
||||
|
||||
// Write the file content
|
||||
writer.write_all(&data)
|
||||
writer.write_all(data)
|
||||
.context("Failed to write file content to cpio archive")?;
|
||||
|
||||
// Finish this entry (returns the underlying writer)
|
||||
@@ -198,6 +203,38 @@ fn create_cpio_archive(rootfs: &Path) -> Result<Vec<u8>> {
|
||||
.context("Failed to finish cpio entry")?;
|
||||
}
|
||||
|
||||
// Add essential device nodes for serial console
|
||||
// These are character devices (mode 0o020xxx)
|
||||
let device_nodes = [
|
||||
// /dev/ttyS0 - serial console (major 4, minor 64)
|
||||
("dev/ttyS0", 0o020644, 4, 64),
|
||||
// /dev/null (major 1, minor 3)
|
||||
("dev/null", 0o020644, 1, 3),
|
||||
// /dev/tty - controlling terminal (major 5, minor 0)
|
||||
("dev/tty", 0o020666, 5, 0),
|
||||
];
|
||||
|
||||
for (name, mode, major, minor) in device_nodes {
|
||||
// Check if this device node already exists in the archive
|
||||
if entry_names.contains(&name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let builder = NewcBuilder::new(name)
|
||||
.mode(mode)
|
||||
.uid(0)
|
||||
.gid(0)
|
||||
.nlink(1)
|
||||
.mtime(0)
|
||||
.rdev_major(major)
|
||||
.rdev_minor(minor);
|
||||
|
||||
// Device nodes have zero size
|
||||
let writer = builder.write(&mut archive, 0);
|
||||
writer.finish()
|
||||
.context("Failed to finish device node entry")?;
|
||||
}
|
||||
|
||||
// Write the trailer (takes ownership and returns the writer)
|
||||
archive = newc::trailer(archive)
|
||||
.context("Failed to write cpio trailer")?;
|
||||
|
||||
Reference in New Issue
Block a user