diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 81d6a09b..a93a66ca 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -66,6 +66,8 @@ pub fn exit_current_and_run_next(exit_code: i32) { inner.children.clear(); // deallocate user space inner.memory_set.recycle_data_pages(); + // drop file descriptors + inner.fd_table.clear(); drop(inner); // **** release current PCB // drop task manually to maintain rc correctly diff --git a/user/src/bin/count_lines.rs b/user/src/bin/count_lines.rs new file mode 100644 index 00000000..ab66f7a8 --- /dev/null +++ b/user/src/bin/count_lines.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::read; + +#[no_mangle] +pub fn main(_argc: usize, _argv: &[&str]) -> i32 { + let mut buf = [0u8; 256]; + let mut lines = 0usize; + let mut total_size = 0usize; + loop { + let len = read(0, &mut buf) as usize; + if len == 0 { break; } + total_size += len; + let string = core::str::from_utf8(&buf[..len]).unwrap(); + lines += string.chars().fold(0, |acc, c| { + acc + if c == '\n' { 1 } else { 0 } + }); + } + if total_size > 0 { + lines += 1; + } + println!("{}", lines); + 0 +} diff --git a/user/src/bin/user_shell.rs b/user/src/bin/user_shell.rs index b2c33f2d..f7cefc12 100644 --- a/user/src/bin/user_shell.rs +++ b/user/src/bin/user_shell.rs @@ -10,6 +10,7 @@ const LF: u8 = 0x0au8; const CR: u8 = 0x0du8; const DL: u8 = 0x7fu8; const BS: u8 = 0x08u8; +const LINE_START: &str = ">> "; use alloc::string::String; use alloc::vec::Vec; @@ -21,105 +22,186 @@ use user_lib::{ OpenFlags, close, dup, + pipe, }; use user_lib::console::getchar; +#[derive(Debug)] +struct ProcessArguments { + input: String, + output: String, + args_copy: Vec, + args_addr: Vec<*const u8>, +} + +impl ProcessArguments { + pub fn new(command: &str) -> Self { + let args: Vec<_> = command.split(' ').collect(); + let mut args_copy: Vec = args + .iter() + .filter(|&arg| { !arg.is_empty() }) + .map(|&arg| { + let mut string = String::new(); + string.push_str(arg); + string.push('\0'); + string + }) + .collect(); + + // redirect input + let mut input = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == "<\0") { + input = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + // redirect output + let mut output = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == ">\0") { + output = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + let mut args_addr: Vec<*const u8> = args_copy + .iter() + .map(|arg| arg.as_ptr()) + .collect(); + args_addr.push(0 as *const u8); + + Self { + input, + output, + args_copy, + args_addr, + } + } +} + #[no_mangle] pub fn main() -> i32 { println!("Rust user shell"); let mut line: String = String::new(); - print!(">> "); + print!("{}", LINE_START); loop { let c = getchar(); match c { LF | CR => { println!(""); if !line.is_empty() { - let args: Vec<_> = line.as_str().split(' ').collect(); - let mut args_copy: Vec = args - .iter() - .map(|&arg| { - let mut string = String::new(); - string.push_str(arg); - string - }) - .collect(); - - args_copy - .iter_mut() - .for_each(|string| { - string.push('\0'); - }); - - // redirect input - let mut input = String::new(); - if let Some((idx, _)) = args_copy - .iter() - .enumerate() - .find(|(_, arg)| arg.as_str() == "<\0") { - input = args_copy[idx + 1].clone(); - args_copy.drain(idx..=idx + 1); - } - - // redirect output - let mut output = String::new(); - if let Some((idx, _)) = args_copy - .iter() - .enumerate() - .find(|(_, arg)| arg.as_str() == ">\0") { - output = args_copy[idx + 1].clone(); - args_copy.drain(idx..=idx + 1); - } - - let mut args_addr: Vec<*const u8> = args_copy + let splited: Vec<_> = line.as_str().split('|').collect(); + let process_arguments_list: Vec<_> = splited .iter() - .map(|arg| arg.as_ptr()) + .map(|&cmd| ProcessArguments::new(cmd)) .collect(); - args_addr.push(0 as *const u8); - let pid = fork(); - if pid == 0 { - // input redirection - if !input.is_empty() { - let input_fd = open(input.as_str(), OpenFlags::RDONLY); - if input_fd == -1 { - println!("Error when opening file {}", input); - return -4; + let mut valid = true; + for (i, process_args) in process_arguments_list.iter().enumerate() { + if i == 0 { + if !process_args.output.is_empty() { valid = false; } + } else if i == process_arguments_list.len() - 1 { + if !process_args.input.is_empty() { valid = false; } + } else { + if !process_args.output.is_empty() || !process_args.input.is_empty() { + valid = false; + } + } + } + if process_arguments_list.len() == 1 { valid = true; } + if !valid { + println!("Invalid command: Inputs/Outputs cannot be correctly binded!"); + } else { + // create pipes + let mut pipes_fd: Vec<[usize; 2]> = Vec::new(); + if !process_arguments_list.is_empty() { + for _ in 0..process_arguments_list.len()-1 { + let mut pipe_fd = [0usize; 2]; + pipe(&mut pipe_fd); + pipes_fd.push(pipe_fd); } - let input_fd = input_fd as usize; - close(0); - assert_eq!(dup(input_fd), 0); - close(input_fd); } - // output redirection - if !output.is_empty() { - let output_fd = open( - output.as_str(), - OpenFlags::CREATE | OpenFlags::WRONLY - ); - if output_fd == -1 { - println!("Error when opening file {}", output); - return -4; + let mut children: Vec<_> = Vec::new(); + for (i, process_argument) in process_arguments_list + .iter() + .enumerate() { + let pid = fork(); + if pid == 0 { + let input = &process_argument.input; + let output = &process_argument.output; + let args_copy = &process_argument.args_copy; + let args_addr = &process_argument.args_addr; + // redirect input + if !input.is_empty() { + let input_fd = open(input.as_str(), OpenFlags::RDONLY); + if input_fd == -1 { + println!("Error when opening file {}", input); + return -4; + } + let input_fd = input_fd as usize; + close(0); + assert_eq!(dup(input_fd), 0); + close(input_fd); + } + // redirect output + if !output.is_empty() { + let output_fd = open( + output.as_str(), + OpenFlags::CREATE | OpenFlags::WRONLY + ); + if output_fd == -1 { + println!("Error when opening file {}", output); + return -4; + } + let output_fd = output_fd as usize; + close(1); + assert_eq!(dup(output_fd), 1); + close(output_fd); + } + // receive input from the previous process + if i > 0 { + close(0); + let read_end = pipes_fd.get(i-1).unwrap()[0]; + assert_eq!(dup(read_end), 0); + } + // send output to the next process + if i < process_arguments_list.len()-1 { + close(1); + let write_end = pipes_fd.get(i).unwrap()[1]; + assert_eq!(dup(write_end), 1); + } + // close all pipe ends inherited from the parent process + for pipe_fd in pipes_fd.iter() { + close(pipe_fd[0]); + close(pipe_fd[1]); + } + // execute new application + if exec(args_copy[0].as_str(), args_addr.as_slice()) == -1 { + println!("Error when executing!"); + return -4; + } + unreachable!(); + } else { + children.push(pid); } - let output_fd = output_fd as usize; - close(1); - assert_eq!(dup(output_fd), 1); - close(output_fd); } - // child process - if exec(args_copy[0].as_str(), args_addr.as_slice()) == -1 { - println!("Error when executing!"); - return -4; + for pipe_fd in pipes_fd.iter() { + close(pipe_fd[0]); + close(pipe_fd[1]); } - unreachable!(); - } else { let mut exit_code: i32 = 0; - let exit_pid = waitpid(pid as usize, &mut exit_code); - assert_eq!(pid, exit_pid); - println!("Shell: Process {} exited with code {}", pid, exit_code); - } + for pid in children.into_iter() { + let exit_pid = waitpid(pid as usize, &mut exit_code); + assert_eq!(pid, exit_pid); + println!("Shell: Process {} exited with code {}", pid, exit_code); + } + } line.clear(); } - print!(">> "); + print!("{}", LINE_START); } BS | DL => { if !line.is_empty() { @@ -135,4 +217,4 @@ pub fn main() -> i32 { } } } -} \ No newline at end of file +}