1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! OS-level process utilities for robust server shutdown and stale process cleanup.
//!
//! This module provides functions for checking process liveness, performing
//! escalating kills (including process-group kills to handle wrapper scripts),
//! and cleaning up stale pidfiles from crashed test runs.
//!
//! All utilities are intentionally synchronous so they can be used from
//! [`Drop`] implementations as well as from async startup paths.
use Path;
use Command;
use thread;
use Duration;
/// Check if a process is alive via `kill -0`.
///
/// Returns `true` if the process exists and is reachable, `false` otherwise.
///
/// # Example
///
/// ```no_run
/// use redis_server_wrapper::process;
///
/// let alive = process::pid_alive(12345);
/// println!("process alive: {alive}");
/// ```
/// Escalating kill: SIGTERM, wait grace period, then SIGKILL process group and individual PID.
///
/// Strategy:
/// 1. Send SIGTERM to give the process a chance to shut down cleanly.
/// 2. Sleep 500ms.
/// 3. If still alive, SIGKILL the process group (`kill -9 -$pid`) to catch wrapper
/// scripts and any children they spawned (e.g. `redis-stack-server`).
/// 4. SIGKILL the individual PID as a fallback.
///
/// Uses synchronous [`std::process::Command`] so this is safe to call from [`Drop`] impls.
///
/// # Example
///
/// ```no_run
/// use redis_server_wrapper::process;
///
/// process::force_kill(12345);
/// ```
/// Read a PID from a pidfile.
///
/// Returns `None` if the file does not exist, cannot be read, or its contents
/// cannot be parsed as a `u32`.
/// Kill any process listening on a TCP port via `lsof -ti :$port`.
///
/// Best-effort -- all errors are silently ignored. This is intended as a
/// final safety net to release the port after shutdown, not as a primary
/// kill mechanism.
///
/// # Example
///
/// ```no_run
/// use redis_server_wrapper::process;
///
/// process::kill_by_port(6379);
/// ```