Skip to content

Commit

Permalink
Add argument to set the environment to run the default command with (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
apognu committed Aug 16, 2024
1 parent 520cb72 commit 986af2e
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 45 deletions.
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Options:
-d, --debug [FILE] enable debug logging to the provided file, or to
/tmp/tuigreet.log
-c, --cmd COMMAND command to run
--env KEY=VALUE environment variables to run the default session with
(can appear more than once)
-s, --sessions DIRS colon-separated list of Wayland session paths
--session-wrapper 'CMD [ARGS]...'
wrapper command to initialize the non-X11 session
Expand Down Expand Up @@ -40,9 +42,7 @@ Options:
minimum UID to display in the user selection menu
--user-menu-max-uid UID
maximum UID to display in the user selection menu
--theme SPEC
Add visual feedback when typing secrets, as one asterisk character for every
keystroke. By default, no feedback is given at all.
--theme THEME define the application theme colors
--asterisks display asterisks when a secret is typed
--asterisks-char CHARS
characters to be used to redact secrets (default: *)
Expand All @@ -52,15 +52,21 @@ Options:
padding inside the main prompt container (default: 1)
--prompt-padding PADDING
padding between prompt rows (default: 1)
--greet-align [left|center|right]
alignment of the greeting text in the main prompt
container (default: 'center')
--power-shutdown 'CMD [ARGS]...'
command to run to shut down the system
--power-reboot 'CMD [ARGS]...'
command to run to reboot the system
--power-no-setsid
do not prefix power commands with setsid
--kb-[command|sessions|power] [1-12]
change the default F-key keybindings to access the
command, sessions and power menus.
--kb-command [1-12]
F-key to use to open the command menu
--kb-sessions [1-12]
F-key to use to open the sessions menu
--kb-power [1-12]
F-key to use to open the power menu
```

## Usage
Expand Down
3 changes: 3 additions & 0 deletions contrib/man/tuigreet-1.scd
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ tuigreet - A graphical console greeter for greetd
Specify which command to run on successful authentication. This can be
overridden by manual selection within *tuigreet*.

*--env KEY=VALUE*
Environment variables to run the default session with (can appear more then once).

*-s, --sessions DIR1[:DIR2]...*
Location of desktop-files to be used as Wayland session definitions. By
default, Wayland sessions are fetched from */usr/share/wayland-sessions*.
Expand Down
10 changes: 9 additions & 1 deletion src/greeter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,13 @@ impl Greeter {
self.config().opt_str(name)
}

pub fn options_multi(&self, name: &str) -> Option<Vec<String>> {
match self.config().opt_present(name) {
true => Some(self.config().opt_strs(name)),
false => None,
}
}

// Returns the width of the main window where content is displayed from the
// provided arguments.
pub fn width(&self) -> u16 {
Expand Down Expand Up @@ -419,6 +426,7 @@ impl Greeter {
opts.optflag("v", "version", "print version information");
opts.optflagopt("d", "debug", "enable debug logging to the provided file, or to /tmp/tuigreet.log", "FILE");
opts.optopt("c", "cmd", "command to run", "COMMAND");
opts.optmulti("", "env", "environment variables to run the default session with (can appear more than once)", "KEY=VALUE");
opts.optopt("s", "sessions", "colon-separated list of Wayland session paths", "DIRS");
opts.optopt("", "session-wrapper", "wrapper command to initialize the non-X11 session", "'CMD [ARGS]...'");
opts.optopt("x", "xsessions", "colon-separated list of X11 session paths", "DIRS");
Expand Down Expand Up @@ -559,7 +567,7 @@ impl Greeter {

// If the `--cmd` argument is provided, it will override the selected session.
if let Some(command) = self.option("cmd") {
self.session_source = SessionSource::Command(command);
self.session_source = SessionSource::DefaultCommand(command, self.options_multi("env"));
}

if let Some(dirs) = self.option("sessions") {
Expand Down
99 changes: 61 additions & 38 deletions src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,22 +172,17 @@ impl Ipc {
greeter.mode = Mode::Processing;

let session = Session::get_selected(greeter);
let (command, env) = wrap_session_command(greeter, session, &command);
let default = DefaultCommand(&command, greeter.session_source.env());
let (command, env) = wrap_session_command(greeter, session, &default);

#[cfg(not(debug_assertions))]
self.send(Request::StartSession { cmd: vec![command.to_string()], env }).await;

#[cfg(debug_assertions)]
{
let _ = command;
let _ = env;

self
.send(Request::StartSession {
cmd: vec!["true".to_string()],
env: vec![],
})
.await;

self.send(Request::StartSession { cmd: vec!["true".to_string()], env }).await;
}
}
}
Expand Down Expand Up @@ -234,47 +229,72 @@ fn desktop_names_to_xdg(names: &str) -> String {
names.replace(';', ":").trim_end_matches(':').to_string()
}

fn wrap_session_command<'a>(greeter: &Greeter, session: Option<&Session>, command: &'a str) -> (Cow<'a, str>, Vec<String>) {
struct DefaultCommand<'a>(&'a str, Option<Vec<String>>);

impl<'a> DefaultCommand<'a> {
fn command(&'a self) -> &'a str {
self.0
}

fn env(&'a self) -> Option<&'a Vec<String>> {
self.1.as_ref()
}
}

fn wrap_session_command<'a>(greeter: &Greeter, session: Option<&Session>, default: &'a DefaultCommand<'a>) -> (Cow<'a, str>, Vec<String>) {
let mut env: Vec<String> = vec![];

if let Some(Session {
slug,
session_type,
xdg_desktop_names,
..
}) = session
{
if let Some(slug) = slug {
env.push(format!("XDG_SESSION_DESKTOP={slug}"));
env.push(format!("DESKTOP_SESSION={slug}"));
}
if *session_type != SessionType::None {
env.push(format!("XDG_SESSION_TYPE={}", session_type.as_xdg_session_type()));
}
if let Some(xdg_desktop_names) = xdg_desktop_names {
env.push(format!("XDG_CURRENT_DESKTOP={}", desktop_names_to_xdg(xdg_desktop_names)));
match session {
// If the target is a defined session, we should be able to deduce all the
// environment we need from the desktop file.
Some(Session {
slug,
session_type,
xdg_desktop_names,
..
}) => {
if let Some(slug) = slug {
env.push(format!("XDG_SESSION_DESKTOP={slug}"));
env.push(format!("DESKTOP_SESSION={slug}"));
}
if *session_type != SessionType::None {
env.push(format!("XDG_SESSION_TYPE={}", session_type.as_xdg_session_type()));
}
if let Some(xdg_desktop_names) = xdg_desktop_names {
env.push(format!("XDG_CURRENT_DESKTOP={}", desktop_names_to_xdg(xdg_desktop_names)));
}

if *session_type == SessionType::X11 {
if let Some(ref wrap) = greeter.xsession_wrapper {
return (Cow::Owned(format!("{} {}", wrap, default.command())), env);
}
} else if let Some(ref wrap) = greeter.session_wrapper {
return (Cow::Owned(format!("{} {}", wrap, default.command())), env);
}
}

if *session_type == SessionType::X11 {
if let Some(ref wrap) = greeter.xsession_wrapper {
return (Cow::Owned(format!("{} {}", wrap, command)), env);
_ => {
// If a wrapper script is used, assume that it is able to set up the
// required environment.
if let Some(ref wrap) = greeter.session_wrapper {
return (Cow::Owned(format!("{} {}", wrap, default.command())), env);
}
// Otherwise, set up the environment from the provided argument.
if let Some(base_env) = default.env() {
env.append(&mut base_env.clone());
}
} else if let Some(ref wrap) = greeter.session_wrapper {
return (Cow::Owned(format!("{} {}", wrap, command)), env);
}
} else if let Some(ref wrap) = greeter.session_wrapper {
return (Cow::Owned(format!("{} {}", wrap, command)), env);
}

(Cow::Borrowed(command), env)
(Cow::Borrowed(default.command()), env)
}

#[cfg(test)]
mod test {
use std::path::PathBuf;

use crate::{
ipc::desktop_names_to_xdg,
ipc::{desktop_names_to_xdg, DefaultCommand},
ui::sessions::{Session, SessionType},
Greeter,
};
Expand All @@ -293,7 +313,8 @@ mod test {
..Default::default()
};

let (command, env) = wrap_session_command(&greeter, Some(&session), &session.command);
let default = DefaultCommand(&session.command, None);
let (command, env) = wrap_session_command(&greeter, Some(&session), &default);

assert_eq!(command.as_ref(), "Session1Cmd");
assert_eq!(env, vec!["XDG_SESSION_TYPE=wayland"]);
Expand All @@ -312,7 +333,8 @@ mod test {
..Default::default()
};

let (command, env) = wrap_session_command(&greeter, Some(&session), &session.command);
let default = DefaultCommand(&session.command, None);
let (command, env) = wrap_session_command(&greeter, Some(&session), &default);

assert_eq!(command.as_ref(), "/wrapper.sh Session1Cmd");
assert_eq!(env, vec!["XDG_SESSION_TYPE=wayland"]);
Expand All @@ -333,7 +355,8 @@ mod test {
..Default::default()
};

let (command, env) = wrap_session_command(&greeter, Some(&session), &session.command);
let default = DefaultCommand(&session.command, None);
let (command, env) = wrap_session_command(&greeter, Some(&session), &default);

assert_eq!(command.as_ref(), "startx /usr/bin/env Session1Cmd");
assert_eq!(
Expand Down
12 changes: 12 additions & 0 deletions src/ui/sessions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use super::common::menu::MenuItem;
pub enum SessionSource {
#[default]
None,
DefaultCommand(String, Option<Vec<String>>),
Command(String),
Session(usize),
}
Expand All @@ -29,6 +30,7 @@ impl SessionSource {
pub fn label<'g, 'ss: 'g>(&'ss self, greeter: &'g Greeter) -> Option<&'g str> {
match self {
SessionSource::None => None,
SessionSource::DefaultCommand(command, _) => Some(command),
SessionSource::Command(command) => Some(command),
SessionSource::Session(index) => greeter.sessions.options.get(*index).map(|session| session.name.as_str()),
}
Expand All @@ -39,10 +41,20 @@ impl SessionSource {
pub fn command<'g, 'ss: 'g>(&'ss self, greeter: &'g Greeter) -> Option<&'g str> {
match self {
SessionSource::None => None,
SessionSource::DefaultCommand(command, _) => Some(command.as_str()),
SessionSource::Command(command) => Some(command.as_str()),
SessionSource::Session(index) => greeter.sessions.options.get(*index).map(|session| session.command.as_str()),
}
}

pub fn env<'g, 'ss: 'g>(&'ss self) -> Option<Vec<String>> {
match self {
SessionSource::None => None,
SessionSource::DefaultCommand(_, env) => env.clone(),
SessionSource::Command(_) => None,
SessionSource::Session(_) => None,
}
}
}

// Represents the XDG type of the selected session.
Expand Down

0 comments on commit 986af2e

Please sign in to comment.