diff options
-rw-r--r-- | Cargo.lock | 17 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/cli.rs | 3 | ||||
-rw-r--r-- | src/client/mod.rs | 34 | ||||
-rw-r--r-- | src/common/os_input_output.rs | 26 | ||||
-rw-r--r-- | src/main.rs | 22 | ||||
-rw-r--r-- | src/server/mod.rs | 95 | ||||
-rw-r--r-- | src/tests/fakes.rs | 2 |
8 files changed, 114 insertions, 86 deletions
diff --git a/Cargo.lock b/Cargo.lock index 2aeaa9269..92b2d0ca7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,6 +249,12 @@ dependencies = [ ] [[package]] +name = "boxfnonce" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5988cb1d626264ac94100be357308f29ff7cbdd3b36bda27f450a4ee3f713426" + +[[package]] name = "bumpalo" version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -468,6 +474,16 @@ dependencies = [ ] [[package]] +name = "daemonize" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70c24513e34f53b640819f0ac9f705b673fcf4006d7aab8778bee72ebfc89815" +dependencies = [ + "boxfnonce", + "libc", +] + +[[package]] name = "darling" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2261,6 +2277,7 @@ dependencies = [ "backtrace", "bincode", "colors-transform", + "daemonize", "directories-next", "futures", "insta", diff --git a/Cargo.toml b/Cargo.toml index 978c06533..099d17f6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ include = ["src/**/*", "assets/plugins/*", "assets/layouts/*", "assets/config/*" ansi_term = "0.12.1" backtrace = "0.3.55" bincode = "1.3.1" +daemonize = "0.4.1" directories-next = "2.0" futures = "0.3.5" libc = "0.2" diff --git a/src/cli.rs b/src/cli.rs index 69496808b..72666dd66 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -16,6 +16,9 @@ pub struct CliArgs { #[structopt(long)] pub data_dir: Option<PathBuf>, + #[structopt(long)] + pub server: Option<PathBuf>, + /// Path to a layout yaml file #[structopt(short, long)] pub layout: Option<PathBuf>, diff --git a/src/client/mod.rs b/src/client/mod.rs index 6f298615f..d24b66d3a 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -5,7 +5,10 @@ pub mod panes; pub mod tab; use serde::{Deserialize, Serialize}; -use std::io::Write; +use std::env::current_exe; +use std::io::{self, Write}; +use std::path::Path; +use std::process::Command; use std::sync::mpsc; use std::thread; @@ -18,6 +21,7 @@ use crate::common::{ input::options::Options, os_input_output::ClientOsApi, thread_bus::{SenderType, SenderWithContext, SyncChannelWithContext}, + utils::consts::ZELLIJ_IPC_PIPE, }; use crate::server::ServerInstruction; @@ -30,8 +34,25 @@ pub enum ClientInstruction { Exit, } +fn spawn_server(socket_path: &Path) -> io::Result<()> { + let status = Command::new(current_exe()?) + .arg("--server") + .arg(socket_path) + .status()?; + if status.success() { + Ok(()) + } else { + let msg = "Process returned non-zero exit code"; + let err_msg = match status.code() { + Some(c) => format!("{}: {}", msg, c), + None => msg.to_string(), + }; + Err(io::Error::new(io::ErrorKind::Other, err_msg)) + } +} + pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: Config) { - let clear_client_terminal_attributes = "\u{1b}[?1l\u{1b}=\u{1b}[r\u{1b}12l\u{1b}[?1000l\u{1b}[?1002l\u{1b}[?1003l\u{1b}[?1005l\u{1b}[?1006l\u{1b}[?12l"; + spawn_server(&*ZELLIJ_IPC_PIPE).unwrap(); let take_snapshot = "\u{1b}[?1049h"; let bracketed_paste = "\u{1b}[?2004h"; os_input.unset_raw_mode(0); @@ -50,12 +71,8 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C let config_options = Options::from_cli(&config.options, opts.option.clone()); let full_screen_ws = os_input.get_terminal_size_using_fd(0); - os_input.connect_to_server(); - os_input.send_to_server(ServerInstruction::NewClient( - full_screen_ws, - opts, - config_options, - )); + os_input.connect_to_server(&*ZELLIJ_IPC_PIPE); + os_input.send_to_server(ServerInstruction::NewClient(full_screen_ws, opts)); os_input.set_raw_mode(0); let _ = os_input .get_stdout_writer() @@ -130,7 +147,6 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C }) .unwrap(); - #[warn(clippy::never_loop)] loop { let (client_instruction, mut err_ctx) = receive_client_instructions .recv() diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs index 6f891e0d5..2a9a14e07 100644 --- a/src/common/os_input_output.rs +++ b/src/common/os_input_output.rs @@ -1,8 +1,5 @@ use crate::client::ClientInstruction; -use crate::common::{ - ipc::{IpcReceiverWithContext, IpcSenderWithContext}, - utils::consts::ZELLIJ_IPC_PIPE, -}; +use crate::common::ipc::{IpcReceiverWithContext, IpcSenderWithContext}; use crate::errors::ErrorContext; use crate::panes::PositionAndSize; use crate::server::ServerInstruction; @@ -319,7 +316,7 @@ pub trait ClientOsApi: Send + Sync { fn recv_from_server(&self) -> (ClientInstruction, ErrorContext); fn receive_sigwinch(&self, cb: Box<dyn Fn()>); /// Establish a connection with the server socket. - fn connect_to_server(&self); + fn connect_to_server(&self, path: &Path); } impl ClientOsApi for ClientOsInputOutput { @@ -379,14 +376,19 @@ impl ClientOsApi for ClientOsInputOutput { } } } - fn connect_to_server(&self) { - let socket = match LocalSocketStream::connect(&**ZELLIJ_IPC_PIPE) { - Ok(sock) => sock, - Err(_) => { - std::thread::sleep(std::time::Duration::from_millis(20)); - LocalSocketStream::connect(&**ZELLIJ_IPC_PIPE).unwrap() + fn connect_to_server(&self, path: &Path) { + let socket; + loop { + match LocalSocketStream::connect(path) { + Ok(sock) => { + socket = sock; + break; + } + Err(_) => { + std::thread::sleep(std::time::Duration::from_millis(50)); + } } - }; + } let sender = IpcSenderWithContext::new(socket); let receiver = sender.get_receiver(); *self.send_instructions_to_server.lock().unwrap() = Some(sender); diff --git a/src/main.rs b/src/main.rs index c1f0f387d..d08deb4af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use crate::command_is_executing::CommandIsExecuting; use crate::common::input::{config::Config, options::Options}; use crate::os_input_output::{get_client_os_input, get_server_os_input, ClientOsApi, ServerOsApi}; use crate::utils::{ - consts::{ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR}, + consts::{ZELLIJ_IPC_PIPE, ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR}, logging::*, }; use std::convert::TryFrom; @@ -39,15 +39,13 @@ pub fn main() { let config_options = Options::from_cli(&config.options, opts.option.clone()); atomic_create_dir(&*ZELLIJ_TMP_DIR).unwrap(); atomic_create_dir(&*ZELLIJ_TMP_LOG_DIR).unwrap(); - let server_os_input = get_server_os_input(); - let os_input = get_client_os_input(); - start( - Box::new(os_input), - opts, - Box::new(server_os_input), - config, - config_options, - ); + if let Some(path) = opts.server { + let os_input = get_server_os_input(); + start_server(Box::new(os_input), path); + } else { + let os_input = get_client_os_input(); + start_client(Box::new(os_input), opts, config); + } } } pub fn start( @@ -57,7 +55,7 @@ pub fn start( config: Config, config_options: Options, ) { - let ipc_thread = start_server(server_os_input, config_options); + start_server(server_os_input, ZELLIJ_IPC_PIPE.clone()); start_client(client_os_input, opts, config); - drop(ipc_thread.join()); + //drop(ipc_thread.join()); } diff --git a/src/server/mod.rs b/src/server/mod.rs index 149e2dbd0..f85a4c7f2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,5 +1,6 @@ pub mod route; +use daemonize::Daemonize; use interprocess::local_socket::LocalSocketListener; use serde::{Deserialize, Serialize}; use std::sync::{Arc, RwLock}; @@ -19,7 +20,7 @@ use crate::common::{ screen::{screen_thread_main, ScreenInstruction}, setup::{get_default_data_dir, install::populate_data_dir}, thread_bus::{ChannelWithContext, SenderType, SenderWithContext}, - utils::consts::ZELLIJ_IPC_PIPE, + utils::consts::ZELLIJ_PROJ_DIR, wasm_vm::{wasm_thread_main, PluginInstruction}, }; use crate::layout::Layout; @@ -56,10 +57,12 @@ impl Drop for SessionMetaData { } } -pub fn start_server( - os_input: Box<dyn ServerOsApi>, - config_options: Options, -) -> thread::JoinHandle<()> { +pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) { + Daemonize::new() + .working_directory(std::env::var("HOME").unwrap()) + .umask(0o077) + .start() + .expect("could not daemonize the server process"); let (to_server, server_receiver): ChannelWithContext<ServerInstruction> = channel(); let to_server = SenderWithContext::new(SenderType::Sender(to_server)); let sessions: Arc<RwLock<Option<SessionMetaData>>> = Arc::new(RwLock::new(None)); @@ -85,13 +88,11 @@ pub fn start_server( let os_input = os_input.clone(); let sessions = sessions.clone(); let to_server = to_server.clone(); - let capabilities = PluginCapabilities { - arrow_fonts: config_options.simplified_ui, - }; + let socket_path = socket_path.clone(); move || { - drop(std::fs::remove_file(&*ZELLIJ_IPC_PIPE)); - let listener = LocalSocketListener::bind(&**ZELLIJ_IPC_PIPE).unwrap(); - set_permissions(&*ZELLIJ_IPC_PIPE).unwrap(); + drop(std::fs::remove_file(&socket_path)); + let listener = LocalSocketListener::bind(&*socket_path).unwrap(); + set_permissions(&socket_path).unwrap(); for stream in listener.incoming() { match stream { Ok(stream) => { @@ -125,48 +126,38 @@ pub fn start_server( } }); - thread::Builder::new() - .name("server_thread".to_string()) - .spawn({ - move || loop { - let (instruction, mut err_ctx) = server_receiver.recv().unwrap(); - err_ctx.add_call(ContextType::IPCServer(ServerContext::from(&instruction))); - match instruction { - ServerInstruction::NewClient(full_screen_ws, opts, config_options) => { - let session_data = init_session( - os_input.clone(), - opts, - config_options, - to_server.clone(), - full_screen_ws, - ); - *sessions.write().unwrap() = Some(session_data); - sessions - .read() - .unwrap() - .as_ref() - .unwrap() - .senders - .send_to_pty(PtyInstruction::NewTab) - .unwrap(); - } - ServerInstruction::UnblockInputThread => { - os_input.send_to_client(ClientInstruction::UnblockInputThread); - } - ServerInstruction::ClientExit => { - *sessions.write().unwrap() = None; - os_input.send_to_client(ClientInstruction::Exit); - drop(std::fs::remove_file(&*ZELLIJ_IPC_PIPE)); - break; - } - ServerInstruction::Render(output) => { - os_input.send_to_client(ClientInstruction::Render(output)) - } - _ => panic!("Received unexpected instruction."), - } + loop { + let (instruction, mut err_ctx) = server_receiver.recv().unwrap(); + err_ctx.add_call(ContextType::IPCServer(ServerContext::from(&instruction))); + match instruction { + ServerInstruction::NewClient(full_screen_ws, opts) => { + let session_data = + init_session(os_input.clone(), opts, to_server.clone(), full_screen_ws); + *sessions.write().unwrap() = Some(session_data); + sessions + .read() + .unwrap() + .as_ref() + .unwrap() + .senders + .send_to_pty(PtyInstruction::NewTab) + .unwrap(); } - }) - .unwrap() + ServerInstruction::UnblockInputThread => { + os_input.send_to_client(ClientInstruction::UnblockInputThread); + } + ServerInstruction::ClientExit => { + *sessions.write().unwrap() = None; + os_input.send_to_client(ClientInstruction::Exit); + drop(std::fs::remove_file(&socket_path)); + break; + } + ServerInstruction::Render(output) => { + os_input.send_to_client(ClientInstruction::Render(output)) + } + _ => panic!("Received unexpected instruction."), + } + } } fn init_session( diff --git a/src/tests/fakes.rs b/src/tests/fakes.rs index eb0d14e86..1697e65f7 100644 --- a/src/tests/fakes.rs +++ b/src/tests/fakes.rs @@ -217,7 +217,7 @@ impl ClientOsApi for FakeInputOutput { cb(); } } - fn connect_to_server(&self) {} + fn connect_to_server(&self, path: &std::path::Path) {} } impl ServerOsApi for FakeInputOutput { |