support running shell scripts

This commit is contained in:
Jianfeng Jiang 2023-03-13 15:59:01 +08:00 committed by Tate, Hongliang Tian
parent 896910b44a
commit 63800a4f65
14 changed files with 123 additions and 68 deletions

4
src/apps/bin/Makefile Normal file
View File

@ -0,0 +1,4 @@
.PHONY: all
all:
ln -sf /busybox/busybox sh

1
src/apps/bin/sh Symbolic link
View File

@ -0,0 +1 @@
/busybox/busybox

13
src/apps/scripts/run_tests.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/sh
set -e
echo "Running tests......"
tests="hello_world/hello_world fork/fork execve/execve fork_c/fork signal_c/signal_test pthread/pthread_test"
for testcase in ${tests}
do
echo "Running test ${testcase}......"
${testcase}
done
echo "All tests passed"

View File

@ -21,7 +21,7 @@
use crate::{
prelude::*,
thread::{kernel_thread::KernelThreadExt, Thread},
user_apps::{get_all_apps, get_busybox_app, UserApp},
user_apps::{get_busybox_app, UserApp},
};
use process::Process;
@ -68,21 +68,6 @@ pub fn init_thread() {
thread.tid()
);
// FIXME: should be running this apps before we running shell?
println!("");
println!("[kernel] Running test programs");
println!("");
// Run test apps
for app in get_all_apps().unwrap().into_iter() {
let UserApp {
executable_path: app_name,
argv,
envp,
} = app;
println!("[jinux-std/lib.rs] spwan {:?} process", app_name);
Process::spawn_user_process(app_name.clone(), argv, Vec::new());
}
// Run busybox ash
let UserApp {
executable_path: app_name,

View File

@ -51,7 +51,7 @@ impl Elf {
pub fn entry_point(&self) -> Vaddr {
self.elf_header.pt2.entry_point as Vaddr
}
/// page header table offset
/// program header table offset
pub fn ph_off(&self) -> u64 {
self.elf_header.pt2.ph_offset
}

View File

@ -1,5 +1,3 @@
use core::ops::Range;
use crate::fs::file_handle::FileHandle;
use crate::fs::utils::SeekFrom;
use crate::prelude::*;

View File

@ -156,6 +156,14 @@ impl InitStack {
}
fn write_stack_content(&mut self, root_vmar: &Vmar<Full>, elf: &Elf) -> Result<()> {
// write a zero page. When a user program tries to read a cstring(like argv) from init stack,
// it will typically read 4096 bytes and then find the first '\0' in the buffer
// (we now read 128 bytes, which is set by MAX_FILENAME_LEN).
// If we don't have this zero page, the read may go into guard page,
// which will cause unrecoverable page fault(The guard page is not backed up by any vmo).
// So we add a zero page here, to ensure the read will not go into guard page.
// FIXME: Some other OSes put the first page of excutable file here.
self.write_bytes(&[0u8; PAGE_SIZE], root_vmar)?;
// write envp string
let envp_pointers = self.write_envp_strings(root_vmar)?;
// write argv string
@ -201,6 +209,7 @@ impl InitStack {
let mut argv_pointers = Vec::with_capacity(argv.len());
for argv in argv.iter().rev() {
let pointer = self.write_cstring(argv, root_vmar)?;
debug!("argv address = 0x{:x}", pointer);
argv_pointers.push(pointer);
}
argv_pointers.reverse();

View File

@ -1,6 +1,6 @@
use core::sync::atomic::{AtomicI32, Ordering};
use self::elf::{ElfLoadInfo, load_elf_to_root_vmar};
use self::elf::{load_elf_to_root_vmar, ElfLoadInfo};
use self::posix_thread::posix_thread_ext::PosixThreadExt;
use self::process_group::ProcessGroup;
use self::process_vm::user_heap::UserHeap;
@ -22,7 +22,6 @@ use crate::thread::{thread_table, Thread};
use crate::tty::get_n_tty;
use crate::vm::vmar::Vmar;
use jinux_frame::sync::WaitQueue;
use jinux_frame::task::Task;
pub mod clone;
pub mod elf;
@ -384,3 +383,76 @@ impl Process {
pub fn get_init_process() -> Option<Arc<Process>> {
process_table::pid_to_process(INIT_PROCESS_PID)
}
/// Set up root vmar for an executable.
/// About recursion_limit: recursion limit is used to limit th recursion depth of shebang executables.
/// If the interpreter program(the program behind !#) of shebang executable is also a shebang,
/// then it will trigger recursion. We will try to setup root vmar for the interpreter program.
/// I guess for most cases, setting the recursion_limit as 1 should be enough.
/// because the interpreter game is usually an elf binary(e.g., /bin/bash)
pub fn setup_root_vmar(
executable_path: String,
argv: Vec<CString>,
envp: Vec<CString>,
fs_resolver: &FsResolver,
root_vmar: &Vmar<Full>,
recursion_limit: usize,
) -> Result<ElfLoadInfo> {
let fs_path = FsPath::new(AT_FDCWD, &executable_path)?;
let file = fs_resolver.open(&fs_path, AccessMode::O_RDONLY as u32, 0)?;
// read the first page of file header
let mut file_header_buffer = [0u8; PAGE_SIZE];
file.seek(SeekFrom::Start(0))?;
file.read(&mut file_header_buffer)?;
if recursion_limit > 0
&& file_header_buffer.starts_with(b"#!")
&& file_header_buffer.contains(&b'\n')
{
return set_up_root_vmar_for_shebang(
argv,
envp,
&file_header_buffer,
fs_resolver,
root_vmar,
recursion_limit,
);
}
let elf_file = Arc::new(FileHandle::new_inode_handle(file));
load_elf_to_root_vmar(&file_header_buffer, elf_file, &root_vmar, argv, envp)
}
fn set_up_root_vmar_for_shebang(
argv: Vec<CString>,
envp: Vec<CString>,
file_header_buffer: &[u8],
fs_resolver: &FsResolver,
root_vmar: &Vmar<Full>,
recursion_limit: usize,
) -> Result<ElfLoadInfo> {
let first_line_len = file_header_buffer.iter().position(|&c| c == b'\n').unwrap();
// skip #!
let shebang_header = &file_header_buffer[2..first_line_len];
let mut shebang_argv = Vec::new();
for arg in shebang_header.split(|&c| c == b' ') {
let arg = CString::new(arg)?;
shebang_argv.push(arg);
}
if shebang_argv.len() != 1 {
return_errno_with_message!(
Errno::EINVAL,
"One and only one intpreter program should be specified"
);
}
for origin_arg in argv.into_iter() {
shebang_argv.push(origin_arg);
}
let shebang_path = shebang_argv[0].to_str()?.to_string();
setup_root_vmar(
shebang_path,
shebang_argv,
envp,
fs_resolver,
root_vmar,
recursion_limit - 1,
)
}

View File

@ -117,11 +117,16 @@ impl PosixThread {
// If clear_ctid !=0 ,do a futex wake and write zero to the clear_ctid addr.
debug!("wake up ctid");
if *clear_ctid != 0 {
debug!("futex wake");
futex_wake(*clear_ctid, 1)?;
debug!("write ctid");
// FIXME: the correct write length?
write_val_to_user(*clear_ctid, &0i32)?;
debug!("ctid = 0x{:x}", *clear_ctid);
write_val_to_user(*clear_ctid, &0u32).unwrap();
debug!("clear ctid");
*clear_ctid = 0;
}
debug!("wake up ctid succeeds");
// exit the robust list: walk the robust list; mark futex words as dead and do futex wake
self.wake_robust_list(tid);

View File

@ -1,6 +1,8 @@
use crate::prelude::*;
pub const MAX_THREAD_NAME_LEN: usize = 16;
#[derive(Debug)]
pub struct ThreadName {
inner: [u8; MAX_THREAD_NAME_LEN],
count: usize,
@ -14,13 +16,13 @@ impl ThreadName {
}
}
pub fn new_from_executable_path(elf_path: &str) -> Result<Self> {
pub fn new_from_executable_path(executable_path: &str) -> Result<Self> {
let mut thread_name = ThreadName::new();
let elf_file_name = elf_path
let executable_file_name = executable_path
.split('/')
.last()
.ok_or(Error::with_message(Errno::EINVAL, "invalid elf path"))?;
let name = CString::new(elf_file_name)?;
let name = CString::new(executable_file_name)?;
thread_name.set_name(&name)?;
Ok(thread_name)
}
@ -40,7 +42,7 @@ impl ThreadName {
Ok(())
}
pub fn get_name(&self) -> Result<Option<&CStr>> {
Ok(Some(&(CStr::from_bytes_with_nul(&self.inner)?)))
pub fn name(&self) -> Result<Option<&CStr>> {
Ok(Some(&(CStr::from_bytes_until_nul(&self.inner)?)))
}
}

View File

@ -29,6 +29,9 @@ pub fn sys_execve(
let mut thread_name = posix_thread.thread_name().lock();
let new_thread_name = ThreadName::new_from_executable_path(&executable_path)?;
*thread_name = Some(new_thread_name);
// clear ctid
// FIXME: should we clear ctid when execve?
*posix_thread.clear_child_tid().lock() = 0;
// let elf_file_content = crate::user_apps::read_execve_hello_content();
let current = current!();

View File

@ -10,14 +10,14 @@ use super::SYS_PRCTL;
pub fn sys_prctl(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_PRCTL);
let prctl_cmd = PrctlCmd::from_args(option, arg2, arg3, arg4, arg5)?;
debug!("prctl cmd = {:?}", prctl_cmd);
debug!("prctl cmd = {:x?}", prctl_cmd);
let current_thread = current_thread!();
let posix_thread = current_thread.as_posix_thread().unwrap();
match prctl_cmd {
PrctlCmd::PR_GET_NAME(write_to_addr) => {
let thread_name = posix_thread.thread_name().lock();
if let Some(thread_name) = &*thread_name {
if let Some(thread_name) = thread_name.get_name()? {
if let Some(thread_name) = thread_name.name()? {
write_bytes_to_user(write_to_addr, thread_name.to_bytes_with_nul())?;
}
}

View File

@ -23,7 +23,7 @@ pub trait KernelThreadExt {
thread.run();
thread
}
/// join a kernel thread
/// join a kernel thread, returns if the kernel thread exit
fn join(&self);
}
@ -36,7 +36,6 @@ impl KernelThreadExt for Thread {
where
F: Fn() + Send + Sync + 'static,
{
let thread_fn = move || {
task_fn();
let current_thread = current_thread!();

View File

@ -18,42 +18,6 @@ impl UserApp {
}
}
pub fn get_all_apps() -> Result<Vec<UserApp>> {
let mut res = Vec::with_capacity(16);
// Most simple hello world, written in assembly
let asm_hello_world = UserApp::new("hello_world/hello_world")?;
res.push(asm_hello_world);
// Hello world, written in C language.
// Since glibc requires the elf path starts with "/", and we don't have filesystem now.
// So we manually add a leading "/" for app written in C language.
let hello_c = UserApp::new("/hello_c/hello")?;
res.push(hello_c);
// Fork process, written in assembly
let asm_fork = UserApp::new("fork/fork")?;
res.push(asm_fork);
// Execve, written in C language.
let execve_c = UserApp::new("/execve/execve")?;
res.push(execve_c);
// Fork new process, written in C language. (Fork in glibc uses syscall clone actually)
let fork_c = UserApp::new("/fork_c/fork")?;
res.push(fork_c);
// signal test
let signal_test = UserApp::new("/signal_c/signal_test")?;
res.push(signal_test);
// pthread test
let pthread_test = UserApp::new("/pthread/pthread_test")?;
res.push(pthread_test);
Ok(res)
}
pub fn get_busybox_app() -> Result<UserApp> {
// busybox
let mut busybox = UserApp::new("/busybox/busybox")?;
@ -65,7 +29,7 @@ pub fn get_busybox_app() -> Result<UserApp> {
"LOGNAME=root",
"HOME=/",
"USER=root",
"PATH=",
"PATH=/bin",
"OLDPWD=/",
];