Move SessionProcess and start on docs

This commit is contained in:
Gijs Burghoorn 2023-02-04 16:06:43 +01:00
parent 40058a81d0
commit 6e8f64439d
4 changed files with 146 additions and 74 deletions

View file

@ -1,6 +1,37 @@
//! # API Example
//! *LeMuRS*-core or *Login Manager RuSt* abstracts the complicated behavior needed when creating
//! Login/Display Manager for \*nix systems.
//!
//! The library is compatible works for systems with and without SystemD. It aims to provide
//! low-level control of the its functionality while not requiring the user to know all the details
//! about the session open process.
//!
//! This library provides 2 main functionalities.
//! 1. [Authentication](#authentication): verification of login credentials
//! 2. [Session Environments](#session-environments): opening a X11, Wayland or Shell environment
//!
//! ## Basic example without Authentication
//!
//! The code below illustrates a basic example of how to use the libary. This example does not
//! verify the user credentials. It performs something very similar to the
//! [`startx`](https://www.x.org/releases/X11R7.5/doc/man/man1/startx.1.html) binary.
//!
//! ```no_run
//! // Fetch the information for the currently active user
//! let user_info = UserInfo::active_user()?;
//!
//! // Define what you want the session to start into
//! let session_environment = SessionEnvironment::X11(
//! &format!("{}/.xinitrc", std::env::get("HOME")?)
//! );
//!
//! // Start a session environment
//! open_session(&user_info, &session_environment)?.wait()?;
//! ```
//!
//! ## Basic example with Authentication
//!
//! ```no_run
//! // TODO: Provide your own methods for fetching the user creditionals
//! let username = "johndoe";
//! let pasword = "*******";
//!
@ -15,9 +46,19 @@
//! )?;
//!
//! // Start a session environment
//! start_session(session_user, &session_environment);
//! open_authenticated_session(
//! session_user,
//! &session_environment
//! )?.wait()?;
//! ```
//!
//! ## Authentication
//!
//! ## Session Environments
//!
//! # API Example
//!
//!
//! # API Expanded
//!
//! ```no_run
@ -48,7 +89,6 @@ use libc::uid_t;
use nix::unistd::{Gid, Uid};
use std::env;
use std::fmt::Display;
use std::process::Child;
use crate::session_environment::env_variables::{
set_basic_variables, set_display, set_seat_vars, set_session_params, set_session_vars,
@ -185,11 +225,11 @@ pub fn authenticate<'a>(
/// In contrast to the [`open_session`] function, this function also allows for finer control of
/// the internal operations with the `context` argument. See the [`StartSessionContext`]
/// documentation for further explanation of what parameters can be controlled.
pub fn open_session_with_context(
pub fn open_session_with_context<'a>(
user_info: &UserInfo,
session_environment: &SessionEnvironment,
context: &StartSessionContext,
) -> Result<SessionProcess<Child>, EnvironmentStartError> {
) -> Result<SessionProcess<'a>, EnvironmentStartError> {
let tty = context.tty;
let mut env_container = EnvironmentContainer::take_snapshot();
@ -204,7 +244,8 @@ pub fn open_session_with_context(
set_basic_variables(&mut env_container, username, homedir, shell);
set_xdg_common_paths(&mut env_container, homedir);
session_environment.spawn(user_info)
unimplemented!()
// session_environment.spawn(user_info)
}
/// Open a `session_environment` with the given `user_info` and return a handler to the opened
@ -223,10 +264,10 @@ pub fn open_session_with_context(
/// the default [`StartSessionContext`]. The default settings can be found in the documentation of
/// [`StartSessionContext`]. If more control is needed, the [`open_session_with_context`] can be
/// used.
pub fn open_session(
pub fn open_session<'a>(
user_info: &UserInfo,
session_environment: &SessionEnvironment,
) -> Result<SessionProcess<Child>, EnvironmentStartError> {
) -> Result<SessionProcess<'a>, EnvironmentStartError> {
let context = StartSessionContext::default();
open_session_with_context(user_info, session_environment, &context)
}
@ -246,12 +287,10 @@ pub fn open_authenticated_session_with_context<'a>(
session_user: SessionUser<'a>,
session_environment: &SessionEnvironment,
context: &StartSessionContext,
) -> Result<SessionProcess<Child>, EnvironmentStartError> {
let session_process =
open_session_with_context(session_user.as_user_info(), session_environment, context)?;
// Insert the pid into the UTMPX entry, if needed
session_user.set_pid(session_process.pid());
) -> Result<SessionProcess<'a>, EnvironmentStartError> {
let mut session_process = open_session_with_context(session_user.as_user_info(), session_environment, context)?;
session_process.authenticate(session_user);
Ok(session_process)
}
@ -271,7 +310,7 @@ pub fn open_authenticated_session_with_context<'a>(
pub fn open_authenticated_session<'a>(
session_user: SessionUser<'a>,
session_environment: &SessionEnvironment,
) -> Result<SessionProcess<Child>, EnvironmentStartError> {
) -> Result<SessionProcess<'a>, EnvironmentStartError> {
let context = StartSessionContext::default();
open_authenticated_session_with_context(session_user, session_environment, &context)
}

