mirror of
https://github.com/apognu/tuigreet.git
synced 2024-11-22 12:44:06 +03:00
Add option to specify environment for default session (#153)
Add argument to set the environment to run the default command with (#148).
This commit is contained in:
parent
ca4161b18f
commit
359410f67f
18
README.md
18
README.md
@ -13,6 +13,8 @@ Options:
|
|||||||
-d, --debug [FILE] enable debug logging to the provided file, or to
|
-d, --debug [FILE] enable debug logging to the provided file, or to
|
||||||
/tmp/tuigreet.log
|
/tmp/tuigreet.log
|
||||||
-c, --cmd COMMAND command to run
|
-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
|
-s, --sessions DIRS colon-separated list of Wayland session paths
|
||||||
--session-wrapper 'CMD [ARGS]...'
|
--session-wrapper 'CMD [ARGS]...'
|
||||||
wrapper command to initialize the non-X11 session
|
wrapper command to initialize the non-X11 session
|
||||||
@ -40,9 +42,7 @@ Options:
|
|||||||
minimum UID to display in the user selection menu
|
minimum UID to display in the user selection menu
|
||||||
--user-menu-max-uid UID
|
--user-menu-max-uid UID
|
||||||
maximum UID to display in the user selection menu
|
maximum UID to display in the user selection menu
|
||||||
--theme SPEC
|
--theme THEME define the application theme colors
|
||||||
Add visual feedback when typing secrets, as one asterisk character for every
|
|
||||||
keystroke. By default, no feedback is given at all.
|
|
||||||
--asterisks display asterisks when a secret is typed
|
--asterisks display asterisks when a secret is typed
|
||||||
--asterisks-char CHARS
|
--asterisks-char CHARS
|
||||||
characters to be used to redact secrets (default: *)
|
characters to be used to redact secrets (default: *)
|
||||||
@ -52,15 +52,21 @@ Options:
|
|||||||
padding inside the main prompt container (default: 1)
|
padding inside the main prompt container (default: 1)
|
||||||
--prompt-padding PADDING
|
--prompt-padding PADDING
|
||||||
padding between prompt rows (default: 1)
|
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]...'
|
--power-shutdown 'CMD [ARGS]...'
|
||||||
command to run to shut down the system
|
command to run to shut down the system
|
||||||
--power-reboot 'CMD [ARGS]...'
|
--power-reboot 'CMD [ARGS]...'
|
||||||
command to run to reboot the system
|
command to run to reboot the system
|
||||||
--power-no-setsid
|
--power-no-setsid
|
||||||
do not prefix power commands with setsid
|
do not prefix power commands with setsid
|
||||||
--kb-[command|sessions|power] [1-12]
|
--kb-command [1-12]
|
||||||
change the default F-key keybindings to access the
|
F-key to use to open the command menu
|
||||||
command, sessions and power menus.
|
--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
|
## Usage
|
||||||
|
@ -24,6 +24,9 @@ tuigreet - A graphical console greeter for greetd
|
|||||||
Specify which command to run on successful authentication. This can be
|
Specify which command to run on successful authentication. This can be
|
||||||
overridden by manual selection within *tuigreet*.
|
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]...*
|
*-s, --sessions DIR1[:DIR2]...*
|
||||||
Location of desktop-files to be used as Wayland session definitions. By
|
Location of desktop-files to be used as Wayland session definitions. By
|
||||||
default, Wayland sessions are fetched from */usr/share/wayland-sessions*.
|
default, Wayland sessions are fetched from */usr/share/wayland-sessions*.
|
||||||
|
@ -339,6 +339,13 @@ impl Greeter {
|
|||||||
self.config().opt_str(name)
|
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
|
// Returns the width of the main window where content is displayed from the
|
||||||
// provided arguments.
|
// provided arguments.
|
||||||
pub fn width(&self) -> u16 {
|
pub fn width(&self) -> u16 {
|
||||||
@ -419,6 +426,7 @@ impl Greeter {
|
|||||||
opts.optflag("v", "version", "print version information");
|
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.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.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("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("", "session-wrapper", "wrapper command to initialize the non-X11 session", "'CMD [ARGS]...'");
|
||||||
opts.optopt("x", "xsessions", "colon-separated list of X11 session paths", "DIRS");
|
opts.optopt("x", "xsessions", "colon-separated list of X11 session paths", "DIRS");
|
||||||
@ -559,7 +567,17 @@ impl Greeter {
|
|||||||
|
|
||||||
// If the `--cmd` argument is provided, it will override the selected session.
|
// If the `--cmd` argument is provided, it will override the selected session.
|
||||||
if let Some(command) = self.option("cmd") {
|
if let Some(command) = self.option("cmd") {
|
||||||
self.session_source = SessionSource::Command(command);
|
let envs = self.options_multi("env");
|
||||||
|
|
||||||
|
if let Some(envs) = envs {
|
||||||
|
for env in envs {
|
||||||
|
if !env.contains('=') {
|
||||||
|
return Err(format!("malformed environment variable definition for '{env}'").into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.session_source = SessionSource::DefaultCommand(command, self.options_multi("env"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(dirs) = self.option("sessions") {
|
if let Some(dirs) = self.option("sessions") {
|
||||||
@ -681,6 +699,10 @@ mod test {
|
|||||||
&[
|
&[
|
||||||
"--cmd",
|
"--cmd",
|
||||||
"uname",
|
"uname",
|
||||||
|
"--env",
|
||||||
|
"A=B",
|
||||||
|
"--env",
|
||||||
|
"C=D=E",
|
||||||
"--asterisks",
|
"--asterisks",
|
||||||
"--asterisks-char",
|
"--asterisks-char",
|
||||||
".",
|
".",
|
||||||
@ -696,7 +718,13 @@ mod test {
|
|||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
Some(|greeter| {
|
Some(|greeter| {
|
||||||
assert!(matches!(&greeter.session_source, SessionSource::Command(cmd) if cmd == "uname"));
|
assert!(matches!(&greeter.session_source, SessionSource::DefaultCommand(cmd, Some(env)) if cmd == "uname" && env.len() == 2));
|
||||||
|
|
||||||
|
if let SessionSource::DefaultCommand(_, Some(env)) = &greeter.session_source {
|
||||||
|
assert_eq!(env[0], "A=B");
|
||||||
|
assert_eq!(env[1], "C=D=E");
|
||||||
|
}
|
||||||
|
|
||||||
assert!(matches!(&greeter.secret_display, SecretDisplay::Character(c) if c == "."));
|
assert!(matches!(&greeter.secret_display, SecretDisplay::Character(c) if c == "."));
|
||||||
assert_eq!(greeter.prompt_padding(), 0);
|
assert_eq!(greeter.prompt_padding(), 0);
|
||||||
assert_eq!(greeter.window_padding(), 1);
|
assert_eq!(greeter.window_padding(), 1);
|
||||||
@ -727,6 +755,8 @@ mod test {
|
|||||||
(&["--issue", "--greeting", "Hello, world!"], false, None),
|
(&["--issue", "--greeting", "Hello, world!"], false, None),
|
||||||
(&["--kb-command", "F2", "--kb-sessions", "F2"], false, None),
|
(&["--kb-command", "F2", "--kb-sessions", "F2"], false, None),
|
||||||
(&["--time-format", "%i %"], false, None),
|
(&["--time-format", "%i %"], false, None),
|
||||||
|
(&["--cmd", "cmd", "--env"], false, None),
|
||||||
|
(&["--cmd", "cmd", "--env", "A"], false, None),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (opts, valid, check) in table {
|
for (opts, valid, check) in table {
|
||||||
|
107
src/ipc.rs
107
src/ipc.rs
@ -172,7 +172,8 @@ impl Ipc {
|
|||||||
greeter.mode = Mode::Processing;
|
greeter.mode = Mode::Processing;
|
||||||
|
|
||||||
let session = Session::get_selected(greeter);
|
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))]
|
#[cfg(not(debug_assertions))]
|
||||||
self.send(Request::StartSession { cmd: vec![command.to_string()], env }).await;
|
self.send(Request::StartSession { cmd: vec![command.to_string()], env }).await;
|
||||||
@ -180,14 +181,8 @@ impl Ipc {
|
|||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
let _ = command;
|
let _ = command;
|
||||||
let _ = env;
|
|
||||||
|
|
||||||
self
|
self.send(Request::StartSession { cmd: vec!["true".to_string()], env }).await;
|
||||||
.send(Request::StartSession {
|
|
||||||
cmd: vec!["true".to_string()],
|
|
||||||
env: vec![],
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,39 +229,64 @@ fn desktop_names_to_xdg(names: &str) -> String {
|
|||||||
names.replace(';', ":").trim_end_matches(':').to_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>>);
|
||||||
let mut env: Vec<String> = vec![];
|
|
||||||
|
|
||||||
if let Some(Session {
|
impl<'a> DefaultCommand<'a> {
|
||||||
slug,
|
fn command(&'a self) -> &'a str {
|
||||||
session_type,
|
self.0
|
||||||
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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if *session_type == SessionType::X11 {
|
|
||||||
if let Some(ref wrap) = greeter.xsession_wrapper {
|
|
||||||
return (Cow::Owned(format!("{} {}", wrap, command)), env);
|
|
||||||
}
|
|
||||||
} 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)
|
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![];
|
||||||
|
|
||||||
|
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 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(Cow::Borrowed(default.command()), env)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -274,7 +294,7 @@ mod test {
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ipc::desktop_names_to_xdg,
|
ipc::{desktop_names_to_xdg, DefaultCommand},
|
||||||
ui::sessions::{Session, SessionType},
|
ui::sessions::{Session, SessionType},
|
||||||
Greeter,
|
Greeter,
|
||||||
};
|
};
|
||||||
@ -293,7 +313,8 @@ mod test {
|
|||||||
..Default::default()
|
..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!(command.as_ref(), "Session1Cmd");
|
||||||
assert_eq!(env, vec!["XDG_SESSION_TYPE=wayland"]);
|
assert_eq!(env, vec!["XDG_SESSION_TYPE=wayland"]);
|
||||||
@ -312,7 +333,8 @@ mod test {
|
|||||||
..Default::default()
|
..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!(command.as_ref(), "/wrapper.sh Session1Cmd");
|
||||||
assert_eq!(env, vec!["XDG_SESSION_TYPE=wayland"]);
|
assert_eq!(env, vec!["XDG_SESSION_TYPE=wayland"]);
|
||||||
@ -333,7 +355,8 @@ mod test {
|
|||||||
..Default::default()
|
..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!(command.as_ref(), "startx /usr/bin/env Session1Cmd");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -61,16 +61,16 @@ impl Theme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if style.time.is_none() {
|
if style.time.is_none() {
|
||||||
style.time = style.text.clone();
|
style.time.clone_from(&style.text);
|
||||||
}
|
}
|
||||||
if style.greet.is_none() {
|
if style.greet.is_none() {
|
||||||
style.greet = style.text.clone();
|
style.greet.clone_from(&style.text);
|
||||||
}
|
}
|
||||||
if style.title.is_none() {
|
if style.title.is_none() {
|
||||||
style.title = style.border.clone();
|
style.title.clone_from(&style.border);
|
||||||
}
|
}
|
||||||
if style.button.is_none() {
|
if style.button.is_none() {
|
||||||
style.button = style.action.clone();
|
style.button.clone_from(&style.action);
|
||||||
}
|
}
|
||||||
|
|
||||||
style
|
style
|
||||||
|
@ -17,6 +17,7 @@ use super::common::menu::MenuItem;
|
|||||||
pub enum SessionSource {
|
pub enum SessionSource {
|
||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
|
DefaultCommand(String, Option<Vec<String>>),
|
||||||
Command(String),
|
Command(String),
|
||||||
Session(usize),
|
Session(usize),
|
||||||
}
|
}
|
||||||
@ -29,6 +30,7 @@ impl SessionSource {
|
|||||||
pub fn label<'g, 'ss: 'g>(&'ss self, greeter: &'g Greeter) -> Option<&'g str> {
|
pub fn label<'g, 'ss: 'g>(&'ss self, greeter: &'g Greeter) -> Option<&'g str> {
|
||||||
match self {
|
match self {
|
||||||
SessionSource::None => None,
|
SessionSource::None => None,
|
||||||
|
SessionSource::DefaultCommand(command, _) => Some(command),
|
||||||
SessionSource::Command(command) => Some(command),
|
SessionSource::Command(command) => Some(command),
|
||||||
SessionSource::Session(index) => greeter.sessions.options.get(*index).map(|session| session.name.as_str()),
|
SessionSource::Session(index) => greeter.sessions.options.get(*index).map(|session| session.name.as_str()),
|
||||||
}
|
}
|
||||||
@ -39,10 +41,20 @@ impl SessionSource {
|
|||||||
pub fn command<'g, 'ss: 'g>(&'ss self, greeter: &'g Greeter) -> Option<&'g str> {
|
pub fn command<'g, 'ss: 'g>(&'ss self, greeter: &'g Greeter) -> Option<&'g str> {
|
||||||
match self {
|
match self {
|
||||||
SessionSource::None => None,
|
SessionSource::None => None,
|
||||||
|
SessionSource::DefaultCommand(command, _) => Some(command.as_str()),
|
||||||
SessionSource::Command(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()),
|
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.
|
// Represents the XDG type of the selected session.
|
||||||
|
Loading…
Reference in New Issue
Block a user