205 lines
6.7 KiB
Rust
205 lines
6.7 KiB
Rust
use std::ffi::OsString;
|
|
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub(crate) struct X11Environment {
|
|
pub display: OsString,
|
|
pub authority: PathBuf,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub(crate) enum SessionPolicy {
|
|
Desktop,
|
|
Embedded,
|
|
}
|
|
|
|
impl SessionPolicy {
|
|
fn environment_value(self) -> &'static str {
|
|
match self {
|
|
Self::Desktop => "desktop",
|
|
Self::Embedded => "embedded",
|
|
}
|
|
}
|
|
|
|
pub(crate) fn allows_autostart(self, embedded_opt_in: bool) -> bool {
|
|
self == Self::Desktop || embedded_opt_in
|
|
}
|
|
}
|
|
|
|
// Child endpoints are separate from the host endpoints used by the nested backend.
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub(crate) struct SessionEnvironment {
|
|
pub wayland_display: OsString,
|
|
pub control_socket: PathBuf,
|
|
pub policy: SessionPolicy,
|
|
pub x11: Option<X11Environment>,
|
|
}
|
|
|
|
impl SessionEnvironment {
|
|
/// Apply before private_client::prepare_command(), which overrides WAYLAND_SOCKET.
|
|
pub fn apply_public(&self, command: &mut Command) {
|
|
command
|
|
.env("WAYLAND_DISPLAY", &self.wayland_display)
|
|
.env("FERESE_PUBLIC_WAYLAND_DISPLAY", &self.wayland_display)
|
|
.env("FERESE_SOCKET", &self.control_socket)
|
|
.env("FERESE_SESSION_MODE", self.policy.environment_value())
|
|
.env_remove("WAYLAND_SOCKET")
|
|
.env_remove("FERESE_SHELL_CONTROL_SOCKET")
|
|
.env_remove("DISPLAY")
|
|
.env_remove("XAUTHORITY");
|
|
|
|
if let Some(x11) = &self.x11 {
|
|
command.env("DISPLAY", &x11.display).env("XAUTHORITY", &x11.authority);
|
|
}
|
|
if self.policy == SessionPolicy::Embedded {
|
|
command.env("FERESE_SESSION_IMPORT_ENV", "0");
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
fn environment(x11: Option<X11Environment>) -> SessionEnvironment {
|
|
SessionEnvironment {
|
|
wayland_display: OsString::from("wayland-7"),
|
|
control_socket: PathBuf::from("/run/user/1000/ferese/instances/preview/control.sock"),
|
|
policy: SessionPolicy::Desktop,
|
|
x11,
|
|
}
|
|
}
|
|
|
|
fn rendered(command: &Command) -> Vec<(String, Option<String>)> {
|
|
command
|
|
.get_envs()
|
|
.map(|(key, value)| {
|
|
(
|
|
key.to_string_lossy().into_owned(),
|
|
value.map(|value| value.to_string_lossy().into_owned()),
|
|
)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn value_of(command: &Command, key: &str) -> Option<Option<String>> {
|
|
rendered(command)
|
|
.into_iter()
|
|
.find(|(name, _)| name == key)
|
|
.map(|(_, value)| value)
|
|
}
|
|
|
|
#[test]
|
|
fn x11_available_publishes_display_and_authority() {
|
|
let mut command = Command::new("true");
|
|
environment(Some(X11Environment {
|
|
display: OsString::from(":7"),
|
|
authority: PathBuf::from("/run/user/1000/ferese-xauth-abc"),
|
|
}))
|
|
.apply_public(&mut command);
|
|
|
|
assert_eq!(value_of(&command, "DISPLAY"), Some(Some(":7".to_owned())));
|
|
assert_eq!(
|
|
value_of(&command, "XAUTHORITY"),
|
|
Some(Some("/run/user/1000/ferese-xauth-abc".to_owned()))
|
|
);
|
|
assert_eq!(
|
|
value_of(&command, "WAYLAND_DISPLAY"),
|
|
Some(Some("wayland-7".to_owned()))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn x11_unavailable_removes_display_and_authority() {
|
|
let mut command = Command::new("true");
|
|
environment(None).apply_public(&mut command);
|
|
|
|
assert_eq!(value_of(&command, "DISPLAY"), Some(None));
|
|
assert_eq!(value_of(&command, "XAUTHORITY"), Some(None));
|
|
}
|
|
|
|
#[test]
|
|
fn the_host_private_socket_is_always_cleared() {
|
|
let mut command = Command::new("true");
|
|
environment(None).apply_public(&mut command);
|
|
|
|
assert_eq!(value_of(&command, "WAYLAND_SOCKET"), Some(None));
|
|
assert_eq!(value_of(&command, "FERESE_SHELL_CONTROL_SOCKET"), Some(None));
|
|
}
|
|
|
|
#[test]
|
|
fn the_public_socket_is_exported_for_shell_launched_apps() {
|
|
let mut command = Command::new("true");
|
|
environment(None).apply_public(&mut command);
|
|
|
|
assert_eq!(
|
|
value_of(&command, "FERESE_PUBLIC_WAYLAND_DISPLAY"),
|
|
Some(Some("wayland-7".to_owned()))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn unrelated_variables_are_left_alone() {
|
|
let mut command = Command::new("true");
|
|
command.env("FERESE_COMPOSITOR_WALLPAPER", "1");
|
|
environment(None).apply_public(&mut command);
|
|
|
|
assert_eq!(
|
|
value_of(&command, "FERESE_COMPOSITOR_WALLPAPER"),
|
|
Some(Some("1".to_owned()))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn a_host_display_is_replaced_by_the_session_display() {
|
|
let mut command = Command::new("true");
|
|
command.env("DISPLAY", ":0");
|
|
environment(Some(X11Environment {
|
|
display: OsString::from(":7"),
|
|
authority: PathBuf::from("/run/user/1000/auth"),
|
|
}))
|
|
.apply_public(&mut command);
|
|
|
|
assert_eq!(value_of(&command, "DISPLAY"), Some(Some(":7".to_owned())));
|
|
}
|
|
|
|
#[test]
|
|
fn a_host_control_socket_is_replaced_by_the_instance_socket() {
|
|
let mut command = Command::new("true");
|
|
command.env("FERESE_SOCKET", "/run/user/1000/ferese/control.sock");
|
|
environment(None).apply_public(&mut command);
|
|
assert_eq!(
|
|
value_of(&command, "FERESE_SOCKET"),
|
|
Some(Some("/run/user/1000/ferese/instances/preview/control.sock".into()))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn embedded_clients_cannot_import_their_environment_into_the_host_session() {
|
|
let mut command = Command::new("true");
|
|
command.env("FERESE_SESSION_IMPORT_ENV", "1");
|
|
command.env("FERESE_SESSION_MODE", "desktop");
|
|
let mut embedded = environment(None);
|
|
embedded.policy = SessionPolicy::Embedded;
|
|
embedded.apply_public(&mut command);
|
|
assert_eq!(value_of(&command, "FERESE_SESSION_IMPORT_ENV"), Some(Some("0".into())));
|
|
assert_eq!(value_of(&command, "FERESE_SESSION_MODE"), Some(Some("embedded".into())));
|
|
}
|
|
|
|
#[test]
|
|
fn desktop_clients_preserve_the_launchers_activation_import_choice() {
|
|
for choice in ["0", "1"] {
|
|
let mut command = Command::new("true");
|
|
command.env("FERESE_SESSION_IMPORT_ENV", choice);
|
|
command.env("FERESE_SESSION_MODE", "embedded");
|
|
environment(None).apply_public(&mut command);
|
|
assert_eq!(
|
|
value_of(&command, "FERESE_SESSION_IMPORT_ENV"),
|
|
Some(Some(choice.into()))
|
|
);
|
|
assert_eq!(value_of(&command, "FERESE_SESSION_MODE"), Some(Some("desktop".into())));
|
|
}
|
|
}
|
|
}
|