meli: store children process metadata

Store children process metadata. Pid and command lines can then be shown
in the UI and in logs.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/372/head
Manos Pitsidianakis 2 months ago
parent 35a9f33aab
commit 3e9144657b
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

@ -53,7 +53,10 @@ use crate::command::actions::AccountAction;
use crate::{ use crate::{
conf::{AccountConf, FileMailboxConf}, conf::{AccountConf, FileMailboxConf},
jobs::{JobId, JoinHandle}, jobs::{JobId, JoinHandle},
types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification}, types::{
ForkType,
UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification},
},
MainLoopHandler, StatusEvent, ThreadEvent, MainLoopHandler, StatusEvent, ThreadEvent,
}; };
@ -781,9 +784,11 @@ impl Account {
StatusEvent::DisplayMessage(format!("Running command {}", refresh_command)), StatusEvent::DisplayMessage(format!("Running command {}", refresh_command)),
))); )));
self.main_loop_handler self.main_loop_handler
.send(ThreadEvent::UIEvent(UIEvent::Fork( .send(ThreadEvent::UIEvent(UIEvent::Fork(ForkType::Generic {
crate::ForkType::Generic(child), id: refresh_command.to_string().into(),
))); command: Some(refresh_command.clone().into()),
child,
})));
return Ok(()); return Ok(());
} }
let refresh_job = self.backend.write().unwrap().refresh(mailbox_hash); let refresh_job = self.backend.write().unwrap().refresh(mailbox_hash);

@ -1876,10 +1876,11 @@ impl Component for Composer {
}; };
if *account_settings!(context[self.account_hash].composing.embedded_pty) { if *account_settings!(context[self.account_hash].composing.embedded_pty) {
let command = [editor, f.path().display().to_string()].join(" ");
match crate::terminal::embedded::create_pty( match crate::terminal::embedded::create_pty(
self.embedded_dimensions.0, self.embedded_dimensions.0,
self.embedded_dimensions.1, self.embedded_dimensions.1,
[editor, f.path().display().to_string()].join(" "), &command,
) { ) {
Ok(terminal) => { Ok(terminal) => {
self.embedded_pty = Some(EmbeddedPty { self.embedded_pty = Some(EmbeddedPty {
@ -1891,14 +1892,17 @@ impl Component for Composer {
context context
.replies .replies
.push_back(UIEvent::ChangeMode(UIMode::Embedded)); .push_back(UIEvent::ChangeMode(UIMode::Embedded));
context.replies.push_back(UIEvent::Fork(ForkType::Embedded( context.replies.push_back(UIEvent::Fork(ForkType::Embedded {
self.embedded_pty id: "editor".into(),
command: Some(command.into()),
pid: self
.embedded_pty
.as_ref() .as_ref()
.unwrap() .unwrap()
.lock() .lock()
.unwrap() .unwrap()
.child_pid, .child_pid,
))); }));
self.mode = ViewMode::EmbeddedPty; self.mode = ViewMode::EmbeddedPty;
} }
Err(err) => { Err(err) => {

@ -726,12 +726,16 @@ impl Component for MailView {
.spawn() .spawn()
{ {
Ok(child) => { Ok(child) => {
context.children.push(child); context
.children
.entry(url_launcher.to_string().into())
.or_default()
.push(child);
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!( StatusEvent::DisplayMessage(format!(
"Couldn't launch {:?}: {}", "Couldn't launch {}: {}",
url_launcher, err url_launcher, err
)), )),
)); ));
@ -766,11 +770,15 @@ impl Component for MailView {
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
{ {
Ok(child) => context.children.push(child), Ok(child) => context
.children
.entry(url_launcher.to_string().into())
.or_default()
.push(child),
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!( StatusEvent::DisplayMessage(format!(
"Couldn't launch {:?}: {}", "Couldn't launch {}: {}",
url_launcher, err url_launcher, err
)), )),
)); ));

@ -152,8 +152,8 @@ impl EnvelopeView {
format!("Failed to start html filter process: {}", filter_invocation,) format!("Failed to start html filter process: {}", filter_invocation,)
.into(), .into(),
), ),
source: None,
body: err.to_string().into(), body: err.to_string().into(),
source: Some(err.into()),
kind: Some(NotificationType::Error(melib::ErrorKind::External)), kind: Some(NotificationType::Error(melib::ErrorKind::External)),
})); }));
// [ref:FIXME]: add `v` configurable shortcut // [ref:FIXME]: add `v` configurable shortcut
@ -1294,15 +1294,15 @@ impl Component for EnvelopeView {
} }
match save_attachment(&path, &self.mail.bytes) { match save_attachment(&path, &self.mail.bytes) {
Err(err) => { Err(err) => {
log::error!("Failed to create file at {}: {err}", path.display());
context.replies.push_back(UIEvent::Notification { context.replies.push_back(UIEvent::Notification {
title: Some( title: Some(
format!("Failed to create file at {}", path.display()).into(), format!("Failed to create file at {}", path.display()).into(),
), ),
source: None,
body: err.to_string().into(), body: err.to_string().into(),
source: Some(err),
kind: Some(NotificationType::Error(melib::ErrorKind::External)), kind: Some(NotificationType::Error(melib::ErrorKind::External)),
}); });
log::error!("Failed to create file at {}: {err}", path.display());
return true; return true;
} }
Ok(()) => { Ok(()) => {
@ -1336,15 +1336,15 @@ impl Component for EnvelopeView {
} }
match save_attachment(&path, &u.decode(Default::default())) { match save_attachment(&path, &u.decode(Default::default())) {
Err(err) => { Err(err) => {
log::error!("Failed to create file at {}: {err}", path.display());
context.replies.push_back(UIEvent::Notification { context.replies.push_back(UIEvent::Notification {
title: Some( title: Some(
format!("Failed to create file at {}", path.display()).into(), format!("Failed to create file at {}", path.display()).into(),
), ),
source: None,
body: err.to_string().into(), body: err.to_string().into(),
source: Some(err),
kind: Some(NotificationType::Error(melib::ErrorKind::External)), kind: Some(NotificationType::Error(melib::ErrorKind::External)),
}); });
log::error!("Failed to create file at {}: {err}", path.display());
} }
Ok(()) => { Ok(()) => {
context.replies.push_back(UIEvent::Notification { context.replies.push_back(UIEvent::Notification {
@ -1365,15 +1365,15 @@ impl Component for EnvelopeView {
} }
match save_attachment(&path, &self.mail.bytes) { match save_attachment(&path, &self.mail.bytes) {
Err(err) => { Err(err) => {
log::error!("Failed to create file at {}: {err}", path.display());
context.replies.push_back(UIEvent::Notification { context.replies.push_back(UIEvent::Notification {
title: Some( title: Some(
format!("Failed to create file at {}", path.display()).into(), format!("Failed to create file at {}", path.display()).into(),
), ),
source: None,
body: err.to_string().into(), body: err.to_string().into(),
source: Some(err),
kind: Some(NotificationType::Error(melib::ErrorKind::External)), kind: Some(NotificationType::Error(melib::ErrorKind::External)),
}); });
log::error!("Failed to create file at {}: {err}", path.display());
return true; return true;
} }
Ok(()) => { Ok(()) => {
@ -1466,7 +1466,11 @@ impl Component for EnvelopeView {
match res { match res {
Ok((p, child)) => { Ok((p, child)) => {
context.temp_files.push(p); context.temp_files.push(p);
context.children.push(child); context
.children
.entry(command.into())
.or_default()
.push(child);
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
@ -1560,13 +1564,17 @@ impl Component for EnvelopeView {
.spawn() .spawn()
{ {
Ok(child) => { Ok(child) => {
context.children.push(child); context
.children
.entry(url_launcher.to_string().into())
.or_default()
.push(child);
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification { context.replies.push_back(UIEvent::Notification {
title: Some(format!("Failed to launch {:?}", url_launcher).into()), title: Some(format!("Failed to launch {:?}", url_launcher).into()),
source: None,
body: err.to_string().into(), body: err.to_string().into(),
source: Some(err.into()),
kind: Some(NotificationType::Error(melib::ErrorKind::External)), kind: Some(NotificationType::Error(melib::ErrorKind::External)),
}); });
} }

@ -343,11 +343,15 @@ impl ViewFilter {
}); });
match res { match res {
Ok((p, child)) => { Ok((p, child)) => {
context context.replies.push_back(UIEvent::StatusEvent(
.replies StatusEvent::UpdateSubStatus(command.clone()),
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateSubStatus(command))); ));
context.temp_files.push(p); context.temp_files.push(p);
context.children.push(child); context
.children
.entry(command.into())
.or_default()
.push(child);
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(

@ -231,7 +231,11 @@ impl Component for NotificationCommand {
.spawn() .spawn()
{ {
Ok(child) => { Ok(child) => {
context.children.push(child); context
.children
.entry(stringify!(NotificationCommand).into())
.or_default()
.push(child);
} }
Err(err) => { Err(err) => {
log::error!("Could not run notification script: {err}."); log::error!("Could not run notification script: {err}.");

@ -35,6 +35,7 @@
//! [`ThreadEvent`]. //! [`ThreadEvent`].
use std::{ use std::{
borrow::Cow,
collections::BTreeSet, collections::BTreeSet,
env, env,
os::fd::{AsRawFd, FromRawFd, OwnedFd}, os::fd::{AsRawFd, FromRawFd, OwnedFd},
@ -143,8 +144,8 @@ pub struct Context {
receiver: Receiver<ThreadEvent>, receiver: Receiver<ThreadEvent>,
input_thread: InputHandler, input_thread: InputHandler,
current_dir: PathBuf, current_dir: PathBuf,
pub children: Vec<std::process::Child>, /// Children processes
pub children: IndexMap<Cow<'static, str>, Vec<std::process::Child>>,
pub temp_files: Vec<File>, pub temp_files: Vec<File>,
} }
@ -250,7 +251,7 @@ impl Context {
unrealized: IndexSet::default(), unrealized: IndexSet::default(),
temp_files: Vec::new(), temp_files: Vec::new(),
current_dir: std::env::current_dir().unwrap(), current_dir: std::env::current_dir().unwrap(),
children: vec![], children: IndexMap::default(),
input_thread: InputHandler { input_thread: InputHandler {
pipe: input_thread_pipe, pipe: input_thread_pipe,
@ -293,18 +294,38 @@ impl Drop for State {
// When done, restore the defaults to avoid messing with the terminal. // When done, restore the defaults to avoid messing with the terminal.
self.screen.switch_to_main_screen(); self.screen.switch_to_main_screen();
use nix::sys::wait::{waitpid, WaitPidFlag}; use nix::sys::wait::{waitpid, WaitPidFlag};
for child in self.context.children.iter_mut() { for (id, pid, err) in self
if let Err(err) = waitpid( .context
nix::unistd::Pid::from_raw(child.id() as i32), .children
Some(WaitPidFlag::WNOHANG), .iter_mut()
) { .flat_map(|(i, v)| v.drain(..).map(move |v| (i, v)))
log::warn!("Failed to wait on subprocess {}: {}", child.id(), err); .filter_map(|(id, child)| {
} if let Err(err) = waitpid(
nix::unistd::Pid::from_raw(child.id() as i32),
Some(WaitPidFlag::WNOHANG),
) {
Some((id, child.id(), err))
} else {
None
}
})
{
log::trace!("Failed to wait on subprocess {} ({}): {}", id, pid, err);
} }
if let Some(ForkType::Embedded(child_pid)) = self.child.take() { if let Some(ForkType::Embedded { id, command, pid }) = self.child.take() {
/* Try wait, we don't want to block */ /* Try wait, we don't want to block */
if let Err(e) = waitpid(child_pid, Some(WaitPidFlag::WNOHANG)) { if let Err(err) = waitpid(pid, Some(WaitPidFlag::WNOHANG)) {
log::warn!("Failed to wait on subprocess {}: {}", child_pid, e); log::trace!(
"Failed to wait on embedded process {} {} ({}): {}",
id,
if let Some(v) = command.as_ref() {
v.as_ref()
} else {
""
},
pid,
err
);
} }
} }
} }
@ -428,7 +449,7 @@ impl State {
unrealized: IndexSet::default(), unrealized: IndexSet::default(),
temp_files: Vec::new(), temp_files: Vec::new(),
current_dir: std::env::current_dir()?, current_dir: std::env::current_dir()?,
children: vec![], children: IndexMap::default(),
input_thread: InputHandler { input_thread: InputHandler {
pipe: input_thread_pipe, pipe: input_thread_pipe,
@ -1005,8 +1026,12 @@ impl State {
self.context.restore_input(); self.context.restore_input();
return; return;
} }
UIEvent::Fork(ForkType::Generic(child)) => { UIEvent::Fork(ForkType::Generic {
self.context.children.push(child); id,
command: _,
child,
}) => {
self.context.children.entry(id).or_default().push(child);
return; return;
} }
UIEvent::Fork(child) => { UIEvent::Fork(child) => {
@ -1194,24 +1219,27 @@ impl State {
pub fn try_wait_on_child(&mut self) -> Option<bool> { pub fn try_wait_on_child(&mut self) -> Option<bool> {
let should_return_flag = match self.child { let should_return_flag = match self.child {
Some(ForkType::NewDraft(_, ref mut c)) => { Some(ForkType::Generic {
let w = c.try_wait(); ref id,
ref command,
ref mut child,
}) => {
let w = child.try_wait();
match w { match w {
Ok(Some(_)) => true, Ok(Some(_)) => true,
Ok(None) => false, Ok(None) => false,
Err(err) => { Err(err) => {
log::error!("Failed to wait on editor process: {err}"); log::error!(
return None; "Failed to wait on child process {} {} ({}): {}",
} id,
} if let Some(v) = command.as_ref() {
} v.as_ref()
Some(ForkType::Generic(ref mut c)) => { } else {
let w = c.try_wait(); ""
match w { },
Ok(Some(_)) => true, child.id(),
Ok(None) => false, err
Err(err) => { );
log::error!("Failed to wait on child process: {err}");
return None; return None;
} }
} }

@ -78,7 +78,7 @@ ioctl_none_bad!(
/// Create a new pseudoterminal (PTY) with given width, size and execute /// Create a new pseudoterminal (PTY) with given width, size and execute
/// `command` in it. /// `command` in it.
pub fn create_pty(width: usize, height: usize, command: String) -> Result<Arc<Mutex<Terminal>>> { pub fn create_pty(width: usize, height: usize, command: &str) -> Result<Arc<Mutex<Terminal>>> {
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
let (frontend_fd, backend_name): (nix::pty::PtyMaster, String) = { let (frontend_fd, backend_name): (nix::pty::PtyMaster, String) = {
// Open a new PTY frontend // Open a new PTY frontend

@ -100,9 +100,16 @@ pub enum ForkType {
/// Already finished fork, we only want to restore input/output /// Already finished fork, we only want to restore input/output
Finished, Finished,
/// Embedded pty /// Embedded pty
Embedded(Pid), Embedded {
Generic(std::process::Child), id: Cow<'static, str>,
NewDraft(File, std::process::Child), command: Option<Cow<'static, str>>,
pid: Pid,
},
Generic {
id: Cow<'static, str>,
command: Option<Cow<'static, str>>,
child: std::process::Child,
},
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@ -140,8 +147,8 @@ pub enum UIEvent {
Command(String), Command(String),
Notification { Notification {
title: Option<Cow<'static, str>>, title: Option<Cow<'static, str>>,
source: Option<Error>,
body: Cow<'static, str>, body: Cow<'static, str>,
source: Option<Error>,
kind: Option<NotificationType>, kind: Option<NotificationType>,
}, },
Action(Action), Action(Action),

@ -167,7 +167,7 @@ impl Component for EmbeddedContainer {
} else { } else {
let theme_default = crate::conf::value(context, "theme_default"); let theme_default = crate::conf::value(context, "theme_default");
grid.clear_area(area, theme_default); grid.clear_area(area, theme_default);
match create_pty(area.width(), area.height(), self.command.clone()) { match create_pty(area.width(), area.height(), &self.command) {
Ok(embedded_pty) => { Ok(embedded_pty) => {
//embedded_pty.lock().unwrap().set_log_file(self.log_file.take()); //embedded_pty.lock().unwrap().set_log_file(self.log_file.take());
self.embedded_pty = Some(EmbeddedPty::Running(embedded_pty)); self.embedded_pty = Some(EmbeddedPty::Running(embedded_pty));
@ -187,8 +187,8 @@ impl Component for EmbeddedContainer {
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification { context.replies.push_back(UIEvent::Notification {
title: Some("Failed to create pseudoterminal".into()), title: Some("Failed to create pseudoterminal".into()),
source: None,
body: err.to_string().into(), body: err.to_string().into(),
source: Some(err),
kind: Some(NotificationType::Error(melib::error::ErrorKind::External)), kind: Some(NotificationType::Error(melib::error::ErrorKind::External)),
}); });
} }

Loading…
Cancel
Save