Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions base/init/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ const WAIT4_RETURN_MSG: &[u8] = b"init: wait4-return\n";
/// then falls back to Limine boot modules (basename match).
const HELLO_PATH: &[u8] = b"/boot/userspace_hello.elf\0";

/// Path to the POSIX shell. Resolved via VFS from the ext2 rootfs;
/// only available when booting with `root=/dev/vda`.
const SH_PATH: &[u8] = b"/bin/sh\0";

/// Smoke-test marker — emitted before exec-ing /bin/sh.
const SH_LAUNCH_MSG: &[u8] = b"init: launching /bin/sh\n";

#[no_mangle]
pub extern "C" fn _start() -> ! {
// Pre-write diagnostic marker — see #478. Emitted on fd=2 so it
Expand Down Expand Up @@ -216,6 +223,89 @@ pub extern "C" fn _start() -> ! {
}
}

// Launch /bin/sh — the POSIX shell. This is only meaningful when
// the ext2 rootfs is mounted (root=/dev/vda), which places the sh
// binary at /bin/sh. When booting from the ISO-only path (no ext2),
// execve will fail and the child exits with status 1; init continues
// to its idle loop regardless.
write(1, SH_LAUNCH_MSG);

let sh_fork_ret: i64;
unsafe {
core::arch::asm!(
"syscall",
inlateout("rax") 57u64 => sh_fork_ret,
lateout("rcx") _,
lateout("rdx") _,
lateout("rdi") _,
lateout("rsi") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
options(nostack, preserves_flags),
);
}

if sh_fork_ret == 0 {
// Child: exec /bin/sh.
unsafe {
core::arch::asm!(
"syscall",
inlateout("rax") 59u64 => _, // execve
inlateout("rdi") SH_PATH.as_ptr() as u64 => _, // path
inlateout("rsi") 0u64 => _, // argv (NULL = empty)
inlateout("rdx") 0u64 => _, // envp (NULL = empty)
lateout("rcx") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
options(nostack, preserves_flags),
);
}
// execve only returns on failure — exit with status 1.
unsafe {
core::arch::asm!(
"syscall",
inlateout("rax") 60u64 => _, // exit
inlateout("rdi") 1u64 => _, // status 1 (exec failed)
lateout("rcx") _,
lateout("rdx") _,
lateout("rsi") _,
lateout("r8") _,
lateout("r9") _,
lateout("r10") _,
lateout("r11") _,
options(nostack, preserves_flags),
);
}
loop {
core::hint::spin_loop();
}
}

// Parent: wait for the shell to exit, then idle.
if sh_fork_ret > 0 {
let sh_pid = sh_fork_ret as u64;
let mut sh_wstatus: i32 = 0;
unsafe {
core::arch::asm!(
"syscall",
inlateout("rax") 61u64 => _, // wait4
inlateout("rdi") sh_pid => _, // pid
inlateout("rsi") &mut sh_wstatus as *mut i32 as u64 => _, // *wstatus
inlateout("rdx") 0u64 => _, // options
inlateout("r10") 0u64 => _, // rusage
lateout("rcx") _,
lateout("r8") _,
lateout("r9") _,
lateout("r11") _,
options(nostack),
);
}
}

// Loop forever.
loop {
core::hint::spin_loop();
Expand Down
27 changes: 21 additions & 6 deletions base/sh/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
#![feature(restricted_std)]

// Provide #[no_mangle] C-ABI shims for POSIX functions that the shell's
// `extern "C"` declarations link against. On vibix, these delegate to
// raw syscalls via `vibix_abi`. The module is excluded from host tests
// which mock these symbols.
#[cfg(not(test))]
mod syscalls;

mod builtins;
mod exec;
mod expand;
Expand All @@ -26,6 +33,9 @@ fn main() {
env.arg0 = "sh".to_string();

// Import environment variables from the process environment.
// On vibix, std::env::vars() is not yet supported (panics),
// so we skip import on that target. $PATH is set above.
#[cfg(not(target_os = "vibix"))]
for (key, value) in std::env::vars() {
env.set(&key, &value, Some(true));
}
Expand All @@ -50,10 +60,11 @@ fn main() {
use std::io::BufRead;
let stdin = std::io::stdin();

// Detect if stdin is a terminal. On vibix, isatty may not be
// available via std, so we check the TERM variable or fall back
// to assuming interactive when stdin is not redirected.
let is_tty = std::env::var("TERM").is_ok();
// On vibix, the serial console is not a POSIX tty, so `isatty()`
// would return false even for the primary interactive session.
// Always treat stdin-mode as interactive to ensure the prompt
// appears over the serial console.
let is_tty = true;

// Create the job table for interactive mode.
let mut jobs = JobTable::new();
Expand All @@ -66,7 +77,9 @@ fn main() {
eprint!("$ ");
}

for line in stdin.lock().lines() {
let locked = stdin.lock();

for line in locked.lines() {
match line {
Ok(input) => {
if input.is_empty() {
Expand All @@ -89,7 +102,9 @@ fn main() {
eprint!("$ ");
}
}
Err(_) => break,
Err(_) => {
break;
}
}
}

Expand Down
Loading