From 16720f5ad22e4c6b811db11ff320d75dd0cc800e Mon Sep 17 00:00:00 2001 From: John Kenyon Date: Mon, 7 Apr 2025 05:17:30 +0000 Subject: [PATCH] Add automatic daemon startup feature to client - Client now checks if daemon is running and starts it if needed - Finds daemon binary based on client executable location - Waits for daemon to start up before continuing - Also implements proper handling of --shutdown flag - Improves user experience by removing need to manually start daemon --- client/src/main.rs | 72 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/client/src/main.rs b/client/src/main.rs index d5ac790..1dbdb13 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -3,7 +3,7 @@ //! This is the client component of the Cypraea shell. It connects to the daemon, //! sends commands, and displays output to the user. -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use clap::Parser; use cypraea_common::paths; use cypraea_common::protocol::{ClientMessage, DaemonMessage}; @@ -66,12 +66,72 @@ async fn main() -> Result<()> { .to_string(), }; - // Connect to the daemon - let stream = UnixStream::connect(&socket_path) - .await - .context("Failed to connect to daemon")?; + // Handle shutdown request if specified + if args.shutdown { + // First try to connect to the daemon + match UnixStream::connect(&socket_path).await { + Ok(stream) => { + info!("Connected to daemon at {}", socket_path); + + // Send the shutdown message + let mut writer = tokio::io::BufWriter::new(stream); + let shutdown_msg = ClientMessage::Shutdown; + send_message(&mut writer, &shutdown_msg).await + .context("Failed to send shutdown message")?; + + println!("Daemon shutdown requested"); + return Ok(()); + }, + Err(_) => { + println!("No daemon running at {}", socket_path); + return Ok(()); + } + } + } - info!("Connected to daemon at {}", socket_path); + // Try to connect to the daemon + let stream = match UnixStream::connect(&socket_path).await { + Ok(stream) => { + info!("Connected to daemon at {}", socket_path); + stream + }, + Err(_e) => { + // Daemon not running, try to start it + info!("Daemon not running, attempting to start it"); + + // Find the daemon binary (should be in the same directory as the client) + let current_exe = std::env::current_exe() + .context("Failed to get current executable path")?; + let daemon_dir = current_exe.parent() + .context("Failed to get parent directory of current executable")?; + let daemon_path = daemon_dir.join("cypraeadd"); + + if !daemon_path.exists() { + return Err(anyhow!("Daemon binary not found at {}", daemon_path.display())); + } + + // Start the daemon process + let _daemon_process = tokio::process::Command::new(&daemon_path) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .spawn() + .context("Failed to start daemon process")?; + + // Give the daemon a moment to start up + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + + // Try to connect again + match UnixStream::connect(&socket_path).await { + Ok(stream) => { + info!("Successfully connected to newly started daemon"); + stream + }, + Err(e) => { + return Err(anyhow!("Failed to connect to daemon after starting it: {}", e)); + } + } + } + }; // Split the stream into reader and writer let (reader, writer) = tokio::io::split(stream);