#![allow(dead_code)]
use std::path::{Path, PathBuf};
use std::time::{Duration, Instant};
fn pids_of(pid_file: &Path) -> Vec<i32> {
let text = std::fs::read_to_string(pid_file).unwrap_or_default();
let mut pids = Vec::new();
for key in ["supervisor", "pid"] {
for line in text.lines() {
if let Some(value) = line.strip_prefix(&format!("{}=", key)) {
if let Ok(pid) = value.trim().parse::<i32>() {
pids.push(pid);
}
}
}
}
pids
}
pub fn pid_files(root: &Path) -> Vec<PathBuf> {
let mut found = Vec::new();
let mut stack = vec![(root.to_path_buf(), 0usize)];
while let Some((dir, depth)) = stack.pop() {
let Ok(entries) = std::fs::read_dir(&dir) else {
continue;
};
for entry in entries.flatten() {
let path = entry.path();
if path.file_name().is_some_and(|n| n == "dolt-server.pid") {
found.push(path);
} else if depth < 6 && entry.file_type().is_ok_and(|t| t.is_dir()) {
let name = entry.file_name();
if name != ".git" && name != "ledger-db" && name != "ledger-remote" {
stack.push((path, depth + 1));
}
}
}
}
found
}
fn alive(pid: i32) -> bool {
if unsafe { libc::kill(pid, 0) } != 0 {
return false;
}
match std::fs::read_to_string(format!("/proc/{}/stat", pid)) {
Ok(stat) => {
stat.rsplit_once(')')
.and_then(|(_, rest)| rest.trim_start().chars().next())
!= Some('Z')
}
Err(_) => true,
}
}
pub fn stop_servers(root: &Path, graceful: bool) {
for file in pid_files(root) {
let pids = pids_of(&file);
if graceful {
if let Some(&supervisor) = pids.first() {
unsafe {
libc::kill(supervisor, libc::SIGTERM);
}
let end = Instant::now() + Duration::from_secs(15);
while alive(supervisor) && Instant::now() < end {
std::thread::sleep(Duration::from_millis(20));
}
}
}
for pid in pids {
if alive(pid) {
unsafe {
libc::kill(pid, libc::SIGKILL);
}
}
}
}
}
pub fn servers_running(needle: &str) -> usize {
let Ok(entries) = std::fs::read_dir("/proc") else {
return 0;
};
entries
.flatten()
.filter(|entry| {
entry
.file_name()
.to_str()
.is_some_and(|n| n.chars().all(|c| c.is_ascii_digit()))
})
.filter(|entry| {
std::fs::read(entry.path().join("cmdline")).is_ok_and(|bytes| {
let line = String::from_utf8_lossy(&bytes).replace('\0', " ");
line.contains("sql-server") && line.contains(needle)
})
})
.count()
}