Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

fix(rome_cli): ensures the service only connects to compatible versions #3642

Merged
merged 3 commits into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 54 additions & 20 deletions crates/rome_cli/src/commands/rage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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!(<Dim>"stopped"</Dim>))}
)
.fmt(f);
let versions = match enumerate_pipes() {
Ok(iter) => iter,
Err(err) => {
(markup! {<Error>"\u{2716} Enumerating Rome instances failed:"</Error>}).fmt(f)?;
return writeln!(f, " {err}");
}
Ok(Some(transport)) => {
markup!("\n"<Emphasis>"Running Rome Server:"</Emphasis>" "{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!(<Dim>"stopped"</Dim>))}
)
.fmt(f)?;
continue;
}
Ok(Some(transport)) => {
markup!("\n"<Emphasis>"Running Rome Server:"</Emphasis>" "{HorizontalLine::new(78)}"

"<Info>"\u{2139} The client isn't connected to any server but rage discovered this running Rome server."</Info>"
")
.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!(<Error>"\u{2716} Failed to connect: "</Error>).fmt(f)?;
writeln!(f, "{err}")?;
}
}
}
Err(err) => {
markup!(<Error>"\u{2716} Failed to connect: "</Error>).fmt(f)?;
markup!("\n"<Error>"\u{2716} Failed to connect: "</Error>).fmt(f)?;
writeln!(f, "{err}")?;
}
}

RomeServerLog.fmt(f)?;
} else {
markup!("\n"<Emphasis>"Incompatible Rome Server:"</Emphasis>" "{HorizontalLine::new(78)}"

"<Info>"\u{2139} Rage discovered this running server using an incompatible version of Rome."</Info>"
")
.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"<Error>"\u{2716} Failed to connect: "</Error>).fmt(f)?;
writeln!(f, "{err}")?;
}
};
}

RomeServerLog.fmt(f)
Ok(())
}
}

Expand Down
10 changes: 6 additions & 4 deletions crates/rome_cli/src/service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 18 additions & 1 deletion crates/rome_cli/src/service/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<impl Iterator<Item = String>> {
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
Expand Down
33 changes: 27 additions & 6 deletions crates/rome_cli/src/service/windows.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{
convert::Infallible,
env,
fs::read_dir,
io::{self, ErrorKind},
mem::swap,
os::windows::process::CommandExt,
Expand All @@ -19,16 +20,36 @@ 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<impl Iterator<Item = String>> {
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;

/// Try to connect to the global pipe and wait for the connection to become ready
async fn try_connect() -> io::Result<NamedPipeClient> {
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
Expand Down Expand Up @@ -165,7 +186,7 @@ pub(crate) async fn ensure_daemon() -> io::Result<bool> {
/// 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(())
}

Expand All @@ -174,11 +195,11 @@ pub(crate) async fn print_socket() -> io::Result<()> {
pub(crate) async fn run_daemon(factory: ServerFactory) -> io::Result<Infallible> {
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();
Expand Down