Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 11 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/agama-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ futures-util = { version = "0.3.30", default-features = false, features = [
"alloc",
] }
libsystemd = "0.7.0"
subprocess = "0.2.9"

[[bin]]
name = "agama-dbus-server"
Expand Down
84 changes: 73 additions & 11 deletions rust/agama-server/src/l10n/l10n.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use std::env;
use std::ffi::OsStr;
use std::io;
use std::process::Command;
use std::time::Duration;

use crate::error::Error;
use agama_locale_data::{KeymapId, LocaleId};
use regex::Regex;
use subprocess::{Popen, PopenConfig, PopenError, Redirection};

use super::keyboard::KeymapsDatabase;
use super::locale::LocalesDatabase;
Expand All @@ -21,6 +25,60 @@ pub struct L10n {
pub ui_keymap: KeymapId,
}

// timeout for the setxkbmap call (in seconds), when there is an authentication
// problem when accessing the X server then it enters an infinite loop
const SETXKBMAP_TIMEOUT: u64 = 3;

// helper function which runs a command with timeout and collects it's standard
// output
fn run_with_timeout(cmd: &[impl AsRef<OsStr>], timeout: u64) -> Result<Option<String>, PopenError> {
// start the subprocess
let mut process = Popen::create(
cmd,
PopenConfig {
stdout: Redirection::Pipe,
stderr: Redirection::Pipe,
..Default::default()
},
)?;

// wait for it to finish or until the timeout is reached
if process
.wait_timeout(Duration::from_secs(timeout))?
.is_none()
{
tracing::warn!("Command timed out!");
Comment thread
imobachgs marked this conversation as resolved.
Outdated
// if the process is still running after the timeout then terminate it,
// ignore errors, there is another attempt later to kill the process
let _ = process.terminate();

// give the process some time to react to SIGTERM
if process.wait_timeout(Duration::from_secs(1))?.is_none() {
// process still running, kill it with SIGKILL
process.kill()?;
}

return Err(PopenError::LogicError("Timeout reached"));
}

// get the collected stdout/stderr
let (out, err) = process.communicate(None)?;

if let Some(err_str) = err {
if err_str.len() > 0 {
Comment thread
imobachgs marked this conversation as resolved.
Outdated
tracing::warn!("Error output size: {}", err_str.len());
}
}

return Ok(out);
}

// helper function to get the X display name, if not set it returns the ":0"
// default value
fn display() -> String {
env::var("DISPLAY").unwrap_or(String::from(":0"))
}

impl L10n {
pub fn new_with_locale(ui_locale: &LocaleId) -> Result<Self, Error> {
const DEFAULT_TIMEZONE: &str = "Europe/Berlin";
Expand Down Expand Up @@ -112,11 +170,15 @@ impl L10n {
.args(["set-x11-keymap", &keymap])
.output()
.map_err(LocaleError::Commit)?;
Command::new("/usr/bin/setxkbmap")
.arg(keymap)
.env("DISPLAY", ":0")
.output()
.map_err(LocaleError::Commit)?;

let output = run_with_timeout(
&["setxkbmap", "-display", &display(), &keymap],
SETXKBMAP_TIMEOUT,
);
output.map_err(|e| {
LocaleError::Commit(io::Error::new(io::ErrorKind::Other, e.to_string()))
})?;

Ok(())
}

Expand All @@ -141,12 +203,12 @@ impl L10n {
}

fn x11_keymap() -> Result<String, io::Error> {
let output = Command::new("setxkbmap")
.arg("-query")
.env("DISPLAY", ":0")
.output()?;
let output = String::from_utf8(output.stdout)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
let output = run_with_timeout(
&["setxkbmap", "-query", "-display", &display()],
SETXKBMAP_TIMEOUT,
);
let output = output.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
let output = output.unwrap_or(String::new());

let keymap_regexp = Regex::new(r"(?m)^layout: (.+)$").unwrap();
let captures = keymap_regexp.captures(&output);
Expand Down