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
parent
35a9f33aab
commit
3e9144657b
|
@ -53,7 +53,10 @@ use crate::command::actions::AccountAction;
|
|||
use crate::{
|
||||
conf::{AccountConf, FileMailboxConf},
|
||||
jobs::{JobId, JoinHandle},
|
||||
types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification},
|
||||
types::{
|
||||
ForkType,
|
||||
UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification},
|
||||
},
|
||||
MainLoopHandler, StatusEvent, ThreadEvent,
|
||||
};
|
||||
|
||||
|
@ -781,9 +784,11 @@ impl Account {
|
|||
StatusEvent::DisplayMessage(format!("Running command {}", refresh_command)),
|
||||
)));
|
||||
self.main_loop_handler
|
||||
.send(ThreadEvent::UIEvent(UIEvent::Fork(
|
||||
crate::ForkType::Generic(child),
|
||||
)));
|
||||
.send(ThreadEvent::UIEvent(UIEvent::Fork(ForkType::Generic {
|
||||
id: refresh_command.to_string().into(),
|
||||
command: Some(refresh_command.clone().into()),
|
||||
child,
|
||||
})));
|
||||
return Ok(());
|
||||
}
|
||||
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) {
|
||||
let command = [editor, f.path().display().to_string()].join(" ");
|
||||
match crate::terminal::embedded::create_pty(
|
||||
self.embedded_dimensions.0,
|
||||
self.embedded_dimensions.1,
|
||||
[editor, f.path().display().to_string()].join(" "),
|
||||
&command,
|
||||
) {
|
||||
Ok(terminal) => {
|
||||
self.embedded_pty = Some(EmbeddedPty {
|
||||
|
@ -1891,14 +1892,17 @@ impl Component for Composer {
|
|||
context
|
||||
.replies
|
||||
.push_back(UIEvent::ChangeMode(UIMode::Embedded));
|
||||
context.replies.push_back(UIEvent::Fork(ForkType::Embedded(
|
||||
self.embedded_pty
|
||||
context.replies.push_back(UIEvent::Fork(ForkType::Embedded {
|
||||
id: "editor".into(),
|
||||
command: Some(command.into()),
|
||||
pid: self
|
||||
.embedded_pty
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.child_pid,
|
||||
)));
|
||||
}));
|
||||
self.mode = ViewMode::EmbeddedPty;
|
||||
}
|
||||
Err(err) => {
|
||||
|
|
|
@ -726,12 +726,16 @@ impl Component for MailView {
|
|||
.spawn()
|
||||
{
|
||||
Ok(child) => {
|
||||
context.children.push(child);
|
||||
context
|
||||
.children
|
||||
.entry(url_launcher.to_string().into())
|
||||
.or_default()
|
||||
.push(child);
|
||||
}
|
||||
Err(err) => {
|
||||
context.replies.push_back(UIEvent::StatusEvent(
|
||||
StatusEvent::DisplayMessage(format!(
|
||||
"Couldn't launch {:?}: {}",
|
||||
"Couldn't launch {}: {}",
|
||||
url_launcher, err
|
||||
)),
|
||||
));
|
||||
|
@ -766,11 +770,15 @@ impl Component for MailView {
|
|||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
{
|
||||
Ok(child) => context.children.push(child),
|
||||
Ok(child) => context
|
||||
.children
|
||||
.entry(url_launcher.to_string().into())
|
||||
.or_default()
|
||||
.push(child),
|
||||
Err(err) => {
|
||||
context.replies.push_back(UIEvent::StatusEvent(
|
||||
StatusEvent::DisplayMessage(format!(
|
||||
"Couldn't launch {:?}: {}",
|
||||
"Couldn't launch {}: {}",
|
||||
url_launcher, err
|
||||
)),
|
||||
));
|
||||
|
|
|
@ -152,8 +152,8 @@ impl EnvelopeView {
|
|||
format!("Failed to start html filter process: {}", filter_invocation,)
|
||||
.into(),
|
||||
),
|
||||
source: None,
|
||||
body: err.to_string().into(),
|
||||
source: Some(err.into()),
|
||||
kind: Some(NotificationType::Error(melib::ErrorKind::External)),
|
||||
}));
|
||||
// [ref:FIXME]: add `v` configurable shortcut
|
||||
|
@ -1294,15 +1294,15 @@ impl Component for EnvelopeView {
|
|||
}
|
||||
match save_attachment(&path, &self.mail.bytes) {
|
||||
Err(err) => {
|
||||
log::error!("Failed to create file at {}: {err}", path.display());
|
||||
context.replies.push_back(UIEvent::Notification {
|
||||
title: Some(
|
||||
format!("Failed to create file at {}", path.display()).into(),
|
||||
),
|
||||
source: None,
|
||||
body: err.to_string().into(),
|
||||
source: Some(err),
|
||||
kind: Some(NotificationType::Error(melib::ErrorKind::External)),
|
||||
});
|
||||
log::error!("Failed to create file at {}: {err}", path.display());
|
||||
return true;
|
||||
}
|
||||
Ok(()) => {
|
||||
|
@ -1336,15 +1336,15 @@ impl Component for EnvelopeView {
|
|||
}
|
||||
match save_attachment(&path, &u.decode(Default::default())) {
|
||||
Err(err) => {
|
||||
log::error!("Failed to create file at {}: {err}", path.display());
|
||||
context.replies.push_back(UIEvent::Notification {
|
||||
title: Some(
|
||||
format!("Failed to create file at {}", path.display()).into(),
|
||||
),
|
||||
source: None,
|
||||
body: err.to_string().into(),
|
||||
source: Some(err),
|
||||
kind: Some(NotificationType::Error(melib::ErrorKind::External)),
|
||||
});
|
||||
log::error!("Failed to create file at {}: {err}", path.display());
|
||||
}
|
||||
Ok(()) => {
|
||||
context.replies.push_back(UIEvent::Notification {
|
||||
|
@ -1365,15 +1365,15 @@ impl Component for EnvelopeView {
|
|||
}
|
||||
match save_attachment(&path, &self.mail.bytes) {
|
||||
Err(err) => {
|
||||
log::error!("Failed to create file at {}: {err}", path.display());
|
||||
context.replies.push_back(UIEvent::Notification {
|
||||
title: Some(
|
||||
format!("Failed to create file at {}", path.display()).into(),
|
||||
),
|
||||
source: None,
|
||||
body: err.to_string().into(),
|
||||
source: Some(err),
|
||||
kind: Some(NotificationType::Error(melib::ErrorKind::External)),
|
||||
});
|
||||
log::error!("Failed to create file at {}: {err}", path.display());
|
||||
return true;
|
||||
}
|
||||
Ok(()) => {
|
||||
|
@ -1466,7 +1466,11 @@ impl Component for EnvelopeView {
|
|||
match res {
|
||||
Ok((p, child)) => {
|
||||
context.temp_files.push(p);
|
||||
context.children.push(child);
|
||||
context
|
||||
.children
|
||||
.entry(command.into())
|
||||
.or_default()
|
||||
.push(child);
|
||||
}
|
||||
Err(err) => {
|
||||
context.replies.push_back(UIEvent::StatusEvent(
|
||||
|
@ -1560,13 +1564,17 @@ impl Component for EnvelopeView {
|
|||
.spawn()
|
||||
{
|
||||
Ok(child) => {
|
||||
context.children.push(child);
|
||||
context
|
||||
.children
|
||||
.entry(url_launcher.to_string().into())
|
||||
.or_default()
|
||||
.push(child);
|
||||
}
|
||||
Err(err) => {
|
||||
context.replies.push_back(UIEvent::Notification {
|
||||
title: Some(format!("Failed to launch {:?}", url_launcher).into()),
|
||||
source: None,
|
||||
body: err.to_string().into(),
|
||||
source: Some(err.into()),
|
||||
kind: Some(NotificationType::Error(melib::ErrorKind::External)),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -343,11 +343,15 @@ impl ViewFilter {
|
|||
});
|
||||
match res {
|
||||
Ok((p, child)) => {
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateSubStatus(command)));
|
||||
context.replies.push_back(UIEvent::StatusEvent(
|
||||
StatusEvent::UpdateSubStatus(command.clone()),
|
||||
));
|
||||
context.temp_files.push(p);
|
||||
context.children.push(child);
|
||||
context
|
||||
.children
|
||||
.entry(command.into())
|
||||
.or_default()
|
||||
.push(child);
|
||||
}
|
||||
Err(err) => {
|
||||
context.replies.push_back(UIEvent::StatusEvent(
|
||||
|
|
|
@ -231,7 +231,11 @@ impl Component for NotificationCommand {
|
|||
.spawn()
|
||||
{
|
||||
Ok(child) => {
|
||||
context.children.push(child);
|
||||
context
|
||||
.children
|
||||
.entry(stringify!(NotificationCommand).into())
|
||||
.or_default()
|
||||
.push(child);
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Could not run notification script: {err}.");
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
//! [`ThreadEvent`].
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::BTreeSet,
|
||||
env,
|
||||
os::fd::{AsRawFd, FromRawFd, OwnedFd},
|
||||
|
@ -143,8 +144,8 @@ pub struct Context {
|
|||
receiver: Receiver<ThreadEvent>,
|
||||
input_thread: InputHandler,
|
||||
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>,
|
||||
}
|
||||
|
||||
|
@ -250,7 +251,7 @@ impl Context {
|
|||
unrealized: IndexSet::default(),
|
||||
temp_files: Vec::new(),
|
||||
current_dir: std::env::current_dir().unwrap(),
|
||||
children: vec![],
|
||||
children: IndexMap::default(),
|
||||
|
||||
input_thread: InputHandler {
|
||||
pipe: input_thread_pipe,
|
||||
|
@ -293,18 +294,38 @@ impl Drop for State {
|
|||
// When done, restore the defaults to avoid messing with the terminal.
|
||||
self.screen.switch_to_main_screen();
|
||||
use nix::sys::wait::{waitpid, WaitPidFlag};
|
||||
for child in self.context.children.iter_mut() {
|
||||
if let Err(err) = waitpid(
|
||||
nix::unistd::Pid::from_raw(child.id() as i32),
|
||||
Some(WaitPidFlag::WNOHANG),
|
||||
) {
|
||||
log::warn!("Failed to wait on subprocess {}: {}", child.id(), err);
|
||||
}
|
||||
for (id, pid, err) in self
|
||||
.context
|
||||
.children
|
||||
.iter_mut()
|
||||
.flat_map(|(i, v)| v.drain(..).map(move |v| (i, v)))
|
||||
.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 */
|
||||
if let Err(e) = waitpid(child_pid, Some(WaitPidFlag::WNOHANG)) {
|
||||
log::warn!("Failed to wait on subprocess {}: {}", child_pid, e);
|
||||
if let Err(err) = waitpid(pid, Some(WaitPidFlag::WNOHANG)) {
|
||||
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(),
|
||||
temp_files: Vec::new(),
|
||||
current_dir: std::env::current_dir()?,
|
||||
children: vec![],
|
||||
children: IndexMap::default(),
|
||||
|
||||
input_thread: InputHandler {
|
||||
pipe: input_thread_pipe,
|
||||
|
@ -1005,8 +1026,12 @@ impl State {
|
|||
self.context.restore_input();
|
||||
return;
|
||||
}
|
||||
UIEvent::Fork(ForkType::Generic(child)) => {
|
||||
self.context.children.push(child);
|
||||
UIEvent::Fork(ForkType::Generic {
|
||||
id,
|
||||
command: _,
|
||||
child,
|
||||
}) => {
|
||||
self.context.children.entry(id).or_default().push(child);
|
||||
return;
|
||||
}
|
||||
UIEvent::Fork(child) => {
|
||||
|
@ -1194,24 +1219,27 @@ impl State {
|
|||
|
||||
pub fn try_wait_on_child(&mut self) -> Option<bool> {
|
||||
let should_return_flag = match self.child {
|
||||
Some(ForkType::NewDraft(_, ref mut c)) => {
|
||||
let w = c.try_wait();
|
||||
Some(ForkType::Generic {
|
||||
ref id,
|
||||
ref command,
|
||||
ref mut child,
|
||||
}) => {
|
||||
let w = child.try_wait();
|
||||
match w {
|
||||
Ok(Some(_)) => true,
|
||||
Ok(None) => false,
|
||||
Err(err) => {
|
||||
log::error!("Failed to wait on editor process: {err}");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(ForkType::Generic(ref mut c)) => {
|
||||
let w = c.try_wait();
|
||||
match w {
|
||||
Ok(Some(_)) => true,
|
||||
Ok(None) => false,
|
||||
Err(err) => {
|
||||
log::error!("Failed to wait on child process: {err}");
|
||||
log::error!(
|
||||
"Failed to wait on child process {} {} ({}): {}",
|
||||
id,
|
||||
if let Some(v) = command.as_ref() {
|
||||
v.as_ref()
|
||||
} else {
|
||||
""
|
||||
},
|
||||
child.id(),
|
||||
err
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ ioctl_none_bad!(
|
|||
|
||||
/// Create a new pseudoterminal (PTY) with given width, size and execute
|
||||
/// `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"))]
|
||||
let (frontend_fd, backend_name): (nix::pty::PtyMaster, String) = {
|
||||
// Open a new PTY frontend
|
||||
|
|
|
@ -100,9 +100,16 @@ pub enum ForkType {
|
|||
/// Already finished fork, we only want to restore input/output
|
||||
Finished,
|
||||
/// Embedded pty
|
||||
Embedded(Pid),
|
||||
Generic(std::process::Child),
|
||||
NewDraft(File, std::process::Child),
|
||||
Embedded {
|
||||
id: Cow<'static, str>,
|
||||
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)]
|
||||
|
@ -140,8 +147,8 @@ pub enum UIEvent {
|
|||
Command(String),
|
||||
Notification {
|
||||
title: Option<Cow<'static, str>>,
|
||||
source: Option<Error>,
|
||||
body: Cow<'static, str>,
|
||||
source: Option<Error>,
|
||||
kind: Option<NotificationType>,
|
||||
},
|
||||
Action(Action),
|
||||
|
|
|
@ -167,7 +167,7 @@ impl Component for EmbeddedContainer {
|
|||
} else {
|
||||
let theme_default = crate::conf::value(context, "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) => {
|
||||
//embedded_pty.lock().unwrap().set_log_file(self.log_file.take());
|
||||
self.embedded_pty = Some(EmbeddedPty::Running(embedded_pty));
|
||||
|
@ -187,8 +187,8 @@ impl Component for EmbeddedContainer {
|
|||
Err(err) => {
|
||||
context.replies.push_back(UIEvent::Notification {
|
||||
title: Some("Failed to create pseudoterminal".into()),
|
||||
source: None,
|
||||
body: err.to_string().into(),
|
||||
source: Some(err),
|
||||
kind: Some(NotificationType::Error(melib::error::ErrorKind::External)),
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue