From 191632ce0ab810b4773f64eaf80ad67e87200c0b Mon Sep 17 00:00:00 2001 From: Tyler Murphy Date: Wed, 9 Nov 2022 10:49:25 -0500 Subject: [PATCH] help and version flags --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/flags.rs | 58 +++++++++++++++++++++ src/help.rs | 11 ++++ src/main.rs | 137 +++++++++---------------------------------------- src/persist.rs | 101 ++++++++++++++++++++++++++++++++++++ 6 files changed, 197 insertions(+), 114 deletions(-) create mode 100644 src/flags.rs create mode 100644 src/help.rs create mode 100644 src/persist.rs diff --git a/Cargo.lock b/Cargo.lock index a1b0e2b..30484cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,7 +34,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crab" -version = "0.0.1" +version = "0.0.3" dependencies = [ "exec", "nix", diff --git a/Cargo.toml b/Cargo.toml index 9db50ac..b80305f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crab" -version = "0.0.1" +version = "0.0.3" edition = "2021" [dependencies] diff --git a/src/flags.rs b/src/flags.rs new file mode 100644 index 0000000..91acf3a --- /dev/null +++ b/src/flags.rs @@ -0,0 +1,58 @@ +pub struct Flags { + pub help: bool, + pub version: bool, + pub dont_persist: bool, + pub arg_count: usize +} + +pub fn parse(args: &[String]) -> Flags { + let mut flags = Flags { + help: false, + version: false, + dont_persist: false, + arg_count: 0 + }; + for arg in args { + if !is_arg(&arg) { break; } + flags.arg_count += 1; + if arg.starts_with("--") { + let flag = &arg[2..]; + check_flag(&flag, &mut flags); + } else { + let flag = &arg[1..]; + for char in flag.chars() { + check_flag(&char.to_string(), &mut flags); + } + } + } + flags +} + +fn is_arg(arg: &str) -> bool { + return arg.starts_with("-"); +} + +const HELP_FLAG: &str = "help h"; +const VERSION_FLAG: &str = "version v"; +const DONT_PERSIST: &str = "d"; + +fn check_flag(arg: &str, flags: &mut Flags) { + if has_flag_set(&arg, HELP_FLAG) { + flags.help = true + } + if has_flag_set(&arg, VERSION_FLAG) { + flags.version = true + } + if has_flag_set(&arg, DONT_PERSIST) { + flags.dont_persist = true + } +} + +fn has_flag_set(arg: &str, check: &str) -> bool { + for check_arg in check.split(" ") { + if check_arg == arg { + return true + } + } + return false +} \ No newline at end of file diff --git a/src/help.rs b/src/help.rs new file mode 100644 index 0000000..422e35d --- /dev/null +++ b/src/help.rs @@ -0,0 +1,11 @@ +pub fn help() { + let help = +"Usage: + crab [-d] command [args] + +Options: + -v --version Get the current version of the package + -h --help Generates the crab help message + -d If your user is set to persist, dont save persistance"; + println!("{}", help); +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9061ade..5fab1d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,35 @@ -use std::fs; -use std::os::linux::fs::MetadataExt; -use std::{env, os::unix::prelude::PermissionsExt}; +use std::env; use std::process::ExitCode; -use std::time::SystemTime; use pwd::Passwd; use nix::unistd; -use serde_json::Value; extern crate time; -const ERROR_ARGS: u8 = 1; +// const ERROR_COMMAND: u8 = 1; const ERROR_CONFIG: u8 = 2; const ERROR_NO_USER: u8 = 3; const ERROR_NOT_AUTHORIZED: u8 = 4; const ERROR_AUTH_FAILED: u8 = 5; const ERROR_RUN_ROOT: u8 = 6; -const SUCCESS: u8 = 0; - -const PERSIST_TIME: u64 = 60 * 3; +mod persist; +mod flags; +mod help; fn main() -> ExitCode { let args: Vec = env::args().collect(); - if args.len() < 2 { - println!("usage: crab command [args]"); - return ExitCode::from(ERROR_ARGS); + let flags = flags::parse(&args[1..]); + if flags.version { + println!("crab version 0.0.3"); + return ExitCode::SUCCESS; + } + if flags.help { + help::help(); + return ExitCode::SUCCESS; + } + if args.len() - flags.arg_count < 2 { + println!("usage: crab [-d] command [args]"); + return ExitCode::SUCCESS; } let config = match config("/etc/crab.conf") { Some(data) => data, @@ -38,7 +43,7 @@ fn main() -> ExitCode { } }; let persist = match allowed(&config, &user.name) { - Some(data) => data, + Some(data) => data && !flags.dont_persist, None => { eprintln!("Operation Not Permitted."); return ExitCode::from(ERROR_NOT_AUTHORIZED); @@ -54,11 +59,12 @@ fn main() -> ExitCode { eprintln!("Failed to set root permissions"); return ExitCode::from(ERROR_RUN_ROOT); }; - - let err = exec::execvp(&args[1], &args[1..]); - println!("Error: {}", err); + let start = 1 + flags.arg_count; + let err = exec::execvp(&args[start], &args[start..]); - ExitCode::from(SUCCESS) + eprintln!("{}", err); + + ExitCode::SUCCESS } struct Config { @@ -66,7 +72,7 @@ struct Config { } fn validate(user: &str, persist: bool) -> bool { - if persist && get_persist(user) { + if persist && persist::get_persist(user) { return true; } let input = match rpassword::prompt_password(format!("crab ({}) password: ", user)) { @@ -82,7 +88,7 @@ fn validate(user: &str, persist: bool) -> bool { return false; } if persist { - set_persist(user); + persist::set_persist(user); } return true; } @@ -126,97 +132,4 @@ fn config(path: &str) -> Option { users.push((user, persist)); } Some(Config{users}) -} - -fn get_terminal_process() -> Option { - let id: i32 = match std::process::id().try_into() { - Ok(data) => data, - Err(_) => return None - }; - let stat = match procinfo::pid::stat(id) { - Ok(data) => data, - Err(_) => return None - }; - Some(stat.session) -} - -fn is_file_root_only(id: &i32) -> bool { - let metadata = match std::fs::metadata(path(&id)) { - Ok(data) => data, - Err(e) => { - if let Some(err) = e.raw_os_error() { - return err == 2; - } - return true - } - }; - let perms = metadata.permissions(); - return perms.mode() == 33200 && metadata.st_uid() == 0 && metadata.st_gid() == 0; -} - -fn get_terminal_config() -> Option { - let id = match get_terminal_process() { - Some(data) => data, - None => return None - }; - if !is_file_root_only(&id) { - return None; - } - let data = match std::fs::read_to_string(path(&id)) { - Ok(data) => data, - Err(_) => "{}".to_string() - }; - let json: Value = match serde_json::from_str(&data) { - Ok(data) => data, - Err(_) => return None - }; - Some(json) -} - -fn write_terminal_config(id: &i32, data: &str) -> Result<(), Box> { - std::fs::write(path(&id), data)?; - unistd::chown(std::path::Path::new(&path(&id)), Some(unistd::Uid::from(0)), Some(unistd::Gid::from(0)))?; - let metadata = std::fs::metadata(path(&id))?; - let mut perms = metadata.permissions(); - perms.set_mode(0o660); - fs::set_permissions(path(&id), perms)?; - Ok(()) -} - -fn get_persist(user: &str) -> bool { - let json = match get_terminal_config() { - Some(data) => data, - None => return false - }; - let timestamp = match json[user].as_u64() { - Some(data) => data, - None => return false - }; - return now() - timestamp < PERSIST_TIME; -} - -fn set_persist(user: &str) { - let mut json = match get_terminal_config() { - Some(data) => data, - None => return - }; - json[user] = Value::from(now()); - let id = match get_terminal_process() { - Some(data) => data, - None => return - }; - match write_terminal_config(&id, &json.to_string()) { - Ok(_) => {}, - Err(e) => { - eprintln!("Internal Error: {}", e) - } - }; -} - -fn now() -> u64 { - return SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); -} - -fn path(id: &i32) -> String { - return format!("/tmp/crab-{}", id); } \ No newline at end of file diff --git a/src/persist.rs b/src/persist.rs new file mode 100644 index 0000000..7d47232 --- /dev/null +++ b/src/persist.rs @@ -0,0 +1,101 @@ +use std::fs; +use std::os::linux::fs::MetadataExt; +use std::os::unix::prelude::PermissionsExt; +use std::time::SystemTime; +use nix::unistd; +use serde_json::Value; + +const PERSIST_TIME: u64 = 60 * 3; + +pub fn get_persist(user: &str) -> bool { + let json = match get_terminal_config() { + Some(data) => data, + None => return false + }; + let timestamp = match json[user].as_u64() { + Some(data) => data, + None => return false + }; + return now() - timestamp < PERSIST_TIME; +} + +pub fn set_persist(user: &str) { + let mut json = match get_terminal_config() { + Some(data) => data, + None => return + }; + json[user] = Value::from(now()); + let id = match get_terminal_process() { + Some(data) => data, + None => return + }; + match write_terminal_config(&id, &json.to_string()) { + Ok(_) => {}, + Err(e) => { + eprintln!("Internal Error: {}", e) + } + }; +} + +fn get_terminal_process() -> Option { + let id: i32 = match std::process::id().try_into() { + Ok(data) => data, + Err(_) => return None + }; + let stat = match procinfo::pid::stat(id) { + Ok(data) => data, + Err(_) => return None + }; + Some(stat.session) +} + +fn is_file_root_only(id: &i32) -> bool { + let metadata = match std::fs::metadata(path(&id)) { + Ok(data) => data, + Err(e) => { + if let Some(err) = e.raw_os_error() { + return err == 2; + } + return true + } + }; + let perms = metadata.permissions(); + return perms.mode() == 33200 && metadata.st_uid() == 0 && metadata.st_gid() == 0; +} + +fn get_terminal_config() -> Option { + let id = match get_terminal_process() { + Some(data) => data, + None => return None + }; + if !is_file_root_only(&id) { + return None; + } + let data = match std::fs::read_to_string(path(&id)) { + Ok(data) => data, + Err(_) => "{}".to_string() + }; + let json: Value = match serde_json::from_str(&data) { + Ok(data) => data, + Err(_) => return None + }; + Some(json) +} + +fn write_terminal_config(id: &i32, data: &str) -> Result<(), Box> { + std::fs::write(path(&id), data)?; + unistd::chown(std::path::Path::new(&path(&id)), Some(unistd::Uid::from(0)), Some(unistd::Gid::from(0)))?; + let metadata = std::fs::metadata(path(&id))?; + let mut perms = metadata.permissions(); + perms.set_mode(0o660); + fs::set_permissions(path(&id), perms)?; + Ok(()) +} + +fn now() -> u64 { + return SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); +} + +fn path(id: &i32) -> String { + return format!("/tmp/crab-{}", id); +} \ No newline at end of file