commit d7d723b549a146e2ec6699835448d4519186dec0 Author: aNNiMON Date: Tue Jul 23 19:37:06 2024 +0300 First working version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6f7a291 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,109 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "pkill" +version = "0.1.0" +dependencies = [ + "itertools", + "windows", +] + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3d2cdb9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "pkill" +version = "0.1.0" +edition = "2021" + +[dependencies] +windows = { version = "0.52.0", features = [ + "Win32_Foundation", + "Win32_System_Diagnostics_ToolHelp", + "Win32_System_Threading", +] } +itertools = "0.13.0" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..07c9c48 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,144 @@ +use itertools::Itertools; +use processes::terminate_process; +use std::process::ExitCode; +use std::{env, fmt::Display}; + +mod processes; + +#[derive(Debug)] +enum Mode { + Minimal, + All, + Filtered(String), + Single(String), + TerminateByPid(u32), + TerminateByName(String), +} + +// pkill => all unique processed, excl. svchost +// pkill --all => all processes, incl. svchost +// pkill => filtered processes +// pkill --pid name => PID of the first occurence +// pkill --kill PID/name => terminate process by its PID or name +fn main() -> ExitCode { + match parse_mode() { + Mode::All => { + show_processes_table(processes::list_processes()); + ExitCode::SUCCESS + } + Mode::Minimal => { + let filterlist = [ + "svchost", + "registry", + "[system process]", + "smss.exe", + "csrss.exe", + "lsass.exe", + ]; + show_processes_table( + processes::list_processes() + .iter() + .filter(|p| { + let pname = p.name.to_ascii_lowercase(); + !filterlist.iter().any(|n| pname.starts_with(n)) + }) + .unique_by(|p| &p.name) + .sorted_by_key(|p| &p.name), + ); + ExitCode::SUCCESS + } + Mode::Filtered(filter) => { + show_processes_table( + processes::list_processes() + .iter() + .filter(|p| p.name.to_ascii_lowercase().contains(&filter)) + .sorted_by_key(|p| &p.name), + ); + ExitCode::SUCCESS + } + Mode::Single(name) => { + if let Some(pid) = processes::list_processes() + .iter() + .find(|p| p.name.to_ascii_lowercase().contains(&name)) + .map(|p| p.pid) + { + println!("{}", pid); + ExitCode::SUCCESS + } else { + ExitCode::FAILURE + } + } + Mode::TerminateByPid(pid) => { + if terminate_process(pid) { + ExitCode::SUCCESS + } else { + ExitCode::FAILURE + } + } + Mode::TerminateByName(name) => { + if processes::list_processes() + .iter() + .find(|p| p.name.to_ascii_lowercase().contains(&name)) + .map(|p| p.pid) + .map(terminate_process) + .is_some() + { + ExitCode::SUCCESS + } else { + ExitCode::FAILURE + } + } + } +} + +fn parse_mode() -> Mode { + let mut mode: Mode = Mode::Minimal; + let mut iter = env::args().skip(1).into_iter(); + while let Some(el) = iter.next() { + match el.as_str() { + "--all" => { + mode = Mode::All; + } + "--pid" => { + if let Some(name) = iter.next() { + mode = Mode::Single(name.to_ascii_lowercase()); + } else { + continue; + } + } + "--kill" => { + if let Some(name) = iter.next() { + mode = match name.parse() { + Ok(pid) => Mode::TerminateByPid(pid), + Err(_) => Mode::TerminateByName(name.to_ascii_lowercase()), + }; + } else { + continue; + } + } + search => { + mode = Mode::Filtered(search.to_ascii_lowercase().to_string()); + } + } + break; + } + mode +} + +fn show_processes_table(items: T) +where + T: IntoIterator, + T::Item: Display, +{ + let lines: Vec = items.into_iter().map(|p| format!("{}\n", p)).collect(); + if lines.is_empty() { + println!("No processes"); + } else { + println!("{:10} {:10} {:8} {:32}", "PID", "Parent", "Threads", "Name"); + println!("{}", lines.concat().trim_end()); + let len = lines.len(); + if len > 10 { + println!("{} processes found", lines.len()); + } + } +} diff --git a/src/processes.rs b/src/processes.rs new file mode 100644 index 0000000..b4cce6b --- /dev/null +++ b/src/processes.rs @@ -0,0 +1,65 @@ +use core::fmt; +use std::{ffi, mem}; +use windows::Win32::{ + Foundation::CloseHandle, + System::{ + Diagnostics::ToolHelp::{ + CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, + TH32CS_SNAPPROCESS, + }, + Threading::{OpenProcess, TerminateProcess, PROCESS_TERMINATE}, + }, +}; + +pub struct ProcessInfo { + pub pid: u32, + pub parent_pid: u32, + pub name: String, + pub threads_count: u32, +} + +impl fmt::Display for ProcessInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:<10} {:<10} {:<8} {:<32}", self.pid, self.parent_pid, self.threads_count, self.name) + } +} + +pub fn list_processes() -> Vec { + let mut processes = Vec::new(); + unsafe { + let snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0).unwrap(); + if let Ok(process_entry_size) = u32::try_from(mem::size_of::()) { + let mut process_entry = mem::zeroed::(); + process_entry.dwSize = process_entry_size; + if Process32First(snapshot_handle, &mut process_entry).is_ok() { + loop { + processes.push(ProcessInfo { + pid: process_entry.th32ProcessID, + parent_pid: process_entry.th32ParentProcessID, + name: ffi::CStr::from_ptr(process_entry.szExeFile.as_ptr().cast()) + .to_string_lossy() + .into_owned(), + threads_count: process_entry.cntThreads, + }); + if Process32Next(snapshot_handle, &mut process_entry).is_err() { + break; + } + } + } + } + CloseHandle(snapshot_handle).unwrap(); + } + processes +} + +pub fn terminate_process(process_id: u32) -> bool { + unsafe { + if let Ok(process_handle) = OpenProcess(PROCESS_TERMINATE, false, process_id) { + let status = TerminateProcess(process_handle, 1); + CloseHandle(process_handle).unwrap(); + status.is_ok() + } else { + false + } + } +} \ No newline at end of file