First working version
This commit is contained in:
commit
d7d723b549
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
109
Cargo.lock
generated
Normal file
109
Cargo.lock
generated
Normal file
@ -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"
|
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -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"
|
144
src/main.rs
Normal file
144
src/main.rs
Normal file
@ -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 <regex> => 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<T>(items: T)
|
||||
where
|
||||
T: IntoIterator,
|
||||
T::Item: Display,
|
||||
{
|
||||
let lines: Vec<String> = 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());
|
||||
}
|
||||
}
|
||||
}
|
65
src/processes.rs
Normal file
65
src/processes.rs
Normal file
@ -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<ProcessInfo> {
|
||||
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::<PROCESSENTRY32>()) {
|
||||
let mut process_entry = mem::zeroed::<PROCESSENTRY32>();
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user