fix: pass systemd user env vars to cosmic-session

Read the env vars of the systemd user manager before cosmic-comp starts and pass them to it.

fixes #171
This commit is contained in:
Christoph Dalski 2026-09-14 19:24:18 +02:00 • committed by Michael Murphy
parent ad097e98ec
commit a692a3006a
3 changed files with 54 additions and 54 deletions

View file

@ -99,6 +99,7 @@ pub fn run_compositor(
process_manager: &ProcessManager,
exec: String,
args: Vec<String>,
env: Vec<(String, String)>,
_token: CancellationToken,
env_tx: oneshot::Sender<HashMap<String, String>>,
session_dbus_tx: mpsc::Sender<SessionRequest>,
@ -119,6 +120,7 @@ pub fn run_compositor(
OwnedFd::from(std_stream)
};
mark_as_not_cloexec(&comp).expect("Failed to mark fd as not cloexec");
Ok(tokio::spawn(async move {
// Create a new process handler for cosmic-comp, with our compositor socket's
// file descriptor as the `COSMIC_SESSION_SOCK` environment variable.
@ -127,7 +129,10 @@ pub fn run_compositor(
Process::new()
.with_executable(exec)
.with_args(args)
.with_env([("COSMIC_SESSION_SOCK", comp.as_raw_fd().to_string())])
.with_env(env.into_iter().chain([(
"COSMIC_SESSION_SOCK".to_string(),
comp.as_raw_fd().to_string(),
)]))
.with_on_exit(move |pman, _, err_code, _will_restart| {
let session_dbus_tx = session_dbus_tx.clone();
async move {

View file

@ -24,9 +24,9 @@ use std::path::PathBuf;
#[cfg(feature = "autostart")]
use std::process::{Command, Stdio};
use std::sync::Arc;
use systemd::is_systemd_used;
#[cfg(feature = "systemd")]
use systemd::{get_systemd_env, spawn_scope};
use systemd::spawn_scope;
use systemd::{get_systemd_env, is_systemd_used};
use tokio::signal::unix::{SignalKind, signal};
use tokio::sync::mpsc::{Receiver, Sender};
use tokio::sync::{Mutex, oneshot};
@ -143,10 +143,12 @@ async fn start(
.await;
let token = CancellationToken::new();
let (env_tx, env_rx) = oneshot::channel();
let systemd_env = get_systemd_env().await;
let compositor_handle = comp::run_compositor(
&process_manager,
executable.clone(),
args,
systemd_env.clone(),
token.child_token(),
env_tx,
session_tx,
@ -163,8 +165,12 @@ async fn start(
env_vars
);
// now that cosmic-comp is ready, set XDG_SESSION_TYPE=wayland for new processes
env_vars.push(("XDG_SESSION_TYPE".to_string(), "wayland".to_string()));
// now that cosmic-comp is ready, extend the env vars with the systemd_env and set XDG_SESSION_TYPE=wayland for new processes
env_vars.extend(
systemd_env
.into_iter()
.chain([("XDG_SESSION_TYPE".to_string(), "wayland".to_string())]),
);
systemd::set_systemd_environment("XDG_SESSION_TYPE", "wayland").await;
// expose the session version
@ -176,32 +182,6 @@ async fn start(
#[cfg(feature = "systemd")]
let _inhibit_fd = if *is_systemd_used() {
match get_systemd_env().await {
Ok(env) => {
for systemd_env in env {
// Only update the envvar if unset
if std::env::var_os(&systemd_env.key).is_none() {
// Blacklist of envvars that we shouldn't touch (taken from KDE)
if (!systemd_env.key.starts_with("XDG_")
|| systemd_env.key == "XDG_DATA_DIRS"
|| systemd_env.key == "XDG_CONFIG_DIRS")
&& systemd_env.key != "DISPLAY"
&& systemd_env.key != "XAUTHORITY"
&& systemd_env.key != "WAYLAND_DISPLAY"
&& systemd_env.key != "WAYLAND_SOCKET"
&& systemd_env.key != "_"
&& systemd_env.key != "SHELL"
&& systemd_env.key != "SHLVL"
{
env_vars.push((systemd_env.key, systemd_env.value));
}
}
}
}
Err(err) => {
warn!("Failed to sync systemd environment {}.", err);
}
};
#[cfg(feature = "logind")]
match zbus::Connection::system().await {
Ok(connection) => match logind_zbus::manager::ManagerProxy::new(&connection).await {

View file

@ -4,24 +4,10 @@ use std::path::Path;
use std::process::{Command, Stdio};
use std::sync::OnceLock;
#[cfg(feature = "systemd")]
use zbus::Connection;
use zbus::zvariant::{Array, OwnedValue};
#[derive(Debug)]
pub struct EnvVar {
pub key: String,
pub value: String,
}
impl From<(&str, &str)> for EnvVar {
fn from(val: (&str, &str)) -> Self {
EnvVar {
key: val.0.to_owned(),
value: val.1.to_owned(),
}
}
}
#[cfg(feature = "systemd")]
use zbus_systemd::systemd1::ManagerProxy as SystemdManagerProxy;
@ -54,18 +40,47 @@ pub fn is_systemd_used() -> &'static bool {
}
#[cfg(feature = "systemd")]
pub async fn get_systemd_env() -> Result<Vec<EnvVar>, zbus::Error> {
async fn load_systemd_env() -> zbus::Result<Vec<String>> {
let connection = Connection::session().await?;
let systemd_manager = SystemdManagerProxy::new(&connection).await?;
let systemd_env = systemd_manager.environment().await?;
systemd_manager.environment().await
}
let mut out: Vec<EnvVar> = Vec::new();
for i in systemd_env {
if let Some(b) = i.split_once("=") {
out.push(b.into());
}
/// Get the systemd user manager environment variables.
/// Is Empty if systemd isn't used.
pub async fn get_systemd_env() -> Vec<(String, String)> {
#[cfg(feature = "systemd")]
if *is_systemd_used() {
return match load_systemd_env().await {
Ok(env) => {
env.iter()
// split into key value pairs
.filter_map(|var| var.split_once("="))
.filter(|&(key, _value)| {
// Only update the envvar if unset
std::env::var_os(key).is_none()
// Blocklist of envvars that we shouldn't touch (taken from KDE)
&& (!key.starts_with("XDG_")
|| key == "XDG_DATA_DIRS"
|| key == "XDG_CONFIG_DIRS")
&& key != "DISPLAY"
&& key != "XAUTHORITY"
&& key != "WAYLAND_DISPLAY"
&& key != "WAYLAND_SOCKET"
&& key != "_"
&& key != "SHELL"
&& key != "SHLVL"
})
.map(|(key, value)| (key.to_owned(), value.to_owned()))
.collect()
}
Err(err) => {
warn!("Failed to sync systemd environment {}.", err);
Vec::new()
}
};
}
Ok(out)
Vec::new()
}
#[cfg(feature = "systemd")]