Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ logind-zbus = { version = "5.3.2", optional = true }

[features]
systemd = ["dep:zbus_systemd", "dep:tracing-journald"]
logind = ["systemd", "logind-zbus"]
default = ["logind"]
logind = ["dep:logind-zbus"]
default = ["logind", "systemd"]
autostart = ["dep:shell-words", "dep:dirs", "dep:freedesktop-desktop-entry"]
109 changes: 62 additions & 47 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod comp;
mod notifications;
mod process;
mod service;
#[cfg(feature = "systemd")]
mod systemd;

use color_eyre::Result;
Expand All @@ -15,18 +16,13 @@ use launch_pad::ProcessManager;
use launch_pad::process::Process;
use service::SessionRequest;
use std::borrow::Cow;
#[cfg(feature = "autostart")]
use std::collections::HashSet;
use std::env;
use std::os::fd::AsRawFd;
#[cfg(feature = "autostart")]
use std::path::PathBuf;
#[cfg(feature = "autostart")]
use std::process::{Command, Stdio};
use std::sync::Arc;
#[cfg(any(feature = "autostart", feature = "systemd"))]
use std::{path::Path, sync::OnceLock};
#[cfg(feature = "systemd")]
use systemd::spawn_scope;
use systemd::{get_systemd_env, is_systemd_used};
use systemd::{get_systemd_env, spawn_scope};
use tokio::signal::unix::{SignalKind, signal};
use tokio::sync::mpsc::{Receiver, Sender};
use tokio::sync::{Mutex, oneshot};
Expand All @@ -36,14 +32,20 @@ use tracing::Instrument;
use tracing::metadata::LevelFilter;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{EnvFilter, fmt};
#[cfg(feature = "autostart")]
use {
std::collections::HashSet,
std::path::PathBuf,
std::process::{Command, Stdio},
};

