Fix: Stdout, Stderr of client & Xserver to file

This fixes the outstanding issue that quitting your client did not
return you to Lemurs.
This commit is contained in:
Gijs Burghoorn 2023-03-30 21:34:42 +02:00
parent b6b9b5d7a3
commit 64ea11a38a
4 changed files with 60 additions and 64 deletions

View file

@ -48,6 +48,13 @@ x11_display = ":1"
# to 0.
xserver_timeout_secs = 60
# Where to log to for the client. The Client is the Desktop Environment or
# Window Manager for Xorg, the Compositor for Wayland and the Shell for TTY.
client_log_path = "/var/log/lemurs.client.log"
# Where to log to for the XServer.
xserver_log_path = "/var/log/lemurs.xorg.log"
# The PAM service that should be used to login
pam_service = "lemurs"

View file

@ -156,6 +156,9 @@ toml_config_struct! { Config, PartialConfig,
x11_display => String,
xserver_timeout_secs => u16,
client_log_path => String,
xserver_log_path => String,
pam_service => String,
shell_login_flag => ShellLoginFlag,

View file

@ -1,7 +1,9 @@
use log::{error, info, warn};
use std::error::Error;
use std::fmt::Display;
use std::fs;
use std::fs::{self, File};
use std::os::fd::{FromRawFd, IntoRawFd};
use std::path::Path;
use users::get_user_groups;
@ -73,6 +75,16 @@ impl From<XSetupError> for EnvironmentStartError {
}
}
fn output_command_to_log(mut command: Command, log_path: &Path) -> Command {
let fd = File::create(log_path).unwrap().into_raw_fd();
command
.stdout(unsafe { Stdio::from_raw_fd(fd) })
.stderr(unsafe { Stdio::from_raw_fd(fd) });
command
}
fn lower_command_permissions_to_user(
mut command: Command,
user_info: &AuthUserInfo<'_>,
@ -113,44 +125,29 @@ impl SpawnedEnvironment {
}
pub fn wait(self) {
let child = match self {
Self::X11 { client, .. } | Self::Wayland(client) | Self::Tty(client) => client,
};
info!("Waiting for client to exit");
let child_output = match child.wait_with_output() {
Ok(output) => output,
Err(err) => {
error!("Failed to wait for environment to exit, Reason: '{}'", err);
return;
}
};
// Print the stdout if it is at all available
match std::str::from_utf8(&child_output.stdout) {
Ok(output) => {
if !output.trim().is_empty() {
info!("Environment's stdout: \"\"\"\n{}\n\"\"\"", output.trim());
}
}
Err(err) => {
warn!("Failed to read STDOUT output as UTF-8. Reason: '{}'", err);
}
};
// Return the `stderr` if the child process did not exit correctly.
if !child_output.status.success() {
warn!("Environment came back with non-zero exit code.");
match std::str::from_utf8(&child_output.stderr) {
Ok(output) => {
if !output.trim().is_empty() {
warn!("Environment's stderr: \"\"\"\n{}\n\"\"\"", output.trim());
match self {
Self::X11 {
mut client,
mut server,
} => {
match client.wait() {
Ok(exit_code) => info!("Client exited with exit code `{exit_code}`"),
Err(err) => {
error!("Failed to wait for client. Reason: {err}");
}
};
match server.kill() {
Ok(_) => {}
Err(err) => error!("Failed to terminate X11. Reason: {err}"),
}
Err(err) => {
warn!("Failed to read STDERR output as UTF-8. Reason: '{}'", err);
}
};
}
Self::Wayland(mut client) | Self::Tty(mut client) => match client.wait() {
Ok(exit_code) => info!("Client exited with exit code `{exit_code}`"),
Err(err) => error!("Failed to wait for client. Reason: {err}"),
},
}
}
}
@ -168,7 +165,13 @@ impl PostLoginEnvironment {
ShellLoginFlag::Long => Some("--login"),
};
let mut client = lower_command_permissions_to_user(Command::new(SYSTEM_SHELL), user_info);
let client = lower_command_permissions_to_user(Command::new(SYSTEM_SHELL), user_info);
info!(
"Setup client to log `stdout` and `stderr` to '{log_path}'",
log_path = config.client_log_path
);
let mut client = output_command_to_log(client, Path::new(&config.client_log_path));
if let Some(shell_login_flag) = shell_login_flag {
client.arg(shell_login_flag);
@ -184,8 +187,6 @@ impl PostLoginEnvironment {
let client = match client
.arg(format!("{} {}", "/etc/lemurs/xsetup.sh", xinitrc_path))
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
{
Ok(child) => child,
@ -199,12 +200,7 @@ impl PostLoginEnvironment {
}
PostLoginEnvironment::Wayland { script_path } => {
info!("Starting Wayland session");
let child = match client
.arg(script_path)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
{
let child = match client.arg(script_path).spawn() {
Ok(child) => child,
Err(err) => {
error!("Failed to start Wayland Compositor. Reason '{err}'");

View file

@ -7,18 +7,18 @@ use std::env;
use std::error::Error;
use std::fmt::Display;
use std::fs::remove_file;
use std::io::Read;
use std::process::{Child, Command, Stdio};
use std::sync::atomic::AtomicBool;
use std::{thread, time};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use log::{error, info};
use crate::auth::AuthUserInfo;
use crate::config::Config;
use crate::env_container::EnvironmentContainer;
use crate::post_login::output_command_to_log;
const XSTART_CHECK_INTERVAL_MILLIS: u64 = 100;
@ -135,11 +135,15 @@ pub fn setup_x(
libc::signal(SIGUSR1, SIG_IGN);
}
let mut child = Command::new(super::SYSTEM_SHELL)
let child = Command::new(super::SYSTEM_SHELL);
info!(
"Setup XServer to log `stdout` and `stderr` to '{log_path}'",
log_path = config.xserver_log_path
);
let mut child = output_command_to_log(child, Path::new(&config.xserver_log_path));
let mut child = child
.arg("-c")
.arg(format!("/usr/bin/X {display_value} vt{doubledigit_vtnr}"))
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.map_err(|err| {
error!("Failed to start X server. Reason: {}", err);
@ -171,20 +175,6 @@ pub fn setup_x(
if let Some(status) = child.try_wait().unwrap_or(None) {
error!("X server died before signaling it was ready to received connections. Status code: {status}.");
if let Some(mut stdout) = child.stdout.take() {
let mut buf = String::new();
if stdout.read_to_string(&mut buf).is_ok() {
error!("X server STDOUT: '''\n{buf}\n'''");
}
}
if let Some(mut stdout) = child.stdout.take() {
let mut buf = String::new();
if stdout.read_to_string(&mut buf).is_ok() {
error!("X server STDERR: '''\n{buf}\n'''");
}
}
return Err(XSetupError::XServerPrematureExit);
}