help and version flags

This commit is contained in:
Tyler Murphy 2022-11-09 10:49:25 -05:00
parent f36e111773
commit 191632ce0a
6 changed files with 197 additions and 114 deletions

2
Cargo.lock generated
View file

@ -34,7 +34,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "crab" name = "crab"
version = "0.0.1" version = "0.0.3"
dependencies = [ dependencies = [
"exec", "exec",
"nix", "nix",

View file

@ -1,6 +1,6 @@
[package] [package]
name = "crab" name = "crab"
version = "0.0.1" version = "0.0.3"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

58
src/flags.rs Normal file
View file

@ -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
}

11
src/help.rs Normal file
View file

@ -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);
}

View file

@ -1,30 +1,35 @@
use std::fs; use std::env;
use std::os::linux::fs::MetadataExt;
use std::{env, os::unix::prelude::PermissionsExt};
use std::process::ExitCode; use std::process::ExitCode;
use std::time::SystemTime;
use pwd::Passwd; use pwd::Passwd;
use nix::unistd; use nix::unistd;
use serde_json::Value;
extern crate time; extern crate time;
const ERROR_ARGS: u8 = 1; // const ERROR_COMMAND: u8 = 1;
const ERROR_CONFIG: u8 = 2; const ERROR_CONFIG: u8 = 2;
const ERROR_NO_USER: u8 = 3; const ERROR_NO_USER: u8 = 3;
const ERROR_NOT_AUTHORIZED: u8 = 4; const ERROR_NOT_AUTHORIZED: u8 = 4;
const ERROR_AUTH_FAILED: u8 = 5; const ERROR_AUTH_FAILED: u8 = 5;
const ERROR_RUN_ROOT: u8 = 6; 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 { fn main() -> ExitCode {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if args.len() < 2 { let flags = flags::parse(&args[1..]);
println!("usage: crab command [args]"); if flags.version {
return ExitCode::from(ERROR_ARGS); 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") { let config = match config("/etc/crab.conf") {
Some(data) => data, Some(data) => data,
@ -38,7 +43,7 @@ fn main() -> ExitCode {
} }
}; };
let persist = match allowed(&config, &user.name) { let persist = match allowed(&config, &user.name) {
Some(data) => data, Some(data) => data && !flags.dont_persist,
None => { None => {
eprintln!("Operation Not Permitted."); eprintln!("Operation Not Permitted.");
return ExitCode::from(ERROR_NOT_AUTHORIZED); return ExitCode::from(ERROR_NOT_AUTHORIZED);
@ -54,11 +59,12 @@ fn main() -> ExitCode {
eprintln!("Failed to set root permissions"); eprintln!("Failed to set root permissions");
return ExitCode::from(ERROR_RUN_ROOT); return ExitCode::from(ERROR_RUN_ROOT);
}; };
let start = 1 + flags.arg_count;
let err = exec::execvp(&args[start], &args[start..]);
let err = exec::execvp(&args[1], &args[1..]); eprintln!("{}", err);
println!("Error: {}", err);
ExitCode::from(SUCCESS) ExitCode::SUCCESS
} }
struct Config { struct Config {
@ -66,7 +72,7 @@ struct Config {
} }
fn validate(user: &str, persist: bool) -> bool { fn validate(user: &str, persist: bool) -> bool {
if persist && get_persist(user) { if persist && persist::get_persist(user) {
return true; return true;
} }
let input = match rpassword::prompt_password(format!("crab ({}) password: ", user)) { let input = match rpassword::prompt_password(format!("crab ({}) password: ", user)) {
@ -82,7 +88,7 @@ fn validate(user: &str, persist: bool) -> bool {
return false; return false;
} }
if persist { if persist {
set_persist(user); persist::set_persist(user);
} }
return true; return true;
} }
@ -127,96 +133,3 @@ fn config(path: &str) -> Option<Config> {
} }
Some(Config{users}) Some(Config{users})
} }
fn get_terminal_process() -> Option<i32> {
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<Value> {
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<dyn std::error::Error>> {
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);
}

101
src/persist.rs Normal file
View file

@ -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<i32> {
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<Value> {
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<dyn std::error::Error>> {
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);
}