support running shell scripts
This commit is contained in:
parent
896910b44a
commit
63800a4f65
|
@ -0,0 +1,4 @@
|
|||
.PHONY: all
|
||||
|
||||
all:
|
||||
ln -sf /busybox/busybox sh
|
|
@ -0,0 +1 @@
|
|||
/busybox/busybox
|
|
@ -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"
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use core::ops::Range;
|
||||
|
||||
use crate::fs::file_handle::FileHandle;
|
||||
use crate::fs::utils::SeekFrom;
|
||||
use crate::prelude::*;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)?)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!();
|
||||
|
|
|
@ -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())?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!();
|
||||
|
|
|
@ -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=/",
|
||||
];
|
||||
|
||||
|
|
Loading…
Reference in New Issue