Skip to content

Commit 07a4a56

Browse files
committed
version: 0.2.0
1 parent 6f46096 commit 07a4a56

File tree

4 files changed

+104
-62
lines changed

4 files changed

+104
-62
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ members = [
88
resolver = "2"
99

1010
[workspace.package]
11-
version = "0.1.5"
11+
version = "0.2.0"
1212
edition = "2021"
1313
authors = ["oneofthezombies"]
1414
repository = "https://github.com/oneofthezombies/kill-tree"

README.md

Lines changed: 99 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
![logo](docs/images/logo.jpg)
44

55
🌳 Kill Tree is a library and CLI tool designed to terminate a specified process and all its child processes recursively, operating independently of other commands like kill or taskkill.
6-
It is written in Rust and powered by [Tokio](https://github.com/tokio-rs/tokio).
6+
It is written in Rust.
7+
If you use this as a Rust library, it supports both synchronous and asynchronous implementation!
8+
Asynchronous implementation is powerd by [Tokio](https://github.com/tokio-rs/tokio).
9+
710
This project was inspired by [node-tree-kill](https://github.com/pkrumins/node-tree-kill). Thank you. 🤟
811

912
[![Build Status][actions-badge]][actions-url]
@@ -14,7 +17,17 @@ This project was inspired by [node-tree-kill](https://github.com/pkrumins/node-t
1417
[crates-badge]: https://img.shields.io/crates/v/kill-tree.svg
1518
[crates-url]: https://crates.io/crates/kill-tree
1619

17-
## Why I Made This
20+
## Why I Created This
21+
22+
### TL;DR
23+
24+
---
25+
26+
The reason I created this is because I have a case where I need to stop grandchild processes.
27+
This is because the way to kill a grandchild process on the Windows platform is to use the `Win32` API to determine the process relationship and either kill them all (Kill Tree's implementation) or call the `taskkill` program.
28+
Even my program is more than __3x faster__ than `taskkill`.
29+
30+
---
1831

1932
This is my first Rust crate and CLI tool. it means I started it because it was a suitable project for development with Rust.
2033
No multi-platform library or CLI existed that could terminate processes recursively.
@@ -87,47 +100,48 @@ kill-tree 777 SIGKILL
87100

88101
### Using as Rust Library
89102

90-
⚠️ Ensure this library is executed within a Tokio runtime environment.
103+
#### Synchronous Method
91104

92105
Add `kill_tree` to your dependencies.
93106

94107
```toml
95108
# Cargo.toml
96109
[dependencies]
97-
kill_tree = "0.1"
110+
kill_tree = "0.2"
98111
```
99112

113+
Synchronous api is started with `kill_tree::blocking`.
114+
100115
Kill process and its children recursively with default signal `SIGTERM`.
101116
Returns a list of process information when a function is called.
102117
Process information is `Killed` or `MaybeAlreadyTerminated`.
103118
If process information is `Killed` type, it has `process_id`, `parent_process_id` and `name`.
104-
Or `MaybeAlreadyTerminated` type, it has `process_id`, `reason`.
119+
Or `MaybeAlreadyTerminated` type, it has `process_id`, `source`.
105120

106121
There are two types because they can be killed during the process of querying and killing processes.
107122
Therefore, consider the operation successful even if the query or kill process appears to fail.
108123
This is because the purpose of this library is to make the process `not exist` state.
109124

110125
```rust
111-
use kill_tree::kill_tree;
126+
use kill_tree::{blocking::kill_tree, Output, Result};
112127

113-
#[tokio::main]
114-
async fn main() -> Result<(), Box<dyn std::error::Error>> {
115-
let process_id = 12345;
116-
let outputs = kill_tree(process_id).await.map_err(|e| e.to_string())?;
117-
for output in outputs {
128+
fn main() -> Result<()> {
129+
let process_id = 777;
130+
let outputs = kill_tree(process_id)?;
131+
for (index, output) in outputs.iter().enumerate() {
118132
match output {
119-
kill_tree::tree::Output::Killed {
133+
Output::Killed {
120134
process_id,
121135
parent_process_id,
122136
name,
123137
} => {
124138
println!(
125-
"Killed process. process id: {process_id}, parent process id: {parent_process_id}, name: {name}"
139+
"[{index}] Killed process. process id: {process_id}, parent process id: {parent_process_id}, name: {name}"
126140
);
127141
}
128-
kill_tree::tree::Output::MaybeAlreadyTerminated { process_id, reason } => {
142+
Output::MaybeAlreadyTerminated { process_id, source } => {
129143
println!(
130-
"Maybe already terminated process. process id: {process_id}, reason: {reason}"
144+
"[{index}] Maybe already terminated process. process id: {process_id}, source: {source}"
131145
);
132146
}
133147
}
@@ -136,39 +150,82 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
136150
}
137151
```
138152

139-
kill process and its children recursively with signal `SIGKILL`.
153+
Kill process and its children recursively with signal `SIGKILL`.
140154

141155
```rust
142-
use kill_tree::kill_tree_with_signal;
156+
use kill_tree::{blocking::kill_tree_with_config, Config, Result};
157+
158+
fn main() -> Result<()> {
159+
let process_id = 777;
160+
let config = Config {
161+
signal: "SIGKILL".to_string(),
162+
..Default::default()
163+
};
164+
let outputs = kill_tree_with_config(process_id, &config)?;
165+
println!("outputs: {outputs:?}"); // The `outputs` value is the same as the example `kill_tree`.
166+
Ok(())
167+
}
168+
```
169+
170+
If you want to recursively kill all child processes except the current process when the `ctrl + c` (`command + c`) event occurs.
171+
172+
```rust
173+
use kill_tree::{blocking::kill_tree_with_config, Config};
174+
use std::sync::mpsc::channel;
175+
176+
fn cleanup_children() {
177+
let current_process_id = std::process::id();
178+
let config = Config {
179+
include_target: false,
180+
..Default::default()
181+
};
182+
let result = kill_tree_with_config(current_process_id, &config);
183+
println!("kill_tree_with_config: {result:?}");
184+
}
185+
186+
fn main() {
187+
let (tx, rx) = channel();
188+
189+
ctrlc::set_handler(move || {
190+
cleanup_children();
191+
tx.send(()).expect("Could not send signal on channel.");
192+
})
193+
.expect("Error setting handler.");
194+
195+
println!("Current process id: {}", std::process::id());
196+
println!("Waiting for signal...");
197+
rx.recv().expect("Could not receive from channel.");
198+
println!("Got it! Exiting...");
199+
}
200+
```
201+
202+
#### Asynchronous Method
203+
204+
Add `kill_tree` to your dependencies with feature `tokio`.
205+
206+
```toml
207+
# Cargo.toml
208+
[dependencies]
209+
kill_tree = { version = "0.2", features = ["tokio"] }
210+
```
211+
212+
Synchronous api is started with `kill_tree::tokio`.
213+
214+
Kill process and its children recursively with default signal `SIGTERM`.
215+
216+
```rust
217+
use kill_tree::{get_available_max_process_id, tokio::kill_tree, Result};
143218

144219
#[tokio::main]
145-
async fn main() -> Result<(), Box<dyn std::error::Error>> {
146-
let process_id = 12345;
147-
let outputs = kill_tree_with_signal(process_id, "SIGKILL")
148-
.await
149-
.map_err(|e| e.to_string())?;
150-
for output in outputs {
151-
match output {
152-
kill_tree::tree::Output::Killed {
153-
process_id,
154-
parent_process_id,
155-
name,
156-
} => {
157-
println!(
158-
"Killed process. process id: {process_id}, parent process id: {parent_process_id}, name: {name}"
159-
);
160-
}
161-
kill_tree::tree::Output::MaybeAlreadyTerminated { process_id, reason } => {
162-
println!(
163-
"Maybe already terminated process. process id: {process_id}, reason: {reason}"
164-
);
165-
}
166-
}
167-
}
220+
async fn main() -> Result<()> {
221+
let outputs = kill_tree(get_available_max_process_id()).await?;
222+
println!("outputs: {outputs:?}"); // The `outputs` value is the same as the example `kill_tree`.
168223
Ok(())
169224
}
170225
```
171226

227+
When sending other signals or receiving and processing `ctrl + c` events, all you have to do is change the above `kill_tree::blocking` api to `kill_tree::tokio` and perform `await` processing, and it will be equivalent.
228+
172229
## Support Platform and Architecture
173230

174231
| Platform | Architecture | Support |
@@ -181,7 +238,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
181238
| Macos | aarch64 ||
182239

183240
This CLI and library depend on an operating system's system library.
184-
Because it's the operating system that owns the processes.
241+
Because it's the operating system that owns the processes.
242+
But, don't worry! It's an OS default library, so there's no need to install anything additional!
185243

186244
| Platform | Dependencies |
187245
| --- | --- |
Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use kill_tree::{blocking::kill_tree_with_config, Config, Output, Result};
1+
use kill_tree::{blocking::kill_tree_with_config, Config, Result};
22

33
fn main() -> Result<()> {
44
let process_id = 777;
@@ -7,23 +7,6 @@ fn main() -> Result<()> {
77
..Default::default()
88
};
99
let outputs = kill_tree_with_config(process_id, &config)?;
10-
for (index, output) in outputs.iter().enumerate() {
11-
match output {
12-
Output::Killed {
13-
process_id,
14-
parent_process_id,
15-
name,
16-
} => {
17-
println!(
18-
"[{index}] Killed process. process id: {process_id}, parent process id: {parent_process_id}, name: {name}"
19-
);
20-
}
21-
Output::MaybeAlreadyTerminated { process_id, source } => {
22-
println!(
23-
"[{index}] Maybe already terminated process. process id: {process_id}, source: {source}"
24-
);
25-
}
26-
}
27-
}
10+
println!("outputs: {outputs:?}"); // The `outputs` value is the same as the example `kill_tree`.
2811
Ok(())
2912
}

crates/examples/readme/src/kill_tree_tokio.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use kill_tree::{get_available_max_process_id, tokio::kill_tree, Result};
22

33
#[tokio::main]
44
async fn main() -> Result<()> {
5-
let _ = kill_tree(get_available_max_process_id()).await?;
5+
let outputs = kill_tree(get_available_max_process_id()).await?;
6+
println!("outputs: {outputs:?}"); // The `outputs` value is the same as the example `kill_tree`.
67
Ok(())
78
}

0 commit comments

Comments
 (0)