Skip to content
Merged
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
19 changes: 15 additions & 4 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;
Comment on lines +7 to +8
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope syscall shims to vibix-only builds.

#[cfg(not(test))] pulls raw syscall POSIX shims into all non-test binaries. That can unintentionally override host libc symbols and introduce host-specific breakage. Restrict this module to vibix target builds.

Suggested fix
-#[cfg(not(test))]
+#[cfg(all(target_os = "vibix", not(test)))]
 mod syscalls;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[cfg(not(test))]
mod syscalls;
#[cfg(all(target_os = "vibix", not(test)))]
mod syscalls;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@base/sh/src/main.rs` around lines 7 - 8, The runtime shims module is
currently exposed to all non-test builds via #[cfg(not(test))] and should be
limited to vibix target builds; update the cfg on the mod syscalls declaration
(the mod syscalls in base/sh/src/main.rs) to only compile on the vibix target
(for example use a cfg like all(target_vendor = "vibix", not(test)) — or
target_os = "vibix" if your vibix target is set as the OS) so the raw syscall
shims are not pulled into non-vibix binaries.


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;
Comment on lines +63 to +67
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t force interactive mode on all targets.

let is_tty = true; makes non-interactive host stdin sessions behave as interactive (prompt output + interactive signal behavior), which can break pipelines/scripts. Keep forced interactive behavior only for vibix.

Suggested fix
-    let is_tty = true;
+    #[cfg(target_os = "vibix")]
+    let is_tty = true;
+    #[cfg(not(target_os = "vibix"))]
+    let is_tty = std::env::var_os("TERM").is_some();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 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;
// 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.
#[cfg(target_os = "vibix")]
let is_tty = true;
#[cfg(not(target_os = "vibix"))]
let is_tty = std::env::var_os("TERM").is_some();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@base/sh/src/main.rs` around lines 63 - 67, The code currently sets let is_tty
= true; which forces interactive mode for all hosts; change it so only vibix
targets force interactive mode by computing is_tty conditionally (e.g. replace
the line in main.rs with: let is_tty = if running_on_vibix() { true } else {
atty::is(atty::Stream::Stdin) };), add a small helper running_on_vibix() that
detects vibix either via a compile-time cfg/feature (cfg!(feature = "vibix") or
cfg!(target_os = "vibix")) or a clear runtime marker like an environment
variable (e.g. VIBIX_SERIAL), and keep references to is_tty and the new
running_on_vibix() helper so only vibix forces interactive behavior while all
other hosts use proper isatty detection.


// Create the job table for interactive mode.
let mut jobs = JobTable::new();
Expand Down
Loading
Loading