mirror of
https://github.com/Noratrieb/service-manager.git
synced 2026-01-17 01:45: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]
|
[pwd]
|
||||||
command = "pwd"
|
command = "pwd"
|
||||||
workdir = "./src"
|
workdir = "./src/controller"
|
||||||
|
|
||||||
[crash]
|
[crash]
|
||||||
command = "cat fkasdjfölashjdflksd"
|
command = "cat fkasdjfölashjdflksd"
|
||||||
|
|
@ -33,3 +33,13 @@ while true; do
|
||||||
counter=$((counter+1))
|
counter=$((counter+1))
|
||||||
done
|
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::controller::{StdioSendBuf, STDIO_SEND_BUF_SIZE};
|
||||||
use crate::model::{ServiceStatus, SmError, SmResult};
|
use crate::model::{ServiceStatus, SmError, SmResult};
|
||||||
use std::io::{Read, Write};
|
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::TryRecvError;
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::{io, thread};
|
use std::{io, thread};
|
||||||
use tracing::error;
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
enum ChildAction {
|
||||||
|
Ignore,
|
||||||
|
Kill,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn child_process_thread(
|
pub fn child_process_thread(
|
||||||
child: Child,
|
mut child: Child,
|
||||||
stdout_send: mpsc::Sender<StdioSendBuf>,
|
mut stdout_send: mpsc::Sender<StdioSendBuf>,
|
||||||
service_status: Arc<Mutex<ServiceStatus>>,
|
service_status: Arc<Mutex<ServiceStatus>>,
|
||||||
service_name: String,
|
service_name: String,
|
||||||
terminate_channel: mpsc::Receiver<()>,
|
terminate_channel: mpsc::Receiver<()>,
|
||||||
) -> SmResult {
|
) -> SmResult {
|
||||||
let mut child = child;
|
let stdout = child
|
||||||
let mut stdout = child
|
|
||||||
.stdout
|
.stdout
|
||||||
.take()
|
.take()
|
||||||
.ok_or(SmError::Bug("Stdout of child could not be taken"))?;
|
.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");
|
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() {
|
match terminate_channel.try_recv() {
|
||||||
Ok(_) | Err(TryRecvError::Disconnected) => {
|
Ok(_) | Err(TryRecvError::Disconnected) => {
|
||||||
// terminating the thread is a best-effort, it doesn't matter if it died
|
// terminating the thread is a best-effort, it doesn't matter if it died
|
||||||
let _ = stderr_terminate_send.send(());
|
let _ = stderr_terminate_send.send(());
|
||||||
break Ok(());
|
return (ChildAction::Kill, Ok(()));
|
||||||
}
|
}
|
||||||
Err(TryRecvError::Empty) => {}
|
Err(TryRecvError::Empty) => {}
|
||||||
}
|
}
|
||||||
|
|
@ -50,19 +94,27 @@ pub fn child_process_thread(
|
||||||
let mut stdout_buf = [0; STDIO_SEND_BUF_SIZE];
|
let mut stdout_buf = [0; STDIO_SEND_BUF_SIZE];
|
||||||
match stdout.read(&mut stdout_buf) {
|
match stdout.read(&mut stdout_buf) {
|
||||||
Ok(0) => {}
|
Ok(0) => {}
|
||||||
Ok(n) => {
|
Ok(n) => match stdout_send.send((stdout_buf, n)) {
|
||||||
stdout_send
|
Ok(()) => {}
|
||||||
.send((stdout_buf, n))
|
Err(_) => {
|
||||||
.map_err(|_| SmError::Bug("Failed to send stdout to main thread"))?;
|
return (
|
||||||
}
|
ChildAction::Kill,
|
||||||
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
|
Err(SmError::Bug("Failed to send stdout to main thread")),
|
||||||
Err(e) => break Err(e.into()),
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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() {
|
match child.try_wait() {
|
||||||
Ok(None) => {}
|
Ok(None) => {}
|
||||||
Ok(Some(status)) => {
|
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() {
|
*status_lock = match status.code() {
|
||||||
Some(0) => ServiceStatus::Exited,
|
Some(0) => ServiceStatus::Exited,
|
||||||
|
|
@ -70,30 +122,11 @@ pub fn child_process_thread(
|
||||||
None => ServiceStatus::Killed,
|
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(
|
fn child_process_stderr_thread(
|
||||||
|
|
@ -101,6 +134,8 @@ fn child_process_stderr_thread(
|
||||||
terminate_channel: mpsc::Receiver<()>,
|
terminate_channel: mpsc::Receiver<()>,
|
||||||
mut stderr: ChildStderr,
|
mut stderr: ChildStderr,
|
||||||
) {
|
) {
|
||||||
|
info!("Entering main processing loop for stderr thread");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match terminate_channel.try_recv() {
|
match terminate_channel.try_recv() {
|
||||||
Ok(_) | Err(TryRecvError::Disconnected) => return,
|
Ok(_) | Err(TryRecvError::Disconnected) => return,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crossterm::event;
|
||||||
use crossterm::event::{Event, KeyCode};
|
use crossterm::event::{Event, KeyCode};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::{ErrorKind, Write};
|
use std::io::{ErrorKind, Write};
|
||||||
use std::process::{ChildStderr, Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{io, thread};
|
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 {
|
match key.code {
|
||||||
KeyCode::Char('q') => match app.selected {
|
KeyCode::Char('q') => match app.selected {
|
||||||
Some(_) => app.leave_service(),
|
Some(_) => app.leave_service(),
|
||||||
None => break,
|
None => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
KeyCode::Char('r') => app.run_service()?,
|
KeyCode::Char('r') => app.run_service()?,
|
||||||
KeyCode::Char('k') => app.kill_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
|
// 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(());
|
let _ = sender.send(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,6 +155,7 @@ impl App {
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
let status = {
|
let status = {
|
||||||
let service = &mut self.table.services[index];
|
let service = &mut self.table.services[index];
|
||||||
|
service.std_io_buf.clear();
|
||||||
*service.status.lock()?
|
*service.status.lock()?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -166,12 +171,13 @@ impl App {
|
||||||
let index = self.selected.or_else(|| self.table.table_state.selected());
|
let index = self.selected.or_else(|| self.table.table_state.selected());
|
||||||
|
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
let status = {
|
let service = &mut self.table.services[index];
|
||||||
let service = &mut self.table.services[index];
|
|
||||||
*service.status.lock()?
|
let status = { *service.status.lock()? };
|
||||||
};
|
|
||||||
|
|
||||||
if status == ServiceStatus::Running {
|
if status == ServiceStatus::Running {
|
||||||
|
info!(name = %service.name,"Killing service");
|
||||||
|
|
||||||
let terminate_sender = &mut self
|
let terminate_sender = &mut self
|
||||||
.thread_terminates
|
.thread_terminates
|
||||||
.get(&index)
|
.get(&index)
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ mod error {
|
||||||
MutexPoisoned,
|
MutexPoisoned,
|
||||||
FailedToSendStdio,
|
FailedToSendStdio,
|
||||||
/// This should never happen and would be a panic in most programs, but panicking here
|
/// 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),
|
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) {
|
fn render_table<B: Backend>(f: &mut Frame<B>, state: &mut AppState, area: Rect) {
|
||||||
let selected_style = Style::default().add_modifier(Modifier::REVERSED);
|
let selected_style = Style::default().add_modifier(Modifier::REVERSED);
|
||||||
let normal_style = Style::default().bg(Color::Blue);
|
let normal_style = Style::default().bg(Color::Blue);
|
||||||
let header_cells = ["name", "status"]
|
let header_cells = ["Name", "Status"]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|h| Cell::from(*h).style(Style::default()));
|
.map(|h| Cell::from(*h).style(Style::default()));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue