mirror of
https://github.com/Noratrieb/service-manager.git
synced 2026-01-14 16:35:05 +01:00
killing
This commit is contained in:
parent
817a14c3bc
commit
f1e1cbd0a1
3 changed files with 76 additions and 25 deletions
|
|
@ -8,7 +8,7 @@ use std::io;
|
||||||
use std::io::{ErrorKind, Read, Write};
|
use std::io::{ErrorKind, Read, Write};
|
||||||
use std::process::{Child, Command, Stdio};
|
use std::process::{Child, Command, Stdio};
|
||||||
use std::sync::mpsc::TryRecvError;
|
use std::sync::mpsc::TryRecvError;
|
||||||
use std::sync::{mpsc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tui::backend::Backend;
|
use tui::backend::Backend;
|
||||||
use tui::widgets::TableState;
|
use tui::widgets::TableState;
|
||||||
|
|
@ -32,6 +32,7 @@ pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> SmResult
|
||||||
None => break,
|
None => break,
|
||||||
},
|
},
|
||||||
KeyCode::Char('r') => app.run_service()?,
|
KeyCode::Char('r') => app.run_service()?,
|
||||||
|
KeyCode::Char('k') => app.kill_service()?,
|
||||||
KeyCode::Down => app.next(),
|
KeyCode::Down => app.next(),
|
||||||
KeyCode::Up => app.previous(),
|
KeyCode::Up => app.previous(),
|
||||||
KeyCode::Enter => app.select_service(),
|
KeyCode::Enter => app.select_service(),
|
||||||
|
|
@ -43,7 +44,7 @@ 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 {
|
for sender in app.thread_terminates.values() {
|
||||||
let _ = sender.send(());
|
let _ = sender.send(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,7 +69,7 @@ impl App {
|
||||||
.ok_or_else(|| io::Error::from(ErrorKind::Other))
|
.ok_or_else(|| io::Error::from(ErrorKind::Other))
|
||||||
.or_else(|_| std::env::current_dir())?,
|
.or_else(|_| std::env::current_dir())?,
|
||||||
env: service.env.unwrap_or_else(HashMap::new),
|
env: service.env.unwrap_or_else(HashMap::new),
|
||||||
status: Mutex::new(ServiceStatus::NotStarted),
|
status: Arc::new(Mutex::new(ServiceStatus::NotStarted)),
|
||||||
std_io_buf: Vec::new(),
|
std_io_buf: Vec::new(),
|
||||||
stdout: StdIoStream {
|
stdout: StdIoStream {
|
||||||
recv: stdout_recv,
|
recv: stdout_recv,
|
||||||
|
|
@ -79,7 +80,7 @@ impl App {
|
||||||
.collect::<io::Result<_>>()?,
|
.collect::<io::Result<_>>()?,
|
||||||
},
|
},
|
||||||
selected: None,
|
selected: None,
|
||||||
thread_terminates: Vec::new(),
|
thread_terminates: HashMap::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,8 +157,31 @@ impl App {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_service(&mut self, service: usize) -> SmResult {
|
fn kill_service(&mut self) -> SmResult {
|
||||||
let service = &mut self.table.services[service];
|
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()?
|
||||||
|
};
|
||||||
|
|
||||||
|
if status == ServiceStatus::Running {
|
||||||
|
let terminate_sender = &mut self
|
||||||
|
.thread_terminates
|
||||||
|
.get(&index)
|
||||||
|
.ok_or(SmError::Bug("Child termination channel not found"))?;
|
||||||
|
terminate_sender.send(()).map_err(|_| {
|
||||||
|
SmError::Bug("Failed to send termination signal to child process")
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_service(&mut self, index: usize) -> SmResult {
|
||||||
|
let service = &mut self.table.services[index];
|
||||||
|
|
||||||
*service.status.lock()? = ServiceStatus::Running;
|
*service.status.lock()? = ServiceStatus::Running;
|
||||||
|
|
||||||
|
|
@ -191,29 +215,38 @@ impl App {
|
||||||
|
|
||||||
let (terminate_send, terminate_recv) = mpsc::channel();
|
let (terminate_send, terminate_recv) = mpsc::channel();
|
||||||
|
|
||||||
self.thread_terminates.push(terminate_send);
|
self.thread_terminates.insert(index, terminate_send);
|
||||||
|
|
||||||
|
let service_status = service.status.clone();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
match child_process_thread(child, stdout_send, terminate_recv) {
|
match child_process_thread(child, stdout_send, service_status, terminate_recv) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => std::fs::write("error.txt", e.to_string()).unwrap(),
|
Err(e) => {
|
||||||
|
let _ = std::fs::write("error.txt", e.to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn child_process_thread(
|
fn child_process_thread(
|
||||||
child: Child,
|
child: Child,
|
||||||
stdout_send: mpsc::Sender<StdioSendBuf>,
|
stdout_send: mpsc::Sender<StdioSendBuf>,
|
||||||
|
service_status: Arc<Mutex<ServiceStatus>>,
|
||||||
terminate_channel: mpsc::Receiver<()>,
|
terminate_channel: mpsc::Receiver<()>,
|
||||||
) -> io::Result<()> {
|
) -> SmResult {
|
||||||
let mut child = child;
|
let mut child = child;
|
||||||
let mut stdout = child.stdout.take().unwrap();
|
let mut stdout = child
|
||||||
|
.stdout
|
||||||
|
.take()
|
||||||
|
.ok_or(SmError::Bug("Stdout of child could not be taken"))?;
|
||||||
|
|
||||||
loop {
|
let result = loop {
|
||||||
match terminate_channel.try_recv() {
|
match terminate_channel.try_recv() {
|
||||||
Ok(_) | Err(TryRecvError::Disconnected) => break,
|
Ok(_) | Err(TryRecvError::Disconnected) => break Ok(()),
|
||||||
Err(TryRecvError::Empty) => {}
|
Err(TryRecvError::Empty) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,18 +257,29 @@ fn child_process_thread(
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
stdout_send
|
stdout_send
|
||||||
.send((stdout_buf, n))
|
.send((stdout_buf, n))
|
||||||
.map_err(|_| io::Error::from(io::ErrorKind::Other))?;
|
.map_err(|_| SmError::Bug("Failed to send stdout to main thread"))?;
|
||||||
}
|
}
|
||||||
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
|
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||||
Err(e) => return Err(e),
|
Err(e) => break Err(e.into()),
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
match child.kill() {
|
match child.kill() {
|
||||||
Ok(()) => {}
|
Ok(()) => {
|
||||||
|
*service_status.lock().map_err(|_| SmError::MutexPoisoned)? = ServiceStatus::Killed
|
||||||
|
}
|
||||||
Err(e) if e.kind() == io::ErrorKind::InvalidInput => {}
|
Err(e) if e.kind() == io::ErrorKind::InvalidInput => {}
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e.into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
src/model.rs
12
src/model.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::controller::StdioSendBuf;
|
use crate::controller::StdioSendBuf;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{mpsc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use tui::widgets::TableState;
|
use tui::widgets::TableState;
|
||||||
|
|
||||||
pub use error::{SmError, SmResult};
|
pub use error::{SmError, SmResult};
|
||||||
|
|
@ -10,7 +10,7 @@ pub use error::{SmError, SmResult};
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub table: AppState,
|
pub table: AppState,
|
||||||
pub selected: Option<usize>,
|
pub selected: Option<usize>,
|
||||||
pub thread_terminates: Vec<mpsc::Sender<()>>,
|
pub thread_terminates: HashMap<usize, mpsc::Sender<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -25,7 +25,7 @@ pub struct Service {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub workdir: PathBuf,
|
pub workdir: PathBuf,
|
||||||
pub env: HashMap<String, String>,
|
pub env: HashMap<String, String>,
|
||||||
pub status: Mutex<ServiceStatus>,
|
pub status: Arc<Mutex<ServiceStatus>>,
|
||||||
pub std_io_buf: Vec<u8>,
|
pub std_io_buf: Vec<u8>,
|
||||||
pub stdout: StdIoStream,
|
pub stdout: StdIoStream,
|
||||||
}
|
}
|
||||||
|
|
@ -37,12 +37,12 @@ pub struct StdIoStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum ServiceStatus {
|
pub enum ServiceStatus {
|
||||||
NotStarted,
|
NotStarted,
|
||||||
Running,
|
Running,
|
||||||
Exited,
|
Exited,
|
||||||
Failed(u8),
|
Failed(u8),
|
||||||
|
Killed,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod config {
|
pub mod config {
|
||||||
|
|
@ -71,6 +71,9 @@ mod error {
|
||||||
FailedToStartChild(io::Error),
|
FailedToStartChild(io::Error),
|
||||||
MutexPoisoned,
|
MutexPoisoned,
|
||||||
FailedToSendStdio,
|
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
|
||||||
|
Bug(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for SmError {
|
impl From<io::Error> for SmError {
|
||||||
|
|
@ -94,6 +97,7 @@ mod error {
|
||||||
SmError::FailedToSendStdio => {
|
SmError::FailedToSendStdio => {
|
||||||
f.write_str("Failed to send stdio to display thread. This is a bug.")
|
f.write_str("Failed to send stdio to display thread. This is a bug.")
|
||||||
}
|
}
|
||||||
|
SmError::Bug(str) => write!(f, "{}. This is a bug.", str),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,12 @@ fn render_help_footer<B: Backend>(f: &mut Frame<B>, app: &App, area: Rect) {
|
||||||
|
|
||||||
let paragraph = Paragraph::new(if app.is_table() {
|
let paragraph = Paragraph::new(if app.is_table() {
|
||||||
vec![Spans::from(
|
vec![Spans::from(
|
||||||
"q-quit down-down up-up enter-select r-run service",
|
"q-quit down-down up-up enter-select r-run service k-kill service",
|
||||||
)]
|
)]
|
||||||
} else {
|
} else {
|
||||||
vec![Spans::from("q-back esc-back r-run service")]
|
vec![Spans::from(
|
||||||
|
"q-back esc-back r-run service k-kill service",
|
||||||
|
)]
|
||||||
})
|
})
|
||||||
.block(block);
|
.block(block);
|
||||||
|
|
||||||
|
|
@ -112,6 +114,7 @@ impl Display for ServiceStatus {
|
||||||
ServiceStatus::Exited => f.write_str("exited (0)"),
|
ServiceStatus::Exited => f.write_str("exited (0)"),
|
||||||
ServiceStatus::Failed(code) => write!(f, "failed ({})", code),
|
ServiceStatus::Failed(code) => write!(f, "failed ({})", code),
|
||||||
ServiceStatus::NotStarted => f.write_str("not started"),
|
ServiceStatus::NotStarted => f.write_str("not started"),
|
||||||
|
ServiceStatus::Killed => f.write_str("killed"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue