From 96acdff581c9307cafd89387c66b306dd6882492 Mon Sep 17 00:00:00 2001 From: Austin Jones Date: Thu, 8 Oct 2020 20:04:13 -0400 Subject: [PATCH 1/2] Add incompatibility warnings if the tab binary or version differs If the tab command client version differs from the daemon (ignoring patch versions), then a message is printed recommending that `tab --shutdown` be invoked. If the tab binary path differs from the daemon executable, then a message is printed suggesting that `tab --shutdown` be invoked. --- Cargo.lock | 156 +++++++++++++++++++++- common/tab-api/src/config.rs | 2 + tab-command/Cargo.toml | 1 + tab-command/src/lib.rs | 58 +++++++- tab-daemon/src/lib.rs | 17 ++- tab-daemon/src/service/daemon/listener.rs | 10 +- tab/src/main.rs | 6 +- 7 files changed, 226 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d9bac224..39cf9a7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,15 +142,42 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.3", +] + [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.3.4" @@ -345,13 +372,22 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.3", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] [[package]] @@ -419,6 +455,12 @@ dependencies = [ "libc", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fnv" version = "1.0.7" @@ -548,6 +590,15 @@ version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.4" @@ -706,6 +757,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matches" version = "0.1.8" @@ -866,6 +923,12 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -902,6 +965,49 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1 0.8.2", +] + [[package]] name = "pin-project" version = "0.4.23" @@ -1130,6 +1236,25 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e012c6c5380fb91897ba7b9261a0f565e624e869d42fe1a1d03fa0d68a083d5" +dependencies = [ + "pest", + "pest_derive", +] + [[package]] name = "serde" version = "1.0.116" @@ -1173,17 +1298,29 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + [[package]] name = "sha-1" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpuid-bool", - "digest", - "opaque-debug", + "digest 0.9.0", + "opaque-debug 0.3.0", ] [[package]] @@ -1344,6 +1481,7 @@ dependencies = [ "crossterm", "lifeline", "log", + "semver", "serde", "serde_yaml", "simplelog", @@ -1634,7 +1772,7 @@ dependencies = [ "input_buffer", "log", "rand", - "sha-1", + "sha-1 0.9.1", "url", "utf-8", ] @@ -1645,6 +1783,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "unicode-bidi" version = "0.3.4" diff --git a/common/tab-api/src/config.rs b/common/tab-api/src/config.rs index 6cef6b1a..15c69d54 100644 --- a/common/tab-api/src/config.rs +++ b/common/tab-api/src/config.rs @@ -10,6 +10,8 @@ use sysinfo::{RefreshKind, SystemExt}; pub struct DaemonConfig { pub pid: i32, pub port: u16, + pub executable: Option, + pub tab_version: Option, pub auth_token: String, } diff --git a/tab-command/Cargo.toml b/tab-command/Cargo.toml index 99a76dff..21234256 100644 --- a/tab-command/Cargo.toml +++ b/tab-command/Cargo.toml @@ -28,6 +28,7 @@ simplelog = "0.8" # error management anyhow = "1.0" thiserror = "1.0" +semver = "0.11" # config serde = {version = "1.0", features = ["derive"]} diff --git a/tab-command/src/lib.rs b/tab-command/src/lib.rs index 3c74171c..81541582 100644 --- a/tab-command/src/lib.rs +++ b/tab-command/src/lib.rs @@ -1,4 +1,7 @@ +use std::path::PathBuf; + use clap::ArgMatches; +use semver::Version; use crate::prelude::*; use service::{main::*, terminal::disable_raw_mode, terminal::reset_cursor}; @@ -9,7 +12,7 @@ use crate::bus::MainBus; use message::main::{MainRecv, MainShutdown}; use lifeline::dyn_bus::DynBus; -use tab_api::{launch::*, log::get_level, tab::normalize_name}; +use tab_api::{config::DaemonConfig, launch::*, log::get_level, tab::normalize_name}; use tab_websocket::resource::connection::WebsocketResource; mod bus; @@ -19,7 +22,7 @@ mod prelude; mod service; mod state; -pub fn command_main(args: ArgMatches) -> anyhow::Result<()> { +pub fn command_main(args: ArgMatches, tab_version: &'static str) -> anyhow::Result<()> { TermLogger::init( get_level().unwrap_or(LevelFilter::Warn), simplelog::ConfigBuilder::new() @@ -38,7 +41,7 @@ pub fn command_main(args: ArgMatches) -> anyhow::Result<()> { .build() .unwrap(); - let result = runtime.block_on(async { main_async(args).await }); + let result = runtime.block_on(async { main_async(args, tab_version).await }); runtime.shutdown_background(); @@ -49,10 +52,10 @@ pub fn command_main(args: ArgMatches) -> anyhow::Result<()> { Ok(()) } -async fn main_async(matches: ArgMatches<'_>) -> anyhow::Result<()> { +async fn main_async(matches: ArgMatches<'_>, tab_version: &'static str) -> anyhow::Result<()> { let select_tab = matches.value_of("TAB-NAME"); let close_tabs = matches.values_of("CLOSE-TAB"); - let (mut tx, rx_shutdown, _service) = spawn().await?; + let (mut tx, rx_shutdown, _service) = spawn(tab_version).await?; let completion = matches.is_present("AUTOCOMPLETE-TAB"); let close_completion = matches.is_present("AUTOCOMPLETE-CLOSE-TAB"); let shutdown = matches.is_present("SHUTDOWN"); @@ -82,12 +85,15 @@ async fn main_async(matches: ArgMatches<'_>) -> anyhow::Result<()> { Ok(()) } -async fn spawn() -> anyhow::Result<( +async fn spawn( + tab_version: &'static str, +) -> anyhow::Result<( impl Sender, impl Receiver, MainService, )> { let daemon_file = launch_daemon().await?; + validate_daemon(&daemon_file, tab_version); let ws_url = format!("ws://127.0.0.1:{}/cli", daemon_file.port); debug!("daemon is ready"); @@ -109,3 +115,43 @@ async fn spawn() -> anyhow::Result<( Ok((tx, main_shutdown, service)) } + +fn validate_daemon(config: &DaemonConfig, tab_version: &'static str) { + let executable = std::env::current_exe() + .ok() + .map(|path| path.to_str().map(str::to_string)) + .flatten(); + + let tab_version = Version::parse(tab_version).ok(); + let daemon_version = config + .tab_version + .as_ref() + .map(String::as_str) + .map(Version::parse) + .map(Result::ok) + .flatten(); + + if let (Some(tab_version), Some(daemon_version)) = (tab_version, daemon_version) { + if tab_version.major != daemon_version.major || tab_version.minor != daemon_version.minor { + eprintln!("Warning: The tab command (v{}) has an incompatible version with the running daemon (v{})", tab_version, daemon_version); + eprintln!( + " You should run `tab --shutdown` to terminate your tabs and relaunch the daemon." + ); + + return; + } + } + + if let (Some(executable), Some(daemon_exec)) = (&executable, &config.executable) { + if executable != daemon_exec { + eprintln!( + "Warning: The tab command has a different executable path than the running daemon." + ); + eprintln!(" You may want to run `tab --shutdown` to relaunch the daemon."); + + eprintln!(" Tab command: {}", executable); + eprintln!(" Daemon command: {}", daemon_exec); + return; + } + } +} diff --git a/tab-daemon/src/lib.rs b/tab-daemon/src/lib.rs index f881d647..e620f4e2 100644 --- a/tab-daemon/src/lib.rs +++ b/tab-daemon/src/lib.rs @@ -24,7 +24,7 @@ mod prelude; mod service; mod state; -pub fn daemon_main() -> anyhow::Result<()> { +pub fn daemon_main(tab_version: &'static str) -> anyhow::Result<()> { let mut runtime = tokio::runtime::Builder::new() .threaded_scheduler() .enable_io() @@ -32,7 +32,7 @@ pub fn daemon_main() -> anyhow::Result<()> { .build() .unwrap(); - let result = runtime.block_on(async { main_async().await }); + let result = runtime.block_on(async { main_async(tab_version).await }); runtime.shutdown_timeout(Duration::from_millis(25)); @@ -41,16 +41,23 @@ pub fn daemon_main() -> anyhow::Result<()> { Ok(()) } -pub async fn new_bus() -> anyhow::Result { +pub async fn new_bus(tab_version: &'static str) -> anyhow::Result { let server = TcpListener::bind("127.0.0.1:0").await?; let port = server.local_addr()?.port(); let websocket = WebsocketListenerResource(server); let auth_token = auth::gen_token(); let pid = std::process::id(); + let executable = std::env::current_exe() + .ok() + .map(|path| path.to_str().map(str::to_string)) + .flatten(); + let config = DaemonConfig { pid: pid as i32, port, + executable, + tab_version: Some(tab_version.to_string()), auth_token: auth_token.clone(), }; @@ -62,7 +69,7 @@ pub async fn new_bus() -> anyhow::Result { Ok(bus) } -async fn main_async() -> anyhow::Result<()> { +async fn main_async(tab_version: &'static str) -> anyhow::Result<()> { let log_file = daemon_log()?; let config = simplelog::ConfigBuilder::new() @@ -76,7 +83,7 @@ async fn main_async() -> anyhow::Result<()> { ]) .unwrap(); - let bus = new_bus().await?; + let bus = new_bus(tab_version).await?; let config = bus.resource::()?; let daemon_file = DaemonFile::new(&config)?; diff --git a/tab-daemon/src/service/daemon/listener.rs b/tab-daemon/src/service/daemon/listener.rs index cafe3f92..7a5788a0 100644 --- a/tab-daemon/src/service/daemon/listener.rs +++ b/tab-daemon/src/service/daemon/listener.rs @@ -194,7 +194,7 @@ mod tests { #[tokio::test] async fn test_listener_spawn() -> anyhow::Result<()> { - let bus = crate::new_bus().await?; + let bus = crate::new_bus("0.0.1").await?; let _listener = ListenerService::spawn(&bus)?; Ok(()) @@ -202,7 +202,7 @@ mod tests { #[tokio::test] async fn test_listener_accepts_connection() -> anyhow::Result<()> { - let bus = crate::new_bus().await?; + let bus = crate::new_bus("0.0.1").await?; let config = bus.resource::()?; let _listener = ListenerService::spawn(&bus)?; @@ -236,7 +236,7 @@ mod tests { #[tokio::test] async fn test_listener_rejects_unauthorized() -> anyhow::Result<()> { - let bus = crate::new_bus().await?; + let bus = crate::new_bus("0.0.1").await?; let config = bus.resource::()?; let _listener = ListenerService::spawn(&bus)?; @@ -249,7 +249,7 @@ mod tests { #[tokio::test] async fn test_listener_rejects_bad_token() -> anyhow::Result<()> { - let bus = crate::new_bus().await?; + let bus = crate::new_bus("0.0.1").await?; let config = bus.resource::()?; let _listener = ListenerService::spawn(&bus)?; @@ -266,7 +266,7 @@ mod tests { #[tokio::test] async fn test_listener_rejects_origin() -> anyhow::Result<()> { - let bus = crate::new_bus().await?; + let bus = crate::new_bus("0.0.1").await?; let config = bus.resource::()?; let _listener = ListenerService::spawn(&bus)?; diff --git a/tab/src/main.rs b/tab/src/main.rs index 43fa6e52..70d79b5b 100644 --- a/tab/src/main.rs +++ b/tab/src/main.rs @@ -8,6 +8,8 @@ use anyhow::Context; use cli::init; use tab_api::{config::history_path, log::set_level, tab::normalize_name}; +const TAB_VERSION: &'static str = env!("CARGO_PKG_VERSION"); + pub fn main() -> anyhow::Result<()> { let args = init(); @@ -26,7 +28,7 @@ pub fn main() -> anyhow::Result<()> { if let Some(launch) = args.value_of("LAUNCH") { match launch { - "daemon" => tab_daemon::daemon_main(), + "daemon" => tab_daemon::daemon_main(TAB_VERSION), "pty" => tab_pty::pty_main(), _ => panic!("unsupported --_launch value"), } @@ -70,6 +72,6 @@ pub fn main() -> anyhow::Result<()> { Ok(()) } else { - tab_command::command_main(args) + tab_command::command_main(args, TAB_VERSION) } } From d3b59fd881ca4f3d6d1a786f0bd503767d61d79f Mon Sep 17 00:00:00 2001 From: Austin Jones Date: Thu, 8 Oct 2020 20:18:55 -0400 Subject: [PATCH 2/2] Update the warning message for incompatible executables --- tab-command/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tab-command/src/lib.rs b/tab-command/src/lib.rs index 81541582..09e4b86e 100644 --- a/tab-command/src/lib.rs +++ b/tab-command/src/lib.rs @@ -147,7 +147,7 @@ fn validate_daemon(config: &DaemonConfig, tab_version: &'static str) { eprintln!( "Warning: The tab command has a different executable path than the running daemon." ); - eprintln!(" You may want to run `tab --shutdown` to relaunch the daemon."); + eprintln!(" You may want to run `tab --shutdown` to terminate your tabs and relaunch the daemon."); eprintln!(" Tab command: {}", executable); eprintln!(" Daemon command: {}", daemon_exec);