Make script-vars use process groups (#40)
* Make script-vars use process groups Tail script-vars now make use of process groups to fix issues with non-finished processes staying around. I don't really understand any of this.
This commit is contained in:
parent
4573f056b0
commit
a0929c0a84
5 changed files with 79 additions and 40 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -303,6 +303,7 @@ dependencies = [
|
||||||
"debug_stub_derive",
|
"debug_stub_derive",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"extend",
|
"extend",
|
||||||
|
"filedescriptor",
|
||||||
"gdk",
|
"gdk",
|
||||||
"gdk-pixbuf",
|
"gdk-pixbuf",
|
||||||
"gdkx11",
|
"gdkx11",
|
||||||
|
@ -345,6 +346,17 @@ dependencies = [
|
||||||
"syn 1.0.44",
|
"syn 1.0.44",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filedescriptor"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e70cb4dda2f343f3b7a98a6536559d04a700136cada190822e5d6a99e4184c06"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"libc",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetime"
|
name = "filetime"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
|
|
|
@ -44,6 +44,7 @@ ref-cast = "1.0"
|
||||||
popol = "0.3"
|
popol = "0.3"
|
||||||
nix = "0.19"
|
nix = "0.19"
|
||||||
smart-default = "0.6"
|
smart-default = "0.6"
|
||||||
|
filedescriptor = "0.7"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.6.1"
|
pretty_assertions = "0.6.1"
|
||||||
|
|
|
@ -103,6 +103,8 @@ impl App {
|
||||||
// remove and close existing window with the same name
|
// remove and close existing window with the same name
|
||||||
let _ = self.close_window(window_name);
|
let _ = self.close_window(window_name);
|
||||||
|
|
||||||
|
log::info!("Opening window {}", window_name);
|
||||||
|
|
||||||
let mut window_def = self
|
let mut window_def = self
|
||||||
.eww_config
|
.eww_config
|
||||||
.get_windows()
|
.get_windows()
|
||||||
|
@ -177,6 +179,7 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload_all_windows(&mut self, config: config::EwwConfig) -> Result<()> {
|
pub fn reload_all_windows(&mut self, config: config::EwwConfig) -> Result<()> {
|
||||||
|
log::info!("Reloading windows");
|
||||||
// refresh script-var poll stuff
|
// refresh script-var poll stuff
|
||||||
util::print_result_err(
|
util::print_result_err(
|
||||||
"while setting up script-var commands",
|
"while setting up script-var commands",
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -113,20 +113,21 @@ pub enum OptAction {
|
||||||
|
|
||||||
impl OptAction {
|
impl OptAction {
|
||||||
fn into_eww_command(self) -> (app::EwwCommand, Option<crossbeam_channel::Receiver<String>>) {
|
fn into_eww_command(self) -> (app::EwwCommand, Option<crossbeam_channel::Receiver<String>>) {
|
||||||
match self {
|
let command = match self {
|
||||||
OptAction::Update { fieldname, value } => (app::EwwCommand::UpdateVar(fieldname, value), None),
|
OptAction::Update { fieldname, value } => app::EwwCommand::UpdateVar(fieldname, value),
|
||||||
OptAction::OpenWindow { window_name, pos, size } => (app::EwwCommand::OpenWindow { window_name, pos, size }, None),
|
OptAction::OpenWindow { window_name, pos, size } => app::EwwCommand::OpenWindow { window_name, pos, size },
|
||||||
OptAction::CloseWindow { window_name } => (app::EwwCommand::CloseWindow { window_name }, None),
|
OptAction::CloseWindow { window_name } => app::EwwCommand::CloseWindow { window_name },
|
||||||
OptAction::KillServer => (app::EwwCommand::KillServer, None),
|
OptAction::KillServer => app::EwwCommand::KillServer,
|
||||||
OptAction::ShowState => {
|
OptAction::ShowState => {
|
||||||
let (send, recv) = crossbeam_channel::unbounded();
|
let (send, recv) = crossbeam_channel::unbounded();
|
||||||
(app::EwwCommand::PrintState(send), Some(recv))
|
return (app::EwwCommand::PrintState(send), Some(recv));
|
||||||
}
|
}
|
||||||
OptAction::ShowDebug => {
|
OptAction::ShowDebug => {
|
||||||
let (send, recv) = crossbeam_channel::unbounded();
|
let (send, recv) = crossbeam_channel::unbounded();
|
||||||
(app::EwwCommand::PrintDebug(send), Some(recv))
|
return (app::EwwCommand::PrintDebug(send), Some(recv));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
(command, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_server_command(&self) -> bool {
|
fn is_server_command(&self) -> bool {
|
||||||
|
@ -163,7 +164,7 @@ fn try_main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_server(opts: Opt) -> Result<()> {
|
fn initialize_server(opts: Opt) -> Result<()> {
|
||||||
if opts.action == OptAction::KillServer || !opts.action.is_server_command() {
|
if !opts.action.is_server_command() {
|
||||||
println!("No eww server running");
|
println!("No eww server running");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -182,6 +183,7 @@ fn initialize_server(opts: Opt) -> Result<()> {
|
||||||
gtk::init()?;
|
gtk::init()?;
|
||||||
let (evt_send, evt_recv) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
let (evt_send, evt_recv) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||||
|
|
||||||
|
log::info!("Initializing script var handler");
|
||||||
let mut script_var_handler = script_var_handler::ScriptVarHandler::new(evt_send.clone())?;
|
let mut script_var_handler = script_var_handler::ScriptVarHandler::new(evt_send.clone())?;
|
||||||
script_var_handler.initialize_clean(eww_config.get_script_vars().clone())?;
|
script_var_handler.initialize_clean(eww_config.get_script_vars().clone())?;
|
||||||
|
|
||||||
|
@ -203,6 +205,7 @@ fn initialize_server(opts: Opt) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the command that eww was started with
|
// run the command that eww was started with
|
||||||
|
log::info!("running command: {:?}", &opts.action);
|
||||||
let (command, maybe_response_recv) = opts.action.into_eww_command();
|
let (command, maybe_response_recv) = opts.action.into_eww_command();
|
||||||
app.handle_command(command);
|
app.handle_command(command);
|
||||||
if let Some(response_recv) = maybe_response_recv {
|
if let Some(response_recv) = maybe_response_recv {
|
||||||
|
@ -292,6 +295,8 @@ fn do_detach() -> Result<()> {
|
||||||
nix::unistd::ForkResult::Child => {}
|
nix::unistd::ForkResult::Child => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nix::unistd::setsid().context("Failed to run setsid")?;
|
||||||
|
|
||||||
let file = std::fs::OpenOptions::new()
|
let file = std::fs::OpenOptions::new()
|
||||||
.create(true)
|
.create(true)
|
||||||
.append(true)
|
.append(true)
|
||||||
|
@ -308,7 +313,6 @@ fn do_detach() -> Result<()> {
|
||||||
if nix::unistd::isatty(2)? {
|
if nix::unistd::isatty(2)? {
|
||||||
nix::unistd::dup2(std::io::stderr().as_raw_fd(), fd)?;
|
nix::unistd::dup2(std::io::stderr().as_raw_fd(), fd)?;
|
||||||
}
|
}
|
||||||
nix::unistd::close(fd)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
use std::{
|
use std::{collections::HashMap, ffi::CString, io::BufReader, time::Duration};
|
||||||
collections::HashMap,
|
|
||||||
io::BufReader,
|
|
||||||
process::{Child, Stdio},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{app, config, eww_state, util, value::PrimitiveValue};
|
use crate::{app, config, eww_state, util, value::PrimitiveValue};
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
|
@ -11,7 +6,7 @@ use app::EwwCommand;
|
||||||
use glib;
|
use glib;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use scheduled_executor;
|
use scheduled_executor;
|
||||||
use std::io::BufRead;
|
use std::{io::BufRead, os::unix::io::AsRawFd};
|
||||||
|
|
||||||
/// Handler that manages running and updating [ScriptVar]s
|
/// Handler that manages running and updating [ScriptVar]s
|
||||||
pub struct ScriptVarHandler {
|
pub struct ScriptVarHandler {
|
||||||
|
@ -54,6 +49,7 @@ impl ScriptVarHandler {
|
||||||
}
|
}
|
||||||
self.setup_poll_tasks(&poll_script_vars)?;
|
self.setup_poll_tasks(&poll_script_vars)?;
|
||||||
self.setup_tail_tasks(&tail_script_vars)?;
|
self.setup_tail_tasks(&tail_script_vars)?;
|
||||||
|
log::info!("Finished initializing script-var-handler");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +76,7 @@ impl ScriptVarHandler {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
log::info!("finished setting up poll tasks");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,13 +85,18 @@ impl ScriptVarHandler {
|
||||||
log::info!("initializing handler for tail script vars");
|
log::info!("initializing handler for tail script vars");
|
||||||
let mut sources = popol::Sources::with_capacity(tail_script_vars.len());
|
let mut sources = popol::Sources::with_capacity(tail_script_vars.len());
|
||||||
|
|
||||||
|
// TODO clean up this unnecessary vec, it really should not be needed.
|
||||||
|
// should be possibel to just keep a BufReader in TailVarProcess directly
|
||||||
let mut command_children = Vec::new();
|
let mut command_children = Vec::new();
|
||||||
let mut command_out_handles = HashMap::new();
|
let mut command_out_handles: HashMap<_, BufReader<filedescriptor::FileDescriptor>> = HashMap::new();
|
||||||
|
|
||||||
for var in tail_script_vars {
|
for var in tail_script_vars {
|
||||||
if let Some(mut child) = try_run_command(&var.command) {
|
match TailVarProcess::run(&var.command) {
|
||||||
command_out_handles.insert(var.name.clone(), BufReader::new(child.stdout.take().unwrap()));
|
Ok(process) => {
|
||||||
command_children.push(child);
|
command_out_handles.insert(var.name.clone(), BufReader::new(process.out_fd.try_clone()?));
|
||||||
|
command_children.push(process);
|
||||||
|
}
|
||||||
|
Err(err) => eprintln!("Failed to launch script-var command for tail: {:?}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,9 +132,7 @@ impl ScriptVarHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop child processes after exit
|
// stop child processes after exit
|
||||||
for mut child in command_children {
|
command_children.drain(..).for_each(|process| process.kill());
|
||||||
let _ = child.kill();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
self.tail_handler_thread = Some(thread_handle);
|
self.tail_handler_thread = Some(thread_handle);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -145,23 +145,42 @@ impl Drop for ScriptVarHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a command in sh, returning its stdout-handle wrapped in a
|
#[derive(Debug)]
|
||||||
/// [`BufReader`]. If running the command fails, will print a warning
|
struct TailVarProcess {
|
||||||
/// and return `None`.
|
pid: nix::unistd::Pid,
|
||||||
fn try_run_command(command: &str) -> Option<Child> {
|
out_fd: filedescriptor::FileDescriptor,
|
||||||
let result = std::process::Command::new("sh")
|
}
|
||||||
.arg("-c")
|
|
||||||
.arg(command)
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.stdin(Stdio::null())
|
|
||||||
.spawn();
|
|
||||||
|
|
||||||
match result {
|
impl TailVarProcess {
|
||||||
Ok(handle) => Some(handle),
|
pub fn run(command: &str) -> Result<Self> {
|
||||||
Err(err) => {
|
use nix::unistd::*;
|
||||||
eprintln!("WARN: Error running command from script-variable: {:?}", err);
|
|
||||||
None
|
let pipe = filedescriptor::Pipe::new()?;
|
||||||
|
|
||||||
|
match unsafe { fork()? } {
|
||||||
|
ForkResult::Child => {
|
||||||
|
std::mem::drop(pipe.read);
|
||||||
|
dup2(pipe.write.as_raw_fd(), std::io::stdout().as_raw_fd())?;
|
||||||
|
setpgid(Pid::from_raw(0), Pid::from_raw(0))?;
|
||||||
|
execv(
|
||||||
|
CString::new("/bin/sh")?.as_ref(),
|
||||||
|
&[CString::new("/bin/sh")?, CString::new("-c")?, CString::new(command)?],
|
||||||
|
)?;
|
||||||
|
unreachable!("Child fork called exec, thus the process was replaced by the command the user provided");
|
||||||
|
}
|
||||||
|
ForkResult::Parent { child, .. } => {
|
||||||
|
std::mem::drop(pipe.write);
|
||||||
|
setpgid(child, child)?;
|
||||||
|
Ok(TailVarProcess {
|
||||||
|
pid: child,
|
||||||
|
out_fd: pipe.read,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn kill(self) {
|
||||||
|
let result = nix::sys::signal::kill(self.pid, Some(nix::sys::signal::SIGTERM));
|
||||||
|
util::print_result_err("Killing tail-var process", &result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue