mirror of
https://github.com/coastalwhite/lemurs
synced 2024-11-22 10:03:03 +00:00
Impr: Use signals to detect when X server is ready
This commit is contained in:
parent
f3f5cd0d1c
commit
812d07114c
3 changed files with 72 additions and 32 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -120,6 +120,7 @@ dependencies = [
|
|||
"libc 0.2.139",
|
||||
"log",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"pam",
|
||||
"pgs-files",
|
||||
"rand",
|
||||
|
@ -220,6 +221,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "pam"
|
||||
version = "0.7.0"
|
||||
|
|
|
@ -33,6 +33,9 @@ pam = "0.7.0"
|
|||
pgs-files = "0.0.7"
|
||||
users = "0.11.0"
|
||||
|
||||
# Once Cell
|
||||
once_cell = "1.17.1"
|
||||
|
||||
# Logging
|
||||
env_logger = { version = "0.9.0", default-features = false }
|
||||
log = "0.4.0"
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
use libc::{signal, SIGUSR1, SIG_DFL, SIG_IGN};
|
||||
use rand::Rng;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fmt::Display;
|
||||
|
@ -13,7 +18,7 @@ use log::{error, info};
|
|||
use crate::auth::AuthUserInfo;
|
||||
use crate::env_container::EnvironmentContainer;
|
||||
|
||||
const XSTART_TIMEOUT_SECS: u64 = 20;
|
||||
const XSTART_CHECK_MAX_TRIES: u64 = 300;
|
||||
const XSTART_CHECK_INTERVAL_MILLIS: u64 = 100;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -51,6 +56,20 @@ fn mcookie() -> String {
|
|||
format!("{cookie:032x}")
|
||||
}
|
||||
|
||||
static X_HAS_STARTED: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn handle_sigusr1(_: i32) {
|
||||
*X_HAS_STARTED.lock().unwrap_or_else(|err| {
|
||||
error!("Failed to grab the `X_HAS_STARTED` Mutex lock. Reason: {err}");
|
||||
std::process::exit(1);
|
||||
}) = true;
|
||||
|
||||
unsafe {
|
||||
signal(SIGUSR1, handle_sigusr1 as usize);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_x(
|
||||
process_env: &mut EnvironmentContainer,
|
||||
user_info: &AuthUserInfo,
|
||||
|
@ -99,10 +118,20 @@ pub fn setup_x(
|
|||
vtnr_value
|
||||
};
|
||||
|
||||
// Here we explicitely ignore the first USR defined signal. Xorg looks at whether this signal
|
||||
// is ignored or not. If it is ignored, it will send that signal to the parent when it ready to
|
||||
// receive connections. This is also how xinit does it.
|
||||
//
|
||||
// After we spawn the Xorg process, we need to make sure to quickly re-enable this signal as we
|
||||
// need to listen to the signal by Xorg.
|
||||
unsafe {
|
||||
libc::signal(SIGUSR1, SIG_IGN);
|
||||
}
|
||||
|
||||
info!("Run X server");
|
||||
let child = Command::new(super::SYSTEM_SHELL)
|
||||
let mut child = Command::new(super::SYSTEM_SHELL)
|
||||
.arg("-c")
|
||||
.arg(format!("/usr/bin/X {display_value} vt{doubledigit_vtnr}",))
|
||||
.arg(format!("/usr/bin/X {display_value} vt{doubledigit_vtnr}"))
|
||||
.stdout(Stdio::null()) // TODO: Maybe this should be logged or something?
|
||||
.stderr(Stdio::null()) // TODO: Maybe this should be logged or something?
|
||||
.spawn()
|
||||
|
@ -111,42 +140,43 @@ pub fn setup_x(
|
|||
XSetupError::XServerStart
|
||||
})?;
|
||||
|
||||
// See note above
|
||||
unsafe {
|
||||
libc::signal(SIGUSR1, SIG_DFL);
|
||||
signal(SIGUSR1, handle_sigusr1 as usize);
|
||||
}
|
||||
|
||||
// Wait for XServer to boot-up
|
||||
let start_time = time::SystemTime::now();
|
||||
loop {
|
||||
// Timeout
|
||||
if match start_time.elapsed() {
|
||||
Ok(dur) => dur.as_secs() >= XSTART_TIMEOUT_SECS,
|
||||
Err(_) => {
|
||||
error!("Failed to resolve elapsed time");
|
||||
std::process::exit(1);
|
||||
}
|
||||
} {
|
||||
error!("Starting X timed out!");
|
||||
return Err(XSetupError::XServerTimeout);
|
||||
}
|
||||
|
||||
match Command::new(super::SYSTEM_SHELL)
|
||||
.arg("-c")
|
||||
.arg("timeout 1s /usr/bin/xset q")
|
||||
.stdout(Stdio::null()) // TODO: Maybe this should be logged or something?
|
||||
.stderr(Stdio::null()) // TODO: Maybe this should be logged or something?
|
||||
.status()
|
||||
{
|
||||
Ok(status) => {
|
||||
if status.success() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
error!("Failed to run `xset` to check X server status");
|
||||
return Err(XSetupError::XServerStatusCheck);
|
||||
}
|
||||
for _ in 0..XSTART_CHECK_MAX_TRIES {
|
||||
// This will be set by the `handle_sigusr1` signal handler.
|
||||
if *X_HAS_STARTED.lock().unwrap() {
|
||||
break;
|
||||
}
|
||||
|
||||
thread::sleep(time::Duration::from_millis(XSTART_CHECK_INTERVAL_MILLIS));
|
||||
}
|
||||
|
||||
// If the value is still `false`, this means we have time-ed out and Xorg is not running.
|
||||
if !*X_HAS_STARTED.lock().unwrap() {
|
||||
child.kill().unwrap_or_else(|err| {
|
||||
error!("Failed kill Xorg after it time-ed out. Reason: {err}");
|
||||
});
|
||||
return Err(XSetupError::XServerTimeout);
|
||||
}
|
||||
|
||||
if let Ok(x_server_start_time) = start_time.elapsed() {
|
||||
info!(
|
||||
"It took X server {start_ms}ms to start",
|
||||
start_ms = x_server_start_time.as_millis()
|
||||
);
|
||||
}
|
||||
|
||||
*X_HAS_STARTED.lock().unwrap_or_else(|err| {
|
||||
error!("Failed to grab the `X_HAS_STARTED` Mutex lock. Reason: {err}");
|
||||
std::process::exit(1);
|
||||
}) = false;
|
||||
|
||||
info!("X server is running");
|
||||
|
||||
Ok(child)
|
||||
|
|
Loading…
Reference in a new issue