indexbus_platform_ops/
process.rs1use crate::errors::{Error, Result};
12
13#[cfg(unix)]
14use std::sync::Arc;
15
16#[cfg(unix)]
17use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum Signal {
22 Term,
24 Kill,
26}
27
28#[cfg(unix)]
29static SIGINT_STOP_FLAG: AtomicPtr<AtomicBool> = AtomicPtr::new(std::ptr::null_mut());
30
31#[cfg(unix)]
32extern "C" fn sigint_set_stop_flag(_signum: libc::c_int) {
33 let ptr = SIGINT_STOP_FLAG.load(Ordering::Relaxed);
35 if ptr.is_null() {
36 return;
37 }
38 unsafe {
39 (*ptr).store(true, Ordering::Relaxed);
40 }
41}
42
43pub fn install_sigint_stop_flag(stop: Arc<AtomicBool>) -> Result<()> {
54 #[cfg(unix)]
55 {
56 let new_ptr = Arc::as_ptr(&stop) as *mut AtomicBool;
57
58 if SIGINT_STOP_FLAG.load(Ordering::SeqCst) == new_ptr {
60 return Ok(());
61 }
62
63 let handler = sigint_set_stop_flag as *const () as usize as libc::sighandler_t;
64 let prev = unsafe { libc::signal(libc::SIGINT, handler) };
65 if prev == libc::SIG_ERR {
66 return Err(Error::msg(format!(
67 "signal(SIGINT) failed: {} (errno={:?})",
68 std::io::Error::last_os_error(),
69 std::io::Error::last_os_error().raw_os_error()
70 )));
71 }
72
73 let _ = Arc::into_raw(stop);
75 SIGINT_STOP_FLAG.store(new_ptr, Ordering::SeqCst);
76 Ok(())
77 }
78
79 #[cfg(not(unix))]
80 {
81 let _ = stop;
82 Ok(())
83 }
84}
85
86#[cfg(unix)]
87fn kill_raw(pid: i32, sig: i32) -> std::io::Result<()> {
88 let rc = unsafe { libc::kill(pid, sig) };
89 if rc == 0 {
90 Ok(())
91 } else {
92 Err(std::io::Error::last_os_error())
93 }
94}
95
96#[cfg(unix)]
97fn pid_to_i32(pid: u32) -> Result<i32> {
98 i32::try_from(pid).map_err(|_| Error::msg(format!("pid {pid} out of range for i32")))
99}
100
101pub fn pid_is_alive(pid: u32) -> bool {
105 #[cfg(unix)]
106 {
107 let Ok(pid) = i32::try_from(pid) else {
108 return false;
109 };
110
111 match kill_raw(pid, 0) {
112 Ok(()) => true,
113 Err(e) => {
114 matches!(e.raw_os_error(), Some(libc::EPERM))
116 }
117 }
118 }
119
120 #[cfg(not(unix))]
121 {
122 let _ = pid;
123 false
124 }
125}
126
127pub fn send_signal(pid: u32, signal: Signal) -> Result<()> {
134 #[cfg(unix)]
135 {
136 let pid_i32 = pid_to_i32(pid)?;
137 let sig = match signal {
138 Signal::Term => libc::SIGTERM,
139 Signal::Kill => libc::SIGKILL,
140 };
141
142 kill_raw(pid_i32, sig).map_err(|e| {
143 Error::msg(format!(
144 "kill(pid={pid}, sig={sig}) failed: {e} (errno={:?})",
145 e.raw_os_error()
146 ))
147 })
148 }
149
150 #[cfg(not(unix))]
151 {
152 let _ = (pid, signal);
153 Ok(())
154 }
155}
156
157pub fn signal_term(pid: u32) -> Result<()> {
159 send_signal(pid, Signal::Term)
160}
161
162pub fn signal_kill(pid: u32) -> Result<()> {
164 send_signal(pid, Signal::Kill)
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn pid_is_alive_is_false_for_out_of_range_pid() {
173 assert!(!pid_is_alive(u32::MAX));
174 }
175
176 #[test]
177 fn send_signal_rejects_pid_out_of_i32_range() {
178 let err = send_signal(u32::MAX, Signal::Term).unwrap_err();
180 assert!(err.to_string().contains("out of range"));
181 }
182}