diff --git a/Cargo.lock b/Cargo.lock index e69f7f6..f7ccd10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,7 +61,7 @@ dependencies = [ "log", "parking", "polling", - "rustix", + "rustix 0.37.23", "slab", "socket2", "waker-fn", @@ -89,7 +89,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix", + "rustix 0.37.23", "signal-hook", "windows-sys", ] @@ -166,6 +166,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "block-buffer" version = "0.10.4" @@ -276,7 +282,7 @@ dependencies = [ "launch-pad", "libc", "log-panics", - "nix 0.26.2", + "rustix 0.38.13", "scopeguard", "sendfd", "serde", @@ -566,7 +572,7 @@ checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "launch-pad" version = "0.1.0" -source = "git+https://github.com/pop-os/launch-pad#702bb0531bcd0aca6c17b85e1b516e46f31ddaa1" +source = "git+https://github.com/pop-os/launch-pad#4707088eb1111946fc5ea91e9b02ff2737552462" dependencies = [ "log", "nix 0.26.2", @@ -595,6 +601,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "linux-raw-sys" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" + [[package]] name = "lock_api" version = "0.4.10" @@ -680,7 +692,7 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cc", "cfg-if", "libc", @@ -693,7 +705,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.7.1", @@ -796,7 +808,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", @@ -875,7 +887,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -934,11 +946,24 @@ version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.8", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.38.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys 0.4.11", "windows-sys", ] @@ -1117,7 +1142,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall", - "rustix", + "rustix 0.37.23", "windows-sys", ] diff --git a/Cargo.toml b/Cargo.toml index ee21211..ff16a1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ futures-util = "0.3" launch-pad = { git = "https://github.com/pop-os/launch-pad" } libc = "0.2" log-panics = { version = "2", features = ["with-backtrace"] } -nix = { version = "0.26", features = ["fs"], default-features = false } +rustix = "0.38" scopeguard = "1" sendfd = { version = "0.4", features = ["tokio"] } serde = { version = "1", features = ["derive"] } diff --git a/src/comp.rs b/src/comp.rs index 9a72dab..a7569ba 100644 --- a/src/comp.rs +++ b/src/comp.rs @@ -101,6 +101,31 @@ async fn receive_ipc(state: &mut IpcState, rx: &mut OwnedReadHalf) -> Result<()> } } +pub fn create_privileged_socket( + sockets: &mut Vec, + env_vars: &[(String, String)], +) -> Result<(Vec<(String, String)>, OwnedFd)> { + // Create a new pair of unnamed Unix sockets + let (comp_socket, client_socket) = + UnixStream::pair().wrap_err("failed to create socket pair")?; + // Push one socket to the list of sockets we were passed + sockets.push(comp_socket); + // Turn the other socket into a non-blocking fd, which we can pass to the child + // process + let client_fd = { + let std_stream = client_socket + .into_std() + .wrap_err("failed to convert client socket to std socket")?; + std_stream + .set_nonblocking(true) + .wrap_err("failed to mark client socket as non-blocking")?; + OwnedFd::from(std_stream) + }; + let mut env_vars = env_vars.to_vec(); + env_vars.push(("WAYLAND_SOCKET".into(), client_fd.as_raw_fd().to_string())); + Ok((env_vars, client_fd)) +} + async fn send_fd(session_tx: &mut OwnedWriteHalf, stream: Vec) -> Result<()> { // Turn our list of Unix streams into non-blocking file descriptors. let fds = stream @@ -134,12 +159,15 @@ async fn send_fd(session_tx: &mut OwnedWriteHalf, stream: Vec) -> Re tokio::time::sleep(std::time::Duration::from_micros(100)).await; // Send our file descriptors. let fd: &UnixStream = session_tx.as_ref(); + info!("sending {} fds", fds.len()); + fd.send_with_fd( &[0], - &fds.iter().map(|fd| fd.as_raw_fd()).collect::>(), + &fds.into_iter() + .map(|fd| fd.into_raw_fd()) + .collect::>(), ) .wrap_err("failed to send fd")?; - info!("sent {} fds", fds.len()); Ok(()) } diff --git a/src/main.rs b/src/main.rs index 6ceebd4..8a199dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,17 +9,19 @@ mod service; mod systemd; use std::{ - os::fd::AsRawFd, + os::fd::{AsRawFd, IntoRawFd}, sync::{Arc, Mutex}, }; use async_signals::Signals; use color_eyre::{eyre::WrapErr, Result}; +use comp::create_privileged_socket; use cosmic_notifications_util::{DAEMON_NOTIFICATIONS_FD, PANEL_NOTIFICATIONS_FD}; use futures_util::StreamExt; use launch_pad::{process::Process, ProcessManager}; use service::SessionRequest; use tokio::{ + net::UnixStream, sync::{ mpsc::{self, Receiver, Sender}, oneshot, @@ -99,7 +101,7 @@ async fn start( )) .await; let token = CancellationToken::new(); - let (_, socket_rx) = mpsc::unbounded_channel(); + let (socket_tx, socket_rx) = mpsc::unbounded_channel(); let (env_tx, env_rx) = oneshot::channel(); let compositor_handle = comp::run_compositor( &process_manager, @@ -160,6 +162,7 @@ async fn start( panel_key.clone(), panel_env_vars.clone(), panel_notifications_fd.as_raw_fd(), + socket_tx.clone(), )) .await .expect("failed to start notifications daemon"), @@ -180,6 +183,7 @@ async fn start( notif_key, daemon_env_vars, daemon_notifications_fd.as_raw_fd(), + socket_tx.clone(), )) .await .expect("failed to start panel"), @@ -187,19 +191,40 @@ async fn start( drop(guard); let span = info_span!(parent: None, "cosmic-app-library"); - start_component("cosmic-app-library", span, &process_manager, &env_vars).await; + start_component( + "cosmic-app-library", + span, + &process_manager, + &env_vars, + &socket_tx, + ) + .await; let span = info_span!(parent: None, "cosmic-launcher"); - start_component("cosmic-launcher", span, &process_manager, &env_vars).await; + start_component( + "cosmic-launcher", + span, + &process_manager, + &env_vars, + &socket_tx, + ) + .await; let span = info_span!(parent: None, "cosmic-workspaces"); - start_component("cosmic-workspaces", span, &process_manager, &env_vars).await; + start_component( + "cosmic-workspaces", + span, + &process_manager, + &env_vars, + &socket_tx, + ) + .await; let span = info_span!(parent: None, "cosmic-osd"); - start_component("cosmic-osd", span, &process_manager, &env_vars).await; + start_component("cosmic-osd", span, &process_manager, &env_vars, &socket_tx).await; let span = info_span!(parent: None, "cosmic-bg"); - start_component("cosmic-bg", span, &process_manager, &env_vars).await; + start_component("cosmic-bg", span, &process_manager, &env_vars, &socket_tx).await; let span = info_span!(parent: None, "xdg-desktop-portal-cosmic"); start_component( @@ -207,6 +232,7 @@ async fn start( span, &process_manager, &env_vars, + &socket_tx, ) .await; @@ -258,9 +284,18 @@ async fn start_component( span: tracing::Span, process_manager: &ProcessManager, env_vars: &[(String, String)], + socket_tx: &mpsc::UnboundedSender>, ) { + let mut sockets = Vec::with_capacity(1); + let (env_vars, fd) = create_privileged_socket(&mut sockets, env_vars).unwrap(); + let fd = fd.into_raw_fd(); + if let Err(why) = socket_tx.send(sockets) { + error!(?why, "Failed to send the privileged socket"); + } + let socket_tx_clone = socket_tx.clone(); let stdout_span = span.clone(); let stderr_span = span.clone(); + let cmd_clone = cmd.to_string(); process_manager .start( Process::new() @@ -279,7 +314,37 @@ async fn start_component( warn!("{}", line); } .instrument(stderr_span) - }), + }) + .with_on_exit(move |mut pman, key, err_code, will_restart| { + if let Some(err) = err_code { + error!("{cmd_clone} exited with error {}", err.to_string()); + } + + let socket_tx_clone = socket_tx_clone.clone(); + async move { + if !will_restart { + return; + } + let mut sockets = Vec::with_capacity(1); + let env_vars = Vec::with_capacity(1); + let (env_vars, new_fd) = + create_privileged_socket(&mut sockets, &env_vars).unwrap(); + + let new_fd = new_fd.into_raw_fd(); + + if let Err(why) = socket_tx_clone.send(sockets) { + error!(?why, "Failed to send the privileged socket"); + } + if let Err(why) = pman.update_process_env(&key, env_vars).await { + error!(?why, "Failed to update environment variables"); + } + if let Err(why) = pman.update_process_fds(&key, move || vec![new_fd]).await + { + error!(?why, "Failed to update fds"); + } + } + }) + .with_fds(move || vec![fd]), ) .await .expect(&format!("failed to start {}", cmd)); diff --git a/src/notifications.rs b/src/notifications.rs index c32b36d..6c8fad1 100644 --- a/src/notifications.rs +++ b/src/notifications.rs @@ -2,11 +2,14 @@ use color_eyre::eyre::Context; use color_eyre::Result; use launch_pad::process::Process; use launch_pad::ProcessKey; -use std::os::fd::{OwnedFd, RawFd}; +use std::os::fd::{IntoRawFd, OwnedFd, RawFd}; use std::os::unix::net::UnixStream; use std::sync::{Arc, Mutex}; +use tokio::sync::mpsc; use tracing::Instrument; +use crate::comp::create_privileged_socket; + pub fn create_socket() -> Result<(OwnedFd, OwnedFd)> { // Create a new pair of unnamed Unix sockets let (sock_1, sock_2) = UnixStream::pair().wrap_err("failed to create socket pair")?; @@ -28,20 +31,28 @@ pub fn notifications_process( span: tracing::Span, cmd: &'static str, key: Arc>>, - env_vars: Vec<(String, String)>, + mut 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, + socket_tx: mpsc::UnboundedSender>, ) -> Process { + env_vars.retain(|v| &v.0 != "WAYLAND_SOCKET"); + let stdout_span = span.clone(); let stderr_span = span.clone(); + let mut sockets = Vec::with_capacity(1); + let (env_vars, privileged_fd) = create_privileged_socket(&mut sockets, &env_vars).unwrap(); + _ = socket_tx.send(sockets); let env_clone = env_vars.clone(); + let socket_tx_clone = socket_tx.clone(); + let privileged_fd = privileged_fd.into_raw_fd(); Process::new() .with_executable(cmd) - .with_fds(move || vec![fd]) + .with_fds(move || vec![privileged_fd, fd]) .with_on_stdout(move |_, _, line| { let stdout_span = stdout_span.clone(); async move { @@ -56,7 +67,7 @@ pub fn notifications_process( } .instrument(stderr_span) }) - .with_on_exit(move |pman, _, _, will_restart| { + .with_on_exit(move |pman, my_key, _, will_restart| { // force restart of notifications / panel when the other exits let new_process = notifications_process( restart_span.clone(), @@ -69,13 +80,37 @@ pub fn notifications_process( key.clone(), env_clone.clone(), fd, + socket_tx_clone.clone(), ); let restart_key = restart_key.clone(); + let socket_tx_clone = socket_tx_clone.clone(); + + let env_clone = env_clone.clone(); + let mut pman_clone = pman.clone(); async move { if will_restart { + let mut sockets = Vec::with_capacity(1); + let (env_vars, new_fd) = + create_privileged_socket(&mut sockets, &env_clone).unwrap(); + + let new_fd = new_fd.into_raw_fd(); + + if let Err(why) = socket_tx_clone.send(sockets) { + error!(?why, "Failed to send the privileged socket"); + } + if let Err(why) = pman_clone.update_process_env(&my_key, env_vars).await { + error!(?why, "Failed to update environment variables"); + } + if let Err(why) = pman_clone + .update_process_fds(&my_key, move || vec![new_fd, fd]) + .await + { + error!(?why, "Failed to update fds"); + } + let Some(old) = restart_key.lock().unwrap().clone() else { - error!("Couldn't stop previous invocation of {}", cmd); - return; + error!("Couldn't stop previous invocation of {}", cmd); + return; }; _ = pman.stop_process(old).await; diff --git a/src/process.rs b/src/process.rs index 7f381e9..033b340 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1,19 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-only -use color_eyre::eyre::{ContextCompat, Result, WrapErr}; -use nix::fcntl; +use color_eyre::eyre::{Result, WrapErr}; +use rustix::io::FdFlags; use std::os::unix::prelude::*; pub(crate) fn mark_as_not_cloexec(file: &impl AsFd) -> Result<()> { - let raw_fd = file.as_fd().as_raw_fd(); - let fd_flags = fcntl::FdFlag::from_bits( - fcntl::fcntl(raw_fd, fcntl::FcntlArg::F_GETFD) - .wrap_err("failed to get GETFD value of stream")?, - ) - .wrap_err("failed to get fd flags from file")?; - fcntl::fcntl( - raw_fd, - fcntl::FcntlArg::F_SETFD(fd_flags.difference(fcntl::FdFlag::FD_CLOEXEC)), - ) - .wrap_err("failed to set CLOEXEC on file")?; - Ok(()) + let flags = rustix::io::fcntl_getfd(file).wrap_err("failed to get GETFD value of stream")?; + rustix::io::fcntl_setfd(file, flags.difference(FdFlags::CLOEXEC)) + .wrap_err("failed to unset CLOEXEC on file") }