diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d990497..826b8ef 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,4 +17,5 @@ jobs: - run: sudo apt-get -qqy update - run: sudo apt-get -qqy install qemu-system-x86 - run: make + - run: make user-rust - run: make test diff --git a/Makefile b/Makefile index 3cf4921..068c057 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ user-rust: -C relocation-model=static basename -s .rs src/bin/*.rs | xargs -I {} \ cp target/x86_64-moros/release/{} dsk/bin/{} + strip dsk/bin/* bin = target/x86_64-moros/release/bootimage-moros.bin img = disk.img diff --git a/dsk/bin/clear b/dsk/bin/clear index 034d10f..ff53afd 100644 Binary files a/dsk/bin/clear and b/dsk/bin/clear differ diff --git a/dsk/bin/halt b/dsk/bin/halt index aad42b0..65e3a90 100644 Binary files a/dsk/bin/halt and b/dsk/bin/halt differ diff --git a/dsk/bin/hello b/dsk/bin/hello index f1b9afe..0e17de4 100755 Binary files a/dsk/bin/hello and b/dsk/bin/hello differ diff --git a/dsk/bin/print b/dsk/bin/print new file mode 100644 index 0000000..6920fbc Binary files /dev/null and b/dsk/bin/print differ diff --git a/dsk/bin/reboot b/dsk/bin/reboot index 9a2dc21..c83f324 100644 Binary files a/dsk/bin/reboot and b/dsk/bin/reboot differ diff --git a/dsk/bin/sleep b/dsk/bin/sleep index 430e4fe..9fe01fd 100644 Binary files a/dsk/bin/sleep and b/dsk/bin/sleep differ diff --git a/src/api/process.rs b/src/api/process.rs index 641f3f5..20f89d2 100644 --- a/src/api/process.rs +++ b/src/api/process.rs @@ -1,8 +1,8 @@ use crate::api::syscall; -pub fn spawn(path: &str) -> Result<(), ()> { +pub fn spawn(path: &str, args: &[&str]) -> Result<(), ()> { if syscall::info(path).is_some() { - return syscall::spawn(path); + return syscall::spawn(path, args); } Err(()) } diff --git a/src/api/syscall.rs b/src/api/syscall.rs index 708341b..0dcaae0 100644 --- a/src/api/syscall.rs +++ b/src/api/syscall.rs @@ -80,10 +80,12 @@ pub fn close(handle: usize) { unsafe { syscall!(CLOSE, handle as usize) }; } -pub fn spawn(path: &str) -> Result<(), ()> { - let ptr = path.as_ptr() as usize; - let len = path.len() as usize; - let res = unsafe { syscall!(SPAWN, ptr, len) } as isize; +pub fn spawn(path: &str, args: &[&str]) -> Result<(), ()> { + let path_ptr = path.as_ptr() as usize; + let path_len = path.len() as usize; + let args_ptr = args.as_ptr() as usize; + let args_len = args.len() as usize; + let res = unsafe { syscall!(SPAWN, path_ptr, path_len, args_ptr, args_len) } as isize; if res.is_negative() { Err(()) } else { @@ -91,12 +93,16 @@ pub fn spawn(path: &str) -> Result<(), ()> { } } +pub fn stop(code: usize) { + unsafe { syscall!(STOP, code) }; +} + pub fn reboot() { - unsafe { syscall!(STOP, 0xcafe) }; + stop(0xcafe); } pub fn halt() { - unsafe { syscall!(STOP, 0xdead) }; + stop(0xdead); } #[test_case] diff --git a/src/bin/print.rs b/src/bin/print.rs new file mode 100644 index 0000000..efca1fc --- /dev/null +++ b/src/bin/print.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +use moros::api::syscall; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + syscall::write(1, b"An exception occured!\n"); + loop {} +} + +#[no_mangle] +pub unsafe extern "sysv64" fn _start(args_ptr: u64, args_len: usize) { + let args = core::slice::from_raw_parts(args_ptr as *const _, args_len); + let code = main(args); + syscall::exit(code); +} + +fn main(args: &[&str]) -> usize { + let n = args.len(); + for i in 1..n { + syscall::write(1, args[i].as_bytes()); + if i < n - 1 { + syscall::write(1, b" "); + } + } + syscall::write(1, b"\n"); + 0 +} diff --git a/src/bin/sleep.rs b/src/bin/sleep.rs new file mode 100644 index 0000000..ab11079 --- /dev/null +++ b/src/bin/sleep.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] + +use moros::api::syscall; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + syscall::write(1, b"An exception occured!\n"); + loop {} +} + +#[no_mangle] +pub unsafe extern "sysv64" fn _start(args_ptr: u64, args_len: usize) { + let args = core::slice::from_raw_parts(args_ptr as *const _, args_len); + let code = main(args); + syscall::exit(code); +} + +fn main(args: &[&str]) -> usize { + if args.len() == 2 { + if let Ok(duration) = args[1].parse::() { + syscall::sleep(duration); + return 0 + } + } + 1 +} diff --git a/src/sys/allocator.rs b/src/sys/allocator.rs index ce474e5..d0069be 100644 --- a/src/sys/allocator.rs +++ b/src/sys/allocator.rs @@ -44,16 +44,18 @@ pub fn init_heap(mapper: &mut impl Mapper, frame_allocator: &mut impl Ok(()) } -pub fn alloc_pages(addr: u64, size: u64) { +pub fn alloc_pages(addr: u64, size: usize) { + //debug!("Alloc pages (addr={:#x}, size={})", addr, size); let mut mapper = unsafe { sys::mem::mapper(VirtAddr::new(sys::mem::PHYS_MEM_OFFSET)) }; let mut frame_allocator = unsafe { sys::mem::BootInfoFrameAllocator::init(sys::mem::MEMORY_MAP.unwrap()) }; let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE; let pages = { let start_page = Page::containing_address(VirtAddr::new(addr)); - let end_page = Page::containing_address(VirtAddr::new(addr + size - 1)); + let end_page = Page::containing_address(VirtAddr::new(addr + (size as u64) - 1)); Page::range_inclusive(start_page, end_page) }; for page in pages { + //debug!("Alloc page {:?}", page); if let Some(frame) = frame_allocator.allocate_frame() { unsafe { if let Ok(mapping) = mapper.map_to(page, frame, flags, &mut frame_allocator) { @@ -71,11 +73,11 @@ pub fn alloc_pages(addr: u64, size: u64) { use x86_64::structures::paging::page::PageRangeInclusive; // TODO: Replace `free` by `dealloc` -pub fn free_pages(addr: u64, size: u64) { +pub fn free_pages(addr: u64, size: usize) { let mut mapper = unsafe { sys::mem::mapper(VirtAddr::new(sys::mem::PHYS_MEM_OFFSET)) }; let pages: PageRangeInclusive = { let start_page = Page::containing_address(VirtAddr::new(addr)); - let end_page = Page::containing_address(VirtAddr::new(addr + size - 1)); + let end_page = Page::containing_address(VirtAddr::new(addr + (size as u64) - 1)); Page::range_inclusive(start_page, end_page) }; for page in pages { diff --git a/src/sys/idt.rs b/src/sys/idt.rs index a766770..276c262 100644 --- a/src/sys/idt.rs +++ b/src/sys/idt.rs @@ -180,13 +180,14 @@ extern "sysv64" fn syscall_handler(stack_frame: &mut InterruptStackFrame, regs: let arg1 = regs.rdi; let arg2 = regs.rsi; let arg3 = regs.rdx; + let arg4 = regs.r8; if n == sys::syscall::number::SPAWN { // Backup CPU context sys::process::set_stack_frame(**stack_frame); sys::process::set_registers(*regs); } - let res = sys::syscall::dispatcher(n, arg1, arg2, arg3); + let res = sys::syscall::dispatcher(n, arg1, arg2, arg3, arg4); if n == sys::syscall::number::EXIT { // Restore CPU context let sf = sys::process::stack_frame(); diff --git a/src/sys/process.rs b/src/sys/process.rs index 6746045..cabf7cc 100644 --- a/src/sys/process.rs +++ b/src/sys/process.rs @@ -14,6 +14,7 @@ use x86_64::structures::idt::InterruptStackFrameValue; const MAX_FILE_HANDLES: usize = 64; const MAX_PROCS: usize = 2; // TODO: Update this when more than one process can run at once +const MAX_PROC_SIZE: usize = 1 << 20; // 1 MB lazy_static! { pub static ref PID: AtomicUsize = AtomicUsize::new(0); @@ -145,7 +146,12 @@ pub fn set_code_addr(addr: u64) { } pub fn ptr_from_addr(addr: u64) -> *mut u8 { - (code_addr() + addr) as *mut u8 + let base = code_addr(); + if addr < base { + (base + addr) as *mut u8 + } else { + addr as *mut u8 + } } pub fn registers() -> Registers { @@ -175,7 +181,7 @@ pub fn set_stack_frame(stack_frame: InterruptStackFrameValue) { pub fn exit() { let table = PROCESS_TABLE.read(); let proc = &table[id()]; - sys::allocator::free_pages(proc.code_addr, proc.code_size); + sys::allocator::free_pages(proc.code_addr, MAX_PROC_SIZE); MAX_PID.fetch_sub(1, Ordering::SeqCst); set_id(0); // FIXME: No process manager so we switch back to process 0 } @@ -193,7 +199,6 @@ use core::sync::atomic::AtomicU64; use x86_64::VirtAddr; static CODE_ADDR: AtomicU64 = AtomicU64::new((sys::allocator::HEAP_START as u64) + (16 << 20)); -const PAGE_SIZE: u64 = 4 * 1024; #[repr(align(8), C)] #[derive(Debug, Clone, Copy, Default)] @@ -216,7 +221,7 @@ const BIN_MAGIC: [u8; 4] = [0x7F, b'B', b'I', b'N']; pub struct Process { id: usize, code_addr: u64, - code_size: u64, + stack_addr: u64, entry_point: u64, stack_frame: InterruptStackFrameValue, registers: Registers, @@ -235,7 +240,7 @@ impl Process { Self { id, code_addr: 0, - code_size: 0, + stack_addr: 0, entry_point: 0, stack_frame: isf, registers: Registers::default(), @@ -243,13 +248,13 @@ impl Process { } } - pub fn spawn(bin: &[u8]) -> Result<(), ()> { + pub fn spawn(bin: &[u8], args_ptr: usize, args_len: usize) -> Result<(), ()> { if let Ok(pid) = Self::create(bin) { let proc = { let table = PROCESS_TABLE.read(); table[pid].clone() }; - proc.exec(); + proc.exec(args_ptr, args_len); Ok(()) } else { Err(()) @@ -257,35 +262,29 @@ impl Process { } fn create(bin: &[u8]) -> Result { - // Allocate some memory for the code and the stack of the program - let code_size = 1 * PAGE_SIZE; - let code_addr = CODE_ADDR.fetch_add(code_size, Ordering::SeqCst); + let proc_size = MAX_PROC_SIZE as u64; + let code_addr = CODE_ADDR.fetch_add(proc_size, Ordering::SeqCst); + let stack_addr = code_addr + proc_size; let mut entry_point = 0; let code_ptr = code_addr as *mut u8; if bin[0..4] == ELF_MAGIC { // ELF binary if let Ok(obj) = object::File::parse(bin) { - sys::allocator::alloc_pages(code_addr, code_size); + //sys::allocator::alloc_pages(code_addr, code_size); entry_point = obj.entry(); for segment in obj.segments() { let addr = segment.address() as usize; if let Ok(data) = segment.data() { - for (i, op) in data.iter().enumerate() { - unsafe { - let ptr = code_ptr.add(addr + i); - core::ptr::write(ptr, *op); - } + for (i, b) in data.iter().enumerate() { + unsafe { core::ptr::write(code_ptr.add(addr + i), *b) }; } } } } } else if bin[0..4] == BIN_MAGIC { // Flat binary - sys::allocator::alloc_pages(code_addr, code_size); - for (i, op) in bin.iter().skip(4).enumerate() { - unsafe { - let ptr = code_ptr.add(i); - core::ptr::write(ptr, *op); - } + //sys::allocator::alloc_pages(code_addr, code_size); + for (i, b) in bin.iter().skip(4).enumerate() { + unsafe { core::ptr::write(code_ptr.add(i), *b) }; } } else { return Err(()); @@ -299,28 +298,58 @@ impl Process { let stack_frame = parent.stack_frame; let id = MAX_PID.fetch_add(1, Ordering::SeqCst); - let proc = Process { id, code_addr, code_size, entry_point, data, stack_frame, registers }; + let proc = Process { id, code_addr, stack_addr, entry_point, data, stack_frame, registers }; table[id] = Box::new(proc); Ok(id) } // Switch to user mode and execute the program - fn exec(&self) { + fn exec(&self, args_ptr: usize, args_len: usize) { + let args_ptr = ptr_from_addr(args_ptr as u64) as usize; + let args: &[&str] = unsafe { core::slice::from_raw_parts(args_ptr as *const &str, args_len) }; + let heap_addr = self.code_addr + (self.stack_addr - self.code_addr) / 2; + let mut ptr = heap_addr; + let vec: Vec<&str> = args.iter().map(|arg| { + let src_len = arg.len(); + let src_ptr = arg.as_ptr(); + let dst_ptr = ptr as *mut u8; + //let dst_ptr_translated = ((dst_ptr as u64) - self.code_addr) as *const u8; // Userspace address + ptr = ((dst_ptr as usize) + src_len) as u64; + unsafe { + core::ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len); + //core::str::from_utf8_unchecked(core::slice::from_raw_parts(dst_ptr_translated, src_len)) + core::str::from_utf8_unchecked(core::slice::from_raw_parts(dst_ptr, src_len)) + } + }).collect(); + let args = vec.as_slice(); + let src_len = args.len(); + let src_ptr = args.as_ptr(); + let dst_ptr = ptr as *mut &str; + let args: &[&str] = unsafe { + core::ptr::copy_nonoverlapping(src_ptr, dst_ptr, src_len); + core::slice::from_raw_parts(dst_ptr, src_len) + }; + + //let args_ptr = (args.as_ptr() as u64) - self.code_addr; // userspace address + let args_ptr = args.as_ptr() as u64; + set_id(self.id); // Change PID unsafe { asm!( "cli", // Disable interrupts - "push rax", // Stack segment (SS) - "push rsi", // Stack pointer (RSP) + "push {:r}", // Stack segment (SS) + "push {:r}", // Stack pointer (RSP) "push 0x200", // RFLAGS with interrupts enabled - "push rdx", // Code segment (CS) - "push rdi", // Instruction pointer (RIP) + "push {:r}", // Code segment (CS) + "push {:r}", // Instruction pointer (RIP) "iretq", - in("rax") GDT.1.user_data.0, - in("rsi") self.code_addr + self.code_size, - in("rdx") GDT.1.user_code.0, - in("rdi") self.code_addr + self.entry_point, + in(reg) GDT.1.user_data.0, + in(reg) self.stack_addr, + in(reg) GDT.1.user_code.0, + in(reg) self.code_addr + self.entry_point, + in("rdi") args_ptr, + in("rsi") args_len, ); } } diff --git a/src/sys/syscall/mod.rs b/src/sys/syscall/mod.rs index 3dc6a6b..00f4313 100644 --- a/src/sys/syscall/mod.rs +++ b/src/sys/syscall/mod.rs @@ -10,7 +10,7 @@ use core::arch::asm; * Dispatching system calls */ -pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize) -> usize { +pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> usize { match n { number::EXIT => { service::exit(arg1) @@ -50,7 +50,7 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize) -> usize { let handle = arg1; let ptr = sys::process::ptr_from_addr(arg2 as u64); let len = arg3; - let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len) }; + let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len) }; // TODO: Remove mut service::write(handle, buf) as usize } number::CLOSE => { @@ -64,10 +64,14 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize) -> usize { service::dup(old_handle, new_handle) as usize } number::SPAWN => { - let ptr = sys::process::ptr_from_addr(arg1 as u64); - let len = arg2; - let path = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len)) }; - service::spawn(path) as usize + let path_ptr = sys::process::ptr_from_addr(arg1 as u64); + let path_len = arg2; + let path = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(path_ptr, path_len)) }; + + let args_ptr = arg3; + let args_len = arg4; + + service::spawn(path, args_ptr, args_len) as usize } number::STOP => { service::stop(arg1) @@ -125,6 +129,17 @@ pub unsafe fn syscall3(n: usize, arg1: usize, arg2: usize, arg3: usize) -> usize res } +#[doc(hidden)] +pub unsafe fn syscall4(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> usize { + let res: usize; + asm!( + "int 0x80", in("rax") n, + in("rdi") arg1, in("rsi") arg2, in("rdx") arg3, in("r8") arg4, + lateout("rax") res + ); + res +} + #[macro_export] macro_rules! syscall { ($n:expr) => ( @@ -139,4 +154,7 @@ macro_rules! syscall { ($n:expr, $a1:expr, $a2:expr, $a3:expr) => ( $crate::sys::syscall::syscall3( $n as usize, $a1 as usize, $a2 as usize, $a3 as usize)); + ($n:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => ( + $crate::sys::syscall::syscall4( + $n as usize, $a1 as usize, $a2 as usize, $a3 as usize, $a4 as usize)); } diff --git a/src/sys/syscall/service.rs b/src/sys/syscall/service.rs index ecbfb52..b1c7b6e 100644 --- a/src/sys/syscall/service.rs +++ b/src/sys/syscall/service.rs @@ -80,7 +80,7 @@ pub fn close(handle: usize) { sys::process::delete_file_handle(handle); } -pub fn spawn(path: &str) -> isize { +pub fn spawn(path: &str, args_ptr: usize, args_len: usize) -> isize { let path = match sys::fs::canonicalize(path) { Ok(path) => path, Err(_) => return -1, @@ -89,7 +89,7 @@ pub fn spawn(path: &str) -> isize { let mut buf = vec![0; file.size()]; if let Ok(bytes) = file.read(&mut buf) { buf.resize(bytes, 0); - if Process::spawn(&buf).is_ok() { + if Process::spawn(&buf, args_ptr, args_len).is_ok() { return 0; } } @@ -110,7 +110,9 @@ pub fn stop(code: usize) -> usize { 0xdead => { // Halt sys::acpi::shutdown(); } - _ => {} + _ => { + debug!("STOP SYSCALL: Invalid code '{:#x}' received", code); + } } 0 } diff --git a/src/usr/install.rs b/src/usr/install.rs index 54ee53e..114e707 100644 --- a/src/usr/install.rs +++ b/src/usr/install.rs @@ -20,7 +20,9 @@ pub fn copy_files(verbose: bool) { copy_file("/bin/clear", include_bytes!("../../dsk/bin/clear"), verbose); copy_file("/bin/halt", include_bytes!("../../dsk/bin/halt"), verbose); copy_file("/bin/hello", include_bytes!("../../dsk/bin/hello"), verbose); + copy_file("/bin/print", include_bytes!("../../dsk/bin/print"), verbose); copy_file("/bin/reboot", include_bytes!("../../dsk/bin/reboot"), verbose); + copy_file("/bin/sleep", include_bytes!("../../dsk/bin/sleep"), verbose); create_dir("/dev/clk", verbose); // Clocks create_dev("/dev/clk/uptime", DeviceType::Uptime, verbose); diff --git a/src/usr/mod.rs b/src/usr/mod.rs index 4e4fe60..1bbaec8 100644 --- a/src/usr/mod.rs +++ b/src/usr/mod.rs @@ -26,11 +26,9 @@ pub mod memory; pub mod net; pub mod pci; pub mod pow; -pub mod print; pub mod r#move; pub mod read; pub mod shell; -pub mod sleep; pub mod socket; pub mod tcp; pub mod time; diff --git a/src/usr/print.rs b/src/usr/print.rs deleted file mode 100644 index 33b11f4..0000000 --- a/src/usr/print.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::usr; - -pub fn main(args: &[&str]) -> usr::shell::ExitCode { - let n = args.len(); - for i in 1..n { - print!("{}", args[i]); - if i < n - 1 { - print!(" "); - } - } - println!(); - usr::shell::ExitCode::CommandSuccessful -} diff --git a/src/usr/shell.rs b/src/usr/shell.rs index 0cbaf96..e1d78ff 100644 --- a/src/usr/shell.rs +++ b/src/usr/shell.rs @@ -12,11 +12,11 @@ use alloc::vec::Vec; use alloc::string::{String, ToString}; // TODO: Scan /bin -const AUTOCOMPLETE_COMMANDS: [&str; 37] = [ +const AUTOCOMPLETE_COMMANDS: [&str; 35] = [ "2048", "base64", "calc", "colors", "copy", "date", "delete", "dhcp", "disk", "edit", "env", "exit", "geotime", "goto", "help", "hex", "host", "http", "httpd", "install", - "keyboard", "lisp", "list", "memory", "move", "net", "pci", "print", "read", - "shell", "sleep", "socket", "tcp", "time", "user", "vga", "write" + "keyboard", "lisp", "list", "memory", "move", "net", "pci", "read", + "shell", "socket", "tcp", "time", "user", "vga", "write" ]; #[repr(u8)] @@ -276,7 +276,6 @@ pub fn exec(cmd: &str, env: &mut BTreeMap) -> ExitCode { "m" | "move" => usr::r#move::main(&args), "n" => ExitCode::CommandUnknown, "o" => ExitCode::CommandUnknown, - "p" | "print" => usr::print::main(&args), "q" | "quit" | "exit" => ExitCode::ShellExit, "r" | "read" => usr::read::main(&args), "s" => ExitCode::CommandUnknown, @@ -289,7 +288,6 @@ pub fn exec(cmd: &str, env: &mut BTreeMap) -> ExitCode { "z" => ExitCode::CommandUnknown, "vga" => usr::vga::main(&args), "sh" | "shell" => usr::shell::main(&args), - "sleep" => usr::sleep::main(&args), "calc" => usr::calc::main(&args), "base64" => usr::base64::main(&args), "date" => usr::date::main(&args), @@ -328,7 +326,7 @@ pub fn exec(cmd: &str, env: &mut BTreeMap) -> ExitCode { ExitCode::CommandSuccessful } Some(FileType::File) => { - if api::process::spawn(&path).is_ok() { + if api::process::spawn(&path, &args[1..]).is_ok() { // TODO: get exit code ExitCode::CommandSuccessful } else { @@ -337,8 +335,12 @@ pub fn exec(cmd: &str, env: &mut BTreeMap) -> ExitCode { } } _ => { - // TODO: add aliases - if api::process::spawn(&format!("/bin/{}", args[0])).is_ok() { + // TODO: add aliases command instead of hardcoding them + let name = match args[0] { + "p" => "print", + arg => arg, + }; + if api::process::spawn(&format!("/bin/{}", name), &args).is_ok() { ExitCode::CommandSuccessful } else { error!("Could not execute '{}'", cmd); diff --git a/src/usr/sleep.rs b/src/usr/sleep.rs deleted file mode 100644 index 047bb9b..0000000 --- a/src/usr/sleep.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::usr; -use crate::api::syscall; - -pub fn main(args: &[&str]) -> usr::shell::ExitCode { - if args.len() == 2 { - if let Ok(duration) = args[1].parse::() { - syscall::sleep(duration); - return usr::shell::ExitCode::CommandSuccessful; - } - } - usr::shell::ExitCode::CommandError -}