From be345853cd754a6cbaaa7abc90abf938bd648eeb Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 22 Jun 2019 13:22:28 -0700 Subject: [PATCH] allow spawning the server automatically when connecting via unix domain --- src/config.rs | 5 +++++ src/main.rs | 3 +-- src/server/client.rs | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/config.rs b/src/config.rs index 95429b14a..48407ed4f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -407,6 +407,11 @@ pub struct UnixDomain { #[serde(default)] pub connect_automatically: bool, + /// If true, do not attempt to start this server if we try and fail to + /// connect to it. + #[serde(default)] + pub no_serve_automatically: bool, + /// If true, bypass checking for secure ownership of the /// socket_path. This is not recommended on a multi-user /// system, but is useful for example when running the diff --git a/src/main.rs b/src/main.rs index 9a0d31553..b684318c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ #![windows_subsystem = "windows"] use failure::{Error, Fallible}; -use log::error; use std::ffi::OsString; use structopt::StructOpt; use tabout::{tabulate_output, Alignment, Column}; @@ -243,7 +242,7 @@ fn main() -> Result<(), Error> { .unwrap_or_else(|| SubCommand::Start(StartCommand::default())) { SubCommand::Start(start) => { - error!("Using configuration: {:#?}\nopts: {:#?}", config, opts); + log::info!("Using configuration: {:#?}\nopts: {:#?}", config, opts); run_terminal_gui(config, &start) } SubCommand::Cli(cli) => { diff --git a/src/server/client.rs b/src/server/client.rs index 4acb175b0..2e16deb7c 100644 --- a/src/server/client.rs +++ b/src/server/client.rs @@ -163,6 +163,22 @@ fn client_thread( } } +fn unix_connect_with_retry(path: &Path) -> Result { + let mut error = std::io::Error::last_os_error(); + + for iter in 0..10 { + if iter > 0 { + std::thread::sleep(std::time::Duration::from_millis(iter * 10)); + } + match UnixStream::connect(path) { + Ok(stream) => return Ok(stream), + Err(err) => error = err, + } + } + + Err(error) +} + impl Client { pub fn new(local_domain_id: DomainId, stream: Box) -> Self { let (sender, receiver) = pollable_channel().expect("failed to create pollable_channel"); @@ -198,7 +214,27 @@ impl Client { ) -> Fallible { let sock_path = unix_dom.socket_path(); info!("connect to {}", sock_path.display()); - let stream = Box::new(UnixStream::connect(sock_path)?); + + let stream = match unix_connect_with_retry(&sock_path) { + Ok(stream) => stream, + Err(e) => { + if unix_dom.no_serve_automatically { + bail!("failed to connect to {}: {}", sock_path.display(), e); + } + log::error!( + "While connecting to {}: {}. Will try spawning the server.", + sock_path.display(), + e + ); + let mut child = std::process::Command::new(std::env::current_exe()?) + .args(&["start", "--daemonize", "--front-end", "MuxServer"]) + .spawn()?; + child.wait()?; + unix_connect_with_retry(&sock_path)? + } + }; + + let stream: Box = Box::new(stream); Ok(Self::new(local_domain_id, stream)) }