View file

@ -8,6 +8,7 @@ use std::io;
use std::os::unix::process::CommandExt;
use std::process::{Child, Command, Stdio};
use crate::auth::SessionUser;
use crate::session_environment::wayland::WaylandStartContext;
use crate::session_environment::x11::X11StartContext;
use crate::{can_run, RunError, UserInfo};
@ -76,11 +77,23 @@ pub enum SessionType {
Shell,
}
pub struct SessionProcess<'a> {
type_specific_content: SessionProcessContent,
session_user: Option<SessionUser<'a>>,
}
#[derive(Debug)]
pub enum SessionProcess<T> {
X11 { server: Child, client: T },
Wayland(T),
Shell(T),
pub enum SessionProcessContent {
X11 { server: Child, client: Child },
Wayland(Child),
Shell(Child),
}
#[derive(Debug)]
pub enum SessionCommand {
X11 { server: Child, client: Command },
Wayland(Command),
Shell(Command),
}
#[derive(Debug, Clone)]
@ -140,22 +153,22 @@ impl SessionType {
}
}
impl<T> SessionProcess<T> {
fn as_ref(&self) -> &T {
match self {
Self::X11 { client, .. } | Self::Wayland(client) | Self::Shell(client) => &client,
}
}
fn as_mut(&mut self) -> &mut T {
match self {
Self::X11 { ref mut client, .. }
| Self::Wayland(ref mut client)
| Self::Shell(ref mut client) => client,
}
}
}
impl<'a> SessionProcess<'a> {
fn as_ref(&self) -> &Child {
use SessionProcessContent::*;
match &self.type_specific_content {
X11 { client, .. } | Wayland(client) | Shell(client) => &client,
}
}
pub fn authenticate(&mut self, session_user: SessionUser<'a>) {
// Insert the pid into the UTMPX entry, if needed
session_user.set_pid(self.pid());
self.session_user = Some(session_user);
}
impl SessionProcess<Child> {
pub fn pid(&self) -> u32 {
self.as_ref().id()
}
@ -164,15 +177,17 @@ impl SessionProcess<Child> {
self,
f: impl Fn(Child) -> Result<(), EnvironmentStartError>,
) -> Result<(), EnvironmentStartError> {
match self {
Self::X11 { mut server, client } => {
use SessionProcessContent::*;
match self.type_specific_content {
X11 { mut server, client } => {
f(client)?;
server.kill().map_err(|err| {
error!("Failed to kill X11 server, Reason: '{}'", err);
EnvironmentStartError::X11ServerKillFailed
})
}
Self::Wayland(client) | Self::Shell(client) => f(client),
Wayland(client) | Shell(client) => f(client),
}
}
@ -223,7 +238,24 @@ impl SessionProcess<Child> {
}
}
impl SessionProcess<Command> {
impl<'a> From<SessionProcessContent> for SessionProcess<'a> {
fn from(type_specific_content: SessionProcessContent) -> Self {
SessionProcess {
type_specific_content,
session_user: None,
}
}
}
impl SessionCommand {
fn as_mut(&mut self) -> &mut Command {
match self {
Self::X11 { ref mut client, .. }
| Self::Wayland(ref mut client)
| Self::Shell(ref mut client) => client,
}
}
pub fn pipe_output(&mut self) {
self.as_mut().stdout(Stdio::piped()).stderr(Stdio::piped());
}
@ -251,14 +283,17 @@ impl SessionProcess<Command> {
unsafe { self.as_mut().pre_exec(to_session_user_env) };
}
pub fn spawn(self) -> io::Result<SessionProcess<Child>> {
Ok(match self {
Self::X11 { server, mut client } => SessionProcess::X11 {
server,
client: client.spawn()?,
pub fn spawn<'a>(self) -> io::Result<SessionProcess<'a>> {
Ok(SessionProcess {
type_specific_content: match self {
Self::X11 { server, mut client } => SessionProcessContent::X11 {
server,
client: client.spawn()?,
},
Self::Wayland(mut client) => SessionProcessContent::Wayland(client.spawn()?),
Self::Shell(mut client) => SessionProcessContent::Shell(client.spawn()?),
},
Self::Wayland(mut client) => SessionProcess::Wayland(client.spawn()?),
Self::Shell(mut client) => SessionProcess::Shell(client.spawn()?),
session_user: None,
})
}
}
@ -272,10 +307,7 @@ impl SessionEnvironment {
}
}
pub fn spawn(
&self,
session_user: &UserInfo,
) -> Result<SessionProcess<Child>, EnvironmentStartError> {
pub fn spawn(&self, session_user: &UserInfo) -> Result<SessionProcess, EnvironmentStartError> {
let context = EnvironmentContext::default();
self.spawn_with_context(session_user, &context)
}
@ -284,7 +316,7 @@ impl SessionEnvironment {
&self,
session_user: &UserInfo,
context: &EnvironmentContext<'a>,
) -> Result<SessionProcess<Child>, EnvironmentStartError> {
) -> Result<SessionProcess, EnvironmentStartError> {
let result = self.internal_spawn_with_context(session_user, context);
info!("Switch back to Lemurs virtual terminal");
@ -301,59 +333,59 @@ impl SessionEnvironment {
&self,
user_info: &UserInfo,
context: &EnvironmentContext<'a>,
) -> Result<SessionProcess<Child>, EnvironmentStartError> {
) -> Result<SessionProcess, EnvironmentStartError> {
can_run()?;
let uid = user_info.user_id();
let gid = user_info.group_id();
let groups = user_info.groups().to_owned();
let mut session_process = match self {
let mut session_command = match self {
SessionEnvironment::X11(initializer) => {
let context = X11StartContext::from(context);
let mut session_process = initializer
let mut session_command = initializer
.start_x11(user_info, &context)
.map_err(EnvironmentStartError::X11Start)?;
// Pipe the stdout and stderr to us so we can read it.
session_process.pipe_output();
session_command.pipe_output();
session_process
session_command
}
SessionEnvironment::Wayland(initializer) => {
let context = WaylandStartContext::from(context);
let mut session_process = initializer
let mut session_command = initializer
.start_wayland(user_info, &context)
.map_err(EnvironmentStartError::WaylandStart)?;
// Pipe the stdout and stderr to us so we can read it.
session_process.pipe_output();
session_command.pipe_output();
session_process
session_command
}
SessionEnvironment::Shell => {
info!("Starting TTY shell");
let shell = &user_info.shell();
let mut session_process = SessionProcess::Shell(Command::new(shell));
session_process.inherit_io();
session_process
let mut session_command = SessionCommand::Shell(Command::new(shell));
session_command.inherit_io();
session_command
}
};
session_process.lower_permission_pre_exec(uid, gid, groups);
session_command.lower_permission_pre_exec(uid, gid, groups);
// Actually spawn the initializer process
let session_process = match session_process.spawn() {
Ok(cmd) => cmd,
match session_command.spawn() {
Ok(cmd) => Ok(cmd.into()),
Err(err) => {
error!("Failed to start initializer. Reason '{}'", err);
return Err(EnvironmentStartError::InitializerFailed);
Err(EnvironmentStartError::InitializerFailed)
}
};
Ok(session_process)
}
}
}

View file

@ -7,7 +7,7 @@ use std::process::Command;
use crate::UserInfo;
use super::{EnvironmentContext, SessionInitializer, SessionProcess};
use super::{EnvironmentContext, SessionInitializer, SessionCommand};
const WAYLAND_SESSIONS_DIR: &str = "/etc/lemurs/wayland";
@ -44,7 +44,7 @@ impl SessionInitializer {
&self,
_user_info: &UserInfo,
context: &WaylandStartContext,
) -> Result<SessionProcess<Command>, WaylandStartError> {
) -> Result<SessionCommand, WaylandStartError> {
info!("Starting Wayland session '{}'", self.name);
let mut initializer = Command::new(context.system_shell);
@ -52,7 +52,7 @@ impl SessionInitializer {
// Make it run the initializer
initializer.arg("-c").arg(&self.path);
Ok(SessionProcess::Wayland(initializer))
Ok(SessionCommand::Wayland(initializer))
}
}

View file

@ -11,8 +11,9 @@ use std::path::PathBuf;
use log::{error, info, warn};
use crate::UserInfo;
use crate::session_environment::SessionCommand;
use super::{EnvironmentContext, SessionInitializer, SessionProcess};
use super::{EnvironmentContext, SessionInitializer};
const SERVER_QUERY_NUM_OF_TRIES: usize = 10;
const SERVER_QUERY_TIMEOUT: Duration = Duration::from_millis(1000);
@ -204,7 +205,7 @@ impl SessionInitializer {
&self,
user_info: &UserInfo,
context: &X11StartContext,
) -> Result<SessionProcess<Command>, X11StartError> {
) -> Result<SessionCommand, X11StartError> {
info!("Starting X11 session '{}'", self.name);
// Start the X Server
@ -219,7 +220,7 @@ impl SessionInitializer {
self.path.display()
));
Ok(SessionProcess::X11 {
Ok(SessionCommand::X11 {
server,
client: initializer,
})