fix: enable job control for shell with --kernel

This commit is contained in:
2026-06-16 23:38:09 +02:00
parent 8875bcc92a
commit a81e699619
2 changed files with 48 additions and 10 deletions
+46 -9
View File
@@ -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")?;