Vain attempts to stop script

This commit is contained in:
Uber Veng
2026-05-22 22:06:32 +07:00
parent 6a79fb2c54
commit 25ccff3a37
7 changed files with 296 additions and 52 deletions

View File

@@ -28,7 +28,9 @@ pub enum StructuredCommand {
text: String,
},
Progress {
percent: u8,
/// Some(n) = deterministic bar (0-100).
/// None = indeterminate spinner (percent omitted from JSON).
percent: Option<u8>,
message: Option<String>,
},
/// A list of checkboxes and radio buttons.
@@ -86,6 +88,7 @@ pub fn spawn(
tokio::sync::oneshot::Receiver<i32>,
UnboundedSender<String>,
tokio::sync::oneshot::Sender<()>, // kill signal
Option<u32>, // bash PID for direct kill
)> {
let mut child = Command::new("bash")
.arg("-c")
@@ -93,10 +96,22 @@ pub fn spawn(
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
// Create a new process group (PGID = bash PID) so that
// kill(-pid, SIGKILL) kills bash AND all its children
// (grep, php, mysql, etc.) without touching the parent ostiary.
.process_group(0)
.spawn()?;
// Capture the PID before moving child into tasks (needed for kill).
let pid = child.id();
dbg_kill(format!("spawn: bash pid={:?}", pid));
// Log the actual process group bash landed in.
#[cfg(unix)]
if let Some(p) = pid {
let pgid = unsafe { libc::getpgid(p as libc::pid_t) };
let my_pgid = unsafe { libc::getpgid(0) };
dbg_kill(format!(" bash pgid={pgid} ostiary pgid={my_pgid}"));
}
let stdin = child.stdin.take().unwrap();
let stdout = child.stdout.take().unwrap();
@@ -160,20 +175,33 @@ pub fn spawn(
let _ = finished_tx.send(code);
});
// Kill-watcher: when kill_tx fires, send SIGTERM to the entire process
// group so bash AND all its children (mysqldump, php, etc.) are killed.
// Kill-watcher: when kill_tx fires, send SIGKILL to the entire process
// group so bash AND all its children are unconditionally terminated.
tokio::spawn(async move {
if kill_rx.await.is_ok() {
let recv = kill_rx.await;
dbg_kill(format!("kill-watcher: recv={:?} pid={:?}", recv.is_ok(), pid));
if recv.is_ok() {
if let Some(pid) = pid {
#[cfg(unix)]
unsafe {
libc::kill(-(pid as libc::pid_t), libc::SIGTERM);
{
let r1 = unsafe { libc::kill(-(pid as libc::pid_t), libc::SIGKILL) };
let r2 = unsafe { libc::kill(pid as libc::pid_t, libc::SIGKILL) };
dbg_kill(format!(" kill-watcher kill(-{pid})={r1}, kill({pid})={r2}"));
}
}
}
});
Ok((output_rx, cmd_rx, finished_rx, reply_tx, kill_tx))
Ok((output_rx, cmd_rx, finished_rx, reply_tx, kill_tx, pid))
}
fn dbg_kill(msg: String) {
use std::io::Write;
if let Ok(mut f) = std::fs::OpenOptions::new()
.create(true).append(true).open("/tmp/ostiary-kill.log")
{
let _ = writeln!(f, "{}", msg);
}
}
fn parse_command(json_str: &str, cmd_tx: UnboundedSender<StructuredCommand>) -> Result<()> {
@@ -218,7 +246,8 @@ fn parse_command(json_str: &str, cmd_tx: UnboundedSender<StructuredCommand>) ->
StructuredCommand::Message { level, text }
}
"progress" => {
let percent = v["percent"].as_u64().unwrap_or(0) as u8;
// percent absent or null → None (indeterminate spinner)
let percent = v["percent"].as_u64().map(|n| n.min(100) as u8);
let message = v["message"].as_str().map(String::from);
StructuredCommand::Progress { percent, message }
}