leftwm_core/utils/
child_process.rsuse std::cmp::Reverse;
use std::collections::BinaryHeap;
use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs;
use std::iter::{Extend, FromIterator};
use std::path::{Path, PathBuf};
use std::process::{Child, Command, Stdio};
use std::sync::{atomic::AtomicBool, Arc};
use xdg::BaseDirectories;
use crate::errors::Result;
pub type ChildID = u32;
#[derive(Default)]
pub struct Nanny {}
impl Nanny {
fn get_config_dir() -> Result<PathBuf> {
BaseDirectories::with_prefix("leftwm")?
.create_config_directory("")
.map_err(Into::into)
}
fn run_script(path: &Path) -> Result<Child> {
Command::new(path)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.map_err(Into::into)
}
pub fn run_global_up_script() -> Result<Child> {
let mut path = Self::get_config_dir()?;
let mut scripts = Self::get_files_in_path_with_ext(&path, "up")?;
while let Some(Reverse(script)) = scripts.pop() {
if let Err(e) = Self::run_script(&script) {
tracing::error!("Unable to run script {script:?}, error: {e}");
}
}
path.push("up");
Self::run_script(&path)
}
fn get_files_in_path_with_ext(
path: impl AsRef<Path>,
ext: impl AsRef<OsStr>,
) -> Result<BinaryHeap<Reverse<PathBuf>>> {
let dir = fs::read_dir(&path)?;
let mut files = BinaryHeap::new();
for entry in dir.flatten() {
let file = entry.path();
if let Some(extension) = file.extension() {
if extension == ext.as_ref() {
files.push(Reverse(file));
}
}
}
Ok(files)
}
pub fn boot_current_theme() -> Result<Child> {
let mut path = Self::get_config_dir()?;
path.push("themes");
path.push("current");
path.push("up");
Self::run_script(&path)
}
}
#[derive(Debug, Default)]
pub struct Children {
inner: HashMap<ChildID, Child>,
}
impl Children {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn insert(&mut self, child: Child) -> bool {
self.inner.insert(child.id(), child).is_none()
}
pub fn merge(&mut self, reaper: Self) {
self.inner.extend(reaper.inner);
}
pub fn remove_finished_children(&mut self) {
self.inner
.retain(|_, child| child.try_wait().map_or(true, |ret| ret.is_none()));
}
}
impl FromIterator<Child> for Children {
fn from_iter<T: IntoIterator<Item = Child>>(iter: T) -> Self {
Self {
inner: iter.into_iter().map(|child| (child.id(), child)).collect(),
}
}
}
impl Extend<Child> for Children {
fn extend<T: IntoIterator<Item = Child>>(&mut self, iter: T) {
self.inner
.extend(iter.into_iter().map(|child| (child.id(), child)));
}
}
pub fn register_child_hook(flag: Arc<AtomicBool>) {
_ = signal_hook::flag::register(signal_hook::consts::signal::SIGCHLD, flag)
.map_err(|err| tracing::error!("Cannot register SIGCHLD signal handler: {:?}", err));
}
pub fn exec_shell(command: &str, children: &mut Children) -> Option<ChildID> {
exec_shell_with_args(command, Vec::new(), children)
}
pub fn exec_shell_with_args(
command: &str,
args: Vec<String>,
children: &mut Children,
) -> Option<ChildID> {
let child = Command::new(command)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.ok()?;
let pid = child.id();
children.insert(child);
Some(pid)
}