forked from microsoft/vscode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommand.rs
More file actions
101 lines (89 loc) · 2.77 KB
/
command.rs
File metadata and controls
101 lines (89 loc) · 2.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use super::errors::{wrap, AnyError, CommandFailed, WrappedError};
use std::{borrow::Cow, ffi::OsStr, process::Stdio};
use tokio::process::Command;
pub async fn capture_command_and_check_status(
command_str: impl AsRef<OsStr>,
args: &[impl AsRef<OsStr>],
) -> Result<std::process::Output, AnyError> {
let output = capture_command(&command_str, args).await?;
if !output.status.success() {
return Err(CommandFailed {
command: format!(
"{} {}",
command_str.as_ref().to_string_lossy(),
args.iter()
.map(|a| a.as_ref().to_string_lossy())
.collect::<Vec<Cow<'_, str>>>()
.join(" ")
),
output,
}
.into());
}
Ok(output)
}
pub async fn capture_command<A, I, S>(
command_str: A,
args: I,
) -> Result<std::process::Output, WrappedError>
where
A: AsRef<OsStr>,
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
Command::new(&command_str)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.output()
.await
.map_err(|e| {
wrap(
e,
format!(
"failed to execute command '{}'",
command_str.as_ref().to_string_lossy()
),
)
})
}
/// Kills and processes and all of its children.
#[cfg(target_os = "windows")]
pub async fn kill_tree(process_id: u32) -> Result<(), WrappedError> {
capture_command("taskkill", &["/t", "/pid", &process_id.to_string()]).await?;
Ok(())
}
/// Kills and processes and all of its children.
#[cfg(not(target_os = "windows"))]
pub async fn kill_tree(process_id: u32) -> Result<(), WrappedError> {
use futures::future::join_all;
use tokio::io::{AsyncBufReadExt, BufReader};
async fn kill_single_pid(process_id_str: String) {
capture_command("kill", &[&process_id_str]).await.ok();
}
// Rusty version of https://github.com/microsoft/vscode-js-debug/blob/main/src/targets/node/terminateProcess.sh
let parent_id = process_id.to_string();
let mut prgrep_cmd = Command::new("pgrep")
.arg("-P")
.arg(&parent_id)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.spawn()
.map_err(|e| wrap(e, "error enumerating process tree"))?;
let mut kill_futures = vec![tokio::spawn(
async move { kill_single_pid(parent_id).await },
)];
if let Some(stdout) = prgrep_cmd.stdout.take() {
let mut reader = BufReader::new(stdout).lines();
while let Some(line) = reader.next_line().await.unwrap_or(None) {
kill_futures.push(tokio::spawn(async move { kill_single_pid(line).await }))
}
}
join_all(kill_futures).await;
prgrep_cmd.kill().await.ok();
Ok(())
}