help and version flags
This commit is contained in:
parent
f36e111773
commit
191632ce0a
6 changed files with 197 additions and 114 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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
58
src/flags.rs
Normal 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
11
src/help.rs
Normal 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);
|
||||||
|
}
|
135
src/main.rs
135
src/main.rs
|
@ -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
101
src/persist.rs
Normal 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);
|
||||||
|
}
|
Loading…
Reference in a new issue