diff --git a/src/main.rs b/src/main.rs index 96f047a..8bb69ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,10 @@ mod process; mod service; mod systemd; -use std::os::fd::AsRawFd; +use std::{ + os::fd::AsRawFd, + sync::{Arc, Mutex}, +}; use async_signals::Signals; use color_eyre::{eyre::WrapErr, Result}; @@ -24,6 +27,8 @@ use tracing::{metadata::LevelFilter, Instrument}; use tracing_subscriber::{fmt, prelude::*, EnvFilter}; use zbus::ConnectionBuilder; +use crate::notifications::notifications_process; + #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { color_eyre::install().wrap_err("failed to install color_eyre error handler")?; @@ -74,72 +79,63 @@ async fn main() -> Result<()> { let (panel_notifications_fd, daemon_notifications_fd) = notifications::create_socket().expect("Failed to create notification socket"); - let mut panel_env_vars = env_vars.clone(); - - panel_env_vars.push(( - PANEL_NOTIFICATIONS_FD.to_string(), - panel_notifications_fd.as_raw_fd().to_string(), - )); - - let span = info_span!(parent: None, "cosmic-panel"); - let stdout_span = span.clone(); - let stderr_span = span; - process_manager - .start( - Process::new() - .with_executable("cosmic-panel") - .with_fds(move || vec![panel_notifications_fd.as_raw_fd()]) - .with_on_stdout(move |_, _, line| { - let stdout_span = stdout_span.clone(); - async move { - info!("{}", line); - } - .instrument(stdout_span) - }) - .with_on_stderr(move |_, _, line| { - let stderr_span = stderr_span.clone(); - async move { - warn!("{}", line); - } - .instrument(stderr_span) - }) - .with_env(panel_env_vars.clone()), - ) - .await - .expect("failed to start panel"); let mut daemon_env_vars = env_vars.clone(); daemon_env_vars.push(( DAEMON_NOTIFICATIONS_FD.to_string(), daemon_notifications_fd.as_raw_fd().to_string(), )); - let span = info_span!(parent: None, "cosmic-notifications"); - let stdout_span = span.clone(); - let stderr_span = span; + let mut panel_env_vars = env_vars.clone(); + panel_env_vars.push(( + PANEL_NOTIFICATIONS_FD.to_string(), + panel_notifications_fd.as_raw_fd().to_string(), + )); - process_manager - .start( - Process::new() - .with_executable("cosmic-notifications") - .with_fds(move || vec![daemon_notifications_fd.as_raw_fd()]) - .with_on_stdout(move |_, _, line| { - let stdout_span = stdout_span.clone(); - async move { - info!("{}", line); - } - .instrument(stdout_span) - }) - .with_on_stderr(move |_, _, line| { - let stderr_span = stderr_span.clone(); - async move { - warn!("{}", line); - } - .instrument(stderr_span) - }) - .with_env(daemon_env_vars.clone()), - ) - .await - .expect("failed to start notifications daemon"); + let panel_key = Arc::new(Mutex::new(None)); + let notif_key = Arc::new(Mutex::new(None)); + + let notifications_span = info_span!(parent: None, "cosmic-notifications"); + let panel_span = info_span!(parent: None, "cosmic-panel"); + + let mut guard = notif_key.lock().unwrap(); + *guard = Some( + process_manager + .start(notifications_process( + notifications_span.clone(), + "cosmic-notifications", + notif_key.clone(), + daemon_env_vars.clone(), + daemon_notifications_fd.as_raw_fd(), + panel_span.clone(), + "cosmic-panel", + panel_key.clone(), + panel_env_vars.clone(), + panel_notifications_fd.as_raw_fd(), + )) + .await + .expect("failed to start notifications daemon"), + ); + drop(guard); + + let mut guard = panel_key.lock().unwrap(); + *guard = Some( + process_manager + .start(notifications_process( + panel_span, + "cosmic-panel", + panel_key.clone(), + panel_env_vars, + panel_notifications_fd.as_raw_fd(), + notifications_span, + "cosmic-notifications", + notif_key, + daemon_env_vars, + daemon_notifications_fd.as_raw_fd(), + )) + .await + .expect("failed to start panel"), + ); + drop(guard); let span = info_span!(parent: None, "cosmic-app-library"); start_component("cosmic-app-library", span, &process_manager, &env_vars).await; diff --git a/src/notifications.rs b/src/notifications.rs index 5f6419d..c32b36d 100644 --- a/src/notifications.rs +++ b/src/notifications.rs @@ -1,7 +1,11 @@ use color_eyre::eyre::Context; use color_eyre::Result; -use std::os::fd::OwnedFd; +use launch_pad::process::Process; +use launch_pad::ProcessKey; +use std::os::fd::{OwnedFd, RawFd}; use std::os::unix::net::UnixStream; +use std::sync::{Arc, Mutex}; +use tracing::Instrument; pub fn create_socket() -> Result<(OwnedFd, OwnedFd)> { // Create a new pair of unnamed Unix sockets @@ -10,12 +14,77 @@ pub fn create_socket() -> Result<(OwnedFd, OwnedFd)> { // Turn the sockets into non-blocking fd, which we can pass to the child // process sock_1 - .set_nonblocking(false) - .wrap_err("failed to mark client socket as blocking")?; + .set_nonblocking(true) + .wrap_err("failed to mark client socket as non-blocking")?; sock_2 - .set_nonblocking(false) - .wrap_err("failed to mark client socket as blocking")?; + .set_nonblocking(true) + .wrap_err("failed to mark client socket as non-blocking")?; Ok((OwnedFd::from(sock_1), OwnedFd::from(sock_2))) } + +pub fn notifications_process( + span: tracing::Span, + cmd: &'static str, + key: Arc>>, + env_vars: Vec<(String, String)>, + fd: RawFd, + restart_span: tracing::Span, + restart_cmd: &'static str, + restart_key: Arc>>, + restart_env_vars: Vec<(String, String)>, + restart_fd: RawFd, +) -> Process { + let stdout_span = span.clone(); + let stderr_span = span.clone(); + let env_clone = env_vars.clone(); + Process::new() + .with_executable(cmd) + .with_fds(move || vec![fd]) + .with_on_stdout(move |_, _, line| { + let stdout_span = stdout_span.clone(); + async move { + info!("{}", line); + } + .instrument(stdout_span) + }) + .with_on_stderr(move |_, _, line| { + let stderr_span = stderr_span.clone(); + async move { + warn!("{}", line); + } + .instrument(stderr_span) + }) + .with_on_exit(move |pman, _, _, will_restart| { + // force restart of notifications / panel when the other exits + let new_process = notifications_process( + restart_span.clone(), + restart_cmd, + restart_key.clone(), + restart_env_vars.clone(), + restart_fd, + span.clone(), + cmd, + key.clone(), + env_clone.clone(), + fd, + ); + let restart_key = restart_key.clone(); + async move { + if will_restart { + let Some(old) = restart_key.lock().unwrap().clone() else { + error!("Couldn't stop previous invocation of {}", cmd); + return; + }; + _ = pman.stop_process(old).await; + + if let Ok(new) = pman.start(new_process).await { + let mut guard = restart_key.lock().unwrap(); + *guard = Some(new); + } + } + } + }) + .with_env(env_vars) +}