diff --git a/Cargo.lock b/Cargo.lock index 06122f130e..2f4c7c67f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -448,7 +448,6 @@ dependencies = [ "smol", "tempfile", "util", - "which 6.0.3", "workspace-hack", ] @@ -11752,6 +11751,7 @@ name = "remote_server" version = "0.1.0" dependencies = [ "anyhow", + "askpass", "async-watch", "backtrace", "cargo_toml", @@ -18204,6 +18204,7 @@ dependencies = [ "agent", "anyhow", "ashpd", + "askpass", "assets", "assistant", "assistant_context_editor", diff --git a/crates/askpass/Cargo.toml b/crates/askpass/Cargo.toml index fbb819a065..d64ee9f7c3 100644 --- a/crates/askpass/Cargo.toml +++ b/crates/askpass/Cargo.toml @@ -18,5 +18,4 @@ gpui.workspace = true smol.workspace = true tempfile.workspace = true util.workspace = true -which.workspace = true workspace-hack.workspace = true diff --git a/crates/askpass/src/askpass.rs b/crates/askpass/src/askpass.rs index ee7dd9bc0e..a4bdb5edb8 100644 --- a/crates/askpass/src/askpass.rs +++ b/crates/askpass/src/askpass.rs @@ -72,6 +72,8 @@ impl AskPassSession { let (askpass_opened_tx, askpass_opened_rx) = oneshot::channel::<()>(); let listener = UnixListener::bind(&askpass_socket).context("failed to create askpass socket")?; + let zed_path = std::env::current_exe() + .context("Failed to figure out current executable path for use in askpass")?; let (askpass_kill_master_tx, askpass_kill_master_rx) = oneshot::channel::<()>(); let mut kill_tx = Some(askpass_kill_master_tx); @@ -110,21 +112,10 @@ impl AskPassSession { drop(temp_dir) }); - anyhow::ensure!( - which::which("nc").is_ok(), - "Cannot find `nc` command (netcat), which is required to connect over SSH." - ); - // Create an askpass script that communicates back to this process. let askpass_script = format!( - "{shebang}\n{print_args} | {nc} -U {askpass_socket} 2> /dev/null \n", - // on macOS `brew install netcat` provides the GNU netcat implementation - // which does not support -U. - nc = if cfg!(target_os = "macos") { - "/usr/bin/nc" - } else { - "nc" - }, + "{shebang}\n{print_args} | {zed_exe} --askpass={askpass_socket} 2> /dev/null \n", + zed_exe = zed_path.display(), askpass_socket = askpass_socket.display(), print_args = "printf '%s\\0' \"$@\"", shebang = "#!/bin/sh", @@ -170,6 +161,51 @@ impl AskPassSession { } } +/// The main function for when Zed is running in netcat mode for use in askpass. +/// Called from both the remote server binary and the zed binary in their respective main functions. +#[cfg(unix)] +pub fn main(socket: &str) { + use std::io::{self, Read, Write}; + use std::os::unix::net::UnixStream; + use std::process::exit; + + let mut stream = match UnixStream::connect(socket) { + Ok(stream) => stream, + Err(err) => { + eprintln!("Error connecting to socket {}: {}", socket, err); + exit(1); + } + }; + + let mut buffer = Vec::new(); + if let Err(err) = io::stdin().read_to_end(&mut buffer) { + eprintln!("Error reading from stdin: {}", err); + exit(1); + } + + if buffer.last() != Some(&b'\0') { + buffer.push(b'\0'); + } + + if let Err(err) = stream.write_all(&buffer) { + eprintln!("Error writing to socket: {}", err); + exit(1); + } + + let mut response = Vec::new(); + if let Err(err) = stream.read_to_end(&mut response) { + eprintln!("Error reading from socket: {}", err); + exit(1); + } + + if let Err(err) = io::stdout().write_all(&response) { + eprintln!("Error writing to stdout: {}", err); + exit(1); + } +} +#[cfg(not(unix))] +pub fn main(_socket: &str) {} + #[cfg(not(unix))] pub struct AskPassSession { path: PathBuf, diff --git a/crates/remote_server/Cargo.toml b/crates/remote_server/Cargo.toml index d50a5c8339..01e2bb97ff 100644 --- a/crates/remote_server/Cargo.toml +++ b/crates/remote_server/Cargo.toml @@ -23,6 +23,7 @@ test-support = ["fs/test-support"] [dependencies] anyhow.workspace = true +askpass.workspace = true async-watch.workspace = true backtrace = "0.3" chrono.workspace = true diff --git a/crates/remote_server/src/main.rs b/crates/remote_server/src/main.rs index c43db645f8..269d19903f 100644 --- a/crates/remote_server/src/main.rs +++ b/crates/remote_server/src/main.rs @@ -8,6 +8,10 @@ use std::path::PathBuf; struct Cli { #[command(subcommand)] command: Option, + /// Used for SSH/Git password authentication, to remove the need for netcat as a dependency, + /// by having Zed act like netcat communicating over a Unix socket. + #[arg(long, hide = true)] + askpass: Option, } #[derive(Subcommand)] @@ -46,6 +50,11 @@ fn main() { let cli = Cli::parse(); + if let Some(socket_path) = &cli.askpass { + askpass::main(socket_path); + return; + } + let result = match cli.command { Some(Commands::Run { log_file, diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 6b61ed00ac..272bf56677 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -18,6 +18,7 @@ path = "src/main.rs" activity_indicator.workspace = true agent.workspace = true anyhow.workspace = true +askpass.workspace = true assets.workspace = true assistant.workspace = true assistant_context_editor.workspace = true diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 967f4aac14..9339128ee0 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -180,6 +180,11 @@ fn main() { let args = Args::parse(); + if let Some(socket) = &args.askpass { + askpass::main(socket); + return; + } + // Set custom data directory. if let Some(dir) = &args.user_data_dir { paths::set_custom_data_dir(dir); @@ -1002,6 +1007,11 @@ struct Args { #[arg(long)] system_specs: bool, + /// Used for SSH/Git password authentication, to remove the need for netcat as a dependency, + /// by having Zed act like netcat communicating over a Unix socket. + #[arg(long, hide = true)] + askpass: Option, + /// Run zed in the foreground, only used on Windows, to match the behavior of the behavior on macOS. #[arg(long)] #[cfg(target_os = "windows")] diff --git a/script/linux b/script/linux index 79e03df841..98ae026896 100755 --- a/script/linux +++ b/script/linux @@ -38,7 +38,6 @@ if [[ -n $apt ]]; then cmake clang jq - netcat-openbsd git curl gettext-base @@ -88,14 +87,12 @@ if [[ -n $dnf ]] || [[ -n $yum ]]; then tar ) # perl used for building openssl-sys crate. See: https://docs.rs/openssl/latest/openssl/ - # openbsd-netcat is unavailable in RHEL8/9 (and nmap-ncat doesn't support sockets) if grep -qP '^ID="?(fedora)' /etc/os-release; then deps+=( perl-FindBin perl-IPC-Cmd perl-File-Compare perl-File-Copy - netcat mold ) elif grep -qP '^ID="?(rhel|rocky|alma|centos|ol)' /etc/os-release; then @@ -152,7 +149,6 @@ if [[ -n $zyp ]]; then libzstd-devel make mold - netcat-openbsd openssl-devel sqlite3-devel tar @@ -179,7 +175,6 @@ if [[ -n $pacman ]]; then libgit2 libxcb libxkbcommon-x11 - openbsd-netcat openssl zstd pkgconf @@ -209,7 +204,6 @@ if [[ -n $xbps ]]; then libxcb-devel libxkbcommon-devel libzstd-devel - openbsd-netcat openssl-devel wayland-devel vulkan-loader @@ -234,7 +228,6 @@ if [[ -n $emerge ]]; then media-libs/alsa-lib media-libs/fontconfig media-libs/vulkan-loader - net-analyzer/openbsd-netcat x11-libs/libxcb x11-libs/libxkbcommon sys-devel/mold