Forbid std::process::Command spawning, replace with smol where appropriate (#38894)

std commands can block for an arbitrary duration and so runs risk of
blocking tasks for too long. This replaces all such uses where sensible
with async processes.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth
2025-09-26 15:17:36 +00:00
committed by GitHub
parent 7f14ab26dd
commit c69912c76a
31 changed files with 220 additions and 141 deletions
+1
View File
@@ -862,6 +862,7 @@ todo = "deny"
declare_interior_mutable_const = "deny"
redundant_clone = "deny"
disallowed_methods = "deny"
# We currently do not restrict any style rules
# as it slows down shipping code to Zed.
+11
View File
@@ -5,3 +5,14 @@ ignore-interior-mutability = [
# and Hash impls do not use fields with interior mutability.
"agent::context::AgentContextKey"
]
disallowed-methods = [
{ path = "std::process::Command::spawn", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::spawn" },
{ path = "std::process::Command::output", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::output" },
{ path = "std::process::Command::status", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::status" },
]
disallowed-types = [
# { path = "std::collections::HashMap", replacement = "collections::HashMap" },
# { path = "std::collections::HashSet", replacement = "collections::HashSet" },
# { path = "indexmap::IndexSet", replacement = "collections::IndexSet" },
# { path = "indexmap::IndexMap", replacement = "collections::IndexMap" },
]
+12 -9
View File
@@ -310,10 +310,10 @@ impl AutoUpdater {
// the app after an update, we use `set_restart_path` to run the auto
// update helper instead of the app, so that it can overwrite the app
// and then spawn the new binary.
let quit_subscription = Some(cx.on_app_quit(|_, _| async move {
#[cfg(target_os = "windows")]
finalize_auto_update_on_quit();
}));
#[cfg(target_os = "windows")]
let quit_subscription = Some(cx.on_app_quit(|_, _| finalize_auto_update_on_quit()));
#[cfg(not(target_os = "windows"))]
let quit_subscription = None;
cx.on_app_restart(|this, _| {
this.quit_subscription.take();
@@ -942,11 +942,12 @@ async fn install_release_windows(downloaded_installer: PathBuf) -> Result<Option
let helper_path = std::env::current_exe()?
.parent()
.context("No parent dir for Zed.exe")?
.join("tools\\auto_update_helper.exe");
.join("tools")
.join("auto_update_helper.exe");
Ok(Some(helper_path))
}
pub fn finalize_auto_update_on_quit() {
pub async fn finalize_auto_update_on_quit() {
let Some(installer_path) = std::env::current_exe()
.ok()
.and_then(|p| p.parent().map(|p| p.join("updates")))
@@ -959,12 +960,14 @@ pub fn finalize_auto_update_on_quit() {
if flag_file.exists()
&& let Some(helper) = installer_path
.parent()
.map(|p| p.join("tools\\auto_update_helper.exe"))
.map(|p| p.join("tools").join("auto_update_helper.exe"))
{
let mut command = std::process::Command::new(helper);
let mut command = smol::process::Command::new(helper);
command.arg("--launch");
command.arg("false");
let _ = command.spawn();
if let Ok(mut cmd) = command.spawn() {
_ = cmd.status().await;
}
}
}
+1
View File
@@ -160,6 +160,7 @@ pub(crate) fn perform_update(app_dir: &Path, hwnd: Option<isize>, launch: bool)
}
}
if launch {
#[allow(clippy::disallowed_methods, reason = "doesn't run in the main binary")]
let _ = std::process::Command::new(app_dir.join("Zed.exe"))
.creation_flags(CREATE_NEW_PROCESS_GROUP.0)
.spawn();
+1
View File
@@ -1,3 +1,4 @@
#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
use std::process::Command;
fn main() {
+4
View File
@@ -1,3 +1,7 @@
#![allow(
clippy::disallowed_methods,
reason = "We are not in an async environment, so std::process::Command is fine"
)]
#![cfg_attr(
any(target_os = "linux", target_os = "freebsd", target_os = "windows"),
allow(dead_code)
-19
View File
@@ -1,27 +1,8 @@
#[cfg(feature = "test-support")]
pub type HashMap<K, V> = FxHashMap<K, V>;
#[cfg(feature = "test-support")]
pub type HashSet<T> = FxHashSet<T>;
#[cfg(feature = "test-support")]
pub type IndexMap<K, V> = indexmap::IndexMap<K, V, rustc_hash::FxBuildHasher>;
#[cfg(feature = "test-support")]
pub type IndexSet<T> = indexmap::IndexSet<T, rustc_hash::FxBuildHasher>;
#[cfg(not(feature = "test-support"))]
pub type HashMap<K, V> = std::collections::HashMap<K, V>;
#[cfg(not(feature = "test-support"))]
pub type HashSet<T> = std::collections::HashSet<T>;
#[cfg(not(feature = "test-support"))]
pub type IndexMap<K, V> = indexmap::IndexMap<K, V>;
#[cfg(not(feature = "test-support"))]
pub type IndexSet<T> = indexmap::IndexSet<T>;
pub use indexmap::Equivalent;
pub use rustc_hash::FxHasher;
pub use rustc_hash::{FxHashMap, FxHashSet};
+6 -5
View File
@@ -3,6 +3,7 @@ use log::info;
use minidumper::{Client, LoopAction, MinidumpBinary};
use release_channel::{RELEASE_CHANNEL, ReleaseChannel};
use serde::{Deserialize, Serialize};
use smol::process::Command;
#[cfg(target_os = "macos")]
use std::sync::atomic::AtomicU32;
@@ -12,7 +13,7 @@ use std::{
io,
panic::{self, PanicHookInfo},
path::{Path, PathBuf},
process::{self, Command},
process::{self},
sync::{
Arc, OnceLock,
atomic::{AtomicBool, Ordering},
@@ -53,13 +54,13 @@ pub async fn init(crash_init: InitCrashHandler) {
// used by the crash handler isn't destroyed correctly which causes it to stay on the file
// system and block further attempts to initialize crash handlers with that socket path.
let socket_name = paths::temp_dir().join(format!("zed-crash-handler-{zed_pid}"));
#[allow(unused)]
let server_pid = Command::new(exe)
let _crash_handler = Command::new(exe)
.arg("--crash-handler")
.arg(&socket_name)
.spawn()
.expect("unable to spawn server process")
.id();
.expect("unable to spawn server process");
#[cfg(target_os = "linux")]
let server_pid = _crash_handler.id();
info!("spawning crash handler process");
let mut elapsed = Duration::ZERO;
@@ -77,6 +77,7 @@ impl IExplorerCommand_Impl for ExplorerCommandInjector_Impl {
for idx in 0..count {
let item = unsafe { items.GetItemAt(idx)? };
let item_path = unsafe { item.GetDisplayName(SIGDN_FILESYSPATH)?.to_string()? };
#[allow(clippy::disallowed_methods, reason = "no async context in sight..")]
std::process::Command::new(&zed_exe)
.arg(&item_path)
.spawn()
+25 -15
View File
@@ -142,7 +142,7 @@ impl ExtensionBuilder {
manifest: &mut ExtensionManifest,
options: CompileExtensionOptions,
) -> anyhow::Result<()> {
self.install_rust_wasm_target_if_needed()?;
self.install_rust_wasm_target_if_needed().await?;
let cargo_toml_content = fs::read_to_string(extension_dir.join("Cargo.toml"))?;
let cargo_toml: CargoToml = toml::from_str(&cargo_toml_content)?;
@@ -151,7 +151,7 @@ impl ExtensionBuilder {
"compiling Rust crate for extension {}",
extension_dir.display()
);
let output = util::command::new_std_command("cargo")
let output = util::command::new_smol_command("cargo")
.args(["build", "--target", RUST_TARGET])
.args(options.release.then_some("--release"))
.arg("--target-dir")
@@ -160,6 +160,7 @@ impl ExtensionBuilder {
.env("RUSTC_WRAPPER", "")
.current_dir(extension_dir)
.output()
.await
.context("failed to run `cargo`")?;
if !output.status.success() {
bail!(
@@ -235,7 +236,8 @@ impl ExtensionBuilder {
&grammar_repo_dir,
&grammar_metadata.repository,
&grammar_metadata.rev,
)?;
)
.await?;
let base_grammar_path = grammar_metadata
.path
@@ -248,7 +250,7 @@ impl ExtensionBuilder {
let scanner_path = src_path.join("scanner.c");
log::info!("compiling {grammar_name} parser");
let clang_output = util::command::new_std_command(&clang_path)
let clang_output = util::command::new_smol_command(&clang_path)
.args(["-fPIC", "-shared", "-Os"])
.arg(format!("-Wl,--export=tree_sitter_{grammar_name}"))
.arg("-o")
@@ -258,6 +260,7 @@ impl ExtensionBuilder {
.arg(&parser_path)
.args(scanner_path.exists().then_some(scanner_path))
.output()
.await
.context("failed to run clang")?;
if !clang_output.status.success() {
@@ -271,15 +274,16 @@ impl ExtensionBuilder {
Ok(())
}
fn checkout_repo(&self, directory: &Path, url: &str, rev: &str) -> Result<()> {
async fn checkout_repo(&self, directory: &Path, url: &str, rev: &str) -> Result<()> {
let git_dir = directory.join(".git");
if directory.exists() {
let remotes_output = util::command::new_std_command("git")
let remotes_output = util::command::new_smol_command("git")
.arg("--git-dir")
.arg(&git_dir)
.args(["remote", "-v"])
.output()?;
.output()
.await?;
let has_remote = remotes_output.status.success()
&& String::from_utf8_lossy(&remotes_output.stdout)
.lines()
@@ -298,10 +302,11 @@ impl ExtensionBuilder {
fs::create_dir_all(directory).with_context(|| {
format!("failed to create grammar directory {}", directory.display(),)
})?;
let init_output = util::command::new_std_command("git")
let init_output = util::command::new_smol_command("git")
.arg("init")
.current_dir(directory)
.output()?;
.output()
.await?;
if !init_output.status.success() {
bail!(
"failed to run `git init` in directory '{}'",
@@ -309,11 +314,12 @@ impl ExtensionBuilder {
);
}
let remote_add_output = util::command::new_std_command("git")
let remote_add_output = util::command::new_smol_command("git")
.arg("--git-dir")
.arg(&git_dir)
.args(["remote", "add", "origin", url])
.output()
.await
.context("failed to execute `git remote add`")?;
if !remote_add_output.status.success() {
bail!(
@@ -323,19 +329,21 @@ impl ExtensionBuilder {
}
}
let fetch_output = util::command::new_std_command("git")
let fetch_output = util::command::new_smol_command("git")
.arg("--git-dir")
.arg(&git_dir)
.args(["fetch", "--depth", "1", "origin", rev])
.output()
.await
.context("failed to execute `git fetch`")?;
let checkout_output = util::command::new_std_command("git")
let checkout_output = util::command::new_smol_command("git")
.arg("--git-dir")
.arg(&git_dir)
.args(["checkout", rev])
.current_dir(directory)
.output()
.await
.context("failed to execute `git checkout`")?;
if !checkout_output.status.success() {
if !fetch_output.status.success() {
@@ -356,11 +364,12 @@ impl ExtensionBuilder {
Ok(())
}
fn install_rust_wasm_target_if_needed(&self) -> Result<()> {
let rustc_output = util::command::new_std_command("rustc")
async fn install_rust_wasm_target_if_needed(&self) -> Result<()> {
let rustc_output = util::command::new_smol_command("rustc")
.arg("--print")
.arg("sysroot")
.output()
.await
.context("failed to run rustc")?;
if !rustc_output.status.success() {
bail!(
@@ -374,11 +383,12 @@ impl ExtensionBuilder {
return Ok(());
}
let output = util::command::new_std_command("rustup")
let output = util::command::new_smol_command("rustup")
.args(["target", "add", RUST_TARGET])
.stderr(Stdio::piped())
.stdout(Stdio::inherit())
.output()
.await
.context("failed to run `rustup target add`")?;
if !output.status.success() {
bail!(
+2 -1
View File
@@ -2,7 +2,6 @@ use std::collections::{BTreeSet, HashMap};
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::Arc;
use ::fs::{CopyOptions, Fs, RealFs, copy_recursive};
@@ -13,6 +12,7 @@ use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder};
use language::LanguageConfig;
use reqwest_client::ReqwestClient;
use rpc::ExtensionProvides;
use tokio::process::Command;
use tree_sitter::{Language, Query, WasmStore};
#[derive(Parser, Debug)]
@@ -89,6 +89,7 @@ async fn main() -> Result<()> {
.current_dir(&output_dir)
.args(["-czvf", "archive.tar.gz", "-C", "archive", "."])
.output()
.await
.context("failed to run tar")?;
if !tar_output.status.success() {
bail!(
+16 -9
View File
@@ -12,7 +12,7 @@ use gpui::BackgroundExecutor;
use gpui::Global;
use gpui::ReadGlobal as _;
use std::borrow::Cow;
use util::command::{new_smol_command, new_std_command};
use util::command::new_smol_command;
#[cfg(unix)]
use std::os::fd::{AsFd, AsRawFd};
@@ -135,7 +135,8 @@ pub trait Fs: Send + Sync {
);
fn open_repo(&self, abs_dot_git: &Path) -> Option<Arc<dyn GitRepository>>;
fn git_init(&self, abs_work_directory: &Path, fallback_branch_name: String) -> Result<()>;
async fn git_init(&self, abs_work_directory: &Path, fallback_branch_name: String)
-> Result<()>;
async fn git_clone(&self, repo_url: &str, abs_work_directory: &Path) -> Result<()>;
fn is_fake(&self) -> bool;
async fn is_case_sensitive(&self) -> Result<bool>;
@@ -835,11 +836,16 @@ impl Fs for RealFs {
)?))
}
fn git_init(&self, abs_work_directory_path: &Path, fallback_branch_name: String) -> Result<()> {
let config = new_std_command("git")
async fn git_init(
&self,
abs_work_directory_path: &Path,
fallback_branch_name: String,
) -> Result<()> {
let config = new_smol_command("git")
.current_dir(abs_work_directory_path)
.args(&["config", "--global", "--get", "init.defaultBranch"])
.output()?;
.output()
.await?;
let branch_name;
@@ -849,11 +855,12 @@ impl Fs for RealFs {
branch_name = Cow::Borrowed(fallback_branch_name.as_str());
}
new_std_command("git")
new_smol_command("git")
.current_dir(abs_work_directory_path)
.args(&["init", "-b"])
.arg(branch_name.trim())
.output()?;
.output()
.await?;
Ok(())
}
@@ -2438,12 +2445,12 @@ impl Fs for FakeFs {
.log_err()
}
fn git_init(
async fn git_init(
&self,
abs_work_directory_path: &Path,
_fallback_branch_name: String,
) -> Result<()> {
smol::block_on(self.create_dir(&abs_work_directory_path.join(".git")))
self.create_dir(&abs_work_directory_path.join(".git")).await
}
async fn git_clone(&self, _repo_url: &str, _abs_work_directory: &Path) -> Result<()> {
+43 -27
View File
@@ -5,6 +5,7 @@ use crate::{Oid, SHORT_SHA_LENGTH};
use anyhow::{Context as _, Result, anyhow, bail};
use collections::HashMap;
use futures::future::BoxFuture;
use futures::io::BufWriter;
use futures::{AsyncWriteExt, FutureExt as _, select_biased};
use git2::BranchType;
use gpui::{AppContext as _, AsyncApp, BackgroundExecutor, SharedString, Task};
@@ -12,20 +13,19 @@ use parking_lot::Mutex;
use rope::Rope;
use schemars::JsonSchema;
use serde::Deserialize;
use smol::io::{AsyncBufReadExt, AsyncReadExt, BufReader};
use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::io::prelude::*;
use std::process::{ExitStatus, Stdio};
use std::{
cmp::Ordering,
future,
io::{BufRead, BufReader, BufWriter, Read},
path::{Path, PathBuf},
sync::Arc,
};
use sum_tree::MapSeekTarget;
use thiserror::Error;
use util::command::{new_smol_command, new_std_command};
use util::command::new_smol_command;
use util::paths::PathStyle;
use util::rel_path::RelPath;
use util::{ResultExt, paths};
@@ -644,7 +644,7 @@ impl GitRepository for RealGitRepository {
self.executor
.spawn(async move {
let working_directory = working_directory?;
let output = new_std_command("git")
let output = new_smol_command("git")
.current_dir(&working_directory)
.args([
"--no-optional-locks",
@@ -653,7 +653,8 @@ impl GitRepository for RealGitRepository {
"--format=%H%x00%B%x00%at%x00%ae%x00%an%x00",
&commit,
])
.output()?;
.output()
.await?;
let output = std::str::from_utf8(&output.stdout)?;
let fields = output.split('\0').collect::<Vec<_>>();
if fields.len() != 6 {
@@ -681,7 +682,7 @@ impl GitRepository for RealGitRepository {
return future::ready(Err(anyhow!("no working directory"))).boxed();
};
cx.background_spawn(async move {
let show_output = util::command::new_std_command("git")
let show_output = util::command::new_smol_command("git")
.current_dir(&working_directory)
.args([
"--no-optional-locks",
@@ -696,6 +697,7 @@ impl GitRepository for RealGitRepository {
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.await
.context("starting git show process")?;
let show_stdout = String::from_utf8_lossy(&show_output.stdout);
@@ -703,7 +705,7 @@ impl GitRepository for RealGitRepository {
let parent_sha = lines.next().unwrap().trim().trim_end_matches('\0');
let changes = parse_git_diff_name_status(lines.next().unwrap_or(""));
let mut cat_file_process = util::command::new_std_command("git")
let mut cat_file_process = util::command::new_smol_command("git")
.current_dir(&working_directory)
.args(["--no-optional-locks", "cat-file", "--batch=%(objectsize)"])
.stdin(Stdio::piped())
@@ -712,7 +714,6 @@ impl GitRepository for RealGitRepository {
.spawn()
.context("starting git cat-file process")?;
use std::io::Write as _;
let mut files = Vec::<CommitFile>::new();
let mut stdin = BufWriter::with_capacity(512, cat_file_process.stdin.take().unwrap());
let mut stdout = BufReader::new(cat_file_process.stdout.take().unwrap());
@@ -726,28 +727,40 @@ impl GitRepository for RealGitRepository {
match status_code {
StatusCode::Modified => {
writeln!(&mut stdin, "{commit}:{path}")?;
writeln!(&mut stdin, "{parent_sha}:{path}")?;
stdin.write_all(commit.as_bytes()).await?;
stdin.write_all(b":").await?;
stdin.write_all(path.as_bytes()).await?;
stdin.write_all(b"\n").await?;
stdin.write_all(parent_sha.as_bytes()).await?;
stdin.write_all(b":").await?;
stdin.write_all(path.as_bytes()).await?;
stdin.write_all(b"\n").await?;
}
StatusCode::Added => {
writeln!(&mut stdin, "{commit}:{path}")?;
stdin.write_all(commit.as_bytes()).await?;
stdin.write_all(b":").await?;
stdin.write_all(path.as_bytes()).await?;
stdin.write_all(b"\n").await?;
}
StatusCode::Deleted => {
writeln!(&mut stdin, "{parent_sha}:{path}")?;
stdin.write_all(parent_sha.as_bytes()).await?;
stdin.write_all(b":").await?;
stdin.write_all(path.as_bytes()).await?;
stdin.write_all(b"\n").await?;
}
_ => continue,
}
stdin.flush()?;
stdin.flush().await?;
info_line.clear();
stdout.read_line(&mut info_line)?;
stdout.read_line(&mut info_line).await?;
let len = info_line.trim_end().parse().with_context(|| {
format!("invalid object size output from cat-file {info_line}")
})?;
let mut text = vec![0; len];
stdout.read_exact(&mut text)?;
stdout.read_exact(&mut newline)?;
stdout.read_exact(&mut text).await?;
stdout.read_exact(&mut newline).await?;
let text = String::from_utf8_lossy(&text).to_string();
let mut old_text = None;
@@ -755,13 +768,13 @@ impl GitRepository for RealGitRepository {
match status_code {
StatusCode::Modified => {
info_line.clear();
stdout.read_line(&mut info_line)?;
stdout.read_line(&mut info_line).await?;
let len = info_line.trim_end().parse().with_context(|| {
format!("invalid object size output from cat-file {}", info_line)
})?;
let mut parent_text = vec![0; len];
stdout.read_exact(&mut parent_text)?;
stdout.read_exact(&mut newline)?;
stdout.read_exact(&mut parent_text).await?;
stdout.read_exact(&mut newline).await?;
old_text = Some(String::from_utf8_lossy(&parent_text).to_string());
new_text = Some(text);
}
@@ -962,7 +975,7 @@ impl GitRepository for RealGitRepository {
self.executor
.spawn(async move {
let working_directory = working_directory?;
let mut process = new_std_command("git")
let mut process = new_smol_command("git")
.current_dir(&working_directory)
.args([
"--no-optional-locks",
@@ -980,12 +993,13 @@ impl GitRepository for RealGitRepository {
.context("no stdin for git cat-file subprocess")?;
let mut stdin = BufWriter::new(stdin);
for rev in &revs {
writeln!(&mut stdin, "{rev}")?;
stdin.write_all(rev.as_bytes()).await?;
stdin.write_all(b"\n").await?;
}
stdin.flush()?;
stdin.flush().await?;
drop(stdin);
let output = process.wait_with_output()?;
let output = process.output().await?;
let output = std::str::from_utf8(&output.stdout)?;
let shas = output
.lines()
@@ -1024,10 +1038,11 @@ impl GitRepository for RealGitRepository {
let args = git_status_args(path_prefixes);
log::debug!("Checking for git status in {path_prefixes:?}");
self.executor.spawn(async move {
let output = new_std_command(&git_binary_path)
let output = new_smol_command(&git_binary_path)
.current_dir(working_directory)
.args(args)
.output()?;
.output()
.await?;
if output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout);
stdout.parse()
@@ -1043,10 +1058,11 @@ impl GitRepository for RealGitRepository {
let working_directory = self.working_directory();
self.executor
.spawn(async move {
let output = new_std_command(&git_binary_path)
let output = new_smol_command(&git_binary_path)
.current_dir(working_directory?)
.args(&["stash", "list", "--pretty=format:%gd%x00%H%x00%ct%x00%s"])
.output()?;
.output()
.await?;
if output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout);
stdout.parse()
+1
View File
@@ -1,3 +1,4 @@
#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
#![cfg_attr(any(not(target_os = "macos"), feature = "macos-blade"), allow(unused))]
//TODO: consider generating shader code for WGSL
+17 -5
View File
@@ -204,6 +204,10 @@ impl<P: LinuxClient + 'static> Platform for P {
app_path = app_path.display()
);
#[allow(
clippy::disallowed_methods,
reason = "We are restarting ourselves, using std command thus is fine"
)]
let restart_process = Command::new("/usr/bin/env")
.arg("bash")
.arg("-c")
@@ -403,11 +407,15 @@ impl<P: LinuxClient + 'static> Platform for P {
let path = path.to_owned();
self.background_executor()
.spawn(async move {
let _ = std::process::Command::new("xdg-open")
let _ = smol::process::Command::new("xdg-open")
.arg(path)
.spawn()
.context("invoking xdg-open")
.log_err();
.log_err()?
.status()
.await
.log_err()?;
Some(())
})
.detach();
}
@@ -591,10 +599,14 @@ pub(super) fn open_uri_internal(
if let Some(token) = activation_token.as_ref() {
command.env("XDG_ACTIVATION_TOKEN", token);
}
match command.spawn() {
Ok(_) => return,
let program = format!("{:?}", command.get_program());
match smol::process::Command::from(command).spawn() {
Ok(mut cmd) => {
cmd.status().await.log_err();
return;
}
Err(e) => {
log::error!("Failed to open with {:?}: {}", command.get_program(), e)
log::error!("Failed to open with {}: {}", program, e)
}
}
}
+9 -2
View File
@@ -543,6 +543,10 @@ impl Platform for MacPlatform {
open "$1"
"#;
#[allow(
clippy::disallowed_methods,
reason = "We are restarting ourselves, using std command thus is fine"
)]
let restart_process = Command::new("/bin/bash")
.arg("-c")
.arg(script)
@@ -855,11 +859,14 @@ impl Platform for MacPlatform {
.lock()
.background_executor
.spawn(async move {
let _ = std::process::Command::new("open")
if let Some(mut child) = smol::process::Command::new("open")
.arg(path)
.spawn()
.context("invoking open command")
.log_err();
.log_err()
{
child.status().await.log_err();
}
})
.detach();
}
@@ -349,6 +349,11 @@ impl Platform for WindowsPlatform {
pid,
app_path.display(),
);
#[allow(
clippy::disallowed_methods,
reason = "We are restarting ourselves, using std command thus is fine"
)]
let restart_process = util::command::new_std_command("powershell.exe")
.arg("-command")
.arg(script)
+1
View File
@@ -1,3 +1,4 @@
#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
#[cfg(target_os = "macos")]
fn main() {
use std::{env, path::PathBuf, process::Command};
+1 -1
View File
@@ -249,7 +249,7 @@ async fn load_shell_environment(
use util::shell_env;
let dir_ = dir.to_owned();
let mut envs = match smol::unblock(move || shell_env::capture(&dir_)).await {
let mut envs = match shell_env::capture(&dir_).await {
Ok(envs) => envs,
Err(err) => {
util::log_err(&err);
+1 -1
View File
@@ -1407,7 +1407,7 @@ impl GitStore {
GitStoreState::Local { fs, .. } => {
let fs = fs.clone();
cx.background_executor()
.spawn(async move { fs.git_init(&path, fallback_branch_name) })
.spawn(async move { fs.git_init(&path, fallback_branch_name).await })
}
GitStoreState::Remote {
upstream_client,
+5 -1
View File
@@ -470,7 +470,7 @@ impl Project {
TerminalSettings::get(settings_location, cx)
}
pub fn exec_in_shell(&self, command: String, cx: &App) -> Result<std::process::Command> {
pub fn exec_in_shell(&self, command: String, cx: &App) -> Result<smol::process::Command> {
let path = self.first_project_directory(cx);
let remote_client = self.remote_client.as_ref();
let settings = self.terminal_settings(&path, cx).clone();
@@ -508,6 +508,10 @@ impl Project {
Ok(command)
}
}
.map(|mut process| {
util::set_pre_exec_to_start_new_session(&mut process);
smol::process::Command::from(process)
})
}
pub fn local_terminal_handles(&self) -> &Vec<WeakEntity<terminal::Terminal>> {
+1
View File
@@ -1,3 +1,4 @@
#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
use std::process::Command;
const ZED_MANIFEST: &str = include_str!("../zed/Cargo.toml");
+37 -30
View File
@@ -547,32 +547,36 @@ pub(crate) fn execute_proxy(
.detach();
log::info!("starting proxy process. PID: {}", std::process::id());
smol::block_on(async {
let server_pid = check_pid_file(&server_paths.pid_file)
.await
.map_err(|source| ExecuteProxyError::CheckPidFile {
source,
path: server_paths.pid_file.clone(),
})?;
let server_running = server_pid.is_some();
if is_reconnecting {
if !server_running {
log::error!("attempted to reconnect, but no server running");
return Err(ExecuteProxyError::ServerNotRunning(
ProxyLaunchError::ServerNotRunning,
));
}
} else {
if let Some(pid) = server_pid {
log::info!(
"proxy found server already running with PID {}. Killing process and cleaning up files...",
pid
);
kill_running_server(pid, &server_paths).await?;
}
let server_pid = check_pid_file(&server_paths.pid_file).map_err(|source| {
ExecuteProxyError::CheckPidFile {
source,
path: server_paths.pid_file.clone(),
}
spawn_server(&server_paths)
.await
.map_err(ExecuteProxyError::SpawnServer)?;
};
Ok(())
})?;
let server_running = server_pid.is_some();
if is_reconnecting {
if !server_running {
log::error!("attempted to reconnect, but no server running");
return Err(ExecuteProxyError::ServerNotRunning(
ProxyLaunchError::ServerNotRunning,
));
}
} else {
if let Some(pid) = server_pid {
log::info!(
"proxy found server already running with PID {}. Killing process and cleaning up files...",
pid
);
kill_running_server(pid, &server_paths)?;
}
spawn_server(&server_paths).map_err(ExecuteProxyError::SpawnServer)?;
};
let stdin_task = smol::spawn(async move {
let stdin = Async::new(std::io::stdin())?;
@@ -626,11 +630,12 @@ pub(crate) fn execute_proxy(
Ok(())
}
fn kill_running_server(pid: u32, paths: &ServerPaths) -> Result<(), ExecuteProxyError> {
async fn kill_running_server(pid: u32, paths: &ServerPaths) -> Result<(), ExecuteProxyError> {
log::info!("killing existing server with PID {}", pid);
std::process::Command::new("kill")
smol::process::Command::new("kill")
.arg(pid.to_string())
.output()
.await
.map_err(|source| ExecuteProxyError::KillRunningServer { source, pid })?;
for file in [
@@ -666,7 +671,7 @@ pub(crate) enum SpawnServerError {
LaunchStatus { status: ExitStatus, paths: String },
}
fn spawn_server(paths: &ServerPaths) -> Result<(), SpawnServerError> {
async fn spawn_server(paths: &ServerPaths) -> Result<(), SpawnServerError> {
if paths.stdin_socket.exists() {
std::fs::remove_file(&paths.stdin_socket).map_err(SpawnServerError::RemoveStdinSocket)?;
}
@@ -678,7 +683,7 @@ fn spawn_server(paths: &ServerPaths) -> Result<(), SpawnServerError> {
}
let binary_name = std::env::current_exe().map_err(SpawnServerError::CurrentExe)?;
let mut server_process = std::process::Command::new(binary_name);
let mut server_process = smol::process::Command::new(binary_name);
server_process
.arg("run")
.arg("--log-file")
@@ -694,6 +699,7 @@ fn spawn_server(paths: &ServerPaths) -> Result<(), SpawnServerError> {
let status = server_process
.status()
.await
.map_err(SpawnServerError::ProcessStatus)?;
if !status.success() {
@@ -733,7 +739,7 @@ pub(crate) struct CheckPidError {
pid: u32,
}
fn check_pid_file(path: &Path) -> Result<Option<u32>, CheckPidError> {
async fn check_pid_file(path: &Path) -> Result<Option<u32>, CheckPidError> {
let Some(pid) = std::fs::read_to_string(&path)
.ok()
.and_then(|contents| contents.parse::<u32>().ok())
@@ -742,10 +748,11 @@ fn check_pid_file(path: &Path) -> Result<Option<u32>, CheckPidError> {
};
log::debug!("Checking if process with PID {} exists...", pid);
match std::process::Command::new("kill")
match smol::process::Command::new("kill")
.arg("-0")
.arg(pid.to_string())
.output()
.await
{
Ok(output) if output.status.success() => {
log::debug!(
+4
View File
@@ -146,6 +146,10 @@ impl Display for SystemSpecs {
fn try_determine_available_gpus() -> Option<String> {
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
{
#[allow(
clippy::disallowed_methods,
reason = "we are not running in an executor"
)]
std::process::Command::new("vulkaninfo")
.args(&["--summary"])
.output()
+5 -6
View File
@@ -5,7 +5,7 @@ use collections::HashMap;
/// Capture all environment variables from the login shell.
#[cfg(unix)]
pub fn capture(directory: &std::path::Path) -> Result<collections::HashMap<String, String>> {
pub async fn capture(directory: &std::path::Path) -> Result<collections::HashMap<String, String>> {
use std::os::unix::process::CommandExt;
use std::process::Stdio;
@@ -59,7 +59,7 @@ pub fn capture(directory: &std::path::Path) -> Result<collections::HashMap<Strin
super::set_pre_exec_to_start_new_session(&mut command);
let (env_output, process_output) = spawn_and_read_fd(command, fd_num)?;
let (env_output, process_output) = spawn_and_read_fd(command, fd_num).await?;
let env_output = String::from_utf8_lossy(&env_output);
anyhow::ensure!(
@@ -77,7 +77,7 @@ pub fn capture(directory: &std::path::Path) -> Result<collections::HashMap<Strin
}
#[cfg(unix)]
fn spawn_and_read_fd(
async fn spawn_and_read_fd(
mut command: std::process::Command,
child_fd: std::os::fd::RawFd,
) -> anyhow::Result<(Vec<u8>, std::process::Output)> {
@@ -91,13 +91,12 @@ fn spawn_and_read_fd(
child_fd,
}])?;
let process = command.spawn()?;
drop(command);
let process = smol::process::Command::from(command).spawn()?;
let mut buffer = Vec::new();
reader.read_to_end(&mut buffer)?;
Ok((buffer, process.wait_with_output()?))
Ok((buffer, process.output().await?))
}
pub fn print_env() {
+2 -2
View File
@@ -308,7 +308,7 @@ pub fn get_shell_safe_zed_path() -> anyhow::Result<String> {
}
#[cfg(unix)]
pub fn load_login_shell_environment() -> Result<()> {
pub async fn load_login_shell_environment() -> Result<()> {
load_shell_from_passwd().log_err();
// If possible, we want to `cd` in the user's `$HOME` to trigger programs
@@ -316,7 +316,7 @@ pub fn load_login_shell_environment() -> Result<()> {
// into shell's `cd` command (and hooks) to manipulate env.
// We do this so that we get the env a user would have when spawning a shell
// in home directory.
for (name, value) in shell_env::capture(paths::home_dir())? {
for (name, value) in shell_env::capture(paths::home_dir()).await? {
unsafe { env::set_var(&name, &value) };
}
+4 -7
View File
@@ -6,6 +6,7 @@ use editor::{
actions::{SortLinesCaseInsensitive, SortLinesCaseSensitive},
display_map::ToDisplayPoint,
};
use futures::AsyncWriteExt as _;
use gpui::{Action, App, AppContext as _, Context, Global, Keystroke, Task, Window, actions};
use itertools::Itertools;
use language::Point;
@@ -16,7 +17,6 @@ use schemars::JsonSchema;
use search::{BufferSearchBar, SearchOptions};
use serde::Deserialize;
use std::{
io::Write,
iter::Peekable,
ops::{Deref, Range},
path::Path,
@@ -1966,7 +1966,6 @@ impl ShellExec {
process.stdin(Stdio::null());
};
util::set_pre_exec_to_start_new_session(&mut process);
let is_read = self.is_read;
let task = cx.spawn_in(window, async move |vim, cx| {
@@ -1984,18 +1983,16 @@ impl ShellExec {
let range = range.clone();
cx.background_spawn(async move {
for chunk in snapshot.text_for_range(range) {
if stdin.write_all(chunk.as_bytes()).log_err().is_none() {
if stdin.write_all(chunk.as_bytes()).await.log_err().is_none() {
return;
}
}
stdin.flush().log_err();
stdin.flush().await.log_err();
})
.detach();
};
let output = cx
.background_spawn(async move { running.wait_with_output() })
.await;
let output = cx.background_spawn(running.output()).await;
let Some(output) = output.log_err() else {
vim.update_in(cx, |vim, window, cx| {
+1
View File
@@ -1,3 +1,4 @@
#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
use std::process::Command;
fn main() {
+1 -1
View File
@@ -339,7 +339,7 @@ pub fn main() {
app.background_executor()
.spawn(async {
#[cfg(unix)]
util::load_login_shell_environment().log_err();
util::load_login_shell_environment().await.log_err();
shell_env_loaded_tx.send(()).ok();
})
.detach()
+1
View File
@@ -23,6 +23,7 @@ allow_attributes_without_reason = "deny" # This covers `expect` also, since we d
let_underscore_must_use = "forbid"
undocumented_unsafe_blocks = "forbid"
missing_safety_doc = "forbid"
disallowed_methods = { level = "allow", priority = 1}
[dependencies]
collections.workspace = true
+1
View File
@@ -1,3 +1,4 @@
#![allow(clippy::disallowed_methods, reason = "tooling is exempt")]
use std::process::Command;
use anyhow::{Context as _, Result, bail};