2022-12-20 18:19:25 +01:00
2022-12-20 18:19:25 +01:00
2022-12-06 17:59:26 +01:00
2022-12-20 18:19:25 +01:00
2022-12-20 18:19:25 +01:00
2022-12-13 23:42:39 +01:00

RShell

Rust Shell. This is an attempt to create a simple shell in Rust, because why not.

My starting point is this article 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.

Done

  • A basic history system. Writes to ~/history.rshell
  • Tests for this history system
  • Added the ! function
  • Order the file (modules...) => Ok, we doing a lib.

Todo

  • Use termion to capture the TAB and autocomplete

Annotated process_line

fn process_line(input: String) -> bool {

    // must be peekable so we know when we are on the last command
    let mut commands = input.trim().split(" | ").peekable();
    //Will be used later, in the case of pipes
    let mut previous_command = None;

    //While have commands to process...
    while let Some(command) = commands.next()  {
        //First command is divided...
        let mut parts = command.trim().split_whitespace();
        //into a command...
        let mut command = parts.next().unwrap();
        //and args
        let args = parts;

        //builtins : exit and cd are special
        match command {
            "cd" => {
                //If a dir is given, we use it by dereferencing
                //otherwise it is /
                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 true,
            "history" => {
                let stdout = Stdio::inherit();
                println!("Insert history here");
            },
            //not builtin: we process it
            command => {
                //If we are at the first command (aka previous_command is None)...
                let stdin = previous_command.map_or(
                        //We inherit, meaning stdin gets the stream from its parent
                        //what I guess that means is that the child (our command)
                        //inherits the file descriptors (stdin, stdout, stderr)
                        //from the process of this very own rust program !
                        Stdio::inherit(),
                        //otherwise, our input is the output from the last command
                        //(from converts a ChildStdOut to a Stdio)
                        |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
                    // the child inherits from the parent's file descriptor
                    Stdio::inherit()
                };
                
                //Run the command
                let output = Command::new(command)
                    .args(args)
                    .stdin(stdin)
                    .stdout(stdout)
                    //spawn : execute the command as a child process, returning a handle to it
                    .spawn();

                //Checking for errors
                //If it matched, previous_command becomes the output of this to be used
                //next loop
                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();
    }
    return false; 
}
Description
Trying to create / improve a shell written in Rust.
Readme 172 KiB
v1.0.1 Latest
2023-11-16 16:58:13 +01:00
Languages
Rust 99.1%
Makefile 0.9%