diff --git a/applications/cat/src/lib.rs b/applications/cat/src/lib.rs index ba4de221b3..5ba0bb87e2 100644 --- a/applications/cat/src/lib.rs +++ b/applications/cat/src/lib.rs @@ -1,28 +1,30 @@ #![no_std] -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; // #[macro_use] extern crate log; -#[macro_use] extern crate alloc; -extern crate task; +#[macro_use] +extern crate alloc; +extern crate core2; +extern crate fs_node; extern crate getopts; extern crate path; -extern crate fs_node; -extern crate core2; +extern crate task; -use core::str; use alloc::{ string::String, vec::Vec, }; +use core::str; + +use fs_node::FileOrDir; use getopts::Options; use path::Path; -use fs_node::FileOrDir; - pub fn main(args: Vec) -> isize { let mut opts = Options::new(); opts.optflag("h", "help", "print this help menu"); - + let matches = match opts.parse(args) { Ok(m) => m, Err(_f) => { @@ -42,42 +44,40 @@ pub fn main(args: Vec) -> isize { } return 0; } - + let Ok(cwd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { println!("failed to get current task"); return -1; }; let path: &Path = matches.free[0].as_ref(); - + // navigate to the filepath specified by first argument match path.get(&cwd) { - Some(file_dir_enum) => { - match file_dir_enum { - FileOrDir::Dir(directory) => { - println!("{:?} is a directory, cannot 'cat' non-files.", directory.lock().get_name()); - return -1; - } - FileOrDir::File(file) => { - let mut file_locked = file.lock(); - let file_size = file_locked.len(); - let mut string_slice_as_bytes = vec![0; file_size]; - - let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes,0) { - Ok(num) => num, - Err(e) => { - println!("Failed to read {:?}, error {:?}", file_locked.get_name(), e); - return -1; - } - }; - let read_string = match str::from_utf8(&string_slice_as_bytes) { - Ok(string_slice) => string_slice, - Err(utf8_err) => { - println!("File {:?} was not a printable UTF-8 text file: {}", file_locked.get_name(), utf8_err); - return -1; - } - }; - println!("{}", read_string); - } + Some(file_dir_enum) => match file_dir_enum { + FileOrDir::Dir(directory) => { + println!("{:?} is a directory, cannot 'cat' non-files.", directory.lock().get_name()); + return -1; + } + FileOrDir::File(file) => { + let mut file_locked = file.lock(); + let file_size = file_locked.len(); + let mut string_slice_as_bytes = vec![0; file_size]; + + let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { + Ok(num) => num, + Err(e) => { + println!("Failed to read {:?}, error {:?}", file_locked.get_name(), e); + return -1; + } + }; + let read_string = match str::from_utf8(&string_slice_as_bytes) { + Ok(string_slice) => string_slice, + Err(utf8_err) => { + println!("File {:?} was not a printable UTF-8 text file: {}", file_locked.get_name(), utf8_err); + return -1; + } + }; + println!("{}", read_string); } }, _ => { @@ -100,8 +100,11 @@ fn echo_from_stdin() -> Result<(), &'static str> { // Read from stdin and print it back. loop { let cnt = stdin.read(&mut buf).or(Err("failed to perform read"))?; - if cnt == 0 { break; } - stdout.write_all(&buf[0..cnt]) + if cnt == 0 { + break; + } + stdout + .write_all(&buf[0..cnt]) .or(Err("faileld to perform write_all"))?; } Ok(()) diff --git a/applications/cd/src/lib.rs b/applications/cd/src/lib.rs index c2d9647ccd..bd63dfdf5d 100644 --- a/applications/cd/src/lib.rs +++ b/applications/cd/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; // #[macro_use] extern crate log; extern crate alloc; @@ -9,9 +10,12 @@ extern crate path; extern crate root; extern crate task; -use alloc::string::String; -use alloc::sync::Arc; -use alloc::vec::Vec; +use alloc::{ + string::String, + sync::Arc, + vec::Vec, +}; + use getopts::Options; pub fn main(args: Vec) -> isize { diff --git a/applications/date/src/lib.rs b/applications/date/src/lib.rs index dbd2d443ee..a161f64760 100644 --- a/applications/date/src/lib.rs +++ b/applications/date/src/lib.rs @@ -2,14 +2,15 @@ // #![feature(plugin)] // #![plugin(application_main_fn)] - extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate rtc; -use alloc::vec::Vec; -use alloc::string::String; - +use alloc::{ + string::String, + vec::Vec, +}; pub fn main(_args: Vec) -> isize { let now = rtc::read_rtc(); diff --git a/applications/deps/src/lib.rs b/applications/deps/src/lib.rs index a49b12186f..f8d346dea1 100644 --- a/applications/deps/src/lib.rs +++ b/applications/deps/src/lib.rs @@ -1,22 +1,22 @@ //! This application is mostly for debugging usage, and allows a developer //! to explore live dependencies between crates and sections at runtime. - #![no_std] #![feature(slice_concat_ext)] -#[macro_use] extern crate log; +#[macro_use] +extern crate log; extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate itertools; +extern crate crate_name_utils; extern crate getopts; -extern crate task; extern crate memory; extern crate mod_mgmt; -extern crate crate_name_utils; extern crate spin; - +extern crate task; use alloc::{ collections::BTreeSet, @@ -24,19 +24,23 @@ use alloc::{ String, ToString, }, - vec::Vec, sync::Arc, + vec::Vec, +}; + +use crate_name_utils::get_containing_crate_name; +use getopts::{ + Matches, + Options, }; use memory::VirtualAddress; -use spin::Once; -use getopts::{Matches, Options}; use mod_mgmt::{ + CrateNamespace, + StrRef, StrongCrateRef, StrongSectionRef, - CrateNamespace, StrRef, }; -use crate_name_utils::get_containing_crate_name; - +use spin::Once; /// calls println!() and then log!() macro_rules! println_log { @@ -46,36 +50,55 @@ macro_rules! println_log { }; } - static VERBOSE: Once = Once::new(); macro_rules! verbose { - () => (VERBOSE.get() == Some(&true)); + () => { + VERBOSE.get() == Some(&true) + }; } - pub fn main(args: Vec) -> isize { let mut opts = Options::new(); - opts.optflag("h", "help", "print this help menu"); - opts.optflag("v", "verbose", "enable verbose output"); - opts.optopt ("a", "address", "output the section that contains the given ADDRESS", "ADDRESS"); - opts.optopt ("s", "sections-in", "output the sections that depend on the given SECTION (incoming weak dependents)", "SECTION"); - opts.optopt ("S", "sections-out", "output the sections that the given SECTION depends on (outgoing strong dependencies)", "SECTION"); - opts.optopt ("c", "crates-in", "output the crates that depend on the given CRATE (incoming weak dependents)", "CRATE"); - opts.optopt ("C", "crates-out", "output the crates that the given CRATE depends on (outgoing strong dependencies)", "CRATE"); - opts.optopt ("l", "list", "list the public sections in the given crate", "CRATE"); - opts.optopt ("", "list-all", "list all sections in the given crate", "CRATE"); - opts.optopt ("", "num-deps-crate", "sum up the count of all dependencies for the given crate", "CRATE"); - opts.optopt ("", "num-deps-section", "sum up the count of all dependencies for the given section", "SECTION"); - opts.optflag("", "num-deps-all", "sum up the count of all dependencies for all crates"); - opts.optflag("", "num-rodata", "count the private .rodata sections for all crates"); - + opts.optflag("h", "help", "print this help menu"); + opts.optflag("v", "verbose", "enable verbose output"); + opts.optopt("a", "address", "output the section that contains the given ADDRESS", "ADDRESS"); + opts.optopt( + "s", + "sections-in", + "output the sections that depend on the given SECTION (incoming weak dependents)", + "SECTION", + ); + opts.optopt( + "S", + "sections-out", + "output the sections that the given SECTION depends on (outgoing strong dependencies)", + "SECTION", + ); + opts.optopt( + "c", + "crates-in", + "output the crates that depend on the given CRATE (incoming weak dependents)", + "CRATE", + ); + opts.optopt( + "C", + "crates-out", + "output the crates that the given CRATE depends on (outgoing strong dependencies)", + "CRATE", + ); + opts.optopt("l", "list", "list the public sections in the given crate", "CRATE"); + opts.optopt("", "list-all", "list all sections in the given crate", "CRATE"); + opts.optopt("", "num-deps-crate", "sum up the count of all dependencies for the given crate", "CRATE"); + opts.optopt("", "num-deps-section", "sum up the count of all dependencies for the given section", "SECTION"); + opts.optflag("", "num-deps-all", "sum up the count of all dependencies for all crates"); + opts.optflag("", "num-rodata", "count the private .rodata sections for all crates"); let matches = match opts.parse(args) { Ok(m) => m, Err(_f) => { println!("{}", _f); print_usage(opts); - return -1; + return -1; } }; @@ -91,67 +114,51 @@ pub fn main(args: Vec) -> isize { Err(e) => { println!("Error:\n{}", e); -1 - } + } } } - fn rmain(matches: Matches) -> Result<(), String> { - if verbose!() { println!("MATCHES: {:?}", matches.free); } + if verbose!() { + println!("MATCHES: {:?}", matches.free); + } if let Some(addr) = matches.opt_str("a") { section_containing_address(&addr) - } - else if let Some(sec_name) = matches.opt_str("s") { + } else if let Some(sec_name) = matches.opt_str("s") { sections_dependent_on_me(&sec_name) - } - else if let Some(sec_name) = matches.opt_str("S") { + } else if let Some(sec_name) = matches.opt_str("S") { sections_i_depend_on(&sec_name) - } - else if let Some(crate_name) = matches.opt_str("c") { + } else if let Some(crate_name) = matches.opt_str("c") { crates_dependent_on_me(&crate_name) - } - else if let Some(crate_name) = matches.opt_str("C") { + } else if let Some(crate_name) = matches.opt_str("C") { crates_i_depend_on(&crate_name) - } - else if let Some(crate_name) = matches.opt_str("l") { + } else if let Some(crate_name) = matches.opt_str("l") { sections_in_crate(&crate_name, false) - } - else if let Some(crate_name) = matches.opt_str("list-all") { + } else if let Some(crate_name) = matches.opt_str("list-all") { sections_in_crate(&crate_name, true) - } - else if let Some(crate_name) = matches.opt_str("num-deps-crate") { + } else if let Some(crate_name) = matches.opt_str("num-deps-crate") { num_deps_crate(&crate_name) - } - else if let Some(crate_name) = matches.opt_str("num-deps-section") { + } else if let Some(crate_name) = matches.opt_str("num-deps-section") { num_deps_section(&crate_name) - } - else if matches.opt_present("num-deps-all") { + } else if matches.opt_present("num-deps-all") { num_deps_all() - } - else if matches.opt_present("num-rodata") { + } else if matches.opt_present("num-rodata") { count_private_rodata_sections() - } - else { + } else { Err("no supported options/arguments found.".to_string()) } } - - /// Outputs the section containing the given address, i.e., symbolication. -/// fn section_containing_address(addr: &str) -> Result<(), String> { - let addr = if addr.starts_with("0x") || addr.starts_with("0X") { - &addr[2..] - } else { - addr - }; - + let addr = if addr.starts_with("0x") || addr.starts_with("0X") { &addr[2..] } else { addr }; + let virt_addr = VirtualAddress::new( usize::from_str_radix(addr, 16) - .map_err(|_| format!("Error: address {addr:?} is not a valid hexademical usize value"))? - ).ok_or_else(|| format!("Error: address {addr:?} is not a valid VirtualAddress"))?; + .map_err(|_| format!("Error: address {addr:?} is not a valid hexademical usize value"))?, + ) + .ok_or_else(|| format!("Error: address {addr:?} is not a valid VirtualAddress"))?; if let Some((sec, offset)) = get_my_current_namespace().get_section_containing_address(virt_addr, false) { println!("Found {:>#018X} in {} + {:#X}, typ: {:?}", virt_addr, sec.name, offset, sec.typ); @@ -163,14 +170,20 @@ fn section_containing_address(addr: &str) -> Result<(), String> { /// Outputs the given section's weak dependents, i.e., /// the sections that depend on the given section. -/// -/// If there are multiple matches, this returns an Error containing +/// +/// If there are multiple matches, this returns an Error containing /// all of the matching section names separated by the newline character `'\n'`. fn sections_dependent_on_me(section_name: &str) -> Result<(), String> { let sec = find_section(section_name)?; println!("Sections that depend on {} (weak dependents):", sec.name); - for dependent_sec in sec.inner.read().sections_dependent_on_me.iter().filter_map(|weak_dep| weak_dep.section.upgrade()) { - if verbose!() { + for dependent_sec in sec + .inner + .read() + .sections_dependent_on_me + .iter() + .filter_map(|weak_dep| weak_dep.section.upgrade()) + { + if verbose!() { println!(" {} in {:?}", dependent_sec.name, dependent_sec.parent_crate.upgrade()); } else { println!(" {}", dependent_sec.name); @@ -179,17 +192,22 @@ fn sections_dependent_on_me(section_name: &str) -> Result<(), String> { Ok(()) } - /// Outputs the given section's strong dependencies, i.e., /// the sections that the given section depends on. -/// -/// If there are multiple matches, this returns an Error containing +/// +/// If there are multiple matches, this returns an Error containing /// all of the matching section names separated by the newline character `'\n'`. fn sections_i_depend_on(section_name: &str) -> Result<(), String> { let sec = find_section(section_name)?; println!("Sections that {} depends on (strong dependencies):", sec.name); - for dependency_sec in sec.inner.read().sections_i_depend_on.iter().map(|dep| &dep.section) { - if verbose!() { + for dependency_sec in sec + .inner + .read() + .sections_i_depend_on + .iter() + .map(|dep| &dep.section) + { + if verbose!() { println!(" {} in {:?}", dependency_sec.name, dependency_sec.parent_crate.upgrade()); } else { println!(" {}", dependency_sec.name); @@ -198,7 +216,6 @@ fn sections_i_depend_on(section_name: &str) -> Result<(), String> { Ok(()) } - fn num_deps_crate(crate_name: &str) -> Result<(), String> { let (_cn, crate_ref) = find_crate(crate_name)?; let (s, w, i) = crate_dependency_count(&crate_ref); @@ -228,10 +245,9 @@ fn num_deps_all() -> Result<(), String> { true // keep going }); - println!("Total Dependency Count for all {} crates ({} sections):\nStrong: {}\nWeak: {}\nIntrnl: {}", - crate_count, - section_count, - s_total, w_total, i_total + println!( + "Total Dependency Count for all {} crates ({} sections):\nStrong: {}\nWeak: {}\nIntrnl: {}", + crate_count, section_count, s_total, w_total, i_total ); Ok(()) } @@ -248,7 +264,12 @@ fn count_private_rodata_sections() -> Result<(), String> { let mut prv = 0; let mut publ = 0; let mut disc = 0; - for sec in crate_ref.lock_as_ref().sections.values().filter(|sec| sec.typ == mod_mgmt::SectionType::Rodata) { + for sec in crate_ref + .lock_as_ref() + .sections + .values() + .filter(|sec| sec.typ == mod_mgmt::SectionType::Rodata) + { section_count += 1; let mut can_discard = true; if sec.global { @@ -277,12 +298,9 @@ fn count_private_rodata_sections() -> Result<(), String> { true // keep going }); - println!("Total of {} .rodata sections for all {} crates: {} public, {} private, {} discardable", - section_count, - crate_count, - public_rodata, - private_rodata, - discardable + println!( + "Total of {} .rodata sections for all {} crates: {} public, {} private, {} discardable", + section_count, crate_count, public_rodata, private_rodata, discardable ); Ok(()) } @@ -290,14 +308,18 @@ fn count_private_rodata_sections() -> Result<(), String> { /// Returns the count of `(strong dependencies, weak dependents, internal dependencies)` /// for all sections in the given crate. . fn crate_dependency_count(crate_ref: &StrongCrateRef) -> (usize, usize, usize) { - let res = crate_ref.lock_as_ref().sections.values() + let res = crate_ref + .lock_as_ref() + .sections + .values() .map(section_dependency_count) .fold((0, 0, 0), |(acc_s, acc_w, acc_i), (s, w, i)| (acc_s + s, acc_w + w, acc_i + i)); // trace!("crate {:?} has deps {:?}", crate_ref, res); res } -/// Returns the given section's count of `(strong dependencies, weak dependents, internal dependencies)`. +/// Returns the given section's count of `(strong dependencies, weak dependents, internal +/// dependencies)`. fn section_dependency_count(sec: &StrongSectionRef) -> (usize, usize, usize) { let inner = sec.inner.read(); ( @@ -312,18 +334,17 @@ fn section_dependency_count(sec: &StrongSectionRef) -> (usize, usize, usize) { /// Outputs the given crate's weak dependents, i.e., /// the crates that depend on the given crate. -/// -/// If there are multiple matches, this returns an Error containing +/// +/// If there are multiple matches, this returns an Error containing /// all of the matching crate names separated by the newline character `'\n'`. fn crates_dependent_on_me(_crate_name: &str) -> Result<(), String> { Err("unimplemented".to_string()) } - /// Outputs the given crate's strong dependencies, i.e., /// the crates that the given crate depends on. -/// -/// If there are multiple matches, this returns an Error containing +/// +/// If there are multiple matches, this returns an Error containing /// all of the matching crate names separated by the newline character `'\n'`. fn crates_i_depend_on(crate_prefix: &str) -> Result<(), String> { let (crate_name, crate_ref) = find_crate(crate_prefix)?; @@ -337,20 +358,17 @@ fn crates_i_depend_on(crate_prefix: &str) -> Result<(), String> { crate_list.sort_unstable(); crate_list.dedup(); - println!("Crate {} has direct dependences:\n {}", crate_name, crate_list.join("\n ")); Ok(()) } - - /// Outputs the list of sections in the given crate. -/// +/// /// # Arguments -/// * `all_sections`: If `true`, then all sections will be printed. -/// If `false`, then only public (global) sections will be printed. -/// -/// If there are multiple matches, this returns an Error containing +/// * `all_sections`: If `true`, then all sections will be printed. If `false`, then only public +/// (global) sections will be printed. +/// +/// If there are multiple matches, this returns an Error containing /// all of the matching section names separated by the newline character `'\n'`. fn sections_in_crate(crate_name: &str, all_sections: bool) -> Result<(), String> { let (crate_name, crate_ref) = find_crate(crate_name)?; @@ -374,15 +392,17 @@ fn sections_in_crate(crate_name: &str, all_sections: bool) -> Result<(), String> } } - let crates_list = containing_crates.into_iter().collect::>().join("\n"); + let crates_list = containing_crates + .into_iter() + .collect::>() + .join("\n"); println_log!("Constituent (or related) crates:\n{}", &crates_list); Ok(()) } - /// Returns the crate matching the given `crate_name` if there is a single match. -/// -/// If there are multiple matches, this returns an Error containing +/// +/// If there are multiple matches, this returns an Error containing /// all of the matching section names separated by the newline character `'\n'`. fn find_crate(crate_name: &str) -> Result<(StrRef, StrongCrateRef), String> { let namespace = get_my_current_namespace(); @@ -391,61 +411,73 @@ fn find_crate(crate_name: &str) -> Result<(StrRef, StrongCrateRef), String> { 0 => Err(format!("couldn't find crate matching {crate_name:?}")), 1 => { let mc = matching_crates.swap_remove(0); - Ok((mc.0, mc.1)) + Ok((mc.0, mc.1)) } - _ => Err(matching_crates.into_iter().map(|(crate_name, _crate_ref, _ns)| crate_name).collect::>().join("\n")), + _ => Err(matching_crates + .into_iter() + .map(|(crate_name, _crate_ref, _ns)| crate_name) + .collect::>() + .join("\n")), } } - /// Returns the section matching the given `section_name` if there is a single match. -/// -/// If there are multiple matches, this returns an Error containing +/// +/// If there are multiple matches, this returns an Error containing /// all of the matching section names separated by the newline character `'\n'`. fn find_section(section_name: &str) -> Result { let namespace = get_my_current_namespace(); let matching_symbols = namespace.find_symbols_starting_with(section_name); match matching_symbols.len() { - 1 => return matching_symbols[0].1 - .upgrade() - .ok_or_else(|| "Found matching symbol name but couldn't get reference to section".to_string()), - 2.. => return Err(matching_symbols - .into_iter() - .map(|(k, _v)| k) - .collect::>() - .join("\n") - ), - _ => { /* no matches, continue on */ }, + 1 => { + return matching_symbols[0] + .1 + .upgrade() + .ok_or_else(|| "Found matching symbol name but couldn't get reference to section".to_string()); + } + 2.. => { + return Err(matching_symbols + .into_iter() + .map(|(k, _v)| k) + .collect::>() + .join("\n")); + } + _ => { /* no matches, continue on */ } } // If it wasn't a global section in the symbol map, then we need to find its containing crate // and search that crate's symbols manually. - let containing_crate_ref = get_containing_crate_name(section_name).first() + let containing_crate_ref = get_containing_crate_name(section_name) + .first() .and_then(|cname| CrateNamespace::get_crate_starting_with(&namespace, &format!("{cname}-"))) - .or_else(|| get_containing_crate_name(section_name).get(1) - .and_then(|cname| CrateNamespace::get_crate_starting_with(&namespace, &format!("{cname}-"))) - ) + .or_else(|| { + get_containing_crate_name(section_name) + .get(1) + .and_then(|cname| CrateNamespace::get_crate_starting_with(&namespace, &format!("{cname}-"))) + }) .map(|(_cname, crate_ref, _ns)| crate_ref) - .ok_or_else(|| format!("Couldn't find section {section_name} in symbol map, and couldn't get its containing crate"))?; + .ok_or_else(|| { + format!("Couldn't find section {section_name} in symbol map, and couldn't get its containing crate") + })?; - let mut matching_sections: Vec = containing_crate_ref.lock_as_ref().sections.values() - .filter_map(|sec| { - if sec.name.starts_with(section_name) { - Some(sec.clone()) - } else { - None - } - }) + let mut matching_sections: Vec = containing_crate_ref + .lock_as_ref() + .sections + .values() + .filter_map(|sec| if sec.name.starts_with(section_name) { Some(sec.clone()) } else { None }) .collect(); - if matching_sections.len() == 1 { + if matching_sections.len() == 1 { Ok(matching_sections.remove(0)) } else { - Err(matching_sections.into_iter().map(|sec| sec.name.clone()).collect::>().join("\n")) + Err(matching_sections + .into_iter() + .map(|sec| sec.name.clone()) + .collect::>() + .join("\n")) } } - fn get_my_current_namespace() -> Arc { task::with_current_task(|t| t.get_namespace().clone()) .or_else(|_| mod_mgmt::get_initial_kernel_namespace().cloned().ok_or(())) @@ -453,11 +485,9 @@ fn get_my_current_namespace() -> Arc { .unwrap() } - fn print_usage(opts: Options) { println!("{}", opts.usage(USAGE)); } - const USAGE: &str = "Usage: deps OPTION ARG Outputs runtime dependency information and metadata known by Theseus's crate manager."; diff --git a/applications/example/src/lib.rs b/applications/example/src/lib.rs index 23f9452f7a..ea310e0581 100644 --- a/applications/example/src/lib.rs +++ b/applications/example/src/lib.rs @@ -3,13 +3,16 @@ #![no_std] extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate getopts; -use alloc::vec::Vec; -use alloc::string::String; -use getopts::Options; +use alloc::{ + string::String, + vec::Vec, +}; +use getopts::Options; pub fn main(args: Vec) -> isize { let mut opts = Options::new(); @@ -22,7 +25,7 @@ pub fn main(args: Vec) -> isize { Err(_f) => { println!("{}", _f); print_usage(opts); - return -1; + return -1; } }; @@ -34,12 +37,9 @@ pub fn main(args: Vec) -> isize { 0 } - - fn print_usage(opts: Options) { println!("{}", opts.usage(USAGE)); } - const USAGE: &str = "Usage: example [ARGS] An example application that just echoes its arguments."; diff --git a/applications/hello/src/lib.rs b/applications/hello/src/lib.rs index c9a3bbbdb5..1986149b0d 100644 --- a/applications/hello/src/lib.rs +++ b/applications/hello/src/lib.rs @@ -2,11 +2,13 @@ // #![feature(plugin)] // #![plugin(application_main_fn)] - extern crate alloc; // #[macro_use] extern crate app_io; -use alloc::{string::String, vec::Vec}; +use alloc::{ + string::String, + vec::Vec, +}; pub fn main(_args: Vec) -> isize { log::info!("Hello, world! Args: {:?}", _args); diff --git a/applications/hull/src/builtin.rs b/applications/hull/src/builtin.rs index 33d559cb4b..839ddcbaf3 100644 --- a/applications/hull/src/builtin.rs +++ b/applications/hull/src/builtin.rs @@ -1,9 +1,15 @@ //! Builtin shell commands. -use crate::{Error, Result, Shell}; use alloc::string::ToString; + use app_io::println; +use crate::{ + Error, + Result, + Shell, +}; + // TODO: Decide which builtins we don't need. impl Shell { @@ -63,12 +69,7 @@ impl Shell { return Err(Error::Command(1)); } - let path = if let Some(arg) = args.first() { - *arg - } else { - "/" - } - .as_ref(); + let path = if let Some(arg) = args.first() { *arg } else { "/" }.as_ref(); let task = task::get_my_current_task().ok_or(Error::CurrentTaskUnavailable)?; diff --git a/applications/hull/src/error.rs b/applications/hull/src/error.rs index 2eb7003646..720d627b56 100644 --- a/applications/hull/src/error.rs +++ b/applications/hull/src/error.rs @@ -1,6 +1,7 @@ //! Shell-specific errors. use alloc::string::String; + use app_io::println; use task::RunState; diff --git a/applications/hull/src/job.rs b/applications/hull/src/job.rs index 078e485880..24f79b08bc 100644 --- a/applications/hull/src/job.rs +++ b/applications/hull/src/job.rs @@ -1,10 +1,20 @@ //! Shell job control. +use alloc::{ + string::String, + vec::Vec, +}; use core::fmt; -use crate::{Error, Result}; -use alloc::{string::String, vec::Vec}; -use task::{KillReason, TaskRef}; +use task::{ + KillReason, + TaskRef, +}; + +use crate::{ + Error, + Result, +}; /// A shell job consisting of multiple parts. /// @@ -89,9 +99,7 @@ impl core::cmp::PartialEq for State { fn eq(&self, other: &Self) -> bool { matches!( (self, other), - (State::Done(_), State::Done(_)) - | (State::Suspended, State::Suspended) - | (State::Running, State::Running) + (State::Done(_), State::Done(_)) | (State::Suspended, State::Suspended) | (State::Running, State::Running) ) } } diff --git a/applications/hull/src/lib.rs b/applications/hull/src/lib.rs index 70f59c7af8..bdbc3077e1 100644 --- a/applications/hull/src/lib.rs +++ b/applications/hull/src/lib.rs @@ -11,8 +11,8 @@ //! Terminology used in this file using `sleep 1 | sleep 2 & sleep 3` as an //! example: //! - A line is an entire line of user input i.e. `sleep 1 | sleep 2 & sleep 3`. -//! - A task is a subset of a line used to spawn an individual task i.e. `sleep -//! 1`, `sleep 2`, and `sleep 3`. +//! - A task is a subset of a line used to spawn an individual task i.e. `sleep 1`, `sleep 2`, and +//! `sleep 3`. //! - A job is a list of piped tasks i.e. `sleep 1 | sleep 2`, and `sleep 3`. //! - A command is the first word in a task i.e. `sleep`. //! - The arguments are any subsequent words in a task i.e. `1`, `2`, and `3`. @@ -28,24 +28,56 @@ mod job; mod parse; mod wrapper; -use crate::{ - job::{JobPart, State}, - parse::{ParsedJob, ParsedLine, ParsedTask}, +use alloc::{ + borrow::ToOwned, + format, + string::String, + sync::Arc, + vec::Vec, }; -use alloc::{borrow::ToOwned, format, string::String, sync::Arc, vec::Vec}; -use app_io::{println, IoStreams}; use core::fmt::Write; + +use app_io::{ + println, + IoStreams, +}; use hashbrown::HashMap; use job::Job; -use log::{error, warn}; -use noline::{builder::EditorBuilder, sync::embedded::IO as Io}; +use log::{ + error, + warn, +}; +use noline::{ + builder::EditorBuilder, + sync::embedded::IO as Io, +}; use path::PathBuf; use stdio::Stdio; use sync_block::Mutex; -use task::{ExitValue, KillReason}; -use tty::{Event, LineDiscipline}; +use task::{ + ExitValue, + KillReason, +}; +use tty::{ + Event, + LineDiscipline, +}; -pub use crate::error::{Error, Result}; +pub use crate::error::{ + Error, + Result, +}; +use crate::{ + job::{ + JobPart, + State, + }, + parse::{ + ParsedJob, + ParsedLine, + ParsedTask, + }, +}; pub fn main(_: Vec) -> isize { let mut shell = Shell { @@ -83,9 +115,7 @@ impl Shell { /// Configures the line discipline for use by applications. fn set_app_discipline(&self) -> AppDisciplineGuard { self.discipline.set_sane(); - AppDisciplineGuard { - discipline: self.discipline.clone(), - } + AppDisciplineGuard { discipline: self.discipline.clone() } } fn run(&mut self) -> Result<()> { @@ -149,12 +179,7 @@ impl Shell { } /// Executes a command. - fn execute_cmd( - &mut self, - parsed_job: ParsedJob, - job_str: &str, - current: bool, - ) -> Result> { + fn execute_cmd(&mut self, parsed_job: ParsedJob, job_str: &str, current: bool) -> Result> { let shell_streams = app_io::streams().unwrap(); let stderr = shell_streams.stderr; @@ -165,11 +190,7 @@ impl Shell { let mut jobs = self.jobs.lock(); let mut job_id = 1; - let mut temp_job = Job { - string: job_str.to_owned(), - parts: Vec::new(), - current, - }; + let mut temp_job = Job { string: job_str.to_owned(), parts: Vec::new(), current }; loop { match jobs.try_insert(job_id, temp_job) { Ok(_) => break, @@ -220,9 +241,7 @@ impl Shell { fn wait_on_job(&mut self, num: usize) -> Result<()> { let jobs = self.jobs.lock(); - let Some(job) = jobs.get(&num) else { - return Ok(()) - }; + let Some(job) = jobs.get(&num) else { return Ok(()) }; if !job.current { warn!("asked to wait on non-current job"); return Ok(()); @@ -251,11 +270,11 @@ impl Shell { if let Some(job) = jobs.get_mut(&num) && let Some(exit_value) = job.exit_value() { - jobs.remove(&num); - return match exit_value { - 0 => Ok(()), - _ => Err(Error::Command(exit_value)), - }; + jobs.remove(&num); + return match exit_value { + 0 => Ok(()), + _ => Err(Error::Command(exit_value)), + }; } } scheduler::schedule(); @@ -288,13 +307,7 @@ impl Shell { }) } - fn resolve_external( - &self, - cmd: &str, - args: Vec<&str>, - streams: IoStreams, - job_id: usize, - ) -> Result { + fn resolve_external(&self, cmd: &str, args: Vec<&str>, streams: IoStreams, job_id: usize) -> Result { let namespace_dir = task::get_my_current_task() .map(|t| t.get_namespace().dir().clone()) .expect("couldn't get namespace dir"); @@ -367,10 +380,7 @@ impl Shell { .spawn() .map_err(Error::SpawnFailed)?; - Ok(JobPart { - state: State::Running, - task: task_ref, - }) + Ok(JobPart { state: State::Running, task: task_ref }) } } @@ -386,20 +396,15 @@ impl Drop for AppDisciplineGuard { #[cfg(test)] mod tests { - use super::*; use alloc::vec; + use super::*; + #[test] fn test_split_pipes() { assert_eq!( split_pipes("a b c |d e f|g | h | i j"), - vec![ - ("a", vec!["b", "c"]), - ("d", vec!["e", "f"]), - ("g", vec![]), - ("h", vec![]), - ("i", vec!["j"]) - ] + vec![("a", vec!["b", "c"]), ("d", vec!["e", "f"]), ("g", vec![]), ("h", vec![]), ("i", vec!["j"])] ); } diff --git a/applications/hull/src/parse.rs b/applications/hull/src/parse.rs index 3ffeb43719..ad7b25d1a4 100644 --- a/applications/hull/src/parse.rs +++ b/applications/hull/src/parse.rs @@ -28,11 +28,7 @@ impl<'a> From<&'a str> for ParsedLine<'a> { // Iterator contains at least one element. let last = iter.next_back().unwrap(); let trimmed = last.trim(); - let foreground = if trimmed.is_empty() { - None - } else { - Some((last, parse_job(trimmed))) - }; + let foreground = if trimmed.is_empty() { None } else { Some((last, parse_job(trimmed))) }; ParsedLine { background: iter @@ -68,9 +64,6 @@ fn parse_task(task: &str) -> ParsedTask { let args = args_str.split(' ').collect(); ParsedTask { command, args } } else { - ParsedTask { - command: task, - args: Vec::new(), - } + ParsedTask { command: task, args: Vec::new() } } } diff --git a/applications/hull/src/wrapper.rs b/applications/hull/src/wrapper.rs index 79668fda6b..5c6614b88f 100644 --- a/applications/hull/src/wrapper.rs +++ b/applications/hull/src/wrapper.rs @@ -1,7 +1,11 @@ //! Allows stdio to be used with `noline`. use alloc::sync::Arc; -use app_io::{ImmutableRead, ImmutableWrite}; + +use app_io::{ + ImmutableRead, + ImmutableWrite, +}; use core2::io; use embedded_hal::serial; @@ -16,10 +20,7 @@ impl serial::Read for Wrapper { fn read(&mut self) -> nb::Result { let mut buf = [0; 1]; match self.stdin.read(&mut buf)? { - 0 => Err(nb::Error::Other(io::Error::new( - io::ErrorKind::Other, - "read zero", - ))), + 0 => Err(nb::Error::Other(io::Error::new(io::ErrorKind::Other, "read zero"))), _ => Ok(buf[0]), } } diff --git a/applications/kill/src/lib.rs b/applications/kill/src/lib.rs index 11cdee2271..36a51e5e5a 100755 --- a/applications/kill/src/lib.rs +++ b/applications/kill/src/lib.rs @@ -1,28 +1,37 @@ #![no_std] extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; // #[macro_use] extern crate debugit; -extern crate task; extern crate getopts; +extern crate task; + +use alloc::{ + string::{ + String, + ToString, + }, + vec::Vec, +}; use getopts::Options; -use alloc::vec::Vec; -use alloc::string::{String, ToString}; pub fn main(args: Vec) -> isize { let mut opts = Options::new(); - + opts.optflag("h", "help", "print this help menu"); - opts.optflag("r", "reap", - "reap the task (consume its exit value) in addition to killing it, removing it from the task list." + opts.optflag( + "r", + "reap", + "reap the task (consume its exit value) in addition to killing it, removing it from the task list.", ); let matches = match opts.parse(args) { Ok(m) => m, Err(_f) => { println!("{}", _f); - return -1; + return -1; } }; @@ -47,12 +56,12 @@ pub fn main(args: Vec) -> isize { return -1; } } - }, - _ => { - println!("Invalid argument {}, not a valid task ID (usize)", task_id_str); + }, + _ => { + println!("Invalid argument {}, not a valid task ID (usize)", task_id_str); return -1; }, - }; + }; } 0 } @@ -62,12 +71,12 @@ fn kill_task(task_id: usize, reap: bool) -> Result<(), String> { if let Some(task_ref) = task::get_task(task_id) { if task_ref.kill(task::KillReason::Requested) .and_then(|_| runqueue::remove_task_from_all(&task_ref)) - .is_ok() + .is_ok() { println!("Killed task {}", &*task_ref); if reap { match task_ref.take_exit_value() { - Some(exit_val) => { + Some(exit_val) => { println!("Reaped task {}, got exit value {}", task_id, debugit!(exit_val)); Ok(()) } @@ -75,7 +84,7 @@ fn kill_task(task_id: usize, reap: bool) -> Result<(), String> { Err(format!("Failed to reap task {}", task_id)) } } - } + } else { // killed the task successfully, no reap request, so return success. Ok(()) @@ -110,4 +119,3 @@ fn print_usage(opts: Options) -> isize { println!("{}", opts.usage(&brief)); 0 } - diff --git a/applications/less/src/lib.rs b/applications/less/src/lib.rs index aea7f9cc65..e9bdf465b7 100644 --- a/applications/less/src/lib.rs +++ b/applications/less/src/lib.rs @@ -19,8 +19,8 @@ extern crate alloc; // use keycodes_ascii::{Keycode, KeyAction}; // use core::str; use alloc::{ - vec::Vec, string::String, + vec::Vec, }; // use getopts::Options; // use path::Path; @@ -45,30 +45,24 @@ use alloc::{ // return Err("failed to get current task".to_string()); // }; // let path = Path::new(file_path); - + // // navigate to the filepath specified by first argument // match path.get(&curr_wd) { -// Some(file_dir_enum) => { +// Some(file_dir_enum) => { // match file_dir_enum { // FileOrDir::Dir(directory) => { -// Err(format!("{:?} is a directory, cannot 'less' non-files.", directory.lock().get_name())) -// } +// Err(format!("{:?} is a directory, cannot 'less' non-files.", +// directory.lock().get_name())) } // FileOrDir::File(file) => { // let mut file_locked = file.lock(); // let file_size = file_locked.len(); // let mut string_slice_as_bytes = vec![0; file_size]; -// let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, 0) { -// Ok(num) => num, -// Err(e) => { -// return Err(format!("Failed to read {:?}, error {:?}", -// file_locked.get_name(), e)) -// } -// }; -// let read_string = match str::from_utf8(&string_slice_as_bytes) { -// Ok(string_slice) => string_slice, -// Err(utf8_err) => { -// return Err(format!("File {:?} was not a printable UTF-8 text file: {}", -// file_locked.get_name(), utf8_err)) +// let _num_bytes_read = match file_locked.read_at(&mut string_slice_as_bytes, +// 0) { Ok(num) => num, Err(e) => { return Err(format!("Failed to read {:?}, +// error {:?}", file_locked.get_name(), e)) } }; let read_string = match +// str::from_utf8(&string_slice_as_bytes) { Ok(string_slice) => string_slice, Err(utf8_err) => { +// return Err(format!("File {:?} was not a printable UTF-8 text file: +// {}", file_locked.get_name(), utf8_err)) // } // }; // Ok(read_string.to_string()) @@ -81,14 +75,14 @@ use alloc::{ // } // } -// /// This function parses the text file. It scans through the whole file and records the string slice -// /// for each line. This function has full UTF-8 support, which means that the case where a single character -// /// occupies multiple bytes are well considered. The slice index returned by this function is guaranteed -// /// not to cause panic. +// /// This function parses the text file. It scans through the whole file and records the string +// slice /// for each line. This function has full UTF-8 support, which means that the case where a +// single character /// occupies multiple bytes are well considered. The slice index returned by +// this function is guaranteed /// not to cause panic. // fn parse_content(content: &String) -> Result, &'static str> { // // Get the width and height of the terminal screen. -// let (width, _height) = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` app")? -// .lock() +// let (width, _height) = app_io::get_my_terminal().ok_or("couldn't get terminal for `less` +// app")? .lock() // .get_text_dimensions(); // // Record the slice index of each line. @@ -99,13 +93,13 @@ use alloc::{ // let mut char_num_in_line: usize = 0; // // Starting index in the String of the current line. // let mut line_start_idx: usize = 0; -// // The previous character during the iteration. Set '\0' as the initial value since we don't expect -// // to encounter this character in the beginning of the file. +// // The previous character during the iteration. Set '\0' as the initial value since we don't +// expect // to encounter this character in the beginning of the file. // let mut previous_char: char = '\0'; // // Iterate through the whole file. -// // `c` is the current character. `str_idx` is the index of the first byte of the current character. -// for (str_idx, c) in content.char_indices() { +// // `c` is the current character. `str_idx` is the index of the first byte of the current +// character. for (str_idx, c) in content.char_indices() { // // When we need to begin a new line, record the previous line in the map. // if char_num_in_line == width || previous_char == '\n' { // map.insert(cur_line_num, LineSlice{ start: line_start_idx, end: str_idx }); @@ -199,9 +193,7 @@ use alloc::{ // } // } - pub fn main(_args: Vec) -> isize { - // // Get stdout. // let stdout = match app_io::stdout() { // Ok(stdout) => stdout, diff --git a/applications/loadc/src/lib.rs b/applications/loadc/src/lib.rs index e715705f7a..663f1850bc 100644 --- a/applications/loadc/src/lib.rs +++ b/applications/loadc/src/lib.rs @@ -1,37 +1,70 @@ //! An application that loads C language ELF executables atop Theseus. //! -//! This will be integrated into the Theseus kernel in the future, +//! This will be integrated into the Theseus kernel in the future, //! likely as a separate crate that integrates well with the `mod_mgmt` crate. #![no_std] extern crate alloc; -#[macro_use] extern crate log; -#[macro_use] extern crate app_io; -extern crate getopts; +#[macro_use] +extern crate log; +#[macro_use] +extern crate app_io; extern crate fs_node; -extern crate path; +extern crate getopts; +extern crate libc; extern crate memory; -extern crate rustc_demangle; extern crate mod_mgmt; +extern crate path; +extern crate rustc_demangle; extern crate task; -extern crate xmas_elf; -extern crate libc; // for basic C types/typedefs used in libc - +extern crate xmas_elf; // for basic C types/typedefs used in libc + +use alloc::{ + collections::BTreeSet, + string::{ + String, + ToString, + }, + sync::Arc, + vec::Vec, +}; use core::{ - cmp::{min, max}, - ops::{AddAssign, SubAssign, Range}, + cmp::{ + max, + min, + }, + ops::{ + AddAssign, + Range, + SubAssign, + }, +}; + +use getopts::{ + Matches, + Options, +}; +use memory::{ + MappedPages, + Page, + PteFlags, + PteFlagsArch, + VirtualAddress, +}; +use mod_mgmt::{ + find_symbol_table, + write_relocation, + CrateNamespace, + RelocationEntry, + StrongDependency, }; -use alloc::{collections::BTreeSet, string::{String, ToString}, sync::Arc, vec::Vec}; -use getopts::{Matches, Options}; -use memory::{Page, MappedPages, VirtualAddress, PteFlagsArch, PteFlags}; -use mod_mgmt::{CrateNamespace, StrongDependency, find_symbol_table, RelocationEntry, write_relocation}; use path::Path; use rustc_demangle::demangle; use xmas_elf::{ - ElfFile, program::SegmentData, sections::ShType, + ElfFile, }; pub fn main(args: Vec) -> isize { @@ -43,7 +76,7 @@ pub fn main(args: Vec) -> isize { Err(_f) => { println!("{}", _f); print_usage(opts); - return -1; + return -1; } }; @@ -57,22 +90,22 @@ pub fn main(args: Vec) -> isize { Err(e) => { println!("Error:\n{}", e); -1 - } + } } } - fn rmain(matches: Matches) -> Result { - let (curr_wd, namespace, mmi) = task::with_current_task(|curr_task| - ( - curr_task.get_env().lock().working_dir.clone(), - curr_task.get_namespace().clone(), - curr_task.mmi.clone(), - ) - ).map_err(|_| String::from("failed to get current task"))?; - - let path = matches.free.first().ok_or_else(|| "Missing path to ELF executable".to_string())?; - let file_ref = Path::new(path).get_file(&curr_wd) + let (curr_wd, namespace, mmi) = task::with_current_task(|curr_task| { + (curr_task.get_env().lock().working_dir.clone(), curr_task.get_namespace().clone(), curr_task.mmi.clone()) + }) + .map_err(|_| String::from("failed to get current task"))?; + + let path = matches + .free + .first() + .ok_or_else(|| "Missing path to ELF executable".to_string())?; + let file_ref = Path::new(path) + .get_file(&curr_wd) .ok_or_else(|| format!("Failed to access file at {path:?}"))?; let file = file_ref.lock(); @@ -82,14 +115,16 @@ fn rmain(matches: Matches) -> Result { let (mut segments, entry_point, _vaddr_offset, elf_file) = parse_and_load_elf_executable(byte_slice)?; debug!("Parsed ELF executable, moving on to overwriting relocations."); - - // Now, overwrite (recalculate) the relocations that refer to symbols that already exist in Theseus, - // most important of which are static data sections, - // as it is logically incorrect to have duplicates of data that are supposed to be global system-wide singletons. - // We should throw a warning here if there are no relocations in the file, as it was probably built/linked with the wrong arguments. + + // Now, overwrite (recalculate) the relocations that refer to symbols that already exist in + // Theseus, most important of which are static data sections, + // as it is logically incorrect to have duplicates of data that are supposed to be global + // system-wide singletons. We should throw a warning here if there are no relocations in the + // file, as it was probably built/linked with the wrong arguments. overwrite_relocations(&namespace, &mut segments, &elf_file, &mmi, false)?; - // Remap each segment's mapped pages using the correct flags; they were previously mapped as always writable. + // Remap each segment's mapped pages using the correct flags; they were previously mapped as + // always writable. { let page_table = &mut mmi.lock().page_table; for segment in segments.iter_mut() { @@ -99,10 +134,12 @@ fn rmain(matches: Matches) -> Result { } } - segments.iter().enumerate().for_each(|(i, seg)| debug!("Segment {} needed {} relocations to be rewritten.", i, seg.sections_i_depend_on.len()) ); + segments.iter().enumerate().for_each(|(i, seg)| { + debug!("Segment {} needed {} relocations to be rewritten.", i, seg.sections_i_depend_on.len()) + }); let _executable = LoadedExecutable { segments, entry_point }; // must persist through the entire executable's runtime. - + debug!("Jumping to entry point {:#X}", entry_point); let dummy_args = ["hello", "world"]; @@ -121,26 +158,24 @@ fn rmain(matches: Matches) -> Result { use libc::c_int; type StartFunction = fn(args: &[&str], env: &[&str]) -> c_int; - #[allow(unused)] struct LoadedExecutable { segments: Vec, entry_point: VirtualAddress, } - -/// Represents an ELF program segment that has been loaded into memory. +/// Represents an ELF program segment that has been loaded into memory. #[derive(Debug)] #[allow(dead_code)] pub struct LoadedSegment { /// The memory region allocated to hold this program segment. mp: MappedPages, - /// The specific range of virtual addresses occupied by this + /// The specific range of virtual addresses occupied by this /// (may be a subset) bounds: Range, /// The proper flags for this segment specified by the ELF file. flags: PteFlagsArch, - /// The indices of the sections in the ELF file + /// The indices of the sections in the ELF file /// that were grouped ("mapped") into this segment by the linker. section_ndxs: BTreeSet, /// The list of sections in existing Theseus crates that this segment's sections depends on, @@ -157,42 +192,38 @@ impl Offset { /// Returns a new `Offset` object that represents the adjustment /// needed to go from `first` to `second`. fn new(first: usize, second: usize) -> Offset { - if first < second { - Offset::Negative(second - first) - } else { - Offset::Positive(first - second) - } + if first < second { Offset::Negative(second - first) } else { Offset::Positive(first - second) } } /// Mutably adjusts the given `obj` by the given `offset`. fn adjust_assign + SubAssign>(obj: &mut T, offset: Offset) { match offset { Offset::Negative(subtrahend) => *obj -= subtrahend, - Offset::Positive(addend) => *obj += addend, + Offset::Positive(addend) => *obj += addend, } } } - /// Parses an elf executable file from the given slice of bytes and load it into memory. /// /// # Important note about memory mappings /// This function will allocate new memory regions to store each program segment /// and copy each segment's data into them. -/// When this function returns, those segments will be mapped as writable in order to allow them +/// When this function returns, those segments will be mapped as writable in order to allow them /// to be modified as needed. /// Before running this executable, each segment's `MappedPages` should be remapped -/// to the proper `flags` specified in its `LoadedSegment.flags` field. +/// to the proper `flags` specified in its `LoadedSegment.flags` field. /// /// # Return /// Returns a tuple of: -/// 1. A list of program segments mapped into memory. -/// 2. The virtual address of the executable's entry point, e.g., the `_start` function. -/// This is the function that we should call to start running the executable. -/// 3. The `Offset` by which all virtual addresses in the loaded executable should be shifted by. -/// This is the difference between where the program is *actually* loaded in memory -/// and where the program *expected* to be loaded into memory. -/// 4. A reference to the parsed `ElfFile`, whose lifetime is tied to the given `file_contents` parameter. +/// 1. A list of program segments mapped into memory. +/// 2. The virtual address of the executable's entry point, e.g., the `_start` function. This is the +/// function that we should call to start running the executable. +/// 3. The `Offset` by which all virtual addresses in the loaded executable should be shifted by. +/// This is the difference between where the program is *actually* loaded in memory and where the +/// program *expected* to be loaded into memory. +/// 4. A reference to the parsed `ElfFile`, whose lifetime is tied to the given `file_contents` +/// parameter. fn parse_and_load_elf_executable( file_contents: &[u8], ) -> Result<(Vec, VirtualAddress, Offset, ElfFile), String> { @@ -200,7 +231,7 @@ fn parse_and_load_elf_executable( let elf_file = ElfFile::new(file_contents).map_err(String::from)?; - // check that elf_file is an executable type + // check that elf_file is an executable type let typ = elf_file.header.pt2.type_().as_type(); if typ != xmas_elf::header::Type::Executable { error!("parse_elf_executable(): ELF file has wrong type {:?}, must be an Executable Elf File!", typ); @@ -208,21 +239,25 @@ fn parse_and_load_elf_executable( } // Currently we aren't building C programs in a position-independent manner, - // so we have to load the C executable at the exact virtual address it specifies (since it's non-relocatable). - - // TODO FIXME: remove this old approach of invalidly loading non-PIE executables at other virtual addresses than what they expect. - // Also remove the whole idea of the "Offset", since that will be built into position-independent executables. - // This is because this only works for SUPER SIMPLE C programs, in which we can just maintain the *relative* position of each segment - // in memory with respect to other segments to ensure they're consistent. - // - // Not really necessary to do this, but we iterate over all segments first to find the total range of virtual pages we must allocate. + // so we have to load the C executable at the exact virtual address it specifies (since it's + // non-relocatable). + + // TODO FIXME: remove this old approach of invalidly loading non-PIE executables at other + // virtual addresses than what they expect. Also remove the whole idea of the + // "Offset", since that will be built into position-independent executables. + // This is because this only works for SUPER SIMPLE C programs, in which we can just maintain + // the *relative* position of each segment in memory with respect to other + // segments to ensure they're consistent. + // + // Not really necessary to do this, but we iterate over all segments first to find the total + // range of virtual pages we must allocate. let (mut start_vaddr, mut end_vaddr) = (usize::MAX, usize::MIN); let mut num_segments = 0; for prog_hdr in elf_file.program_iter() { if prog_hdr.get_type() == Ok(xmas_elf::program::Type::Load) { num_segments += 1; start_vaddr = min(start_vaddr, prog_hdr.virtual_addr() as usize); - end_vaddr = max(end_vaddr, prog_hdr.virtual_addr() as usize + prog_hdr.mem_size() as usize); + end_vaddr = max(end_vaddr, prog_hdr.virtual_addr() as usize + prog_hdr.mem_size() as usize); } } @@ -231,10 +266,12 @@ fn parse_and_load_elf_executable( // Allocate enough virtually-contiguous space for all the segments together. let total_size_in_bytes = end_vaddr - start_vaddr; let mut all_pages = memory::allocate_pages_by_bytes_at( - VirtualAddress::new(start_vaddr).ok_or_else(|| format!("Segment had invalid virtual address {start_vaddr:#X}"))?, - total_size_in_bytes - ).map_err(|_| format!("Failed to allocate {total_size_in_bytes} bytes at {start_vaddr}"))?; - let vaddr_adjustment = Offset::new(all_pages.start_address().value(), start_vaddr); + VirtualAddress::new(start_vaddr) + .ok_or_else(|| format!("Segment had invalid virtual address {start_vaddr:#X}"))?, + total_size_in_bytes, + ) + .map_err(|_| format!("Failed to allocate {total_size_in_bytes} bytes at {start_vaddr}"))?; + let vaddr_adjustment = Offset::new(all_pages.start_address().value(), start_vaddr); // Iterate through each segment again and map them into pages we just allocated above, // copying their segment data to the proper location. @@ -245,18 +282,19 @@ fn parse_and_load_elf_executable( continue; } - // A segment (program header) has two sizes: - // 1) memory size: the size in memory that the segment, when loaded, will actually consume. - // This is how much virtual memory space we have to allocate for it. - // 2) file size: the size of the segment's actual data from the ELF file itself. - // This is how much data we will actually copy from the file's segment into our allocated memory. - // The difference is primarily due to .bss sections, in which the file size will be less than the memory size. - // If memory size > file size, the difference should be filled with zeros. + // A segment (program header) has two sizes: + // 1) memory size: the size in memory that the segment, when loaded, will actually consume. This is + // how much virtual memory space we have to allocate for it. + // 2) file size: the size of the segment's actual data from the ELF file itself. This is how much + // data we will actually copy from the file's segment into our allocated memory. + // The difference is primarily due to .bss sections, in which the file size will be less + // than the memory size. If memory size > file size, the difference should be filled + // with zeros. let memory_size_in_bytes = prog_hdr.mem_size() as usize; let file_size_in_bytes = prog_hdr.file_size() as usize; if memory_size_in_bytes == 0 { // warn!("Skipping zero-sized LOAD segment {:?}", prog_hdr); - continue; + continue; } let mut start_vaddr = VirtualAddress::new(prog_hdr.virtual_addr() as usize).ok_or_else(|| { @@ -268,32 +306,39 @@ fn parse_and_load_elf_executable( // debug!("Splitting {:?} after end page {:?}", all_pages, end_page); - let (this_ap, remaining_pages) = all_pages.split(end_page + 1).map_err(|_ap| - format!("Failed to split allocated pages {_ap:?} at page {start_vaddr:#X}") - )?; + let (this_ap, remaining_pages) = all_pages + .split(end_page + 1) + .map_err(|_ap| format!("Failed to split allocated pages {_ap:?} at page {start_vaddr:#X}"))?; all_pages = remaining_pages; // debug!("Successfully split pages into {:?} and {:?}", this_ap, all_pages); - // debug!("Adjusted segment vaddr: {:#X}, size: {:#X}, {:?}", start_vaddr, memory_size_in_bytes, this_ap.start_address()); + // debug!("Adjusted segment vaddr: {:#X}, size: {:#X}, {:?}", start_vaddr, + // memory_size_in_bytes, this_ap.start_address()); let initial_flags = convert_to_pte_flags(prog_hdr.flags()); let mmi = task::with_current_task(|t| t.mmi.clone()).unwrap(); - // Must initially map the memory as writable so we can copy the segment data to it later. - let mut mp = mmi.lock().page_table + // Must initially map the memory as writable so we can copy the segment data to it later. + let mut mp = mmi + .lock() + .page_table .map_allocated_pages(this_ap, initial_flags.writable(true)) .map_err(String::from)?; // Copy data from this section into the correct offset into our newly-mapped pages - let offset_into_mp = mp.offset_of_address(start_vaddr).ok_or_else(|| - format!("BUG: destination address {start_vaddr:#X} wasn't within segment's {mp:?}") - )?; + let offset_into_mp = mp + .offset_of_address(start_vaddr) + .ok_or_else(|| format!("BUG: destination address {start_vaddr:#X} wasn't within segment's {mp:?}"))?; match prog_hdr.get_data(&elf_file).map_err(String::from)? { SegmentData::Undefined(segment_data) => { // debug!("Segment had undefined data of {} ({:#X}) bytes, file size {} ({:#X})", - // segment_data.len(), segment_data.len(), file_size_in_bytes, file_size_in_bytes); - let dest_slice: &mut [u8] = mp.as_slice_mut(offset_into_mp, memory_size_in_bytes).map_err(String::from)?; + // segment_data.len(), segment_data.len(), file_size_in_bytes, + // file_size_in_bytes); + let dest_slice: &mut [u8] = mp + .as_slice_mut(offset_into_mp, memory_size_in_bytes) + .map_err(String::from)?; dest_slice[..file_size_in_bytes].copy_from_slice(&segment_data[..file_size_in_bytes]); if memory_size_in_bytes > file_size_in_bytes { - // debug!(" Zero-filling extra bytes for segment from range [{}:{}).", file_size_in_bytes, dest_slice.len()); + // debug!(" Zero-filling extra bytes for segment from range [{}:{}).", + // file_size_in_bytes, dest_slice.len()); dest_slice[file_size_in_bytes..].fill(0); } } @@ -302,7 +347,7 @@ fn parse_and_load_elf_executable( } }; - let segment_bounds = start_vaddr .. (start_vaddr + memory_size_in_bytes); + let segment_bounds = start_vaddr..(start_vaddr + memory_size_in_bytes); // Populate the set of sections that comprise this segment. let mut section_ndxs = BTreeSet::new(); @@ -319,7 +364,8 @@ fn parse_and_load_elf_executable( bounds: segment_bounds, flags: initial_flags.into(), section_ndxs, - sections_i_depend_on: Vec::new(), // this is populated later in `overwrite_relocations()` + sections_i_depend_on: Vec::new(), /* this is populated later in + * `overwrite_relocations()` */ }); } @@ -332,41 +378,46 @@ fn parse_and_load_elf_executable( Ok((mapped_segments, entry_point_vaddr, vaddr_adjustment, elf_file)) } - - -/// This function uses the relocation sections in the given `ElfFile` to -/// rewrite relocations that depend on source sections already existing and currently loaded in Theseus. +/// This function uses the relocation sections in the given `ElfFile` to +/// rewrite relocations that depend on source sections already existing and currently loaded in +/// Theseus. /// -/// This is necessary to ensure that the newly-loaded ELF executable depends on and references -/// the real singleton instances of each data sections (aka `OBJECT`s in ELF terminology) -/// rather than using the duplicate instance of those data sections in the executable itself. +/// This is necessary to ensure that the newly-loaded ELF executable depends on and references +/// the real singleton instances of each data sections (aka `OBJECT`s in ELF terminology) +/// rather than using the duplicate instance of those data sections in the executable itself. fn overwrite_relocations( namespace: &Arc, segments: &mut [LoadedSegment], elf_file: &ElfFile, mmi: &memory::MmiRef, - verbose_log: bool + verbose_log: bool, ) -> Result<(), String> { let symtab = find_symbol_table(elf_file)?; // Fix up the sections that were just loaded, using proper relocation info. // Iterate over every non-zero relocation section in the file - for sec in elf_file.section_iter().filter(|sec| sec.get_type() == Ok(ShType::Rela) && sec.size() != 0) { + for sec in elf_file + .section_iter() + .filter(|sec| sec.get_type() == Ok(ShType::Rela) && sec.size() != 0) + { use xmas_elf::sections::SectionData::Rela64; - if verbose_log { - trace!("Found Rela section name: {:?}, type: {:?}, target_sec_index: {:?}", - sec.get_name(elf_file), sec.get_type(), sec.info() - ); + if verbose_log { + trace!( + "Found Rela section name: {:?}, type: {:?}, target_sec_index: {:?}", + sec.get_name(elf_file), + sec.get_type(), + sec.info() + ); } let rela_sec_name = sec.get_name(elf_file).unwrap(); - // Skip debug special sections for now, those can be processed later. - if rela_sec_name.starts_with(".rela.debug") { + // Skip debug special sections for now, those can be processed later. + if rela_sec_name.starts_with(".rela.debug") { continue; } // Skip .eh_frame relocations, since they are all local to the .text section // and cannot depend on external symbols directly - if rela_sec_name == ".rela.eh_frame" { + if rela_sec_name == ".rela.eh_frame" { continue; } @@ -376,84 +427,116 @@ fn overwrite_relocations( let err = format!("Found Rela section that wasn't able to be parsed as Rela64: {sec:?}"); error!("{}", err); return Err(err); - } + } }; // The target section (segment) is where we write the relocation data to. - // The source section is where we get the data from. - // There is one target section per rela section (`rela_array`), and one source section per `rela_entry` in each `rela_array`. - // The "info" field in the Rela section specifies which section is the target of the relocation. - + // The source section is where we get the data from. + // There is one target section per rela section (`rela_array`), and one source section per + // `rela_entry` in each `rela_array`. The "info" field in the Rela section specifies + // which section is the target of the relocation. + // Get the target section (that we already loaded) for this rela_array Rela section. let target_sec_shndx = sec.info() as usize; - let target_segment = segments.iter_mut() + let target_segment = segments + .iter_mut() .find(|seg| seg.section_ndxs.contains(&target_sec_shndx)) .ok_or_else(|| { - let err = format!("ELF file error: couldn't find loaded segment that contained section for Rela section {:?}!", sec.get_name(elf_file)); + let err = format!( + "ELF file error: couldn't find loaded segment that contained section for Rela section {:?}!", + sec.get_name(elf_file) + ); error!("{}", err); err })?; - + let mut target_segment_dependencies: Vec = Vec::new(); let target_segment_start_addr = target_segment.bounds.start; - let target_segment_slice: &mut [u8] = target_segment.mp.as_slice_mut( - 0, - target_segment.bounds.end.value() - target_segment.bounds.start.value(), - )?; + let target_segment_slice: &mut [u8] = target_segment + .mp + .as_slice_mut(0, target_segment.bounds.end.value() - target_segment.bounds.start.value())?; // iterate through each relocation entry in the relocation array for the target_sec for rela_entry in rela_array { - use xmas_elf::symbol_table::{Type, Entry}; + use xmas_elf::symbol_table::{ + Entry, + Type, + }; let source_sec_entry = &symtab[rela_entry.get_symbol_table_index() as usize]; - // Ignore relocations that refer/point to irrelevant things: sections, files, notypes, or nothing. + // Ignore relocations that refer/point to irrelevant things: sections, files, notypes, + // or nothing. match source_sec_entry.get_type() { Err(_) | Ok(Type::NoType) | Ok(Type::Section) | Ok(Type::File) => continue, - _ => { } // keep going to process the relocation + _ => {} // keep going to process the relocation } if verbose_log { - trace!(" Rela64 entry has offset: {:#X}, addend: {:#X}, symtab_index: {}, type: {:#X}", - rela_entry.get_offset(), rela_entry.get_addend(), rela_entry.get_symbol_table_index(), rela_entry.get_type()); + trace!( + " Rela64 entry has offset: {:#X}, addend: {:#X}, symtab_index: {}, type: {:#X}", + rela_entry.get_offset(), + rela_entry.get_addend(), + rela_entry.get_symbol_table_index(), + rela_entry.get_type() + ); } - let source_sec_shndx = source_sec_entry.shndx() as usize; + let source_sec_shndx = source_sec_entry.shndx() as usize; let source_sec_name = match source_sec_entry.get_name(elf_file) { Ok(name) => name, _ => continue, }; - if verbose_log { - let source_sec_header_name = source_sec_entry.get_section_header(elf_file, rela_entry.get_symbol_table_index() as usize) + if verbose_log { + let source_sec_header_name = source_sec_entry + .get_section_header(elf_file, rela_entry.get_symbol_table_index() as usize) .and_then(|s| s.get_name(elf_file)); - trace!(" --> Points to relevant section [{}]: {:?}", source_sec_shndx, source_sec_header_name); - trace!(" Entry name {} {:?} vis {:?} bind {:?} type {:?} shndx {} value {} size {}", - source_sec_entry.name(), source_sec_entry.get_name(elf_file), - source_sec_entry.get_other(), source_sec_entry.get_binding(), source_sec_entry.get_type(), - source_sec_entry.shndx(), source_sec_entry.value(), source_sec_entry.size()); + trace!( + " --> Points to relevant section [{}]: {:?}", + source_sec_shndx, + source_sec_header_name + ); + trace!( + " Entry name {} {:?} vis {:?} bind {:?} type {:?} shndx {} value {} size {}", + source_sec_entry.name(), + source_sec_entry.get_name(elf_file), + source_sec_entry.get_other(), + source_sec_entry.get_binding(), + source_sec_entry.get_type(), + source_sec_entry.shndx(), + source_sec_entry.value(), + source_sec_entry.size() + ); } let demangled = demangle(source_sec_name).to_string(); - // If the source section exists in this namespace already, rewrite the relocation entry to point to the existing section instead. - if let Some(existing_source_sec) = namespace.get_symbol_or_load(&demangled, None, mmi, verbose_log).upgrade() { + // If the source section exists in this namespace already, rewrite the relocation entry + // to point to the existing section instead. + if let Some(existing_source_sec) = namespace + .get_symbol_or_load(&demangled, None, mmi, verbose_log) + .upgrade() + { let mut relocation_entry = RelocationEntry::from_elf_relocation(rela_entry); let original_relocation_offset = relocation_entry.offset; - - // Here, in an executable ELF file, the relocation entry's "offset" represents an absolute virtual address - // rather than an offset from the beginning of the section/segment (I think). - // Therefore, we need to adjust that value before we invoke `write_relocation()`, - // which expects a regular `offset` + an offset into the target segment's mapped pages. - let relocation_offset_as_vaddr = VirtualAddress::new(relocation_entry.offset).ok_or_else(|| + + // Here, in an executable ELF file, the relocation entry's "offset" represents an + // absolute virtual address rather than an offset from the beginning + // of the section/segment (I think). Therefore, we need to adjust + // that value before we invoke `write_relocation()`, which expects a + // regular `offset` + an offset into the target segment's mapped pages. + let relocation_offset_as_vaddr = VirtualAddress::new(relocation_entry.offset).ok_or_else(|| { format!("relocation_entry.offset {:#X} was not a valid virtual address", relocation_entry.offset) - )?; + })?; let offset_into_target_segment = relocation_offset_as_vaddr.value() - target_segment_start_addr.value(); - // Now that we have incorporated the relocation_entry's actual offset into the target_segment offset, - // we set it to zero for the duration of this call. - // TODO: this is hacky as hell, we should just create a new `write_relocation()` function instead. + // Now that we have incorporated the relocation_entry's actual offset into the + // target_segment offset, we set it to zero for the duration of this + // call. TODO: this is hacky as hell, we should just create a new + // `write_relocation()` function instead. relocation_entry.offset = 0; - if verbose_log { - debug!(" Performing relocation target {:#X} + {:#X} <-- source {}", + if verbose_log { + debug!( + " Performing relocation target {:#X} + {:#X} <-- source {}", target_segment_start_addr, offset_into_target_segment, existing_source_sec.name ); } @@ -462,34 +545,36 @@ fn overwrite_relocations( target_segment_slice, offset_into_target_segment, existing_source_sec.virt_addr, - verbose_log + verbose_log, )?; relocation_entry.offset = original_relocation_offset; - - // Here, we typically tell the existing_source_sec that the target_segment is dependent upon it. - // However, the `WeakDependent` entry type only accepts a weak section reference at the moment, - // and we don't have that -- we only have a target segment. - // TODO: if/when we wish to track weak dependencies from a section to a target segment, we should add it below. + + // Here, we typically tell the existing_source_sec that the target_segment is + // dependent upon it. However, the `WeakDependent` entry type only + // accepts a weak section reference at the moment, and we don't have + // that -- we only have a target segment. TODO: if/when we wish to + // track weak dependencies from a section to a target segment, we should add it + // below. // // let weak_dep = WeakDependent { // section: Arc::downgrade(&target_sec), // relocation: relocation_entry, // }; // existing_source_sec.inner.write().sections_dependent_on_me.push(weak_dep); - + // tell the target_sec that it has a strong dependency on the existing_source_sec - let strong_dep = StrongDependency { - section: Arc::clone(&existing_source_sec), - relocation: relocation_entry, - }; - target_segment_dependencies.push(strong_dep); + let strong_dep = + StrongDependency { section: Arc::clone(&existing_source_sec), relocation: relocation_entry }; + target_segment_dependencies.push(strong_dep); } else { trace!("Skipping relocation that points to non-Theseus section: {:?}", demangled); } } // debug!("Target segment dependencies: {:#X?}", target_segment_dependencies); - target_segment.sections_i_depend_on.append(&mut target_segment_dependencies); + target_segment + .sections_i_depend_on + .append(&mut target_segment_dependencies); } Ok(()) diff --git a/applications/ls/src/lib.rs b/applications/ls/src/lib.rs index dd6cb40bef..27042c70cd 100644 --- a/applications/ls/src/lib.rs +++ b/applications/ls/src/lib.rs @@ -1,7 +1,8 @@ #![no_std] extern crate task; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate alloc; extern crate fs_node; extern crate getopts; @@ -12,7 +13,11 @@ use alloc::{ vec::Vec, }; use core::fmt::Write; -use fs_node::{FileOrDir, DirRef}; + +use fs_node::{ + DirRef, + FileOrDir, +}; use getopts::Options; use path::Path; @@ -25,7 +30,7 @@ pub fn main(args: Vec) -> isize { Err(_f) => { println!("{}", _f); print_usage(opts); - return -1; + return -1; } }; @@ -53,11 +58,14 @@ pub fn main(args: Vec) -> isize { 0 } Some(FileOrDir::File(file)) => { - println!("'{}' is not a directory; `ls` currently only supports listing directory contents.", file.lock().get_name()); + println!( + "'{}' is not a directory; `ls` currently only supports listing directory contents.", + file.lock().get_name() + ); -1 } _ => { - println!("Couldn't find path: {}", path); + println!("Couldn't find path: {}", path); -1 } } @@ -65,7 +73,7 @@ pub fn main(args: Vec) -> isize { fn print_children(dir: &DirRef) { let mut child_string = String::new(); - let mut child_list = dir.lock().list(); + let mut child_list = dir.lock().list(); child_list.reverse(); for child in child_list.iter() { writeln!(child_string, "{child}").expect("Failed to write child_string"); @@ -77,7 +85,6 @@ fn print_usage(opts: Options) { println!("{}", opts.usage(USAGE)); } - const USAGE: &str = "Usage: ls [DIR | FILE] List the contents of the given directory or info about the given file. -If no arguments are provided, it lists the contents of the current directory."; \ No newline at end of file +If no arguments are provided, it lists the contents of the current directory."; diff --git a/applications/lspci/src/lib.rs b/applications/lspci/src/lib.rs index 9ede0f89bf..763cb3cc42 100644 --- a/applications/lspci/src/lib.rs +++ b/applications/lspci/src/lib.rs @@ -3,14 +3,18 @@ #![no_std] extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate getopts; -use alloc::vec::Vec; -use alloc::string::String; +use alloc::{ + string::String, + vec::Vec, +}; + use getopts::Options; -use pci::pci_device_iter; use memory::PhysicalAddress; +use pci::pci_device_iter; pub fn main(args: Vec) -> isize { let mut opts = Options::new(); @@ -21,7 +25,7 @@ pub fn main(args: Vec) -> isize { Err(_f) => { println!("{}", _f); print_usage(opts); - return -1; + return -1; } }; @@ -65,11 +69,9 @@ fn list_pci_devices() -> Result<(), &'static str> { Ok(()) } - fn print_usage(opts: Options) { println!("{}", opts.usage(USAGE)); } - const USAGE: &str = "Usage: lspci An application which lists currently connected PCI devices."; diff --git a/applications/mkdir/src/lib.rs b/applications/mkdir/src/lib.rs index bcb9aeb545..743953d29a 100644 --- a/applications/mkdir/src/lib.rs +++ b/applications/mkdir/src/lib.rs @@ -1,15 +1,21 @@ #![no_std] -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate alloc; -extern crate task; -extern crate getopts; extern crate fs_node; +extern crate getopts; +extern crate task; extern crate vfs_node; -use alloc::vec::Vec; -use alloc::string::String; -use alloc::string::ToString; +use alloc::{ + string::{ + String, + ToString, + }, + vec::Vec, +}; + // use fs_node::FileOrDir; use vfs_node::VFSDirectory; @@ -35,4 +41,4 @@ pub fn main(args: Vec) -> isize { } ret -} \ No newline at end of file +} diff --git a/applications/ns/src/lib.rs b/applications/ns/src/lib.rs index c49baad568..d33b970058 100644 --- a/applications/ns/src/lib.rs +++ b/applications/ns/src/lib.rs @@ -1,46 +1,61 @@ -//! This application allows querying about and interacting with namespaces in Theseus, +//! This application allows querying about and interacting with namespaces in Theseus, //! specifically `CrateNamespace`s. #![no_std] extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; +extern crate fs_node; extern crate getopts; extern crate memory; -extern crate task; extern crate mod_mgmt; -extern crate fs_node; extern crate path; +extern crate task; -use core::{ - ops::Deref, - fmt::Write, -}; use alloc::{ - string::String, - string::ToString, + string::{ + String, + ToString, + }, sync::Arc, vec::Vec, }; -use getopts::{Options, Matches}; -use mod_mgmt::CrateNamespace; +use core::{ + fmt::Write, + ops::Deref, +}; + use fs_node::FileRef; +use getopts::{ + Matches, + Options, +}; +use mod_mgmt::CrateNamespace; use path::PathBuf; - pub fn main(args: Vec) -> isize { let mut opts = Options::new(); opts.optflag("h", "help", "print this help menu"); opts.optflag("r", "recursive", "include recursive namespaces"); - opts.optflag("f", "files", "lists crate object files available in this namespace rather than currently-loaded crates"); - opts.optopt("", "load", "load a crate into the current namespace. Ignores all other arguments.", "CRATE_OBJ_FILE_PATH"); + opts.optflag( + "f", + "files", + "lists crate object files available in this namespace rather than currently-loaded crates", + ); + opts.optopt( + "", + "load", + "load a crate into the current namespace. Ignores all other arguments.", + "CRATE_OBJ_FILE_PATH", + ); let matches = match opts.parse(args) { Ok(m) => m, Err(_f) => { println!("{}", _f); print_usage(opts); - return -1; + return -1; } }; @@ -58,20 +73,19 @@ pub fn main(args: Vec) -> isize { } } - fn rmain(matches: Matches) -> Result<(), String> { - let (namespace, curr_wd) = task::with_current_task(|t| - (t.get_namespace().clone(), t.get_env().lock().working_dir.clone()) - ).map_err(|_| String::from("failed to get current task"))?; + let (namespace, curr_wd) = + task::with_current_task(|t| (t.get_namespace().clone(), t.get_env().lock().working_dir.clone())) + .map_err(|_| String::from("failed to get current task"))?; let recursive = matches.opt_present("r"); let mut output = String::new(); if let Some(crate_obj_file_path) = matches.opt_str("load") { let path = PathBuf::from(crate_obj_file_path); - let file = path.get_file(&curr_wd).ok_or_else(|| - format!("Couldn't resolve path to crate object file at {path:?}") - )?; + let file = path + .get_file(&curr_wd) + .ok_or_else(|| format!("Couldn't resolve path to crate object file at {path:?}"))?; load_crate(&mut output, file, &namespace)?; } else if matches.opt_present("f") { print_files(&mut output, 0, namespace.deref(), recursive) @@ -85,20 +99,16 @@ fn rmain(matches: Matches) -> Result<(), String> { Ok(()) } - fn load_crate(output: &mut String, crate_file_ref: FileRef, namespace: &Arc) -> Result<(), String> { let kernel_mmi_ref = memory::get_kernel_mmi_ref().ok_or_else(|| "Cannot get kernel_mmi_ref".to_string())?; - let (_new_crate_ref, _new_syms) = namespace.load_crate( - &crate_file_ref, - None, - kernel_mmi_ref, - false - ).map_err(String::from)?; - writeln!(output, "Loaded crate with {} new symbols from {}", _new_syms, crate_file_ref.lock().get_absolute_path()).unwrap(); + let (_new_crate_ref, _new_syms) = namespace + .load_crate(&crate_file_ref, None, kernel_mmi_ref, false) + .map_err(String::from)?; + writeln!(output, "Loaded crate with {} new symbols from {}", _new_syms, crate_file_ref.lock().get_absolute_path()) + .unwrap(); Ok(()) } - fn print_files(output: &mut String, indent: usize, namespace: &CrateNamespace, recursive: bool) -> core::fmt::Result { writeln!(output, "\n{:indent$}{} CrateNamespace has crate object files:", "", namespace.name(), indent = indent)?; let mut files = namespace.dir().lock().list(); @@ -116,13 +126,22 @@ fn print_files(output: &mut String, indent: usize, namespace: &CrateNamespace, r Ok(()) } - fn print_crates(output: &mut String, indent: usize, namespace: &CrateNamespace, recursive: bool) -> core::fmt::Result { writeln!(output, "\n{:indent$}{} CrateNamespace has loaded crates:", "", namespace.name(), indent = indent)?; let mut crates: Vec = Vec::new(); // We do recursion manually here so we can separately print each recursive namespace. namespace.for_each_crate(false, |crate_name, crate_ref| { - crates.push(format!("{:indent$}{} {:?}", "", crate_name, crate_ref.lock_as_ref().object_file.lock().get_absolute_path(), indent = (indent + 4))); + crates.push(format!( + "{:indent$}{} {:?}", + "", + crate_name, + crate_ref + .lock_as_ref() + .object_file + .lock() + .get_absolute_path(), + indent = (indent + 4) + )); true }); crates.sort(); @@ -139,11 +158,9 @@ fn print_crates(output: &mut String, indent: usize, namespace: &CrateNamespace, Ok(()) } - fn print_usage(opts: Options) { println!("{}", opts.usage(USAGE)); } - const USAGE: &str = "\nUsage: ns [OPTION] Lists the crates that are loaded in the currently-active crate namespace."; diff --git a/applications/ping/src/lib.rs b/applications/ping/src/lib.rs index dd5e53523b..2a09f3a7eb 100644 --- a/applications/ping/src/lib.rs +++ b/applications/ping/src/lib.rs @@ -8,14 +8,33 @@ extern crate alloc; -use alloc::{string::String, vec, vec::Vec}; +use alloc::{ + string::String, + vec, + vec::Vec, +}; +use core::{ + str::FromStr, + time::Duration, +}; + use app_io::println; -use core::{str::FromStr, time::Duration}; -use getopts::{Matches, Options}; +use getopts::{ + Matches, + Options, +}; use net::{ - icmp::{Endpoint, PacketBuffer, PacketMetadata, Socket}, + icmp::{ + Endpoint, + PacketBuffer, + PacketMetadata, + Socket, + }, phy::ChecksumCapabilities, - wire::{Icmpv4Packet, Icmpv4Repr}, + wire::{ + Icmpv4Packet, + Icmpv4Repr, + }, IpAddress, }; use time::Instant; @@ -24,18 +43,8 @@ pub fn main(args: Vec) -> isize { let mut opts = Options::new(); opts.optflag("h", "help", "print this help menu"); opts.optopt("c", "count", "stop after replies", ""); - opts.optopt( - "i", - "wait", - "wait seconds between sending each packet (default: 1)", - "", - ); - opts.optopt( - "s", - "packet size", - "send data bytes in each packet (max: 248, default: 56)", - "", - ); + opts.optopt("i", "wait", "wait seconds between sending each packet (default: 1)", ""); + opts.optopt("s", "packet size", "send data bytes in each packet (max: 248, default: 56)", ""); opts.optopt( "t", "timeout", @@ -67,8 +76,8 @@ pub fn main(args: Vec) -> isize { } fn _main(matches: Matches) -> Result<(), &'static str> { - let remote = IpAddress::from_str(matches.free.first().ok_or("no arguments_provided")?) - .map_err(|_| "invalid argument")?; + let remote = + IpAddress::from_str(matches.free.first().ok_or("no arguments_provided")?).map_err(|_| "invalid argument")?; let interface = net::get_default_interface().ok_or("no network interfaces available")?; @@ -106,11 +115,7 @@ fn _main(matches: Matches) -> Result<(), &'static str> { let mut num_sent = 0; let mut num_received = 0; - let end = if let Some(timeout) = timeout { - Instant::now() + timeout - } else { - Instant::MAX - }; + let end = if let Some(timeout) = timeout { Instant::now() + timeout } else { Instant::MAX }; let mut last_sent = Instant::ZERO; loop { @@ -133,11 +138,7 @@ fn _main(matches: Matches) -> Result<(), &'static str> { if can_send && num_sent < count && last_sent + wait <= now { last_sent = now; - let repr = Icmpv4Repr::EchoRequest { - ident: 0x22b, - seq_no: num_sent, - data: &data, - }; + let repr = Icmpv4Repr::EchoRequest { ident: 0x22b, seq_no: num_sent, data: &data }; let mut locked = socket.lock(); let payload = locked @@ -156,8 +157,7 @@ fn _main(matches: Matches) -> Result<(), &'static str> { if can_recv { let mut locked = socket.lock(); let (payload, _) = locked.recv().map_err(|_| "failed to receive packet")?; - let packet = Icmpv4Packet::new_checked(&payload) - .map_err(|_| "incoming packet had incorrect length")?; + let packet = Icmpv4Packet::new_checked(&payload).map_err(|_| "incoming packet had incorrect length")?; let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored()) .map_err(|_| "failed to parse incoming packet")?; diff --git a/applications/pmu_sample_start/src/lib.rs b/applications/pmu_sample_start/src/lib.rs index bbde45ae15..8a4a4e989f 100644 --- a/applications/pmu_sample_start/src/lib.rs +++ b/applications/pmu_sample_start/src/lib.rs @@ -3,13 +3,16 @@ #![no_std] extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate getopts; extern crate pmu_x86; extern crate spawn; -use alloc::vec::Vec; -use alloc::string::String; +use alloc::{ + string::String, + vec::Vec, +}; pub fn main(_args: Vec) -> isize { if let Err(e) = pmu_x86::init() { @@ -28,7 +31,7 @@ pub fn main(_args: Vec) -> isize { }, Err(x) => println!("Could not create counters for PMU stat: {:?}", x) } */ - + // run event based sampling on this core if let Err(e) = pmu_x86::start_samples(pmu_x86::EventType::UnhaltedReferenceCycles, 0xF_FFFF, None, 10) { println!("Could not start PMU sampling: {:?}", e); diff --git a/applications/pmu_sample_stop/src/lib.rs b/applications/pmu_sample_stop/src/lib.rs index 36dbbb0e73..014ede8694 100644 --- a/applications/pmu_sample_stop/src/lib.rs +++ b/applications/pmu_sample_stop/src/lib.rs @@ -4,12 +4,15 @@ #![no_std] extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate getopts; extern crate pmu_x86; -use alloc::vec::Vec; -use alloc::string::String; +use alloc::{ + string::String, + vec::Vec, +}; pub fn main(_args: Vec) -> isize { let sampler = pmu_x86::retrieve_samples(); @@ -18,12 +21,10 @@ pub fn main(_args: Vec) -> isize { if let Err(e) = pmu_x86::find_function_names_from_samples(&my_sampler) { println!("Error finding function names from samples: {:?}", e); } - } - else { + } else { println!("Could not retrieve samples"); return -1; } 0 } - diff --git a/applications/print_fault_log/src/lib.rs b/applications/print_fault_log/src/lib.rs index 548f977b1b..92795da164 100644 --- a/applications/print_fault_log/src/lib.rs +++ b/applications/print_fault_log/src/lib.rs @@ -5,8 +5,11 @@ extern crate alloc; extern crate fault_log; -use alloc::vec::Vec; -use alloc::string::String; +use alloc::{ + string::String, + vec::Vec, +}; + use fault_log::print_fault_log; pub fn main(_args: Vec) -> isize { diff --git a/applications/ps/src/lib.rs b/applications/ps/src/lib.rs index 6bb2cb151b..853a932be4 100644 --- a/applications/ps/src/lib.rs +++ b/applications/ps/src/lib.rs @@ -1,16 +1,20 @@ #![no_std] extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; -extern crate task; extern crate getopts; extern crate scheduler; +extern crate task; -use getopts::Options; -use alloc::vec::Vec; -use alloc::string::String; +use alloc::{ + string::String, + vec::Vec, +}; use core::fmt::Write; +use getopts::Options; + pub fn main(args: Vec) -> isize { let mut opts = Options::new(); opts.optflag("h", "help", "print this help menu"); @@ -18,25 +22,29 @@ pub fn main(args: Vec) -> isize { let matches = match opts.parse(args) { Ok(m) => m, - Err(_f) => { + Err(_f) => { println!("{} \n", _f); - return -1; + return -1; } }; if matches.opt_present("h") { - return print_usage(opts) + return print_usage(opts); } // Print headers if matches.opt_present("b") { println!("{0:<5} {1}", "ID", "NAME"); - } - else { - #[cfg(any(epoch_scheduler, priority_scheduler))] { - println!("{0:<5} {1:<10} {2:<4} {3:<4} {4:<5} {5:<10} {6}", "ID", "RUNSTATE", "CPU", "PIN", "TYPE", "PRIORITY", "NAME"); + } else { + #[cfg(any(epoch_scheduler, priority_scheduler))] + { + println!( + "{0:<5} {1:<10} {2:<4} {3:<4} {4:<5} {5:<10} {6}", + "ID", "RUNSTATE", "CPU", "PIN", "TYPE", "PRIORITY", "NAME" + ); } - #[cfg(not(any(epoch_scheduler, priority_scheduler)))] { + #[cfg(not(any(epoch_scheduler, priority_scheduler)))] + { println!("{0:<5} {1:<10} {2:<4} {3:<4} {4:<5} {5}", "ID", "RUNSTATE", "CPU", "PIN", "TYPE", "NAME"); } } @@ -49,32 +57,50 @@ pub fn main(args: Vec) -> isize { num_tasks += 1; if matches.opt_present("b") { writeln!(task_string, "{0:<5} {1}", id, task.name).expect("Failed to write to task_string."); - } - else { - // All printed fields below must be strings to ensure the width formatting specifier below works properly. + } else { + // All printed fields below must be strings to ensure the width formatting specifier + // below works properly. let runstate = format!("{:?}", task.runstate()); - let cpu = task.running_on_cpu().map(|cpu| format!("{cpu}")).unwrap_or_else(|| String::from("-")); - let pinned = task.pinned_cpu().map(|pin| format!("{pin}")).unwrap_or_else(|| String::from("-")); - let task_type = if task.is_an_idle_task {"I"} - else if task.is_application() {"A"} - else {" "} ; + let cpu = task + .running_on_cpu() + .map(|cpu| format!("{cpu}")) + .unwrap_or_else(|| String::from("-")); + let pinned = task + .pinned_cpu() + .map(|pin| format!("{pin}")) + .unwrap_or_else(|| String::from("-")); + let task_type = if task.is_an_idle_task { + "I" + } else if task.is_application() { + "A" + } else { + " " + }; - #[cfg(any(epoch_scheduler, priority_scheduler))] { - let priority = scheduler::priority(&task).map(|priority| format!("{}", priority)).unwrap_or_else(|| String::from("-")); - task_string.push_str( - &format!("{0:<5} {1:<10} {2:<4} {3:<4} {4:<5} {5:<10} {6}\n", - id, runstate, cpu, pinned, task_type, priority, task.name) - ); + #[cfg(any(epoch_scheduler, priority_scheduler))] + { + let priority = scheduler::priority(&task) + .map(|priority| format!("{}", priority)) + .unwrap_or_else(|| String::from("-")); + task_string.push_str(&format!( + "{0:<5} {1:<10} {2:<4} {3:<4} {4:<5} {5:<10} {6}\n", + id, runstate, cpu, pinned, task_type, priority, task.name + )); } - #[cfg(not(any(epoch_scheduler, priority_scheduler)))] { - writeln!(task_string, "{0:<5} {1:<10} {2:<4} {3:<4} {4:<5} {5}", - id, runstate, cpu, pinned, task_type, task.name).expect("Failed to write to task_string."); + #[cfg(not(any(epoch_scheduler, priority_scheduler)))] + { + writeln!( + task_string, + "{0:<5} {1:<10} {2:<4} {3:<4} {4:<5} {5}", + id, runstate, cpu, pinned, task_type, task.name + ) + .expect("Failed to write to task_string."); } } } print!("{}", task_string); println!("Total number of tasks: {}", num_tasks); - + 0 } @@ -90,4 +116,3 @@ const BRIEF: &str = "Usage: ps [options]\n RUNSTATE: runnability status of this task, e.g., whether it can be scheduled in. ID: the unique identifier for this task. NAME: the name of the task."; - \ No newline at end of file diff --git a/applications/pwd/src/lib.rs b/applications/pwd/src/lib.rs index bdc9d24688..6aec5d7ea3 100644 --- a/applications/pwd/src/lib.rs +++ b/applications/pwd/src/lib.rs @@ -1,19 +1,23 @@ #![no_std] -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate alloc; -extern crate task; extern crate getopts; +extern crate task; -use alloc::vec::Vec; -use alloc::string::String; +use alloc::{ + string::String, + vec::Vec, +}; pub fn main(_args: Vec) -> isize { task::with_current_task(|t| { println!("{}", t.get_env().lock().cwd()); 0 - }).unwrap_or_else(|_| { + }) + .unwrap_or_else(|_| { println!("failed to get current task"); -1 }) -} \ No newline at end of file +} diff --git a/applications/qemu_test/src/lib.rs b/applications/qemu_test/src/lib.rs index c79d2fc778..e4d0495a62 100644 --- a/applications/qemu_test/src/lib.rs +++ b/applications/qemu_test/src/lib.rs @@ -6,12 +6,28 @@ #![no_std] -use alloc::{boxed::Box, string::String, vec::Vec}; - -use app_io::{print, println}; -use qemu_exit::{QEMUExit, X86}; -use task::{ExitValue, KillReason}; -use path::{Path, PathBuf}; +use alloc::{ + boxed::Box, + string::String, + vec::Vec, +}; + +use app_io::{ + print, + println, +}; +use path::{ + Path, + PathBuf, +}; +use qemu_exit::{ + QEMUExit, + X86, +}; +use task::{ + ExitValue, + KillReason, +}; extern crate alloc; diff --git a/applications/raw_mode/src/lib.rs b/applications/raw_mode/src/lib.rs index 78c1a56e55..828a4813f0 100644 --- a/applications/raw_mode/src/lib.rs +++ b/applications/raw_mode/src/lib.rs @@ -4,7 +4,10 @@ extern crate alloc; -use alloc::{string::String, vec::Vec}; +use alloc::{ + string::String, + vec::Vec, +}; pub fn main(_args: Vec) -> isize { if let Err(e) = run() { diff --git a/applications/rm/src/lib.rs b/applications/rm/src/lib.rs index 3ad41784bd..80f1b715ca 100644 --- a/applications/rm/src/lib.rs +++ b/applications/rm/src/lib.rs @@ -1,21 +1,30 @@ #![no_std] -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; // #[macro_use] extern crate log; -#[macro_use] extern crate alloc; -extern crate task; +#[macro_use] +extern crate alloc; +extern crate fs_node; extern crate getopts; extern crate path; -extern crate fs_node; extern crate root; +extern crate task; + +use alloc::{ + string::{ + String, + ToString, + }, + vec::Vec, +}; -use alloc::vec::Vec; -use alloc::string::String; -use alloc::string::ToString; +use fs_node::{ + FileOrDir, + FsNode, +}; use getopts::Options; use path::PathBuf; -use fs_node::{FsNode, FileOrDir}; - pub fn main(args: Vec) -> isize { match remove_node(args) { @@ -31,7 +40,7 @@ pub fn remove_node(args: Vec) -> Result<(), String> { let mut opts = Options::new(); opts.optflag("h", "help", "print this help menu"); opts.optflag("r", "recursive", "recursively remove directories and their contents"); - + let matches = match opts.parse(args) { Ok(m) => m, Err(e) => { @@ -45,9 +54,7 @@ pub fn remove_node(args: Vec) -> Result<(), String> { return Ok(()); } - let Ok(working_dir) = task::with_current_task(|t| - t.get_env().lock().working_dir.clone() - ) else { + let Ok(working_dir) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { return Err("failed to get current task".into()); }; @@ -62,21 +69,29 @@ pub fn remove_node(args: Vec) -> Result<(), String> { _ => return Err(format!("Couldn't find path {path}")), }; - // Only remove directories if the user specified "-r". + // Only remove directories if the user specified "-r". let can_remove_dirs = matches.opt_present("r"); - let path_error = || { format!("Couldn't remove {} from its parent directory.", &path) }; + let path_error = || format!("Couldn't remove {} from its parent directory.", &path); let parent = node_to_delete.get_parent_dir().ok_or_else(path_error)?; match node_to_delete { FileOrDir::File(_) => { - parent.lock().remove(&node_to_delete).ok_or_else(path_error)?; - } + parent + .lock() + .remove(&node_to_delete) + .ok_or_else(path_error)?; + } FileOrDir::Dir(_) => { if can_remove_dirs { - parent.lock().remove(&node_to_delete).ok_or_else(path_error)?; + parent + .lock() + .remove(&node_to_delete) + .ok_or_else(path_error)?; } else { - println!("Skipping the removal of directory '{}', try specifying the \"-r\" flag", - node_to_delete.get_name()); + println!( + "Skipping the removal of directory '{}', try specifying the \"-r\" flag", + node_to_delete.get_name() + ); } } } @@ -89,6 +104,5 @@ fn print_usage(opts: Options) { println!("{}", opts.usage(USAGE)); } - const USAGE: &str = "Usage: rm [PATH] -Remove files or directories from filesystem"; \ No newline at end of file +Remove files or directories from filesystem"; diff --git a/applications/rq/src/lib.rs b/applications/rq/src/lib.rs index 906bf591ed..b506bb8be2 100644 --- a/applications/rq/src/lib.rs +++ b/applications/rq/src/lib.rs @@ -4,11 +4,17 @@ extern crate alloc; use alloc::{ fmt::Write, - string::{String, ToString}, + string::{ + String, + ToString, + }, vec::Vec, }; -use app_io::{print, println}; +use app_io::{ + print, + println, +}; use getopts::Options; pub fn main(args: Vec) -> isize { @@ -29,24 +35,14 @@ pub fn main(args: Vec) -> isize { let bootstrap_cpu = cpu::bootstrap_cpu(); for (cpu, task_list) in task::scheduler::tasks() { - let core_type = if Some(cpu) == bootstrap_cpu { - "Boot CPU" - } else { - "Secondary CPU" - }; + let core_type = if Some(cpu) == bootstrap_cpu { "Boot CPU" } else { "Secondary CPU" }; println!("\n{} (CPU: {})", core_type, cpu); let mut runqueue_contents = String::new(); for task in task_list.iter() { - writeln!( - runqueue_contents, - " {} ({}) {}", - task.name, - task.id, - if task.is_running() { "*" } else { "" } - ) - .expect("Failed to write to runqueue_contents"); + writeln!(runqueue_contents, " {} ({}) {}", task.name, task.id, if task.is_running() { "*" } else { "" }) + .expect("Failed to write to runqueue_contents"); } print!("{}", runqueue_contents); } diff --git a/applications/seconds_counter/src/lib.rs b/applications/seconds_counter/src/lib.rs index bb0fb12261..16e299c8b6 100644 --- a/applications/seconds_counter/src/lib.rs +++ b/applications/seconds_counter/src/lib.rs @@ -2,7 +2,10 @@ extern crate alloc; -use alloc::{string::String, vec::Vec}; +use alloc::{ + string::String, + vec::Vec, +}; pub fn main(_: Vec) { let mut counter = 0; diff --git a/applications/serial_echo/src/lib.rs b/applications/serial_echo/src/lib.rs index 2cf4f21660..91b700aead 100644 --- a/applications/serial_echo/src/lib.rs +++ b/applications/serial_echo/src/lib.rs @@ -1,31 +1,35 @@ //! Simple tests for I/O transfers across the [`serial_port::SerialPort`]. -//! #![no_std] extern crate alloc; // #[macro_use] extern crate log; -#[macro_use] extern crate app_io; -extern crate task; +#[macro_use] +extern crate app_io; extern crate core2; extern crate io; -extern crate sync_irq; extern crate serial_port; +extern crate sync_irq; +extern crate task; - -use core::convert::TryFrom; use alloc::{ string::String, vec::Vec, }; +use core::convert::TryFrom; + use core2::io::Read; use io::LockableIo; +use serial_port::{ + get_serial_port, + SerialPort, + SerialPortAddress, +}; use sync_irq::IrqSafeMutex; -use serial_port::{SerialPort, SerialPortAddress, get_serial_port}; - pub fn main(args: Vec) -> isize { - let serial_port_address = args.first() + let serial_port_address = args + .first() .and_then(|s| SerialPortAddress::try_from(&**s).ok()) .unwrap_or(SerialPortAddress::COM1); @@ -36,7 +40,7 @@ pub fn main(args: Vec) -> isize { return -1; } }; - + if true { let mut serial_port_io = LockableIo::, _>::from(serial_port); let mut serial_port_io2 = serial_port_io.clone(); @@ -46,23 +50,27 @@ pub fn main(args: Vec) -> isize { if let Ok(bytes_read) = res { if let Ok(s) = core::str::from_utf8(&buf[..bytes_read]) { use core2::io::Write; - serial_port_io.write(s.as_bytes()).expect("serial port write failed"); + serial_port_io + .write(s.as_bytes()) + .expect("serial port write failed"); } } } - } - else { + } else { let mut buf = [0; 100]; loop { let res = serial_port.lock().read(&mut buf); if let Ok(bytes_read) = res { if let Ok(s) = core::str::from_utf8(&buf[..bytes_read]) { use core::fmt::Write; - serial_port.lock().write_str(s).expect("serial port write failed"); + serial_port + .lock() + .write_str(s) + .expect("serial port write failed"); } } } } - + // 0 } diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index ea8197dd84..5c53a42dc5 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -1,49 +1,78 @@ //! Shell with event-driven architecture //! Commands that can be run are the names of the crates in the applications directory -//! -//! The shell has the following responsibilities: handles key events delivered from terminal, manages terminal display, -//! spawns and manages tasks, and records the history of executed user commands. +//! +//! The shell has the following responsibilities: handles key events delivered from terminal, +//! manages terminal display, spawns and manages tasks, and records the history of executed user +//! commands. #![no_std] -extern crate keycodes_ascii; -extern crate spin; +extern crate app_io; +extern crate core2; extern crate dfqueue; -extern crate spawn; -extern crate task; -extern crate event_types; -extern crate window_manager; +extern crate environment; +extern crate event_types; +extern crate fs_node; +extern crate keycodes_ascii; +extern crate libterm; extern crate path; extern crate root; extern crate scheduler; +extern crate spawn; +extern crate spin; extern crate stdio; -extern crate core2; -extern crate app_io; -extern crate fs_node; -extern crate environment; -extern crate libterm; +extern crate task; +extern crate window_manager; -#[macro_use] extern crate alloc; -#[macro_use] extern crate log; +#[macro_use] +extern crate alloc; +#[macro_use] +extern crate log; + +use alloc::{ + collections::BTreeMap, + string::{ + String, + ToString, + }, + sync::Arc, + vec::Vec, +}; +use core::{ + mem, + ops::Deref, +}; +use app_io::IoStreams; +use core2::io::Write; +use dfqueue::{ + DFQueue, + DFQueueConsumer, + DFQueueProducer, +}; +use environment::Environment; use event_types::Event; -use keycodes_ascii::{Keycode, KeyAction, KeyEvent}; -use alloc::string::{String, ToString}; -use alloc::vec::Vec; -use path::Path; -use task::{ExitValue, KillReason, JoinableTaskRef}; +use fs_node::FileOrDir; +use keycodes_ascii::{ + KeyAction, + KeyEvent, + Keycode, +}; use libterm::Terminal; -use dfqueue::{DFQueue, DFQueueConsumer, DFQueueProducer}; -use alloc::sync::Arc; +use path::Path; use spin::Mutex; -use environment::Environment; -use core::mem; -use alloc::collections::BTreeMap; -use stdio::{Stdio, KeyEventQueue, KeyEventQueueReader, KeyEventQueueWriter, - StdioReader, StdioWriter}; -use core2::io::Write; -use core::ops::Deref; -use app_io::IoStreams; -use fs_node::FileOrDir; +use stdio::{ + KeyEventQueue, + KeyEventQueueReader, + KeyEventQueueWriter, + Stdio, + StdioReader, + StdioWriter, +}; +use task::{ + ExitValue, + JoinableTaskRef, + KillReason, +}; /// The status of a job. #[derive(PartialEq)] @@ -52,7 +81,7 @@ enum JobStatus { Running, /// The job is suspended (but not killed), e.g. upon ctrl-Z. /// All the tasks in this job are either blocked or exited. - Stopped + Stopped, } /// This structure is used by shell to track its spawned applications. Each successfully @@ -85,20 +114,22 @@ struct Job { /// The output reader of the job. It is the reader of `pipe_queues[N]`. stdout_reader: StdioReader, /// Command line that was used to create the job. - cmd: String + cmd: String, } -/// A main function that spawns a new shell and waits for the shell loop to exit before returning an exit value +/// A main function that spawns a new shell and waits for the shell loop to exit before returning an +/// exit value pub fn main(_args: Vec) -> isize { { let _task_ref = match spawn::new_task_builder(shell_loop, ()) .name("shell_loop".to_string()) - .spawn() { - Ok(task_ref) => { task_ref } + .spawn() + { + Ok(task_ref) => task_ref, Err(err) => { error!("{}", err); error!("failed to spawn shell"); - return -1; + return -1; } }; } @@ -123,16 +154,16 @@ pub fn main(_args: Vec) -> isize { // return 0; } -/// Errors when attempting to invoke an application from the terminal. +/// Errors when attempting to invoke an application from the terminal. enum AppErr { - /// The command does not match the name of any existing application in the - /// application namespace directory. + /// The command does not match the name of any existing application in the + /// application namespace directory. NotFound(String), - /// The terminal could not find the application namespace due to a filesystem error. + /// The terminal could not find the application namespace due to a filesystem error. NamespaceErr, /// The terminal could not spawn a new task to run the new application. /// Includes the String error returned from the task spawn function. - SpawnErr(String) + SpawnErr(String), } struct Shell { @@ -148,15 +179,17 @@ struct Shell { fg_job_num: Option, /// The string that stores the users keypresses after the prompt cmdline: String, - /// This buffer stores characters before sending them to running application on `enter` key strike + /// This buffer stores characters before sending them to running application on `enter` key + /// strike input_buffer: String, /// Vector that stores the history of commands that the user has entered command_history: Vec, - /// Variable used to track the net number of times the user has pressed up/down to cycle through the commands - /// ex. if the user has pressed up twice and down once, then command shift = # ups - # downs = 1 (cannot be negative) + /// Variable used to track the net number of times the user has pressed up/down to cycle + /// through the commands ex. if the user has pressed up twice and down once, then command + /// shift = # ups - # downs = 1 (cannot be negative) history_index: usize, - /// When someone enters some commands, but before pressing `enter` it presses `up` to see previous commands, - /// we must push it to command_history. We don't want to push it twice. + /// When someone enters some commands, but before pressing `enter` it presses `up` to see + /// previous commands, we must push it to command_history. We don't want to push it twice. buffered_cmd_recorded: bool, /// The consumer to the terminal's print dfqueue print_consumer: DFQueueConsumer, @@ -166,7 +199,7 @@ struct Shell { /// The terminal's current environment env: Arc>, /// the terminal that is bind with the shell instance - terminal: Arc> + terminal: Arc>, } impl Shell { @@ -176,7 +209,7 @@ impl Shell { // Initialize a dfqueue for the terminal object to handle printing from applications. // Note that this is only to support legacy output. Newly developed applications should // turn to use `stdio` provided by the `stdio` crate together with the support of `app_io`. - let terminal_print_dfq: DFQueue = DFQueue::new(); + let terminal_print_dfq: DFQueue = DFQueue::new(); let print_consumer = terminal_print_dfq.into_consumer(); let print_producer = print_consumer.obtain_producer(); @@ -202,12 +235,12 @@ impl Shell { print_consumer, print_producer, env: Arc::new(Mutex::new(env)), - terminal + terminal, }) } /// Insert a character to the command line buffer in the shell. - /// The position to insert is determined by the position of the cursor in the terminal. + /// The position to insert is determined by the position of the cursor in the terminal. /// `sync_terminal` indicates whether the terminal screen will be synchronically updated. fn insert_char_to_cmdline(&mut self, c: char, sync_terminal: bool) -> Result<(), &'static str> { let mut terminal = self.terminal.lock(); @@ -215,7 +248,8 @@ impl Shell { let insert_idx = self.cmdline.len() - offset_from_end; self.cmdline.insert(insert_idx, c); if sync_terminal { - // disable cursor before updating in case the cursor is not at the end and the old text is the prefix of the new one + // disable cursor before updating in case the cursor is not at the end and the old text + // is the prefix of the new one terminal.cursor.disable(); terminal.display_cursor()?; terminal.insert_char(c, offset_from_end)?; @@ -230,14 +264,18 @@ impl Shell { /// `sync_terminal` indicates whether the terminal screen will be synchronically updated. fn remove_char_from_cmdline(&mut self, erase_left: bool, sync_terminal: bool) -> Result<(), &'static str> { let mut cursor_offset_from_end = self.terminal.lock().get_cursor_offset_from_end(); - if erase_left { cursor_offset_from_end += 1; } - if cursor_offset_from_end > self.cmdline.len() || cursor_offset_from_end == 0 { return Ok(()); } + if erase_left { + cursor_offset_from_end += 1; + } + if cursor_offset_from_end > self.cmdline.len() || cursor_offset_from_end == 0 { + return Ok(()); + } let erase_idx = self.cmdline.len() - cursor_offset_from_end; self.cmdline.remove(erase_idx); if sync_terminal { self.terminal.lock().remove_char(cursor_offset_from_end)?; } - if !erase_left { + if !erase_left { self.update_cursor_pos(cursor_offset_from_end - 1)?; } Ok(()) @@ -302,8 +340,8 @@ impl Shell { Ok(()) } - /// Move the cursor a character left. If the cursor is already at the beginning of the command line, - /// it simply returns. + /// Move the cursor a character left. If the cursor is already at the beginning of the command + /// line, it simply returns. fn move_cursor_left(&mut self) -> Result<(), &'static str> { let offset_from_end = self.terminal.lock().get_cursor_offset_from_end(); if offset_from_end < self.cmdline.len() { @@ -312,19 +350,20 @@ impl Shell { Ok(()) } - /// Move the cursor a character to the right. If the cursor is already at the end of the command line, - /// it simply returns. + /// Move the cursor a character to the right. If the cursor is already at the end of the command + /// line, it simply returns. fn move_cursor_right(&mut self) -> Result<(), &'static str> { let offset_from_end = self.terminal.lock().get_cursor_offset_from_end(); if offset_from_end > 0 { self.update_cursor_pos(offset_from_end - 1)?; } self.terminal.lock().cursor.enable(); - + Ok(()) } - /// Update the position of cursor. `offset_from_end` specifies the position relative to the end of the text in number of characters. + /// Update the position of cursor. `offset_from_end` specifies the position relative to the end + /// of the text in number of characters. fn update_cursor_pos(&mut self, offset_from_end: usize) -> Result<(), &'static str> { let mut terminal = self.terminal.lock(); terminal.cursor.disable(); @@ -335,7 +374,7 @@ impl Shell { terminal.update_cursor_pos(offset_from_end, self.cmdline.as_bytes()[self.cmdline.len() - offset_from_end]); } terminal.cursor.enable(); - + Ok(()) } @@ -364,14 +403,16 @@ impl Shell { return Ok(()); } if self.history_index == 1 && self.buffered_cmd_recorded { - let selected_command = self.command_history.pop() + let selected_command = self + .command_history + .pop() .ok_or("BUG: shell::goto_next_command(): empty command line history when history_index was 1")?; self.set_cmdline(selected_command, true)?; self.history_index -= 1; self.buffered_cmd_recorded = false; return Ok(()); } - self.history_index -=1; + self.history_index -= 1; if self.history_index == 0 { self.clear_cmdline(true)?; return Ok(()); @@ -381,10 +422,10 @@ impl Shell { Ok(()) } - fn handle_key_event(&mut self, keyevent: KeyEvent) -> Result<(), &'static str> { + fn handle_key_event(&mut self, keyevent: KeyEvent) -> Result<(), &'static str> { // EVERYTHING BELOW HERE WILL ONLY OCCUR ON A KEY PRESS (not key release) if keyevent.action != KeyAction::Pressed { - return Ok(()); + return Ok(()); } // Ctrl+C signals the shell to exit the job @@ -405,7 +446,9 @@ impl Shell { // Lock the shared structure in `app_io` and then kill the running application // Kill all tasks in the job. for task_ref in task_refs { - if task_ref.has_exited() { continue; } + if task_ref.has_exited() { + continue; + } match task_ref.kill(KillReason::Requested) { Ok(_) => { task::scheduler::remove_task(task_ref); @@ -413,11 +456,13 @@ impl Shell { Err(e) => error!("Could not kill task, error: {}", e), } - // Here we must wait for the running application to quit before releasing the lock, - // because the previous `kill` method will NOT stop the application immediately. - // We must circumvent the situation where the application is killed while holding the - // lock. We wait for the application to finish its last time slice. It will then be - // removed from the run queue. We can thereafter release the lock. + // Here we must wait for the running application to quit before releasing the + // lock, because the previous `kill` method will NOT stop + // the application immediately. We must circumvent the + // situation where the application is killed while holding the + // lock. We wait for the application to finish its last time slice. It will then + // be removed from the run queue. We can thereafter release + // the lock. loop { scheduler::schedule(); // yield the CPU if !task_ref.is_running() { @@ -433,7 +478,7 @@ impl Shell { self.redisplay_prompt(); return Ok(()); } - + return Ok(()); } @@ -449,14 +494,19 @@ impl Shell { if let Some(task_refs) = self.jobs.get(&fg_job_num).map(|job| &job.tasks) { // Stop all tasks in the job. for task_ref in task_refs { - if task_ref.has_exited() { continue; } - if task_ref.block().is_err() { continue; } - - // Here we must wait for the running application to stop before releasing the lock, - // because the previous `block` method will NOT stop the application immediately. - // We must circumvent the situation where the application is stopped while holding the - // lock. We wait for the application to finish its last time slice. It will then be - // truly blocked. We can thereafter release the lock. + if task_ref.has_exited() { + continue; + } + if task_ref.block().is_err() { + continue; + } + + // Here we must wait for the running application to stop before releasing the + // lock, because the previous `block` method will NOT stop + // the application immediately. We must circumvent the + // situation where the application is stopped while holding the + // lock. We wait for the application to finish its last time slice. It will then + // be truly blocked. We can thereafter release the lock. loop { scheduler::schedule(); // yield the CPU if !task_ref.is_running() { @@ -487,7 +537,7 @@ impl Shell { } // Tracks what the user does whenever she presses the backspace button - if keyevent.keycode == Keycode::Backspace { + if keyevent.keycode == Keycode::Backspace { if self.fg_job_num.is_some() { self.remove_char_from_input_buff(true)?; } else { @@ -501,43 +551,52 @@ impl Shell { return Ok(()); } - // Attempts to run the command whenever the user presses enter and updates the cursor tracking variables + // Attempts to run the command whenever the user presses enter and updates the cursor + // tracking variables if keyevent.keycode == Keycode::Enter && keyevent.keycode.to_ascii(keyevent.modifiers).is_some() { let cmdline = self.cmdline.clone(); if cmdline.is_empty() && self.fg_job_num.is_none() { - // reprints the prompt on the next line if the user presses enter and hasn't typed anything into the prompt + // reprints the prompt on the next line if the user presses enter and hasn't typed + // anything into the prompt self.terminal.lock().print_to_terminal("\n".to_string()); self.redisplay_prompt(); return Ok(()); - } else if let Some(ref fg_job_num) = self.fg_job_num { // send buffered characters to the running application + } else if let Some(ref fg_job_num) = self.fg_job_num { + // send buffered characters to the running application if let Some(job) = self.jobs.get(fg_job_num) { self.terminal.lock().print_to_terminal("\n".to_string()); let mut buffered_string = String::new(); mem::swap(&mut buffered_string, &mut self.input_buffer); buffered_string.push('\n'); - job.stdin_writer.lock().write_all(buffered_string.as_bytes()) + job.stdin_writer + .lock() + .write_all(buffered_string.as_bytes()) .or(Err("shell failed to write to stdin"))?; } return Ok(()); - } else { // start a new job + } else { + // start a new job self.terminal.lock().print_to_terminal("\n".to_string()); self.command_history.push(cmdline); self.command_history.dedup(); // Removes any duplicates self.history_index = 0; - if self.is_internal_command() { // shell executes internal commands + if self.is_internal_command() { + // shell executes internal commands self.execute_internal()?; self.clear_cmdline(false)?; - } else { // shell invokes user programs + } else { + // shell invokes user programs let new_job_num = self.build_new_job()?; self.fg_job_num = Some(new_job_num); - // If the new job is to run in the background, then we should not put it to foreground. + // If the new job is to run in the background, then we should not put it to + // foreground. if let Some(last) = self.cmdline.split_whitespace().last() { if last == "&" { - self.terminal.lock().print_to_terminal( - format!("[{}] [running] {}\n", new_job_num, self.cmdline) - ); + self.terminal + .lock() + .print_to_terminal(format!("[{}] [running] {}\n", new_job_num, self.cmdline)); self.fg_job_num = None; self.clear_cmdline(false)?; self.redisplay_prompt(); @@ -550,17 +609,17 @@ impl Shell { return Ok(()); } - // handle navigation keys: home, end, page up, page down, up arrow, down arrow + // handle navigation keys: home, end, page up, page down, up arrow, down arrow if keyevent.keycode == Keycode::Home && keyevent.modifiers.is_control() { return self.terminal.lock().move_screen_to_begin(); } - if keyevent.keycode == Keycode::End && keyevent.modifiers.is_control(){ + if keyevent.keycode == Keycode::End && keyevent.modifiers.is_control() { return self.terminal.lock().move_screen_to_end(); } - if keyevent.modifiers.is_control() && keyevent.modifiers.is_shift() && keyevent.keycode == Keycode::Up { + if keyevent.modifiers.is_control() && keyevent.modifiers.is_shift() && keyevent.keycode == Keycode::Up { return self.terminal.lock().move_screen_line_up(); } - if keyevent.modifiers.is_control() && keyevent.modifiers.is_shift() && keyevent.keycode == Keycode::Down { + if keyevent.modifiers.is_control() && keyevent.modifiers.is_shift() && keyevent.keycode == Keycode::Down { return self.terminal.lock().move_screen_line_down(); } @@ -573,7 +632,7 @@ impl Shell { } // Cycles to the next previous command - if keyevent.keycode == Keycode::Up { + if keyevent.keycode == Keycode::Up { self.goto_previous_command()?; return Ok(()); } @@ -586,24 +645,25 @@ impl Shell { // Jumps to the beginning of the input string if keyevent.keycode == Keycode::Home { - return self.move_cursor_leftmost() + return self.move_cursor_leftmost(); } // Jumps to the end of the input string if keyevent.keycode == Keycode::End { - return self.move_cursor_rightmost() + return self.move_cursor_rightmost(); } // Adjusts the cursor tracking variables when the user presses the left and right arrow keys if keyevent.keycode == Keycode::Left { - return self.move_cursor_left() + return self.move_cursor_left(); } if keyevent.keycode == Keycode::Right { - return self.move_cursor_right() + return self.move_cursor_right(); } - // Tracks what the user has typed so far, excluding any keypresses by the backspace and Enter key, which are special and are handled directly below + // Tracks what the user has typed so far, excluding any keypresses by the backspace and + // Enter key, which are special and are handled directly below if keyevent.keycode.to_ascii(keyevent.modifiers).is_some() { match keyevent.keycode.to_ascii(keyevent.modifiers) { Some(c) => { @@ -612,11 +672,10 @@ impl Shell { if let Some(_fg_job_num) = self.fg_job_num { self.insert_char_to_input_buff(c, true)?; return Ok(()); - } - else { + } else { self.insert_char_to_cmdline(c, true)?; } - }, + } None => { return Err("Couldn't get key event"); } @@ -628,16 +687,17 @@ impl Shell { /// Create a single task. `cmd` is the name of the application. `args` are the provided /// arguments. It returns a task reference on success. fn create_single_task(&mut self, cmd: String, args: Vec) -> Result { - // Check that the application actually exists - let namespace_dir = task::with_current_task(|t| - t.get_namespace().dir().clone() - ).map_err(|_| AppErr::NamespaceErr)?; + let namespace_dir = + task::with_current_task(|t| t.get_namespace().dir().clone()).map_err(|_| AppErr::NamespaceErr)?; let cmd_crate_name = format!("{cmd}-"); - let mut matching_apps = namespace_dir.get_files_starting_with(&cmd_crate_name).into_iter(); + let mut matching_apps = namespace_dir + .get_files_starting_with(&cmd_crate_name) + .into_iter(); let app_file = matching_apps.next(); - let second_match = matching_apps.next(); // return an error if there are multiple matching apps - let app_path = app_file.xor(second_match) + let second_match = matching_apps.next(); // return an error if there are multiple matching apps + let app_path = app_file + .xor(second_match) .map(|f| f.lock().get_absolute_path()) .ok_or(AppErr::NotFound(cmd))?; @@ -647,28 +707,31 @@ impl Shell { .block() .spawn() .map_err(|e| AppErr::SpawnErr(e.to_string()))?; - + taskref.set_env(self.env.clone()); // Set environment variable of application to the same as terminal task // Gets the task id so we can reference this task if we need to kill it with Ctrl+C Ok(taskref) } - /// Evaluate the command line. It creates a sequence of jobs, which forms a chain of applications that - /// pipe the output from one to the next, and finally back to the shell. If any task fails to start up, - /// all tasks that have already been spawned will be killed immeidately before returning error. + /// Evaluate the command line. It creates a sequence of jobs, which forms a chain of + /// applications that pipe the output from one to the next, and finally back to the shell. + /// If any task fails to start up, all tasks that have already been spawned will be killed + /// immeidately before returning error. fn eval_cmdline(&mut self) -> Result, AppErr> { - let cmdline = self.cmdline.trim().to_string(); let mut task_refs = Vec::new(); // If the command line is empty or starts with '|', return 'AppErr' if cmdline.is_empty() || cmdline.starts_with('|') { - return Err(AppErr::NotFound(cmdline)) + return Err(AppErr::NotFound(cmdline)); } for single_task_cmd in cmdline.split('|') { - let mut args: Vec = single_task_cmd.split_whitespace().map(|s| s.to_string()).collect(); + let mut args: Vec = single_task_cmd + .split_whitespace() + .map(|s| s.to_string()) + .collect(); let command = args.remove(0); // If the last arg is `&`, remove it. @@ -680,7 +743,8 @@ impl Shell { match self.create_single_task(command, args) { Ok(task_ref) => task_refs.push(task_ref), - // Once we run into an error, we must kill all previously spawned tasks in this command line. + // Once we run into an error, we must kill all previously spawned tasks in this + // command line. Err(e) => { for task_ref in task_refs { if let Err(kill_error) = task_ref.kill(KillReason::Requested) { @@ -698,7 +762,6 @@ impl Shell { fn build_new_job(&mut self) -> Result { match self.eval_cmdline() { Ok(task_refs) => { - let mut task_ids = Vec::new(); let mut pipe_queues = Vec::new(); let mut stderr_queues = Vec::new(); @@ -707,8 +770,9 @@ impl Shell { task_ids.push(task_ref.id); } - // Set up the chain of queues between applications, and between shell and applications. - // See the comments for `Job` to get a view of how queues are chained. + // Set up the chain of queues between applications, and between shell and + // applications. See the comments for `Job` to get a view of how + // queues are chained. let first_stdio_queue = Stdio::new(); let job_stdin_writer = first_stdio_queue.get_writer(); let mut previous_queue_reader = first_stdio_queue.get_reader(); @@ -739,7 +803,7 @@ impl Shell { stderr_queues, stdin_writer: job_stdin_writer, stdout_reader: job_stdout_reader, - cmd: self.cmdline.clone() + cmd: self.cmdline.clone(), }; // All IO streams have been set up for the new tasks. Safe to unblock them now. @@ -747,8 +811,8 @@ impl Shell { task_ref.unblock().unwrap(); } - // Allocate a job number for the new job. It will start from 1 and choose the smallest number - // that has not yet been allocated. + // Allocate a job number for the new job. It will start from 1 and choose the + // smallest number that has not yet been allocated. let mut new_job_num: isize = 1; for (key, _) in self.jobs.iter() { if new_job_num != *key { @@ -764,20 +828,21 @@ impl Shell { self.jobs.insert(new_job_num, new_job); Ok(new_job_num) - }, + } Err(err) => { let err_msg = match err { AppErr::NotFound(command) => { // No need to return err if command is empty if command.trim().is_empty() { String::new() - } - else { + } else { format!("{command:?} command not found.\n") } - }, - AppErr::NamespaceErr => "Failed to find directory of application executables.\n".to_string(), - AppErr::SpawnErr(e) => format!("Failed to spawn new task to run command. Error: {e}.\n"), + } + AppErr::NamespaceErr => "Failed to find directory of application executables.\n".to_string(), + AppErr::SpawnErr(e) => { + format!("Failed to spawn new task to run command. Error: {e}.\n") + } }; self.terminal.lock().print_to_terminal(err_msg); if let Err(msg) = self.clear_cmdline(false) { @@ -805,9 +870,8 @@ impl Shell { /// Try to match the incomplete command against all applications in the same namespace. /// Returns a vector that contains all matching results. fn find_app_name_match(&mut self, incomplete_cmd: &str) -> Result, &'static str> { - let namespace_dir = task::with_current_task(|t| - t.get_namespace().dir().clone() - ).map_err(|_| "Failed to get namespace_dir while completing cmdline.")?; + let namespace_dir = task::with_current_task(|t| t.get_namespace().dir().clone()) + .map_err(|_| "Failed to get namespace_dir while completing cmdline.")?; let mut names = namespace_dir.get_file_and_dir_names_starting_with(incomplete_cmd); @@ -831,13 +895,10 @@ impl Shell { /// it succeeds, it then lists all filenames under `foo/bar` and tries to match `examp` against /// those filenames. It returns a vector that contains all matching results. fn find_file_path_match(&mut self, incomplete_cmd: &str) -> Result, &'static str> { - // Stores all possible matches. let mut match_list = Vec::new(); // Get current working dir. - let Ok(mut curr_wd) = task::with_current_task(|t| - t.get_env().lock().working_dir.clone() - ) else { + let Ok(mut curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { return Err("failed to get current task while completing cmdline"); }; @@ -845,36 +906,39 @@ impl Shell { let slash_ending = matches!(incomplete_cmd.chars().last(), Some('/')); // Split the path by slash and filter out consecutive slashes. - let mut nodes: Vec<_> = incomplete_cmd.split('/').filter(|node| { !node.is_empty() }).collect(); + let mut nodes: Vec<_> = incomplete_cmd + .split('/') + .filter(|node| !node.is_empty()) + .collect(); // Get the last node in the path, which is to be completed. let incomplete_node = { // If the command ends with a slash, then we should list all files under // that directory. An empty string is always the prefix of any string. - if slash_ending { - "" - } else { - nodes.pop().unwrap_or("") - } + if slash_ending { "" } else { nodes.pop().unwrap_or("") } }; // Walk through nodes existing in the command. for node in &nodes { let path: &Path = node.as_ref(); match path.get(&curr_wd) { - Some(file_dir_enum) => { - match file_dir_enum { - FileOrDir::Dir(dir) => { curr_wd = dir; }, - FileOrDir::File(_file) => { return Ok(match_list); } + Some(file_dir_enum) => match file_dir_enum { + FileOrDir::Dir(dir) => { + curr_wd = dir; + } + FileOrDir::File(_file) => { + return Ok(match_list); } }, - _ => { return Ok(match_list); } + _ => { + return Ok(match_list); + } }; } // Try to match the name of the file. let locked_working_dir = curr_wd.lock(); - let mut child_list = locked_working_dir.list(); + let mut child_list = locked_working_dir.list(); child_list.reverse(); for child in child_list.iter() { if child.starts_with(incomplete_node) { @@ -893,7 +957,9 @@ impl Shell { // Print all command line choices in aligned colomns. fn aligned_print_match(&mut self, possible_names: Vec) -> Result<(), &'static str> { - if possible_names.is_empty() { return Ok(()); } + if possible_names.is_empty() { + return Ok(()); + } // Get terminal screen width. let (width, _) = self.terminal.lock().get_text_dimensions(); @@ -901,7 +967,7 @@ impl Shell { // Find the length of the longest string. let longest_len = match possible_names.iter().map(|name| name.len()).max() { Some(length) => length, - None => return Ok(()) + None => return Ok(()), }; // Calculate how many we can put on each line. We use four spaces to separate @@ -925,10 +991,11 @@ impl Shell { let mut current_in_line = 0; let mut first_in_line = true; for name in possible_names { - // Pad every string to the same length, same as the longest. let mut padded = name.clone(); - for _ in 0..(longest_len-name.len()) { padded.push(' '); } + for _ in 0..(longest_len - name.len()) { + padded.push(' '); + } // Write to the terminal buffer. if !first_in_line { @@ -964,18 +1031,18 @@ impl Shell { /// Otherwise, it does nothing. It tries to match against all internal commands, /// all applications in the namespace, and all valid file paths. fn complete_cmdline(&mut self) -> Result<(), &'static str> { - // Get the last string slice in the pipe chain. - let cmdline = self.cmdline[0..self.cmdline.len()-self.terminal.lock().get_cursor_offset_from_end()].to_string(); + let cmdline = + self.cmdline[0..self.cmdline.len() - self.terminal.lock().get_cursor_offset_from_end()].to_string(); let last_cmd_in_pipe = match cmdline.split('|').last() { Some(cmd) => cmd, - None => return Ok(()) + None => return Ok(()), }; // Get the last word in the args (or maybe the command name itself). let last_word_in_cmd = match last_cmd_in_pipe.split(' ').last() { Some(word) => word.to_string(), - None => return Ok(()) + None => return Ok(()), }; // Try to find matches. Only match against internal commands and applications @@ -984,14 +1051,18 @@ impl Shell { let mut possible_names = self.find_internal_cmd_match(&last_word_in_cmd)?; possible_names.extend(self.find_app_name_match(&last_word_in_cmd)?.iter().cloned()); if !last_cmd_in_pipe.trim().is_empty() { - possible_names.extend(self.find_file_path_match(&last_word_in_cmd)?.iter().cloned()); + possible_names.extend( + self.find_file_path_match(&last_word_in_cmd)? + .iter() + .cloned(), + ); } // If there is only one possiblity, complete the command line. if possible_names.len() == 1 { let char_num_to_pop = match last_word_in_cmd.split('/').last() { Some(incomplete_basename) => incomplete_basename.len(), - None => last_word_in_cmd.len() + None => last_word_in_cmd.len(), }; for _ in 0..char_num_to_pop { self.remove_char_from_cmdline(true, true)?; @@ -999,7 +1070,8 @@ impl Shell { for c in possible_names[0].chars() { self.insert_char_to_cmdline(c, true)?; } - } else { // Print our choice to the terminal. + } else { + // Print our choice to the terminal. self.aligned_print_match(possible_names)?; } @@ -1011,44 +1083,48 @@ impl Shell { let mut need_prompt = false; let mut job_to_be_removed: Vec = Vec::new(); - // Iterate through all jobs. If any job has exited, remove its stdio queues and remove it from - // the job list. If any job has just stopped, mark it as stopped in the job list. + // Iterate through all jobs. If any job has exited, remove its stdio queues and remove it + // from the job list. If any job has just stopped, mark it as stopped in the job + // list. for (job_num, job) in self.jobs.iter_mut() { - let mut has_alive = false; // mark if there is still non-exited task in the job + let mut has_alive = false; // mark if there is still non-exited task in the job let mut is_stopped = false; // mark if any one of the task has been stopped in this job let task_refs = &job.tasks; for task_ref in task_refs { - if task_ref.has_exited() { // a task has exited + if task_ref.has_exited() { + // a task has exited let exited_task_id = task_ref.id; match task_ref.join() { Ok(ExitValue::Completed(exit_status)) => { - // here: the task ran to completion successfully, so it has an exit value. - // we know the return type of this task is `isize`, - // so we need to downcast it from Any to isize. + // here: the task ran to completion successfully, so it has an exit + // value. we know the return type of this + // task is `isize`, so we need to downcast + // it from Any to isize. let val: Option<&isize> = exit_status.downcast_ref::(); info!("terminal: task [{}] returned exit value: {:?}", exited_task_id, val); if let Some(val) = val { - self.terminal.lock().print_to_terminal( - format!("task [{exited_task_id}] exited with code {val} ({val:#X})\n") - ); + self.terminal.lock().print_to_terminal(format!( + "task [{exited_task_id}] exited with code {val} ({val:#X})\n" + )); } - }, + } Ok(ExitValue::Killed(KillReason::Requested)) => { - // Nothing to do. We have already print "^C" while handling keyboard event. - }, + // Nothing to do. We have already print "^C" while handling keyboard + // event. + } // If the user manually aborts the task Ok(ExitValue::Killed(kill_reason)) => { warn!("task [{}] was killed because {:?}", exited_task_id, kill_reason); - self.terminal.lock().print_to_terminal( - format!("task [{exited_task_id}] was killed because {kill_reason:?}\n") - ); + self.terminal.lock().print_to_terminal(format!( + "task [{exited_task_id}] was killed because {kill_reason:?}\n" + )); } Err(_e) => { - let err_msg = format!("Failed to `join` task [{exited_task_id}] {task_ref:?}, error: {_e:?}", - ); + let err_msg = + format!("Failed to `join` task [{exited_task_id}] {task_ref:?}, error: {_e:?}",); error!("{}", err_msg); self.terminal.lock().print_to_terminal(err_msg); } @@ -1060,12 +1136,11 @@ impl Shell { let mut pipe_queue_iter = job.pipe_queues.iter(); let mut stderr_queue_iter = job.stderr_queues.iter(); let mut task_id_iter = job.task_ids.iter(); - while let (Some(pipe_queue), Some(stderr_queue), Some(task_id)) - = (pipe_queue_iter.next(), stderr_queue_iter.next(), task_id_iter.next()) { - + while let (Some(pipe_queue), Some(stderr_queue), Some(task_id)) = + (pipe_queue_iter.next(), stderr_queue_iter.next(), task_id_iter.next()) + { // Find the exited task by matching task id. if *task_id == exited_task_id { - // Set the EOF flag of its `stdin`, which effectively prevents it's // producer from writing more. (It returns an error upon writing to // the queue which has the EOF flag set.) @@ -1074,8 +1149,9 @@ impl Shell { // Also set the EOF of `stderr`. stderr_queue.get_writer().lock().set_eof(); - // Set the EOF flag of its `stdout`, which effectively notifies the reader - // of the queue that the stream has ended. The `if let` clause should not + // Set the EOF flag of its `stdout`, which effectively notifies the + // reader of the queue that the stream has + // ended. The `if let` clause should not // fail. if let Some(pipe_queue) = pipe_queue_iter.next() { pipe_queue.get_writer().lock().set_eof(); @@ -1083,11 +1159,11 @@ impl Shell { break; } } + } else if !task_ref.is_runnable() && job.status != JobStatus::Stopped { + // task has just stopped - } else if !task_ref.is_runnable() && job.status != JobStatus::Stopped { // task has just stopped - - // One task in this job is stopped, but the status of the Job has not been set to - // `Stopped`. Let's set it now. + // One task in this job is stopped, but the status of the Job has not been set + // to `Stopped`. Let's set it now. job.status = JobStatus::Stopped; // If this is the foreground job, remove it from foreground. @@ -1097,10 +1173,10 @@ impl Shell { } need_refresh = true; - has_alive = true; // This task is stopped, but yet alive. + has_alive = true; // This task is stopped, but yet alive. is_stopped = true; // Mark that this task is just stopped. } else { - has_alive = true; // This is a running task, which is alive. + has_alive = true; // This is a running task, which is alive. } } @@ -1108,10 +1184,9 @@ impl Shell { #[cfg(not(bm_ipc))] { if is_stopped { - self.terminal.lock().print_to_terminal( - format!("[{}] [stopped] {}\n", job_num, job.cmd) - .to_string() - ); + self.terminal + .lock() + .print_to_terminal(format!("[{}] [stopped] {}\n", job_num, job.cmd).to_string()); } } @@ -1125,10 +1200,9 @@ impl Shell { } else { #[cfg(not(bm_ipc))] { - self.terminal.lock().print_to_terminal( - format!("[{}] [finished] {}\n", job_num, job.cmd) - .to_string() - ); + self.terminal + .lock() + .print_to_terminal(format!("[{}] [finished] {}\n", job_num, job.cmd).to_string()); } } } @@ -1144,9 +1218,9 @@ impl Shell { while let Some(_key_event) = consumer.read_one() {} } - // Actually remove the exited jobs from the job list. We could not do it previously since we were - // iterating through the job list. At the same time, remove them from the task_to_job mapping, and - // remove the queues in app_io. + // Actually remove the exited jobs from the job list. We could not do it previously since we + // were iterating through the job list. At the same time, remove them from the + // task_to_job mapping, and remove the queues in app_io. for finished_job_num in job_to_be_removed { if let Some(job) = self.jobs.remove(&finished_job_num) { for task_id in job.task_ids { @@ -1168,7 +1242,8 @@ impl Shell { self.terminal.lock().print_to_terminal(self.cmdline.clone()); } - /// If there is any output event from running application, print it to the screen, otherwise it does nothing. + /// If there is any output event from running application, print it to the screen, otherwise it + /// does nothing. fn check_and_print_app_output(&mut self) -> bool { let mut need_refresh = false; @@ -1178,15 +1253,15 @@ impl Shell { self.terminal.lock().print_to_terminal(s.clone()); } print_event.mark_completed(); - // Goes to the next iteration of the loop after processing print event to ensure that printing is handled before keypresses - need_refresh = true; + // Goes to the next iteration of the loop after processing print event to ensure that + // printing is handled before keypresses + need_refresh = true; } let mut buf: [u8; 256] = [0; 256]; // iterate through all jobs to see if they have something to print for (_job_num, job) in self.jobs.iter() { - // Deal with all stdout output. let mut stdout = job.stdout_reader.lock(); match stdout.try_read(&mut buf) { @@ -1195,8 +1270,10 @@ impl Shell { let s = String::from_utf8_lossy(&buf[0..cnt]); let mut locked_terminal = self.terminal.lock(); locked_terminal.print_to_terminal(s.to_string()); - if cnt != 0 { need_refresh = true; } - }, + if cnt != 0 { + need_refresh = true; + } + } Err(_) => { mem::drop(stdout); error!("failed to read from stdout"); @@ -1213,8 +1290,10 @@ impl Shell { let s = String::from_utf8_lossy(&buf[0..cnt]); let mut locked_terminal = self.terminal.lock(); locked_terminal.print_to_terminal(s.to_string()); - if cnt != 0 { need_refresh = true; } - }, + if cnt != 0 { + need_refresh = true; + } + } Err(_) => { mem::drop(stderr); error!("failed to read from stderr"); @@ -1226,18 +1305,19 @@ impl Shell { need_refresh } - /// This main loop is the core component of the shell's event-driven architecture. The shell receives events - /// from two queues - /// - /// 1) The print queue handles print events from applications. The producer to this queue - /// is any EXTERNAL application that prints to the terminal. - /// - /// 2) The input queue (provided by the window manager when the temrinal request a window) gives key events - /// and resize event to the application. - /// - /// The print queue is handled first inside the loop iteration, which means that all print events in the print - /// queue will always be printed to the text display before input events or any other managerial functions are handled. - /// This allows for clean appending to the scrollback buffer and prevents interleaving of text. + /// This main loop is the core component of the shell's event-driven architecture. The shell + /// receives events from two queues + /// + /// 1) The print queue handles print events from applications. The producer to this queue is any + /// EXTERNAL application that prints to the terminal. + /// + /// 2) The input queue (provided by the window manager when the temrinal request a window) gives + /// key events and resize event to the application. + /// + /// The print queue is handled first inside the loop iteration, which means that all print + /// events in the print queue will always be printed to the text display before input events + /// or any other managerial functions are handled. This allows for clean appending to the + /// scrollback buffer and prevents interleaving of text. fn start(mut self) -> Result<(), &'static str> { let mut need_refresh = false; let mut need_prompt = false; @@ -1245,16 +1325,17 @@ impl Shell { self.terminal.lock().refresh_display()?; loop { - // If there is anything from running applications to be printed, it printed on the screen and then - // return true, so that the loop continues, otherwise nothing happens and we keep on going with the - // loop body. We do so to ensure that printing is handled before keypresses. + // If there is anything from running applications to be printed, it printed on the + // screen and then return true, so that the loop continues, otherwise + // nothing happens and we keep on going with the loop body. We do so to + // ensure that printing is handled before keypresses. if self.check_and_print_app_output() { need_refresh = true; continue; } - // Handles the cleanup of any application task that has finished running, returns whether we need - // a new prompt or need to refresh the screen. + // Handles the cleanup of any application task that has finished running, returns + // whether we need a new prompt or need to refresh the screen. let (need_refresh_on_task_event, need_prompt_on_task_event) = self.task_handler()?; // Print prompt or refresh the screen based on needs. @@ -1265,7 +1346,8 @@ impl Shell { // Handle all available events from the terminal's (its window's) event queue. while let Some(ev) = { - // this weird syntax ensures the terminal lock is dropped before entering the loop body + // this weird syntax ensures the terminal lock is dropped before entering the loop + // body let mut locked_terminal = self.terminal.lock(); locked_terminal.get_event() } { @@ -1286,11 +1368,11 @@ impl Shell { self.key_event_producer.write_one(input_event.key_event); } - _unhandled => { + _unhandled => { // trace!("Shell is ignoring unhandled event: {:?}", _unhandled); } }; - } + } if need_refresh || need_refresh_on_task_event { // update if there are outputs from applications self.terminal.lock().refresh_display()?; @@ -1300,7 +1382,7 @@ impl Shell { let term = self.terminal.lock(); term.window.is_active() }; - + if is_active { self.terminal.lock().display_cursor()?; } @@ -1315,11 +1397,15 @@ impl Shell { if let Err(e) = self.handle_key_event(key_event) { error!("{}", e); } - if key_event.action == KeyAction::Pressed { need_refresh = true; } - } else { // currently the key event queue is empty, break the loop + if key_event.action == KeyAction::Pressed { + need_refresh = true; + } + } else { + // currently the key event queue is empty, break the loop break; } - } else { // currently the key event queue is taken by an application + } else { + // currently the key event queue is taken by an application break; } } @@ -1344,7 +1430,7 @@ impl Shell { "fg" => return true, "bg" => return true, "clear" => return true, - _ => return false + _ => return false, } } false @@ -1361,7 +1447,7 @@ impl Shell { "fg" => self.execute_internal_fg(), "bg" => self.execute_internal_bg(), "clear" => self.execute_internal_clear(), - _ => Ok(()) + _ => Ok(()), } } else { Ok(()) @@ -1382,7 +1468,9 @@ impl Shell { iter.next(); let args: Vec<&str> = iter.collect(); if args.len() != 1 { - self.terminal.lock().print_to_terminal("Usage: bg %job_num\n".to_string()); + self.terminal + .lock() + .print_to_terminal("Usage: bg %job_num\n".to_string()); return Ok(()); } if let Some('%') = args[0].chars().next() { @@ -1400,11 +1488,15 @@ impl Shell { self.redisplay_prompt(); return Ok(()); } - self.terminal.lock().print_to_terminal(format!("No job number {job_num} found!\n")); + self.terminal + .lock() + .print_to_terminal(format!("No job number {job_num} found!\n")); return Ok(()); } } - self.terminal.lock().print_to_terminal("Usage: bg %job_num\n".to_string()); + self.terminal + .lock() + .print_to_terminal("Usage: bg %job_num\n".to_string()); Ok(()) } @@ -1415,7 +1507,9 @@ impl Shell { iter.next(); let args: Vec<&str> = iter.collect(); if args.len() != 1 { - self.terminal.lock().print_to_terminal("Usage: fg %job_num\n".to_string()); + self.terminal + .lock() + .print_to_terminal("Usage: fg %job_num\n".to_string()); return Ok(()); } if let Some('%') = args[0].chars().next() { @@ -1432,11 +1526,15 @@ impl Shell { } return Ok(()); } - self.terminal.lock().print_to_terminal(format!("No job number {job_num} found!\n")); + self.terminal + .lock() + .print_to_terminal(format!("No job number {job_num} found!\n")); return Ok(()); } } - self.terminal.lock().print_to_terminal("Usage: fg %job_num\n".to_string()); + self.terminal + .lock() + .print_to_terminal("Usage: fg %job_num\n".to_string()); Ok(()) } @@ -1445,12 +1543,16 @@ impl Shell { for (job_num, job_ref) in self.jobs.iter() { let status = match &job_ref.status { JobStatus::Running => "running", - JobStatus::Stopped => "stopped" + JobStatus::Stopped => "stopped", }; - self.terminal.lock().print_to_terminal(format!("[{}] [{}] {}\n", job_num, status, job_ref.cmd).to_string()); + self.terminal + .lock() + .print_to_terminal(format!("[{}] [{}] {}\n", job_num, status, job_ref.cmd).to_string()); } if self.jobs.is_empty() { - self.terminal.lock().print_to_terminal("No running or stopped jobs.\n".to_string()); + self.terminal + .lock() + .print_to_terminal("No running or stopped jobs.\n".to_string()); } self.clear_cmdline(false)?; self.redisplay_prompt(); @@ -1458,8 +1560,8 @@ impl Shell { } } - -/// Start a new shell. Shell::start() is an infinite loop, so normally we do not return from this function. +/// Start a new shell. Shell::start() is an infinite loop, so normally we do not return from this +/// function. fn shell_loop(mut _dummy: ()) -> Result<(), &'static str> { Shell::new()?.start()?; Ok(()) diff --git a/applications/swap/src/lib.rs b/applications/swap/src/lib.rs index 675340a0ba..af2df92285 100644 --- a/applications/swap/src/lib.rs +++ b/applications/swap/src/lib.rs @@ -1,54 +1,78 @@ - //! This application is for performing crate management, such as swapping. - #![no_std] #![feature(slice_concat_ext)] extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate itertools; +extern crate crate_swap; +extern crate fs_node; extern crate getopts; +extern crate hpet; extern crate memory; extern crate mod_mgmt; -extern crate crate_swap; -extern crate hpet; -extern crate task; extern crate path; -extern crate fs_node; +extern crate task; use alloc::{ - string::{String, ToString}, - vec::Vec, + string::{ + String, + ToString, + }, sync::Arc, + vec::Vec, }; -use getopts::{Options, Matches}; -use mod_mgmt::{NamespaceDir, IntoCrateObjectFile}; + use crate_swap::SwapRequest; +use fs_node::{ + DirRef, + FileOrDir, +}; +use getopts::{ + Matches, + Options, +}; use hpet::get_hpet; +use mod_mgmt::{ + IntoCrateObjectFile, + NamespaceDir, +}; use path::Path; -use fs_node::{FileOrDir, DirRef}; - pub fn main(args: Vec) -> isize { - #[cfg(not(loadable))] { - println!("****************\nWARNING: Theseus was not built in 'loadable' mode, so crate swapping may not work.\n****************"); + #[cfg(not(loadable))] + { + println!( + "****************\nWARNING: Theseus was not built in 'loadable' mode, so crate swapping may not work.\n****************" + ); } let mut opts = Options::new(); opts.optflag("h", "help", "print this help menu"); opts.optflag("v", "verbose", "enable verbose logging of crate swapping actions"); opts.optflag("c", "cache", "enable caching of the old crate(s) removed by the swapping action"); - opts.optopt("d", "directory-crates", "the absolute path of the base directory where new crates will be loaded from", "PATH"); - opts.optmulti("t", "state-transfer", "the fully-qualified symbol names of state transfer functions, to be run in the order given", "SYMBOL"); + opts.optopt( + "d", + "directory-crates", + "the absolute path of the base directory where new crates will be loaded from", + "PATH", + ); + opts.optmulti( + "t", + "state-transfer", + "the fully-qualified symbol names of state transfer functions, to be run in the order given", + "SYMBOL", + ); let matches = match opts.parse(args) { Ok(m) => m, Err(_f) => { println!("{}", _f); print_usage(opts); - return -1; + return -1; } }; @@ -60,13 +84,12 @@ pub fn main(args: Vec) -> isize { match rmain(matches) { Ok(_) => 0, Err(e) => { - println!("Error: {}", e); + println!("Error: {}", e); -1 } } } - fn rmain(matches: Matches) -> Result<(), String> { let Ok(curr_dir) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { return Err("failed to get current task".to_string()); @@ -75,7 +98,9 @@ fn rmain(matches: Matches) -> Result<(), String> { let path: &Path = path.as_ref(); let dir = match path.get(&curr_dir) { Some(FileOrDir::Dir(dir)) => dir, - _ => return Err(format!("Error: could not find specified namespace crate directory: {path}.")), + _ => { + return Err(format!("Error: could not find specified namespace crate directory: {path}.")); + } }; Some(NamespaceDir::new(dir)) } else { @@ -92,19 +117,10 @@ fn rmain(matches: Matches) -> Result<(), String> { let tuples = parse_input_tuples(&free_args)?; println!("tuples: {:?}", tuples); - - do_swap( - tuples, - &curr_dir, - override_namespace_crate_dir, - state_transfer_functions, - verbose, - cache_old_crates - ) + do_swap(tuples, &curr_dir, override_namespace_crate_dir, state_transfer_functions, verbose, cache_old_crates) } - -/// Takes a string of arguments and parses it into a series of tuples, formatted as +/// Takes a string of arguments and parses it into a series of tuples, formatted as /// `(OLD,NEW[,REEXPORT]) (OLD,NEW[,REEXPORT]) (OLD,NEW[,REEXPORT])...` fn parse_input_tuples(args: &str) -> Result, String> { let mut v: Vec<(&str, &str, bool)> = Vec::new(); @@ -112,16 +128,19 @@ fn parse_input_tuples(args: &str) -> Result, String> { // looking for open parenthesis for (paren_start, _) in open_paren_iter { - let the_rest = args.get((paren_start + 1) ..).ok_or_else(|| "unmatched open parenthesis.".to_string())?; + let the_rest = args + .get((paren_start + 1)..) + .ok_or_else(|| "unmatched open parenthesis.".to_string())?; // find close parenthesis - let parsed = the_rest.find(')') - .and_then(|end_index| the_rest.get(.. end_index)) + let parsed = the_rest + .find(')') + .and_then(|end_index| the_rest.get(..end_index)) .and_then(|inside_paren| { let mut token_iter = inside_paren.split(',').map(str::trim); token_iter.next().and_then(|first| { - token_iter.next().map(|second| { - (first, second, token_iter.next()) - }) + token_iter + .next() + .map(|second| (first, second, token_iter.next())) }) }); match parsed { @@ -134,37 +153,33 @@ fn parse_input_tuples(args: &str) -> Result, String> { } } - if v.is_empty() { - Err("no crate tuples specified.".to_string()) - } - else { - Ok(v) - } + if v.is_empty() { Err("no crate tuples specified.".to_string()) } else { Ok(v) } } - /// Performs the actual swapping of crates. fn do_swap( - tuples: Vec<(&str, &str, bool)>, - curr_dir: &DirRef, - override_namespace_crate_dir: Option, + tuples: Vec<(&str, &str, bool)>, + curr_dir: &DirRef, + override_namespace_crate_dir: Option, state_transfer_functions: Vec, verbose_log: bool, - cache_old_crates: bool + cache_old_crates: bool, ) -> Result<(), String> { let kernel_mmi_ref = memory::get_kernel_mmi_ref().ok_or_else(|| "couldn't get kernel_mmi_ref".to_string())?; - let namespace = task::with_current_task(|t| t.get_namespace().clone()) - .map_err(|_| "Couldn't get current task")?; + let namespace = task::with_current_task(|t| t.get_namespace().clone()).map_err(|_| "Couldn't get current task")?; let swap_requests = { let mut requests: Vec = Vec::with_capacity(tuples.len()); for (old_crate_name, new_crate_str, reexport) in tuples { - - // Check that the new crate file exists. It could be a regular path, or a prefix for a file in the namespace's dir. - // If it's a full path, then we just check that the path points to a valid crate object file. - // Otherwise, we treat it as a prefix for a crate object file name that may be found + // Check that the new crate file exists. It could be a regular path, or a prefix for a + // file in the namespace's dir. If it's a full path, then we just check that + // the path points to a valid crate object file. Otherwise, we treat it as a + // prefix for a crate object file name that may be found let (into_new_crate_file, new_namespace) = { - if let Some(f) = override_namespace_crate_dir.as_ref().and_then(|ns_dir| ns_dir.get_file_starting_with(new_crate_str)) { + if let Some(f) = override_namespace_crate_dir + .as_ref() + .and_then(|ns_dir| ns_dir.get_file_starting_with(new_crate_str)) + { (IntoCrateObjectFile::File(f), None) } else if let Some(FileOrDir::File(f)) = Path::new(new_crate_str).get(curr_dir) { (IntoCrateObjectFile::File(f), None) @@ -172,54 +187,62 @@ fn do_swap( (IntoCrateObjectFile::Prefix(String::from(new_crate_str)), None) } }; - + let swap_req = SwapRequest::new( Some(old_crate_name), Arc::clone(&namespace), into_new_crate_file, new_namespace, - reexport - ).map_err(|invalid_req| format!("{invalid_req:#?}"))?; + reexport, + ) + .map_err(|invalid_req| format!("{invalid_req:#?}"))?; requests.push(swap_req); } requests }; - - let start = get_hpet().as_ref().ok_or("couldn't get HPET timer")?.get_counter(); + + let start = get_hpet() + .as_ref() + .ok_or("couldn't get HPET timer")? + .get_counter(); let swap_result = crate_swap::swap_crates( &namespace, - swap_requests, + swap_requests, override_namespace_crate_dir, state_transfer_functions, kernel_mmi_ref, verbose_log, cache_old_crates, ); - - let end = get_hpet().as_ref().ok_or("couldn't get HPET timer")?.get_counter(); - let hpet_period = get_hpet().as_ref().ok_or("couldn't get HPET timer")?.counter_period_femtoseconds(); + + let end = get_hpet() + .as_ref() + .ok_or("couldn't get HPET timer")? + .get_counter(); + let hpet_period = get_hpet() + .as_ref() + .ok_or("couldn't get HPET timer")? + .counter_period_femtoseconds(); let elapsed_ticks = end - start; - - + match swap_result { Ok(()) => { - println!("Swap operation complete. Elapsed HPET ticks: {}, (HPET Period: {} femtoseconds)", - elapsed_ticks, hpet_period); + println!( + "Swap operation complete. Elapsed HPET ticks: {}, (HPET Period: {} femtoseconds)", + elapsed_ticks, hpet_period + ); Ok(()) } - Err(e) => Err(e.to_string()) + Err(e) => Err(e.to_string()), } } - - fn print_usage(opts: Options) { println!("{}", opts.usage(USAGE)); } - const USAGE: &str = "Usage: swap (OLD1, NEW1 [, true | false]) [(OLD2, NEW2 [, true | false])]... Swaps the given list of crate tuples, with NEW# replacing OLD# in each tuple. The OLD and NEW values are crate names, such as \"my_crate-\". diff --git a/applications/upd/src/lib.rs b/applications/upd/src/lib.rs index 562f6af8cc..08881acff6 100644 --- a/applications/upd/src/lib.rs +++ b/applications/upd/src/lib.rs @@ -2,74 +2,90 @@ //! Theseus's update server to download updated crate object files, //! apply live updates to evolve Theseus, traverse update history, etc. - #![no_std] #![feature(slice_concat_ext)] extern crate alloc; -#[macro_use] extern crate app_io; +#[macro_use] +extern crate app_io; extern crate itertools; +extern crate crate_swap; +extern crate fs_node; extern crate getopts; -extern crate task; -extern crate ota_update_client; -extern crate net; +extern crate memfs; extern crate memory; extern crate mod_mgmt; -extern crate crate_swap; +extern crate net; +extern crate ota_update_client; extern crate path; -extern crate memfs; -extern crate fs_node; -extern crate vfs_node; extern crate spin; +extern crate task; +extern crate vfs_node; - -use core::str::FromStr; use alloc::{ borrow::ToOwned, - string::{String, ToString}, - vec::Vec, collections::BTreeSet, + string::{ + String, + ToString, + }, sync::Arc, + vec::Vec, }; -use spin::Once; -use getopts::{Matches, Options}; -use net::{get_default_interface, IpEndpoint}; -use mod_mgmt::{ - CrateNamespace, - NamespaceDir, - IntoCrateObjectFile, -}; +use core::str::FromStr; + use crate_swap::{ SwapRequest, SwapRequestList, }; +use fs_node::{ + DirRef, + FileOrDir, +}; +use getopts::{ + Matches, + Options, +}; use memfs::MemFile; +use mod_mgmt::{ + CrateNamespace, + IntoCrateObjectFile, + NamespaceDir, +}; +use net::{ + get_default_interface, + IpEndpoint, +}; +use ota_update_client::DIFF_FILE_NAME; use path::Path; +use spin::Once; use vfs_node::VFSDirectory; -use fs_node::{FileOrDir, DirRef}; -use ota_update_client::DIFF_FILE_NAME; - - static VERBOSE: Once = Once::new(); macro_rules! verbose { - () => (VERBOSE.get() == Some(&true)); + () => { + VERBOSE.get() == Some(&true) + }; } - pub fn main(args: Vec) -> isize { let mut opts = Options::new(); opts.optflag("h", "help", "print this help menu"); opts.optflag("v", "verbose", "enable verbose logging"); - opts.optopt ("d", "destination", "specify the IP address (and optionally, the port) of the update server", "IP_ADDR[:PORT]"); + opts.optopt( + "d", + "destination", + "specify the IP address (and optionally, the port) of the update server", + "IP_ADDR[:PORT]", + ); let matches = match opts.parse(args) { Ok(m) => m, Err(_f) => { println!("{}", _f); print_usage(opts); - return -1; + return -1; } }; @@ -78,7 +94,7 @@ pub fn main(args: Vec) -> isize { return 0; } - if matches.free.is_empty() { + if matches.free.is_empty() { println!("Error: missing command."); print_usage(opts); return -1; @@ -91,15 +107,13 @@ pub fn main(args: Vec) -> isize { Err(e) => { println!("Error: {}", e); -1 - } + } } } - fn rmain(matches: Matches) -> Result<(), String> { let mut remote_endpoint = if let Some(ip_str) = matches.opt_str("d") { - IpEndpoint::from_str(&ip_str) - .map_err(|_e| "couldn't parse destination IP address/port".to_string())? + IpEndpoint::from_str(&ip_str).map_err(|_e| "couldn't parse destination IP address/port".to_string())? } else { ota_update_client::default_remote_endpoint() }; @@ -107,64 +121,66 @@ fn rmain(matches: Matches) -> Result<(), String> { remote_endpoint.port = ota_update_client::default_remote_endpoint().port; } - if verbose!() { println!("MATCHES: {:?}", matches.free); } + if verbose!() { + println!("MATCHES: {:?}", matches.free); + } match &*matches.free[0] { - "list" | "ls" => { - list(remote_endpoint, matches.free.get(1)) - } + "list" | "ls" => list(remote_endpoint, matches.free.get(1)), "list-diff" | "ls-diff" => { - let update_build = matches.free.get(1).ok_or_else(|| String::from("missing UPDATE_BUILD argument"))?; + let update_build = matches + .free + .get(1) + .ok_or_else(|| String::from("missing UPDATE_BUILD argument"))?; diff(remote_endpoint, update_build) } "download" | "dl" => { - let update_build = matches.free.get(1).ok_or_else(|| String::from("missing UPDATE_BUILD argument"))?; + let update_build = matches + .free + .get(1) + .ok_or_else(|| String::from("missing UPDATE_BUILD argument"))?; download(remote_endpoint, update_build, matches.free.get(2..)) } "apply" | "ap" => { - let base_dir_path = matches.free.get(1).ok_or_else(|| String::from("missing BASE_DIR path argument"))?; + let base_dir_path = matches + .free + .get(1) + .ok_or_else(|| String::from("missing BASE_DIR path argument"))?; apply(base_dir_path.as_ref()) } - other => { - Err(format!("unrecognized command {other:?}")) - } + other => Err(format!("unrecognized command {other:?}")), } } - - /// Lists the set of crates in the given update_build, /// or if no update build is specified, lists all available update builds by default. fn list(remote_endpoint: IpEndpoint, update_build: Option<&String>) -> Result<(), String> { let iface = get_default_interface().ok_or_else(|| "couldn't get default interface".to_owned())?; if let Some(ub) = update_build { - let listing = ota_update_client::download_listing(&iface, remote_endpoint, ub) - .map_err(|e| e.to_string())?; + let listing = ota_update_client::download_listing(&iface, remote_endpoint, ub).map_err(|e| e.to_string())?; println!("{}", listing.join("\n")); } else { - let update_builds = ota_update_client::download_available_update_builds(&iface, remote_endpoint) - .map_err(|e| e.to_string())?; + let update_builds = + ota_update_client::download_available_update_builds(&iface, remote_endpoint).map_err(|e| e.to_string())?; println!("{}", update_builds.join("\n")); } Ok(()) -} - +} /// Lists the contents of the diff file for the given update build. fn diff(remote_endpoint: IpEndpoint, update_build: &str) -> Result<(), String> { let iface = get_default_interface().ok_or_else(|| "couldn't get default interface".to_owned())?; - let file_str = ota_update_client::download_diff(&iface, remote_endpoint, update_build) - .map_err(|e| e.to_string())?; + let file_str = + ota_update_client::download_diff(&iface, remote_endpoint, update_build).map_err(|e| e.to_string())?; println!("{}", file_str.join("\n")); Ok(()) -} - +} -/// Downloads all of the new or changed crates from the `diff` file of the +/// Downloads all of the new or changed crates from the `diff` file of the fn download(remote_endpoint: IpEndpoint, update_build: &str, crate_list: Option<&[String]>) -> Result<(), String> { let iface = get_default_interface().ok_or_else(|| "couldn't get default interface".to_owned())?; println!("Downloading crates..."); @@ -174,7 +190,8 @@ fn download(remote_endpoint: IpEndpoint, update_build: &str, crate_list: Option< let crates = if let Some(crate_list) = crate_list { let crate_set = crate_list.iter().cloned().collect::>(); - ota_update_client::download_crates(&iface, remote_endpoint, update_build, crate_set).map_err(|e| e.to_string())? + ota_update_client::download_crates(&iface, remote_endpoint, update_build, crate_set) + .map_err(|e| e.to_string())? } else { let diff_lines = ota_update_client::download_diff(&iface, remote_endpoint, update_build) .map_err(|e| format!("failed to download diff file for {update_build}, error: {e}"))?; @@ -182,12 +199,13 @@ fn download(remote_endpoint: IpEndpoint, update_build: &str, crate_list: Option< // download all of the new crates let new_crates_to_download: BTreeSet = diff.pairs.iter().map(|(_old, new)| new.clone()).collect(); - let crates = ota_update_client::download_crates(&iface, remote_endpoint, update_build, new_crates_to_download).map_err(|e| e.to_string())?; + let crates = ota_update_client::download_crates(&iface, remote_endpoint, update_build, new_crates_to_download) + .map_err(|e| e.to_string())?; diff_file_lines = Some(diff_lines); crates }; - - // save each new crate to a file + + // save each new crate to a file let Ok(curr_dir) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { return Err("failed to get current task's working directory".to_string()); }; @@ -195,9 +213,12 @@ fn download(remote_endpoint: IpEndpoint, update_build: &str, crate_list: Option< for df in crates.into_iter() { let content = df.content.as_result_err_str()?; let size = content.len(); - // The name of the crate file that we downloaded is something like: "/keyboard_log/k#keyboard-36be916209949cef.o". - // We need to get just the basename of the file, then remove the crate type prefix ("k#"). - let file_name = Path::new(&df.name).file_name().ok_or("crate file path did not have file name")?; + // The name of the crate file that we downloaded is something like: + // "/keyboard_log/k#keyboard-36be916209949cef.o". We need to get just the basename + // of the file, then remove the crate type prefix ("k#"). + let file_name = Path::new(&df.name) + .file_name() + .ok_or("crate file path did not have file name")?; let cfile = new_namespace_dir.write_crate_object_file(file_name, content)?; println!("Downloaded crate: {:?}, size {}", cfile.lock().get_absolute_path(), size); } @@ -211,7 +232,6 @@ fn download(remote_endpoint: IpEndpoint, update_build: &str, crate_list: Option< Ok(()) } - /// Applies an already-downloaded update according the "diff.txt" file /// that must be in the given base directory. fn apply(base_dir_path: &Path) -> Result<(), String> { @@ -227,26 +247,28 @@ fn apply(base_dir_path: &Path) -> Result<(), String> { Some(FileOrDir::Dir(d)) => NamespaceDir::new(d), _ => return Err(format!("cannot find an update base directory at path {base_dir_path}")), }; - let diff_file = match new_namespace_dir.lock().get(DIFF_FILE_NAME) { + let diff_file = match new_namespace_dir.lock().get(DIFF_FILE_NAME) { Some(FileOrDir::File(f)) => f, - _ => return Err(format!("cannot find diff file expected at {base_dir_path}/{DIFF_FILE_NAME}")), + _ => { + return Err(format!("cannot find diff file expected at {base_dir_path}/{DIFF_FILE_NAME}")); + } }; - let mut diff_content: Vec = alloc::vec::from_elem(0, diff_file.lock().len()); + let mut diff_content: Vec = alloc::vec::from_elem(0, diff_file.lock().len()); let _bytes_read = diff_file.lock().read_at(&mut diff_content, 0)?; - let diffs = ota_update_client::as_lines(&diff_content).map_err(|e| e.to_string()) + let diffs = ota_update_client::as_lines(&diff_content) + .map_err(|e| e.to_string()) .and_then(|diff_lines| ota_update_client::parse_diff_lines(&diff_lines).map_err(|e| e.to_string()))?; - - // We can't immediately just replace the existing files in the current namespace + // We can't immediately just replace the existing files in the current namespace // because that would cause inconsistencies if another crate was loaded (using the new files) - // before the currently-loaded ones were replaced. + // before the currently-loaded ones were replaced. // Instead, we need to just keep the new files in a new folder for now (which they already are), - // and tell the crate swapping routine to use them. - // But first, we must create SwapRequests, which validates that the current namespace + // and tell the crate swapping routine to use them. + // But first, we must create SwapRequests, which validates that the current namespace // actually has all of the old crates that are expected/listed in the diff file. // After the live swap of all loaded crates in the namespace has completed, - // it is safe to actually replace the old crate object files with the new ones. - + // it is safe to actually replace the old crate object files with the new ones. + let curr_namespace = get_my_current_namespace(); // Now we create swap requests based on the contents of the diff file let mut swap_requests = SwapRequestList::new(); @@ -254,59 +276,67 @@ fn apply(base_dir_path: &Path) -> Result<(), String> { for (old_crate_module_file_name, new_crate_module_file_name) in diffs.pairs.into_iter() { println!("Looking at diff {} -> {}", old_crate_module_file_name, new_crate_module_file_name); let old_crate_name = if old_crate_module_file_name.is_empty() { - // An empty old_crate_name indicates that there is no old crate or object file to remove, we are just loading a new crate (or inserting its object file) + // An empty old_crate_name indicates that there is no old crate or object file to + // remove, we are just loading a new crate (or inserting its object file) None } else { let old_crate_name = mod_mgmt::crate_name_from_path(old_crate_module_file_name.as_ref()) .ok_or("invalid old crate module file name")? .to_string(); if curr_namespace.get_crate(&old_crate_name).is_none() { - println!("\t Note: old crate {:?} was not currently loaded into namespace {:?}.", old_crate_name, curr_namespace.name()); + println!( + "\t Note: old crate {:?} was not currently loaded into namespace {:?}.", + old_crate_name, + curr_namespace.name() + ); } Some(old_crate_name) }; - - let new_crate_file = new_namespace_dir.get_crate_object_file(&new_crate_module_file_name).ok_or_else(|| - format!("cannot find new crate file {new_crate_module_file_name:?} in new namespace dir {base_dir_path}") - )?; + + let new_crate_file = new_namespace_dir + .get_crate_object_file(&new_crate_module_file_name) + .ok_or_else(|| { + format!( + "cannot find new crate file {new_crate_module_file_name:?} in new namespace dir {base_dir_path}" + ) + })?; let swap_req = SwapRequest::new( old_crate_name.as_deref(), Arc::clone(&curr_namespace), IntoCrateObjectFile::File(new_crate_file), None, // all diff-based swaps occur within the same namespace - false - ).map_err(|invalid_req| format!("{invalid_req:#?}"))?; + false, + ) + .map_err(|invalid_req| format!("{invalid_req:#?}"))?; swap_requests.push(swap_req); } // now do the actual live crate swap at runtime crate_swap::swap_crates( &curr_namespace, - swap_requests, - Some(new_namespace_dir), - diffs.state_transfer_functions, + swap_requests, + Some(new_namespace_dir), + diffs.state_transfer_functions, kernel_mmi_ref, false, // verbose logging false, // enable_crate_cache - ).map_err(|e| format!("crate swapping failed, error: {e}"))?; + ) + .map_err(|e| format!("crate swapping failed, error: {e}"))?; Ok(()) } - fn get_my_current_namespace() -> Arc { - task::with_current_task(|t| t.get_namespace().clone()) - .unwrap_or_else(|_| - mod_mgmt::get_initial_kernel_namespace() - .expect("BUG: initial kernel namespace wasn't initialized") - .clone() - ) + task::with_current_task(|t| t.get_namespace().clone()).unwrap_or_else(|_| { + mod_mgmt::get_initial_kernel_namespace() + .expect("BUG: initial kernel namespace wasn't initialized") + .clone() + }) } - -/// Creates a new directory with a unique name in the given `parent_dir`. -/// For example, given a base_name of "my_dir", +/// Creates a new directory with a unique name in the given `parent_dir`. +/// For example, given a base_name of "my_dir", /// it will create a directory "my_dir.2" if "my_dir" and "my_dir.1" already exist. fn make_unique_directory(base_name: &str, parent_dir: &DirRef) -> Result { if parent_dir.lock().get(base_name).is_none() { @@ -316,18 +346,16 @@ fn make_unique_directory(base_name: &str, parent_dir: &DirRef) -> Result