commit 2fa6237406f05c2d2849360e10c523e22f72bc36 Author: Justine Pelletreau Date: Tue Dec 6 17:59:26 2022 +0100 Starting point diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e420ee4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/* diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..348d06b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Rshell" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6ea7fb4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "Rshell" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f9f57f --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# RShell +Rust Shell. This is an attempt to create a simple shell in Rust, because why not. + +My starting point is [this article](https://www.joshmcguigan.com/blog/build-your-own-shell-rust/) which is excellent. I will start with the final form of a shell given in the article and try to reformat it: +* Make it more easy to extend +* Make it a lib + +I can also add basic features: +* History +* customizable prompt +* Colours ? + +Later, I can even make something useful out of it. For example, I could make it so that it is configurable via a config file. I could make it the default terminal via ssh for some user and make it so the terminal opens directly in a docker container, or chrooted or smthing, as a basic form of honeypot. + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0547b25 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,80 @@ +use std::process::Command; +use std::io::stdin; +use std::io::stdout; +use std::io::Write; +use std::path::Path; +use std::env; +use std::process::Child; +use std::process::Stdio; + +fn main(){ + loop { + print!("> "); + stdout().flush(); + + let mut input = String::new(); + stdin().read_line(&mut input).unwrap(); + + // must be peekable so we know when we are on the last command + let mut commands = input.trim().split(" | ").peekable(); + let mut previous_command = None; + + while let Some(command) = commands.next() { + + let mut parts = command.trim().split_whitespace(); + let command = parts.next().unwrap(); + let args = parts; + + match command { + "cd" => { + let new_dir = args.peekable().peek() + .map_or("/", |x| *x); + let root = Path::new(new_dir); + if let Err(e) = env::set_current_dir(&root) { + eprintln!("{}", e); + } + + previous_command = None; + }, + "exit" => return, + command => { + let stdin = previous_command + .map_or( + Stdio::inherit(), + |output: Child| Stdio::from(output.stdout.unwrap()) + ); + + let stdout = if commands.peek().is_some() { + // there is another command piped behind this one + // prepare to send output to the next command + Stdio::piped() + } else { + // there are no more commands piped behind this one + // send output to shell stdout + Stdio::inherit() + }; + + let output = Command::new(command) + .args(args) + .stdin(stdin) + .stdout(stdout) + .spawn(); + + match output { + Ok(output) => { previous_command = Some(output); }, + Err(e) => { + previous_command = None; + eprintln!("{}", e); + }, + }; + } + } + } + + if let Some(mut final_command) = previous_command { + // block until the final command has finished + final_command.wait(); + } + + } +}