Skip to content

Commit

Permalink
Added more CLI options and changed some logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Kihau committed Mar 1, 2023
1 parent 8b69de8 commit 347d5a6
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 51 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "procnuke"
version = "0.1.0"
version = "0.2.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
135 changes: 135 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,141 @@ pub mod linux;
#[cfg(target_os = "windows")]
pub mod windows;

pub const VERSION: &str = env!("CARGO_PKG_VERSION");

pub struct Config {
pub case_sensitive: bool,
pub match_cmdline: bool,
pub match_exact: bool,
pub match_pid: bool,
pub listing: bool,

}

impl Default for Config {
fn default() -> Self {
Self {
match_cmdline: false,
case_sensitive: false,
match_exact: false,
match_pid: false,
listing: false,
}
}
}

pub fn print_help(program_name: &String) {
println!("ProcNuke v{VERSION} (also known as fuckoff): Simple process killer.");
println!();
println!("Usage:");
println!(" {program_name} -a [string to match against]");
println!(" {program_name} [program name]");
println!(" TODO: Add some more");
println!();
println!("Options:");
println!(" -c, --cmdline Matches processes by their name AND their execution arguments");
println!(" -s, --case-sensitive Don't ignore case when matching strings");
println!(" -e, --exact Match exact string");
println!(" -l, --list List processes");
println!(" -p, --pid Find/Kill process by pid");
println!(" -h, --help Display this prompt");
std::process::exit(0);
}

pub fn print_version() {
println!("Version: ProcNuke-v{VERSION}");
std::process::exit(0);
}

pub fn list_processes(pids_to_list: Vec<ProcessID>) {
for pid in pids_to_list {
if let Some(process_name) = get_process_name(pid) {
println!("{pid} - {process_name}");
} else {
println!("{pid} - <Unavailable>");
}
}

std::process::exit(0);
}

pub fn get_matching_by_pid(kill_args: &Vec<String>) -> Vec<ProcessID> {
let mut matched_pids = Vec::<ProcessID>::new();

let self_pid = std::process::id();
println!("Self pid is {self_pid}");

let all_pids = get_all_process_ids();

for arg in kill_args {
let Ok(pid) = arg.parse::<ProcessID>() else {
eprintln!("ERROR: Failed to parse: {arg}");
continue;
};

if self_pid != pid && all_pids.contains(&pid) {
matched_pids.push(pid);
}
}

return matched_pids;
}

pub fn get_matching_by_string(config: &Config, kill_args: &Vec<String>) -> Vec<ProcessID> {
let mut matched_pids = Vec::<ProcessID>::new();

let self_pid = std::process::id();
println!("Self pid is {self_pid}");

let all_pids = get_all_process_ids();
let mut kill_string = kill_args.join(" ");
for pid in all_pids {
if pid == self_pid {
continue;
}

let process_string = if config.match_cmdline {
get_process_cmdline(pid)
} else {
get_process_name(pid)
};

let Some(mut process_string) = process_string else {
continue;
};

if !config.case_sensitive {
process_string = process_string.to_lowercase();
kill_string = kill_string.to_lowercase();
}

let string_matching = if config.match_exact {
kill_string == process_string
} else {
process_string.contains(&kill_string)
};

if string_matching {
matched_pids.push(pid);
}

}

return matched_pids;
}

pub fn kill_processes(pids: Vec<ProcessID>) {
for pid in pids {
if let Some(process_name) = get_process_name(pid) {
println!("Killing process: {pid} - {process_name}");
} else {
println!("Killing process: {pid} - <Unavailable>");
}
kill_process(pid);
}
}


