allow interactive commands
This commit is contained in:
128
main.rs
128
main.rs
@@ -1,13 +1,101 @@
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Output};
|
||||
use std::io::Result as IoResult;
|
||||
use inquire::{Text, validator::Validation};
|
||||
use inquire::Select;
|
||||
use inquire::{Select, Confirm};
|
||||
use inquire::ui::{RenderConfig, Styled};
|
||||
use regex::Regex;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::process::{Command, Output};
|
||||
use std::ffi::CString;
|
||||
use libc::{c_char, execvp, fork, waitpid, WIFEXITED, WEXITSTATUS};
|
||||
|
||||
static AUTHOR_STRING: &str = r#"
|
||||
Author: Spencer
|
||||
Description: A shell to promote proper system changes!
|
||||
Ask for the source code!
|
||||
"#;
|
||||
|
||||
fn cd(path: &str) -> Result<(), std::io::Error> {
|
||||
env::set_current_dir(Path::new(path))
|
||||
}
|
||||
|
||||
fn execute_command(command: &str) -> IoResult<String> {
|
||||
// Use Command to run the bash shell with the -c option to pass the command string
|
||||
// Checking to see if the command is a builtin
|
||||
match command.split_whitespace().next() {
|
||||
Some(first_word) if first_word == "cd" => {
|
||||
let path: String = command.split_whitespace().skip(1).collect::<Vec<&str>>().join(" ");
|
||||
match cd(&path) {
|
||||
Ok(_) => return Ok("".to_string()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
},
|
||||
Some(first_word) if first_word == "author" => {
|
||||
println!("{}", AUTHOR_STRING);
|
||||
return Ok("".to_string())
|
||||
}
|
||||
Some(_) => (),
|
||||
None => (),
|
||||
};
|
||||
|
||||
// Split the command into arguments
|
||||
let args: Vec<&str> = command.split_whitespace().collect();
|
||||
if args.is_empty() {
|
||||
return Err(Error::new(ErrorKind::Other, "No command provided"));
|
||||
}
|
||||
|
||||
// Convert command and arguments to CStrings
|
||||
let c_args: Vec<CString> = args.iter()
|
||||
.map(|s| CString::new(*s).expect("CString::new failed"))
|
||||
.collect();
|
||||
|
||||
// Create an array of pointers to the CStrings
|
||||
let mut argv: Vec<*const c_char> = c_args.iter()
|
||||
.map(|s| s.as_ptr())
|
||||
.chain(std::iter::once(std::ptr::null()))
|
||||
.collect();
|
||||
|
||||
// Fork and exec
|
||||
unsafe {
|
||||
match fork() {
|
||||
-1 => return Err(Error::last_os_error()),
|
||||
0 => { // Child process
|
||||
if execvp(c_args[0].as_ptr(), argv.as_mut_ptr()) == -1 {
|
||||
let e = Error::last_os_error();
|
||||
std::process::exit(e.raw_os_error().unwrap_or(1));
|
||||
} else {
|
||||
Err(Error::last_os_error())
|
||||
}
|
||||
},
|
||||
pid => { // Parent process
|
||||
let mut status: i32 = 0;
|
||||
if waitpid(pid, &mut status, 0) == -1 {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
if WIFEXITED(status) && WEXITSTATUS(status) == 0 {
|
||||
Ok(String::new())
|
||||
} else {
|
||||
let exit_status = WEXITSTATUS(status);
|
||||
Err(Error::new(ErrorKind::Other, format!("Command exited with status {}", exit_status)))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt(name: &str) -> String {
|
||||
let mut line = String::new();
|
||||
print!("{}", name);
|
||||
std::io::stdout().flush().unwrap();
|
||||
std::io::stdin().read_line(&mut line).expect("Error: Could not read a line");
|
||||
|
||||
return line.trim().to_string()
|
||||
}
|
||||
|
||||
fn generate_prompt() -> IoResult<String> {
|
||||
let command = r#"echo -n "[${USER}@${HOSTNAME} ${PWD##*/}]$ ""#;
|
||||
let output: Output = Command::new("bash")
|
||||
.arg("-c")
|
||||
.arg(command)
|
||||
@@ -26,27 +114,19 @@ fn execute_command(command: &str) -> IoResult<String> {
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt(name:&str) -> String {
|
||||
let mut line = String::new();
|
||||
print!("{}", name);
|
||||
std::io::stdout().flush().unwrap();
|
||||
std::io::stdin().read_line(&mut line).expect("Error: Could not read a line");
|
||||
|
||||
return line.trim().to_string()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let prompt_gen_command = "echo -n \"r-[${USER}@${HOSTNAME} ${PWD##*/}]$ \"";
|
||||
|
||||
// RenderConfig for inquire
|
||||
let mut empty_render_config: RenderConfig = RenderConfig::default();
|
||||
empty_render_config = empty_render_config.with_prompt_prefix(Styled::new(""));
|
||||
empty_render_config = empty_render_config.with_answered_prompt_prefix(Styled::new(""));
|
||||
empty_render_config = empty_render_config.with_canceled_prompt_indicator(Styled::new(""));
|
||||
|
||||
let re = Regex::new(r"CHG[0-9]{6}$").unwrap();
|
||||
// Change type options
|
||||
let options: Vec<&str> = vec!["File change", "Package Update", "Package Removal",
|
||||
"File Removal", "Misc. Command"];
|
||||
|
||||
// Change request input validator
|
||||
let re = Regex::new(r"CHG[0-9]{6}$").unwrap();
|
||||
let change_validator = move |input: &str| if input.chars().count() == 0 {
|
||||
Ok(Validation::Invalid("Change request cannot be empty".into()))
|
||||
} else if re.is_match(input) {
|
||||
@@ -57,7 +137,7 @@ fn main() {
|
||||
|
||||
let mut bypass_change = false;
|
||||
loop {
|
||||
let prompt_command = match execute_command(&prompt_gen_command) {
|
||||
let prompt_command = match generate_prompt() {
|
||||
Ok(output) => {
|
||||
let prompt_output = prompt(&output);
|
||||
if prompt_output.chars().count() == 0 {
|
||||
@@ -80,25 +160,33 @@ fn main() {
|
||||
|
||||
if !bypass_change {
|
||||
let change_request = match Text::new("(e.g. CHGXXXXXX) Enter change request: ")
|
||||
.with_render_config(empty_render_config.clone())
|
||||
.with_validator(change_validator.clone())
|
||||
.prompt() {
|
||||
Ok(input) => input,
|
||||
Err(_) => return,
|
||||
Err(_) => continue,
|
||||
};
|
||||
println!("Change request validated: {}", change_request);
|
||||
|
||||
let ans = Select::new("Change Type?", options.clone())
|
||||
let _change_type = Select::new("Change Type?", options.clone())
|
||||
.with_render_config(empty_render_config.clone())
|
||||
.prompt();
|
||||
|
||||
let ans = Confirm::new("Are you within the change window?")
|
||||
.with_default(false)
|
||||
.with_help_message("Ensure you are running commands within the change time window")
|
||||
.prompt();
|
||||
|
||||
match ans {
|
||||
Ok(selected_option) => {
|
||||
println!("Selected option: {}", selected_option);
|
||||
Ok(true) => {
|
||||
match execute_command(&prompt_command) {
|
||||
Ok(output) => print!("{}", output),
|
||||
Err(e) => eprintln!("Command failed: {}", e),
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
continue
|
||||
}
|
||||
Err(_) => println!("not running command..."),
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user