diff options
Diffstat (limited to '')
| -rw-r--r-- | crates/daemon/src/server.rs | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/crates/daemon/src/server.rs b/crates/daemon/src/server.rs new file mode 100644 index 00000000..d3427769 --- /dev/null +++ b/crates/daemon/src/server.rs @@ -0,0 +1,109 @@ +use std::{os::unix::net::SocketAddr, path::PathBuf}; + +use eyre::Result; +use eyre::{OptionExt, WrapErr}; +use turtle_api::generated::{ + control::control_server::ControlServer, history::history_server::HistoryServer, +}; + +use crate::api::{control::ControlService, history::HistoryService}; +use crate::{aclient::settings::Settings, daemon::DaemonHandle}; + +/// Run the gRPC server with the given services. +/// +/// This starts the gRPC server in the background and returns immediately. +/// The server will shut down when a [`ShutdownRequested`] event is received. +#[cfg(unix)] +pub(crate) fn run_grpc_server( + settings: &Settings, + history_service: HistoryServer<HistoryService>, + control_service: ControlServer<ControlService>, + handle: DaemonHandle, +) -> Result<()> { + use tokio::net::UnixListener; + use tokio_stream::wrappers::UnixListenerStream; + + let socket_path = settings.daemon.socket_path.clone(); + + let (uds, cleanup) = if settings.daemon.systemd_socket { + tracing::info!("getting systemd socket"); + let listener = listenfd::ListenFd::from_env() + .take_unix_listener(0)? + .ok_or_eyre("missing systemd socket")?; + listener.set_nonblocking(true)?; + let actual_path: Result<PathBuf, eyre::Report> = listener + .local_addr() + .context("getting systemd socket's path") + .and_then(|addr: SocketAddr| { + addr.as_pathname() + .ok_or_eyre("systemd socket missing path") + .map(|path: &std::path::Path| path.to_owned()) + }); + match actual_path { + Ok(actual_path) => { + tracing::info!("listening on systemd socket: {actual_path:?}"); + if actual_path != std::path::Path::new(&socket_path) { + tracing::warn!( + "systemd socket is not at configured client path: {socket_path:?}" + ); + } + } + Err(err) => { + tracing::warn!( + "could not detect systemd socket path, ensure that it's at the configured path: {socket_path:?}, error: {err:?}" + ); + } + } + (UnixListener::from_std(listener)?, false) + } else { + tracing::info!("listening on unix socket {socket_path:?}"); + ( + UnixListener::bind(socket_path.clone()).with_context(|| { + format!("Failed to bind to unix socket at: {socket_path}") + })?, + true, + ) + }; + + let uds_stream = UnixListenerStream::new(uds); + + // Create shutdown signal from daemon handle + let shutdown_signal = async move { + let mut rx = handle.subscribe(); + + loop { + use crate::events::DaemonEvent; + + match rx.recv().await { + Err(_) | Ok(DaemonEvent::ShutdownRequested) => break, + Ok(_) => (), + } + } + + if cleanup { + eprintln!("Removing socket..."); + if let Err(e) = std::fs::remove_file(&socket_path) + && e.kind() != std::io::ErrorKind::NotFound + { + eprintln!("failed to remove socket: {e}"); + } + } + eprintln!("Shutting down gRPC server..."); + }; + + // Spawn the server in the background + tokio::spawn(async move { + use tonic::transport::Server; + + if let Err(e) = Server::builder() + .add_service(history_service) + .add_service(control_service) + .serve_with_incoming_shutdown(uds_stream, shutdown_signal) + .await + { + tracing::error!("gRPC server error: {e}"); + } + }); + + Ok(()) +} |