/// Retrieves process ID's by matching the process program name with provided pattern
pub fn get_matching_pids_name(pattern: &String) -> Vec<ProcessID> {
let all_pids = get_all_process_ids();
Expand Down
47 changes: 40 additions & 7 deletions src/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,35 @@ pub fn get_all_process_ids() -> Vec<ProcessID> {
return all_pids;
}

pub fn get_process_name(process_id: ProcessID) -> Option<String> {
let status_string = format!("/proc/{process_id}/status");
let status_path = Path::new(&status_string);
let status = read_to_string(status_path);

if status.is_err() {
return None;
}
let status = status.unwrap();

let input: Vec<&str> = status.lines().collect();
let name = input.get(0)?.split("\t").nth(1)?.to_string();

return Some(name);
}

pub fn get_process_cmdline(process_id: ProcessID) -> Option<String> {
let cmdline_string = format!("/proc/{process_id}/cmdline");
let cmdline_path = Path::new(&cmdline_string);
let cmdline = read_to_string(cmdline_path);

if cmdline.is_err() {
return None;
}
let cmdline = cmdline.unwrap();
return Some(cmdline);
}

// change this or something
pub fn full_name_matching(process_id: ProcessID, pattern: &String) -> bool {
let cmdline_string = format!("/proc/{process_id}/cmdline");
let cmdline_path = Path::new(&cmdline_string);
Expand Down Expand Up @@ -76,13 +105,17 @@ pub fn program_name_matching(process_id: ProcessID, pattern: &String) -> bool {
return false;
}

pub fn kill_processes(pids: Vec<ProcessID>) {
for pid in pids {
println!("Killing process: {pid}");
unsafe {
kill(pid, SIGKILL);
}
pub fn kill_process(process_id: ProcessID) {
unsafe {
kill(process_id, SIGKILL);
}
}


// pub fn kill_processes(pids: Vec<ProcessID>) {
// for pid in pids {
// println!("Killing process: {pid}");
// unsafe {
// kill(pid, SIGKILL);
// }
// }
// }
76 changes: 44 additions & 32 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::Path;
use std::{path::Path, num::IntErrorKind};

use procnuke::*;

Expand All @@ -8,61 +8,73 @@ use procnuke::windows::*;
#[cfg(target_os = "linux")]
use procnuke::linux::*;

fn print_help(program_name: &String) {
println!("ProcNuke (also known as fuckoff): Simple process killer.");
println!();
println!("Usage:");
println!(" {program_name} -a [string to match against]");
println!(" {program_name} [program name]");
println!();
println!("Options:");
println!(" -a, --aggressive Aggressive mode. Matches processes by their name and execution arguments");
println!(" -h, --help Display this prompt");
}

fn get_program_name(program_path: String) -> Option<String> {
let path = Path::new(&program_path);
let os_name = path.file_name()?.to_str()?;
return Some(String::from(os_name));
}

// TODO: Add more options:
// -s, --case-sensitice case sensitive
// -l, --list list processes with pids, don't kill anything
// -p, --pid kill by pid
// -os, --operating-system shutdown operating system (requested by frisk)

fn main() {
let mut args = std::env::args();
let program_path = args.next().unwrap();
let program_name = get_program_name(program_path).unwrap();
let exec_path = args.next().unwrap();
let exec_name = get_program_name(exec_path).unwrap();

let cmdline_args: Vec<String> = args.collect();
let mut aggressive = false;
let mut config = Config::default();

let mut kill_string = String::new();
let mut kill_args = Vec::new();

for arg in cmdline_args {
match arg.as_str() {
"-a" | "--aggressive" => aggressive = true,
"-h" | "--help" => {
print_help(&program_name);
return;
}
_ => kill_string.push_str(&arg),
"-c" | "--cmdline" => config.match_cmdline = true,
"-s" | "--case-sensitive" => config.case_sensitive = true,
"-e" | "--exact" => config.match_exact = true,
"-l" | "--list" => config.listing = true,
"-p" | "--pid" => config.match_pid = true,
"-v" | "--version" => print_version(),
"-h" | "--help" => print_help(&exec_name),
_ => kill_args.push(arg),
}
}

if kill_string.is_empty() {
eprintln!("You must provide a process name to kill. Use {program_name} --help for more info.");
// Handle some error cases
let mut error_occurred = true;
match config {
Config { match_pid: true, match_cmdline: true, .. } =>
eprintln!("ERROR: Option for matching by PID cannot be used with option for command line matching"),
Config { match_pid: true, match_exact: true, .. } =>
eprintln!("ERROR: Option for matching by PID cannot be used with option for exact string matching"),
Config { match_pid: true, .. } if kill_args.is_empty() && !config.listing =>
eprintln!("ERROR: You must provide one or more process ids to kill. Use {exec_name} --help for more info."),
_ if kill_args.is_empty() && !config.listing => print_help(&exec_name),
// eprintln!("ERROR: You must provide a process name to kill. Use {exec_name} --help for more info."),
_ => error_occurred = false,
}

if error_occurred {
return;
}

let pids = if aggressive {
get_matching_pids_full(&kill_string)
let mut pids = if !config.match_pid {
get_matching_by_string(&config, &kill_args)
} else {
get_matching_pids_name(&kill_string)
get_matching_by_pid(&kill_args)
};

kill_processes(pids);
if !config.listing {
if pids.len() == 0 {
println!("No matching processes found.");
return;
}

kill_processes(pids);
} else {
if pids.len() == 0 && kill_args.is_empty(){
pids = get_all_process_ids();
}
list_processes(pids);
}
}
Loading

0 comments on commit 347d5a6

Please sign in to comment.