From 90ec8a48b28431d78a57fa7a61e6f3fdeb84131a Mon Sep 17 00:00:00 2001 From: l3ops Date: Thu, 10 Nov 2022 15:29:52 +0100 Subject: [PATCH] fix(rome_cli): ensures the service only connects to compatible versions (#3642) * fix(rome_cli): ensures the service only connects to compatible versions * enumerate running rome servers in `rome rage` * fix the build on unix --- crates/rome_cli/src/commands/rage.rs | 74 +++++++++++++++++++------- crates/rome_cli/src/service/mod.rs | 10 ++-- crates/rome_cli/src/service/unix.rs | 19 ++++++- crates/rome_cli/src/service/windows.rs | 33 +++++++++--- 4 files changed, 105 insertions(+), 31 deletions(-) diff --git a/crates/rome_cli/src/commands/rage.rs b/crates/rome_cli/src/commands/rage.rs index 4d570eb434c..a88aeb454b9 100644 --- a/crates/rome_cli/src/commands/rage.rs +++ b/crates/rome_cli/src/commands/rage.rs @@ -9,6 +9,7 @@ use std::{env, io, ops::Deref}; use tokio::runtime::Runtime; use crate::commands::daemon::read_most_recent_log_file; +use crate::service::enumerate_pipes; use crate::{service, CliSession, Termination, VERSION}; /// Handler for the `rage` command @@ -85,38 +86,71 @@ struct RunningRomeServer; impl Display for RunningRomeServer { fn fmt(&self, f: &mut Formatter) -> io::Result<()> { - let runtime = Runtime::new()?; - - match service::open_transport(runtime) { - Ok(None) => { - return markup!( - {Section("Server")} - {KeyValuePair("Status", markup!("stopped"))} - ) - .fmt(f); + let versions = match enumerate_pipes() { + Ok(iter) => iter, + Err(err) => { + (markup! {"\u{2716} Enumerating Rome instances failed:"}).fmt(f)?; + return writeln!(f, " {err}"); } - Ok(Some(transport)) => { - markup!("\n""Running Rome Server:"" "{HorizontalLine::new(78)}" + }; + + for version in versions { + if version == rome_service::VERSION { + let runtime = Runtime::new()?; + match service::open_transport(runtime) { + Ok(None) => { + markup!( + {Section("Server")} + {KeyValuePair("Status", markup!("stopped"))} + ) + .fmt(f)?; + continue; + } + Ok(Some(transport)) => { + markup!("\n""Running Rome Server:"" "{HorizontalLine::new(78)}" ""\u{2139} The client isn't connected to any server but rage discovered this running Rome server."" ") .fmt(f)?; - match client(transport) { - Ok(client) => WorkspaceRage(client.deref()).fmt(f)?, + match client(transport) { + Ok(client) => WorkspaceRage(client.deref()).fmt(f)?, + Err(err) => { + markup!("\u{2716} Failed to connect: ").fmt(f)?; + writeln!(f, "{err}")?; + } + } + } Err(err) => { - markup!("\u{2716} Failed to connect: ").fmt(f)?; + markup!("\n""\u{2716} Failed to connect: ").fmt(f)?; writeln!(f, "{err}")?; } } + + RomeServerLog.fmt(f)?; + } else { + markup!("\n""Incompatible Rome Server:"" "{HorizontalLine::new(78)}" + +""\u{2139} Rage discovered this running server using an incompatible version of Rome."" +") + .fmt(f)?; + + // Version 10.0.0 and below did not include a service version in the pipe name + let version = if version.is_empty() { + "<=10.0.0" + } else { + version.as_str() + }; + + markup!( + {Section("Server")} + {KeyValuePair("Version", markup!({version}))} + ) + .fmt(f)?; } - Err(err) => { - markup!("\n""\u{2716} Failed to connect: ").fmt(f)?; - writeln!(f, "{err}")?; - } - }; + } - RomeServerLog.fmt(f) + Ok(()) } } diff --git a/crates/rome_cli/src/service/mod.rs b/crates/rome_cli/src/service/mod.rs index e983010b367..a0b5b3b6d8b 100644 --- a/crates/rome_cli/src/service/mod.rs +++ b/crates/rome_cli/src/service/mod.rs @@ -43,14 +43,16 @@ use tokio::{ #[cfg(windows)] mod windows; #[cfg(windows)] -pub(crate) use self::windows::{ensure_daemon, open_socket, print_socket, run_daemon}; +pub(crate) use self::windows::{ + ensure_daemon, enumerate_pipes, open_socket, print_socket, run_daemon, +}; #[cfg(unix)] mod unix; #[cfg(unix)] -pub(crate) use self::unix::open_socket; -#[cfg(unix)] -pub(crate) use self::unix::{ensure_daemon, print_socket, run_daemon}; +pub(crate) use self::unix::{ + ensure_daemon, enumerate_pipes, open_socket, print_socket, run_daemon, +}; /// Tries to open a connection to a running daemon instance, returning a /// [WorkspaceTransport] instance if the socket is currently active diff --git a/crates/rome_cli/src/service/unix.rs b/crates/rome_cli/src/service/unix.rs index b9b0cfdbc4b..f5d046f92da 100644 --- a/crates/rome_cli/src/service/unix.rs +++ b/crates/rome_cli/src/service/unix.rs @@ -21,7 +21,24 @@ use tracing::Instrument; /// Returns the filesystem path of the global socket used to communicate with /// the server daemon fn get_socket_name() -> PathBuf { - env::temp_dir().join("rome-socket") + env::temp_dir().join(format!("rome-socket-{}", rome_service::VERSION)) +} + +pub(crate) fn enumerate_pipes() -> io::Result> { + fs::read_dir(env::temp_dir()).map(|iter| { + iter.filter_map(|entry| { + let entry = entry.ok()?.path(); + let file_name = entry.file_name()?; + let file_name = file_name.to_str()?; + + let rome_version = file_name.strip_prefix("rome-socket")?; + if rome_version.is_empty() { + Some(String::new()) + } else { + Some(rome_version.strip_prefix('-')?.to_string()) + } + }) + }) } /// Try to connect to the global socket and wait for the connection to become ready diff --git a/crates/rome_cli/src/service/windows.rs b/crates/rome_cli/src/service/windows.rs index b873cd46bdb..513cb16cae5 100644 --- a/crates/rome_cli/src/service/windows.rs +++ b/crates/rome_cli/src/service/windows.rs @@ -1,6 +1,7 @@ use std::{ convert::Infallible, env, + fs::read_dir, io::{self, ErrorKind}, mem::swap, os::windows::process::CommandExt, @@ -19,8 +20,28 @@ use tokio::{ }; use tracing::Instrument; -/// Name of the global named pipe used to communicate with the server daemon -const PIPE_NAME: &str = r"\\.\pipe\rome-service"; +/// Returns the name of the global named pipe used to communicate with the +/// server daemon +fn get_pipe_name() -> String { + format!(r"\\.\pipe\rome-service-{}", rome_service::VERSION) +} + +pub(crate) fn enumerate_pipes() -> io::Result> { + read_dir(r"\\.\pipe").map(|iter| { + iter.filter_map(|entry| { + let entry = entry.ok()?.path(); + let file_name = entry.file_name()?; + let file_name = file_name.to_str()?; + + let rome_version = file_name.strip_prefix("rome-service")?; + if rome_version.is_empty() { + Some(String::new()) + } else { + Some(rome_version.strip_prefix('-')?.to_string()) + } + }) + }) +} /// Error code from the Win32 API const ERROR_PIPE_BUSY: i32 = 231; @@ -28,7 +49,7 @@ const ERROR_PIPE_BUSY: i32 = 231; /// Try to connect to the global pipe and wait for the connection to become ready async fn try_connect() -> io::Result { loop { - match ClientOptions::new().open(PIPE_NAME) { + match ClientOptions::new().open(get_pipe_name()) { Ok(client) => return Ok(client), // If the connection failed with ERROR_PIPE_BUSY, wait a few // milliseconds then retry the connection (we should be using @@ -165,7 +186,7 @@ pub(crate) async fn ensure_daemon() -> io::Result { /// print the global pipe name in the standard output pub(crate) async fn print_socket() -> io::Result<()> { ensure_daemon().await?; - println!("{PIPE_NAME}"); + println!("{}", get_pipe_name()); Ok(()) } @@ -174,11 +195,11 @@ pub(crate) async fn print_socket() -> io::Result<()> { pub(crate) async fn run_daemon(factory: ServerFactory) -> io::Result { let mut prev_server = ServerOptions::new() .first_pipe_instance(true) - .create(PIPE_NAME)?; + .create(get_pipe_name())?; loop { prev_server.connect().await?; - let mut next_server = ServerOptions::new().create(PIPE_NAME)?; + let mut next_server = ServerOptions::new().create(get_pipe_name())?; swap(&mut prev_server, &mut next_server); let connection = factory.create();