mirror of
https://github.com/Noratrieb/service-manager.git
synced 2026-01-14 16:35:05 +01:00
improve everything
This commit is contained in:
parent
d69c1be8c8
commit
2910482b03
5 changed files with 98 additions and 47 deletions
12
config.toml
12
config.toml
|
|
@ -10,7 +10,7 @@ env = { HELLO = "uwu hi ヾ(•ω•`)o" }
|
|||
|
||||
[pwd]
|
||||
command = "pwd"
|
||||
workdir = "./src"
|
||||
workdir = "./src/controller"
|
||||
|
||||
[crash]
|
||||
command = "cat fkasdjfölashjdflksd"
|
||||
|
|
@ -32,4 +32,14 @@ while true; do
|
|||
echo $counter
|
||||
counter=$((counter+1))
|
||||
done
|
||||
"""
|
||||
|
||||
[many-lines]
|
||||
command = """
|
||||
counter=0
|
||||
|
||||
while [ $counter -lt 100 ]; do
|
||||
echo $counter
|
||||
counter=$((counter+1))
|
||||
done
|
||||
"""
|
||||
|
|
@ -1,21 +1,25 @@
|
|||
use crate::controller::{StdioSendBuf, STDIO_SEND_BUF_SIZE};
|
||||
use crate::model::{ServiceStatus, SmError, SmResult};
|
||||
use std::io::{Read, Write};
|
||||
use std::process::{Child, ChildStderr};
|
||||
use std::process::{Child, ChildStderr, ChildStdout};
|
||||
use std::sync::mpsc::TryRecvError;
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::{io, thread};
|
||||
use tracing::error;
|
||||
use tracing::{error, info};
|
||||
|
||||
enum ChildAction {
|
||||
Ignore,
|
||||
Kill,
|
||||
}
|
||||
|
||||
pub fn child_process_thread(
|
||||
child: Child,
|
||||
stdout_send: mpsc::Sender<StdioSendBuf>,
|
||||
mut child: Child,
|
||||
mut stdout_send: mpsc::Sender<StdioSendBuf>,
|
||||
service_status: Arc<Mutex<ServiceStatus>>,
|
||||
service_name: String,
|
||||
terminate_channel: mpsc::Receiver<()>,
|
||||
) -> SmResult {
|
||||
let mut child = child;
|
||||
let mut stdout = child
|
||||
let stdout = child
|
||||
.stdout
|
||||
.take()
|
||||
.ok_or(SmError::Bug("Stdout of child could not be taken"))?;
|
||||
|
|
@ -37,12 +41,52 @@ pub fn child_process_thread(
|
|||
error!(error = %err, "Failed to spawn stderr thread");
|
||||
}
|
||||
|
||||
let result = loop {
|
||||
info!(name = %service_name, "Entering main processing loop");
|
||||
let (action, result) = child_process_main_thread(
|
||||
&mut stdout_send,
|
||||
terminate_channel,
|
||||
stderr_terminate_send,
|
||||
stdout,
|
||||
&mut child,
|
||||
service_status.clone(),
|
||||
);
|
||||
|
||||
if let ChildAction::Kill = action {
|
||||
match child.kill() {
|
||||
Ok(()) => {
|
||||
*service_status.lock().map_err(|_| SmError::MutexPoisoned)? = ServiceStatus::Killed
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::InvalidInput => {}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
|
||||
let mut send_message_buf = [0; STDIO_SEND_BUF_SIZE];
|
||||
let kill_msg = "\n\n<Process was killed>\n";
|
||||
send_message_buf
|
||||
.as_mut_slice()
|
||||
.write_all(kill_msg.as_bytes())?;
|
||||
stdout_send
|
||||
.send((send_message_buf, kill_msg.len()))
|
||||
.map_err(|_| SmError::Bug("Failed to send stdout to main thread"))?;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn child_process_main_thread(
|
||||
stdout_send: &mut mpsc::Sender<StdioSendBuf>,
|
||||
terminate_channel: mpsc::Receiver<()>,
|
||||
stderr_terminate_send: mpsc::Sender<()>,
|
||||
mut stdout: ChildStdout,
|
||||
child: &mut Child,
|
||||
service_status: Arc<Mutex<ServiceStatus>>,
|
||||
) -> (ChildAction, SmResult) {
|
||||
loop {
|
||||
match terminate_channel.try_recv() {
|
||||
Ok(_) | Err(TryRecvError::Disconnected) => {
|
||||
// terminating the thread is a best-effort, it doesn't matter if it died
|
||||
let _ = stderr_terminate_send.send(());
|
||||
break Ok(());
|
||||
return (ChildAction::Kill, Ok(()));
|
||||
}
|
||||
Err(TryRecvError::Empty) => {}
|
||||
}
|
||||
|
|
@ -50,19 +94,27 @@ pub fn child_process_thread(
|
|||
let mut stdout_buf = [0; STDIO_SEND_BUF_SIZE];
|
||||
match stdout.read(&mut stdout_buf) {
|
||||
Ok(0) => {}
|
||||
Ok(n) => {
|
||||
stdout_send
|
||||
.send((stdout_buf, n))
|
||||
.map_err(|_| SmError::Bug("Failed to send stdout to main thread"))?;
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||
Err(e) => break Err(e.into()),
|
||||
Ok(n) => match stdout_send.send((stdout_buf, n)) {
|
||||
Ok(()) => {}
|
||||
Err(_) => {
|
||||
return (
|
||||
ChildAction::Kill,
|
||||
Err(SmError::Bug("Failed to send stdout to main thread")),
|
||||
)
|
||||
}
|
||||
},
|
||||
Err(err) if err.kind() == io::ErrorKind::Interrupted => {}
|
||||
// todo: maybe we are overreacting here?
|
||||
Err(err) => return (ChildAction::Kill, Err(err.into())),
|
||||
};
|
||||
|
||||
match child.try_wait() {
|
||||
Ok(None) => {}
|
||||
Ok(Some(status)) => {
|
||||
let mut status_lock = service_status.lock().map_err(|_| SmError::MutexPoisoned)?;
|
||||
let mut status_lock = match service_status.lock() {
|
||||
Ok(lock) => lock,
|
||||
Err(_) => return (ChildAction::Kill, Err(SmError::MutexPoisoned)),
|
||||
};
|
||||
|
||||
*status_lock = match status.code() {
|
||||
Some(0) => ServiceStatus::Exited,
|
||||
|
|
@ -70,30 +122,11 @@ pub fn child_process_thread(
|
|||
None => ServiceStatus::Killed,
|
||||
};
|
||||
|
||||
return Ok(());
|
||||
return (ChildAction::Ignore, Ok(()));
|
||||
}
|
||||
Err(e) => break Err(e.into()),
|
||||
Err(err) => return (ChildAction::Kill, Err(err.into())),
|
||||
}
|
||||
};
|
||||
|
||||
match child.kill() {
|
||||
Ok(()) => {
|
||||
*service_status.lock().map_err(|_| SmError::MutexPoisoned)? = ServiceStatus::Killed
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::InvalidInput => {}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
|
||||
let mut send_message_buf = [0; STDIO_SEND_BUF_SIZE];
|
||||
let kill_msg = "\n\n<Process was killed>\n";
|
||||
send_message_buf
|
||||
.as_mut_slice()
|
||||
.write_all(kill_msg.as_bytes())?;
|
||||
stdout_send
|
||||
.send((send_message_buf, kill_msg.len()))
|
||||
.map_err(|_| SmError::Bug("Failed to send stdout to main thread"))?;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn child_process_stderr_thread(
|
||||
|
|
@ -101,6 +134,8 @@ fn child_process_stderr_thread(
|
|||
terminate_channel: mpsc::Receiver<()>,
|
||||
mut stderr: ChildStderr,
|
||||
) {
|
||||
info!("Entering main processing loop for stderr thread");
|
||||
|
||||
loop {
|
||||
match terminate_channel.try_recv() {
|
||||
Ok(_) | Err(TryRecvError::Disconnected) => return,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crossterm::event;
|
|||
use crossterm::event::{Event, KeyCode};
|
||||
use std::collections::HashMap;
|
||||
use std::io::{ErrorKind, Write};
|
||||
use std::process::{ChildStderr, Command, Stdio};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
|
|
@ -34,7 +34,9 @@ pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> SmResult
|
|||
match key.code {
|
||||
KeyCode::Char('q') => match app.selected {
|
||||
Some(_) => app.leave_service(),
|
||||
None => break,
|
||||
None => {
|
||||
break;
|
||||
}
|
||||
},
|
||||
KeyCode::Char('r') => app.run_service()?,
|
||||
KeyCode::Char('k') => app.kill_service()?,
|
||||
|
|
@ -49,7 +51,9 @@ pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> SmResult
|
|||
}
|
||||
|
||||
// terminate the child processes
|
||||
for sender in app.thread_terminates.values() {
|
||||
for (i, sender) in app.thread_terminates.values().enumerate() {
|
||||
info!(index = i, "Terminating child thread...");
|
||||
|
||||
let _ = sender.send(());
|
||||
}
|
||||
|
||||
|
|
@ -151,6 +155,7 @@ impl App {
|
|||
if let Some(index) = index {
|
||||
let status = {
|
||||
let service = &mut self.table.services[index];
|
||||
service.std_io_buf.clear();
|
||||
*service.status.lock()?
|
||||
};
|
||||
|
||||
|
|
@ -166,12 +171,13 @@ impl App {
|
|||
let index = self.selected.or_else(|| self.table.table_state.selected());
|
||||
|
||||
if let Some(index) = index {
|
||||
let status = {
|
||||
let service = &mut self.table.services[index];
|
||||
*service.status.lock()?
|
||||
};
|
||||
let service = &mut self.table.services[index];
|
||||
|
||||
let status = { *service.status.lock()? };
|
||||
|
||||
if status == ServiceStatus::Running {
|
||||
info!(name = %service.name,"Killing service");
|
||||
|
||||
let terminate_sender = &mut self
|
||||
.thread_terminates
|
||||
.get(&index)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ mod error {
|
|||
MutexPoisoned,
|
||||
FailedToSendStdio,
|
||||
/// This should never happen and would be a panic in most programs, but panicking here
|
||||
/// might fuck things up badly, so we don't want to
|
||||
/// is not a good idea
|
||||
Bug(&'static str),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ fn render_full_view<B: Backend>(f: &mut Frame<B>, state: &mut AppState, index: u
|
|||
fn render_table<B: Backend>(f: &mut Frame<B>, state: &mut AppState, area: Rect) {
|
||||
let selected_style = Style::default().add_modifier(Modifier::REVERSED);
|
||||
let normal_style = Style::default().bg(Color::Blue);
|
||||
let header_cells = ["name", "status"]
|
||||
let header_cells = ["Name", "Status"]
|
||||
.iter()
|
||||
.map(|h| Cell::from(*h).style(Style::default()));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue