Skip to main content

rustpython_host_env/
signal.rs

1use std::io;
2#[cfg(windows)]
3use std::sync::Once;
4
5#[cfg(unix)]
6use crate::os::CheckLibcResult;
7#[cfg(any(unix, windows))]
8use crate::os::CheckLibcZero;
9
10#[cfg(any(unix, windows))]
11pub use libc::sighandler_t;
12
13#[cfg(unix)]
14pub use libc::{SIG_DFL, SIG_ERR, SIG_IGN};
15
16#[cfg(not(unix))]
17pub const SIG_DFL: libc::sighandler_t = 0;
18#[cfg(not(unix))]
19pub const SIG_IGN: libc::sighandler_t = 1;
20#[cfg(not(unix))]
21pub const SIG_ERR: libc::sighandler_t = -1 as _;
22
23#[cfg(unix)]
24pub use libc::{SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK};
25
26#[cfg(any(unix, windows))]
27pub use libc::{SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM};
28
29#[cfg(unix)]
30pub use libc::{
31    SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGHUP, SIGIO, SIGKILL, SIGPIPE, SIGPROF, SIGQUIT, SIGSTOP,
32    SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH,
33    SIGXCPU, SIGXFSZ,
34};
35
36#[cfg(all(
37    unix,
38    not(any(
39        target_vendor = "apple",
40        target_os = "openbsd",
41        target_os = "freebsd",
42        target_os = "netbsd"
43    ))
44))]
45pub use libc::{SIGPWR, SIGSTKFLT};
46
47#[cfg(all(unix, not(target_os = "android")))]
48pub use libc::{ITIMER_PROF, ITIMER_REAL, ITIMER_VIRTUAL};
49
50#[cfg(target_os = "android")]
51pub const ITIMER_REAL: libc::c_int = 0;
52#[cfg(target_os = "android")]
53pub const ITIMER_VIRTUAL: libc::c_int = 1;
54#[cfg(target_os = "android")]
55pub const ITIMER_PROF: libc::c_int = 2;
56
57#[cfg(unix)]
58#[must_use]
59pub fn timeval_to_double(tv: &libc::timeval) -> f64 {
60    tv.tv_sec as f64 + (tv.tv_usec as f64 / 1_000_000.0)
61}
62
63#[cfg(unix)]
64#[must_use]
65pub fn double_to_timeval(val: f64) -> libc::timeval {
66    libc::timeval {
67        tv_sec: val.trunc() as _,
68        tv_usec: (val.fract() * 1_000_000.0) as _,
69    }
70}
71
72#[cfg(unix)]
73#[must_use]
74pub fn itimerval_to_tuple(it: &libc::itimerval) -> (f64, f64) {
75    (
76        timeval_to_double(&it.it_value),
77        timeval_to_double(&it.it_interval),
78    )
79}
80
81#[cfg(all(unix, not(target_os = "redox")))]
82unsafe extern "C" {
83    #[link_name = "siginterrupt"]
84    fn c_siginterrupt(sig: i32, flag: i32) -> i32;
85}
86
87#[cfg(any(target_os = "linux", target_os = "android"))]
88mod ffi {
89    unsafe extern "C" {
90        pub(super) fn getitimer(
91            which: libc::c_int,
92            curr_value: *mut libc::itimerval,
93        ) -> libc::c_int;
94        pub(super) fn setitimer(
95            which: libc::c_int,
96            new_value: *const libc::itimerval,
97            old_value: *mut libc::itimerval,
98        ) -> libc::c_int;
99    }
100}
101
102/// # Safety
103///
104/// The caller must ensure `signalnum` is a valid platform signal number.
105#[cfg(any(unix, windows))]
106pub unsafe fn probe_handler(signalnum: i32) -> Option<sighandler_t> {
107    let handler = unsafe { libc::signal(signalnum, libc::SIG_IGN) };
108    if handler == libc::SIG_ERR as sighandler_t {
109        None
110    } else {
111        unsafe { libc::signal(signalnum, handler) };
112        Some(handler)
113    }
114}
115
116/// # Safety
117///
118/// The caller must ensure `signalnum` is a valid platform signal number and
119/// `handler` is accepted by the platform signal ABI.
120#[cfg(any(unix, windows))]
121pub unsafe fn install_handler(signalnum: i32, handler: sighandler_t) -> io::Result<sighandler_t> {
122    let old = unsafe { libc::signal(signalnum, handler) };
123    if old == libc::SIG_ERR as sighandler_t {
124        return Err(io::Error::last_os_error());
125    }
126    #[cfg(all(unix, not(target_os = "redox")))]
127    let _ = siginterrupt(signalnum, 1);
128    Ok(old)
129}
130
131#[cfg(any(unix, windows))]
132pub fn raise_signal(signalnum: i32) -> io::Result<()> {
133    unsafe { libc::raise(signalnum) }.check_libc_zero()
134}
135
136#[cfg(unix)]
137pub fn alarm(seconds: u32) -> u32 {
138    unsafe { libc::alarm(seconds) }
139}
140
141#[cfg(unix)]
142pub fn pause() {
143    unsafe { libc::pause() };
144}
145
146#[cfg(unix)]
147pub fn set_sigint_default_onstack() -> io::Result<()> {
148    let mut action: libc::sigaction = unsafe { core::mem::zeroed() };
149    action.sa_sigaction = libc::SIG_DFL;
150    action.sa_flags = libc::SA_ONSTACK;
151    unsafe { libc::sigemptyset(&mut action.sa_mask) }.check_libc_zero()?;
152    unsafe { libc::sigaction(libc::SIGINT, &action, core::ptr::null_mut()) }.check_libc_zero()
153}
154
155#[cfg(unix)]
156pub fn send_sigint_to_self() -> io::Result<()> {
157    unsafe { libc::kill(libc::getpid(), libc::SIGINT) }.check_libc_zero()
158}
159
160#[cfg(unix)]
161pub fn setitimer(which: i32, new: &libc::itimerval) -> io::Result<libc::itimerval> {
162    let mut old = core::mem::MaybeUninit::<libc::itimerval>::uninit();
163    #[cfg(any(target_os = "linux", target_os = "android"))]
164    let ret = unsafe { ffi::setitimer(which, new, old.as_mut_ptr()) };
165    #[cfg(not(any(target_os = "linux", target_os = "android")))]
166    let ret = unsafe { libc::setitimer(which, new, old.as_mut_ptr()) };
167    ret.check_libc_zero()?;
168    Ok(unsafe { old.assume_init() })
169}
170
171#[cfg(unix)]
172pub fn getitimer(which: i32) -> io::Result<libc::itimerval> {
173    let mut old = core::mem::MaybeUninit::<libc::itimerval>::uninit();
174    #[cfg(any(target_os = "linux", target_os = "android"))]
175    let ret = unsafe { ffi::getitimer(which, old.as_mut_ptr()) };
176    #[cfg(not(any(target_os = "linux", target_os = "android")))]
177    let ret = unsafe { libc::getitimer(which, old.as_mut_ptr()) };
178    ret.check_libc_zero()?;
179    Ok(unsafe { old.assume_init() })
180}
181
182#[cfg(unix)]
183pub fn sigemptyset() -> io::Result<libc::sigset_t> {
184    let mut set: libc::sigset_t = unsafe { core::mem::zeroed() };
185    unsafe { libc::sigemptyset(&mut set) }.check_libc_zero()?;
186    Ok(set)
187}
188
189#[cfg(unix)]
190pub fn sigaddset(set: &mut libc::sigset_t, signum: i32) -> io::Result<()> {
191    unsafe { libc::sigaddset(set, signum) }.check_libc_zero()
192}
193
194#[cfg(unix)]
195pub fn pthread_sigmask(how: i32, set: &libc::sigset_t) -> io::Result<libc::sigset_t> {
196    let mut old_mask: libc::sigset_t = unsafe { core::mem::zeroed() };
197    let err = unsafe { libc::pthread_sigmask(how, set, &mut old_mask) };
198    if err != 0 {
199        Err(io::Error::from_raw_os_error(err))
200    } else {
201        Ok(old_mask)
202    }
203}
204
205#[cfg(any(target_os = "android", target_os = "linux"))]
206pub fn pidfd_send_signal(pidfd: i32, sig: i32, flags: u32) -> io::Result<()> {
207    let ret = unsafe {
208        libc::syscall(
209            libc::SYS_pidfd_send_signal,
210            pidfd,
211            sig,
212            core::ptr::null::<libc::siginfo_t>(),
213            flags,
214        ) as libc::c_long
215    };
216    ret.check_libc_neg()?;
217    Ok(())
218}
219
220#[cfg(all(unix, not(target_os = "redox")))]
221pub fn siginterrupt(signalnum: i32, flag: i32) -> io::Result<()> {
222    unsafe { c_siginterrupt(signalnum, flag) }.check_libc_neg()?;
223    Ok(())
224}
225
226#[cfg(windows)]
227pub const VALID_SIGNALS: &[i32] = &[
228    libc::SIGINT,
229    libc::SIGILL,
230    libc::SIGFPE,
231    libc::SIGSEGV,
232    libc::SIGTERM,
233    21, // SIGBREAK / _SIGBREAK
234    libc::SIGABRT,
235];
236
237#[cfg(windows)]
238pub const SIGBREAK: i32 = 21;
239#[cfg(windows)]
240pub const CTRL_C_EVENT: u32 = 0;
241#[cfg(windows)]
242pub const CTRL_BREAK_EVENT: u32 = 1;
243#[cfg(windows)]
244pub const INVALID_SOCKET: libc::SOCKET = windows_sys::Win32::Networking::WinSock::INVALID_SOCKET;
245
246#[cfg(windows)]
247fn init_winsock() {
248    static WSA_INIT: Once = Once::new();
249    WSA_INIT.call_once(|| unsafe {
250        let mut wsa_data = core::mem::MaybeUninit::uninit();
251        let _ = windows_sys::Win32::Networking::WinSock::WSAStartup(0x0101, wsa_data.as_mut_ptr());
252    });
253}
254
255#[cfg(windows)]
256pub fn wakeup_fd_is_socket(fd: libc::SOCKET) -> io::Result<bool> {
257    use windows_sys::Win32::Networking::WinSock;
258
259    init_winsock();
260    let mut res = 0i32;
261    let mut res_size = core::mem::size_of::<i32>() as i32;
262    let getsockopt_res = unsafe {
263        WinSock::getsockopt(
264            fd,
265            WinSock::SOL_SOCKET,
266            WinSock::SO_ERROR,
267            &mut res as *mut i32 as *mut _,
268            &mut res_size,
269        )
270    };
271    if getsockopt_res == 0 {
272        return Ok(true);
273    }
274
275    let err = io::Error::last_os_error();
276    if err.raw_os_error() != Some(WinSock::WSAENOTSOCK) {
277        return Err(err);
278    }
279
280    let fd_i32 =
281        i32::try_from(fd).map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid fd"))?;
282    let borrowed = unsafe { crate::crt_fd::Borrowed::try_borrow_raw(fd_i32) }?;
283    crate::fileutils::fstat(borrowed)?;
284    Ok(false)
285}
286
287#[cfg(windows)]
288pub fn notify_signal(
289    signum: i32,
290    wakeup_fd: libc::SOCKET,
291    wakeup_is_socket: bool,
292    sigint_event: Option<isize>,
293) {
294    if signum == libc::SIGINT
295        && let Some(handle) = sigint_event
296    {
297        unsafe {
298            windows_sys::Win32::System::Threading::SetEvent(handle as _);
299        }
300    }
301
302    if wakeup_fd == INVALID_SOCKET {
303        return;
304    }
305
306    let sigbyte = signum as u8;
307    if wakeup_is_socket {
308        unsafe {
309            let _ = windows_sys::Win32::Networking::WinSock::send(
310                wakeup_fd,
311                &sigbyte as *const u8 as *const _,
312                1,
313                0,
314            );
315        }
316    } else {
317        unsafe {
318            let _ = libc::write(wakeup_fd as _, &sigbyte as *const u8 as *const _, 1);
319        }
320    }
321}
322
323#[cfg(unix)]
324pub fn notify_signal(signum: i32, wakeup_fd: i32) {
325    if wakeup_fd == -1 {
326        return;
327    }
328    let sigbyte = signum as u8;
329    unsafe {
330        let _ = libc::write(wakeup_fd, &sigbyte as *const u8 as *const _, 1);
331    }
332}
333
334#[cfg(unix)]
335pub fn strsignal(signalnum: i32) -> Option<String> {
336    let s = unsafe { libc::strsignal(signalnum) };
337    if s.is_null() {
338        None
339    } else {
340        let cstr = unsafe { core::ffi::CStr::from_ptr(s) };
341        Some(cstr.to_string_lossy().into_owned())
342    }
343}
344
345#[cfg(windows)]
346pub fn strsignal(signalnum: i32) -> Option<String> {
347    let name = match signalnum {
348        libc::SIGINT => "Interrupt",
349        libc::SIGILL => "Illegal instruction",
350        libc::SIGFPE => "Floating-point exception",
351        libc::SIGSEGV => "Segmentation fault",
352        libc::SIGTERM => "Terminated",
353        21 => "Break",
354        libc::SIGABRT => "Aborted",
355        _ => return None,
356    };
357    Some(name.to_owned())
358}
359
360#[cfg(unix)]
361pub fn valid_signals(max_signum: usize) -> io::Result<Vec<i32>> {
362    let mut mask: libc::sigset_t = unsafe { core::mem::zeroed() };
363    unsafe { libc::sigfillset(&mut mask) }.check_libc_zero()?;
364    let mut signals = Vec::new();
365    for signum in 1..max_signum {
366        if unsafe { libc::sigismember(&mask, signum as i32) } == 1 {
367            signals.push(signum as i32);
368        }
369    }
370    Ok(signals)
371}
372
373#[cfg(unix)]
374pub fn sigset_contains(mask: libc::sigset_t, signum: i32) -> bool {
375    unsafe { libc::sigismember(&mask, signum) == 1 }
376}
377
378#[cfg(windows)]
379pub fn valid_signals(_max_signum: usize) -> io::Result<Vec<i32>> {
380    Ok(VALID_SIGNALS.to_vec())
381}