use crate::logging;
use crate::sessiond_state::{self, monitor};
use std::os::fd::AsRawFd;
use std::{fs, io, path, rc};
pub struct Sessiond {
logger: rc::Rc<logging::Logger>,
state: sessiond_state::State,
monitor: monitor::Monitor,
}
impl Sessiond {
pub fn try_connect(logger: rc::Rc<logging::Logger>) -> Option<Self> {
let mut state = sessiond_state::State::new();
state.try_read().ok()?;
Some(Self {
logger,
monitor: state.monitor().ok()?,
state,
})
}
}
impl super::Backend for Sessiond {
fn get_backend(&self) -> crate::LibsessionBackend {
crate::LibsessionBackend::Sessiond
}
fn list_sessions(&mut self) -> super::Result<Vec<String>> {
self.logger.log(crate::LibsessionLogLevel::Debug, || {
"sessiond: listing all sessions".to_string()
});
if self.monitor.has_changed()? {
self.state.try_read()?;
}
Ok(self.state.sessions.keys().cloned().collect())
}
fn get_session_id(&mut self, pid: u32) -> Option<String> {
self.logger.log(crate::LibsessionLogLevel::Debug, || {
format!("sessiond: looking up session for process {pid}")
});
let cgroup_path = path::PathBuf::from("/proc")
.join(pid.to_string())
.join("cgroup");
let cgroup = fs::read_to_string(cgroup_path).ok()?;
cgroup.lines().find_map(|line| {
let path = line.splitn(3, ':').nth(2)?;
path.split('/')
.find_map(|part| part.strip_prefix("session-")?.parse().ok())
})
}
fn get_owner_uid(&mut self, pid: u32) -> super::Result<u32> {
if self.monitor.has_changed()? {
self.state.try_read()?;
}
let path = path::PathBuf::from("/proc")
.join(pid.to_string())
.join("cgroup");
let cgroup_path = fs::read_to_string(path)?
.lines()
.find_map(|line| line.strip_prefix("0::"))
.map(|hierarchy_path| {
path::PathBuf::from("/sys/fs/cgroup").join(hierarchy_path.trim_start_matches('/'))
})
.ok_or_else(|| {
io::Error::new(io::ErrorKind::NotFound, "process has no cgroup v2 entry")
})?;
let relative_path = cgroup_path
.strip_prefix(&self.state.cgroup_root)
.map_err(|_| {
io::Error::new(
io::ErrorKind::NotFound,
"process is outside sessiond's cgroup root",
)
})?;
let component = relative_path
.components()
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "process has no user cgroup"))?;
let name = component.as_os_str().to_str().ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "user cgroup name is not UTF-8")
})?;
name.strip_prefix("user-")
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
"process is not in a sessiond user cgroup",
)
})?
.parse::<u32>()
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err).into())
}
fn get_uid(&mut self, session_id: &str) -> Option<u32> {
if self.monitor.has_changed().ok()? {
self.state.try_read().ok()?;
}
let session = self.state.sessions.get(session_id)?;
Some(session.uid)
}
fn get_state(&mut self, uid: u32) -> Option<crate::LibsessionUserState> {
if self.monitor.has_changed().ok()? {
self.state.try_read().ok()?;
}
let user = self.state.users.get(&uid)?;
match user.state {
sessiond_state::UserState::Offline => Some(crate::LibsessionUserState::Offline),
sessiond_state::UserState::Lingering => Some(crate::LibsessionUserState::Lingering),
sessiond_state::UserState::Online => Some(crate::LibsessionUserState::Online),
sessiond_state::UserState::Active => Some(crate::LibsessionUserState::Active),
sessiond_state::UserState::Closing => Some(crate::LibsessionUserState::Closing),
sessiond_state::UserState::Unknown => Some(crate::LibsessionUserState::Unknown),
}
}
fn get_display(&mut self, uid: u32) -> Option<String> {
if self.monitor.has_changed().ok()? {
self.state.try_read().ok()?;
}
self.state
.sessions
.values()
.find(|session| session.uid == uid)
.map(|session| session.display.to_string())
}
fn get_session_state(
&mut self,
session_id: &str,
) -> super::Result<crate::LibsessionSessionState> {
if self.monitor.has_changed()? {
self.state.try_read()?;
}
let session = self
.state
.sessions
.get(session_id)
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "session not found"))?;
match session.state {
sessiond_state::SessionState::Online => Ok(crate::LibsessionSessionState::Online),
sessiond_state::SessionState::Closing => Ok(crate::LibsessionSessionState::Closing),
sessiond_state::SessionState::Opening => Ok(crate::LibsessionSessionState::Opening),
sessiond_state::SessionState::Active => Ok(crate::LibsessionSessionState::Active),
sessiond_state::SessionState::Unknown => Ok(crate::LibsessionSessionState::Unkown),
}
}
fn is_local_session(&self, session_id: &str) -> Option<bool> {
let session = self.state.sessions.get(session_id)?;
Some(session.remote_host.is_none())
}
fn get_monitor(&self) -> Option<Box<dyn super::BackendMonitor>> {
Some(Box::new(self.state.monitor().ok()?))
}
}
impl super::BackendMonitor for monitor::Monitor {
fn fd(&self) -> std::os::fd::RawFd {
self.as_raw_fd()
}
fn flush(&mut self) {
let _ = self.has_changed();
}
}