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#[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#[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, 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}