use crate::notifications::{
DAEMON_NOTIFICATIONS_FD, PANEL_NOTIFICATIONS_FD, notifications_process,
};
#[cfg(feature = "autostart")]
const AUTOSTART_DIR: &'static str = "autostart";
const AUTOSTART_DIR: &str = "autostart";
#[cfg(feature = "autostart")]
const ENVIRONMENT_NAME: &'static str = "COSMIC";
const ENVIRONMENT_NAME: &str = "COSMIC";

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
Expand Down Expand Up @@ -143,7 +145,15 @@ async fn start(
.await;
let token = CancellationToken::new();
let (env_tx, env_rx) = oneshot::channel();
let systemd_env = get_systemd_env().await;

#[cfg(feature = "systemd")]
let systemd_env = if *is_systemd_used() {
get_systemd_env().await
} else {
Vec::new()
};
#[cfg(not(feature = "systemd"))]
let systemd_env = Vec::new();
let compositor_handle = comp::run_compositor(
&process_manager,
executable.clone(),
Expand All @@ -165,55 +175,50 @@ async fn start(
env_vars
);

// now that cosmic-comp is ready, extend the env vars with the systemd_env and set XDG_SESSION_TYPE=wayland for new processes
// now that cosmic-comp is ready, set XDG_SESSION_TYPE=wayland for new processes
env_vars.extend(
systemd_env
.into_iter()
.chain([("XDG_SESSION_TYPE".to_string(), "wayland".to_string())]),
);
#[cfg(feature = "systemd")]
systemd::set_systemd_environment("XDG_SESSION_TYPE", "wayland").await;

// expose the session version
env_vars.push((
"COSMIC_VERSION".to_string(),
env!("CARGO_PKG_VERSION").to_string(),
));
#[cfg(feature = "systemd")]
systemd::set_systemd_environment("COSMIC_VERSION", env!("CARGO_PKG_VERSION")).await;

#[cfg(feature = "systemd")]
let _inhibit_fd = if *is_systemd_used() {
#[cfg(feature = "logind")]
match zbus::Connection::system().await {
Ok(connection) => match logind_zbus::manager::ManagerProxy::new(&connection).await {
Ok(proxy) => match proxy
.inhibit(
logind_zbus::manager::InhibitType::HandlePowerKey,
"Cosmic Session",
"Show confirmation dialog.",
"block",
)
.await
{
Ok(fd) => Some(fd),
Err(err) => {
error!("Failed to inhibit power key {err:?}");
None
}
},
#[cfg(feature = "logind")]
let _inhibit_fd = match zbus::Connection::system().await {
Ok(connection) => match logind_zbus::manager::ManagerProxy::new(&connection).await {
Ok(proxy) => match proxy
.inhibit(
logind_zbus::manager::InhibitType::HandlePowerKey,
"Cosmic Session",
"Show confirmation dialog.",
"block",
)
.await
{
Ok(fd) => Some(fd),
Err(err) => {
error!("Failed to connect to logind manager {err:?}");
error!("Failed to inhibit power key {err:?}");
None
}
},
Err(err) => {
error!("Failed to connect to system dbus {err:?}");
error!("Failed to connect to logind manager {err:?}");
None
}
},
Err(err) => {
error!("Failed to connect to system dbus {err:?}");
None
}
#[cfg(not(feature = "logind"))]
None
} else {
None
};

let stdout_span = info_span!(parent: None, "cosmic-settings-daemon");
Expand Down Expand Up @@ -254,8 +259,10 @@ async fn start(
// - cosmic-comp is ready
// - we've set any related variables
// - cosmic-settings-daemon is ready
#[cfg(feature = "systemd")]
systemd::start_systemd_target().await;
// Always stop the target when the process exits or panics.
#[cfg(feature = "systemd")]
scopeguard::defer! {
systemd::stop_systemd_target();
}
Expand Down Expand Up @@ -388,17 +395,17 @@ async fn start(
}

// skip if we have an OnlyShowIn entry that doesn't include COSMIC
if let Some(only_show_in) = entry.only_show_in() {
if !only_show_in.contains(&ENVIRONMENT_NAME) {
continue;
}
if let Some(only_show_in) = entry.only_show_in()
&& !only_show_in.contains(&ENVIRONMENT_NAME)
{
continue;
}

// ... OR we have a NotShowIn entry that includes COSMIC
if let Some(not_show_in) = entry.not_show_in() {
if not_show_in.contains(&ENVIRONMENT_NAME) {
continue;
}
if let Some(not_show_in) = entry.not_show_in()
&& not_show_in.contains(&ENVIRONMENT_NAME)
{
continue;
}

info!(
Expand All @@ -417,7 +424,7 @@ async fn start(
.collect::<Vec<_>>();

// escape them
let escaped_args = shell_words::split(&*filtered_args.join(" "));
let escaped_args = shell_words::split(&filtered_args.join(" "));
if let Ok(args) = escaped_args {
info!("trying to start {} {}", program_name, args.join(" "));

Expand Down Expand Up @@ -556,3 +563,11 @@ async fn start_component(
error!("failed to start {}: {}", cmd, err);
}
}

/// Determine if systemd is used as the init system. This should work on all
/// linux distributions.
#[cfg(any(feature = "autostart", feature = "systemd"))]
fn is_systemd_used() -> &'static bool {
static IS_SYSTEMD_USED: OnceLock<bool> = OnceLock::new();
IS_SYSTEMD_USED.get_or_init(|| Path::new("/run/systemd/system").exists())
}
51 changes: 16 additions & 35 deletions src/systemd.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-only

use std::path::Path;
use std::process::{Command, Stdio};
use std::sync::OnceLock;

#[cfg(feature = "systemd")]
use zbus::Connection;
use zbus::zvariant::{Array, OwnedValue};

#[cfg(feature = "systemd")]
use zbus_systemd::systemd1::ManagerProxy as SystemdManagerProxy;

pub async fn set_systemd_environment(key: &str, value: &str) {
Expand All @@ -32,33 +27,22 @@ pub fn stop_systemd_target() {
)
}

/// Determine if systemd is used as the init system. This should work on all
/// linux distributions.
pub fn is_systemd_used() -> &'static bool {
static IS_SYSTEMD_USED: OnceLock<bool> = OnceLock::new();
IS_SYSTEMD_USED.get_or_init(|| Path::new("/run/systemd/system").exists())
}

#[cfg(feature = "systemd")]
async fn load_systemd_env() -> zbus::Result<Vec<String>> {
let connection = Connection::session().await?;
let systemd_manager = SystemdManagerProxy::new(&connection).await?;
systemd_manager.environment().await
}

/// 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()
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"
Expand All @@ -70,20 +54,17 @@ pub async fn get_systemd_env() -> Vec<(String, String)> {
&& 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()
}
};
})
.map(|(key, value)| (key.to_owned(), value.to_owned()))
.collect()
}
Err(err) => {
warn!("Failed to sync systemd environment {}.", err);
Vec::new()
}
}
Vec::new()
}

#[cfg(feature = "systemd")]
/// Spawn a systemd scope unit with the given name and PIDs.
pub async fn spawn_scope(mut command: String, pids: Vec<u32>) -> Result<(), zbus::Error> {
let connection = Connection::session().await?;
Expand Down
Loading