1use crate::{builtins::PyModule, PyRef, VirtualMachine};
2
3pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
4 let module = _signal::make_module(vm);
5
6 #[cfg(any(unix, windows))]
7 _signal::init_signal_handlers(&module, vm);
8
9 module
10}
11
12#[pymodule]
13pub(crate) mod _signal {
14 use crate::{
15 convert::{IntoPyException, TryFromBorrowedObject},
16 signal, Py, PyObjectRef, PyResult, VirtualMachine,
17 };
18 use std::sync::atomic::{self, Ordering};
19
20 #[cfg(any(unix, windows))]
21 use libc::sighandler_t;
22 #[allow(non_camel_case_types)]
23 #[cfg(not(any(unix, windows)))]
24 type sighandler_t = usize;
25
26 cfg_if::cfg_if! {
27 if #[cfg(windows)] {
28 type WakeupFdRaw = libc::SOCKET;
29 struct WakeupFd(WakeupFdRaw);
30 const INVALID_WAKEUP: libc::SOCKET = windows_sys::Win32::Networking::WinSock::INVALID_SOCKET;
31 static WAKEUP: atomic::AtomicUsize = atomic::AtomicUsize::new(INVALID_WAKEUP);
32 static WAKEUP_IS_SOCKET: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
35
36 impl<'a> TryFromBorrowedObject<'a> for WakeupFd {
37 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a crate::PyObject) -> PyResult<Self> {
38 use num_traits::One;
39
40 let fd: &crate::Py<crate::builtins::PyInt> = obj.try_to_value(vm)?;
41 match fd.try_to_primitive::<usize>(vm) {
42 Ok(fd) => Ok(WakeupFd(fd as _)),
43 Err(e) => if (-fd.as_bigint()).is_one() {
44 Ok(WakeupFd(INVALID_WAKEUP))
45 } else {
46 Err(e)
47 },
48 }
49 }
50 }
51 } else {
52 type WakeupFdRaw = i32;
53 type WakeupFd = WakeupFdRaw;
54 const INVALID_WAKEUP: WakeupFd = -1;
55 static WAKEUP: atomic::AtomicI32 = atomic::AtomicI32::new(INVALID_WAKEUP);
56 }
57 }
58
59 #[cfg(unix)]
60 pub use libc::SIG_ERR;
61 #[cfg(unix)]
62 pub use nix::unistd::alarm as sig_alarm;
63
64 #[cfg(unix)]
65 #[pyattr]
66 pub use libc::{SIG_DFL, SIG_IGN};
67
68 #[cfg(not(unix))]
69 #[pyattr]
70 pub const SIG_DFL: sighandler_t = 0;
71 #[cfg(not(unix))]
72 #[pyattr]
73 pub const SIG_IGN: sighandler_t = 1;
74 #[cfg(not(unix))]
75 #[allow(dead_code)]
76 pub const SIG_ERR: sighandler_t = -1 as _;
77
78 #[cfg(all(unix, not(target_os = "redox")))]
79 extern "C" {
80 fn siginterrupt(sig: i32, flag: i32) -> i32;
81 }
82
83 #[pyattr]
84 use crate::signal::NSIG;
85
86 #[cfg(any(unix, windows))]
87 #[pyattr]
88 pub use libc::{SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM};
89
90 #[cfg(unix)]
91 #[pyattr]
92 use libc::{
93 SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGHUP, SIGIO, SIGKILL, SIGPIPE, SIGPROF, SIGQUIT,
94 SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM,
95 SIGWINCH, SIGXCPU, SIGXFSZ,
96 };
97
98 #[cfg(unix)]
99 #[cfg(not(any(
100 target_vendor = "apple",
101 target_os = "openbsd",
102 target_os = "freebsd",
103 target_os = "netbsd"
104 )))]
105 #[pyattr]
106 use libc::{SIGPWR, SIGSTKFLT};
107
108 #[cfg(any(unix, windows))]
109 pub(super) fn init_signal_handlers(
110 module: &Py<crate::builtins::PyModule>,
111 vm: &VirtualMachine,
112 ) {
113 let sig_dfl = vm.new_pyobj(SIG_DFL as u8);
114 let sig_ign = vm.new_pyobj(SIG_IGN as u8);
115
116 for signum in 1..NSIG {
117 let handler = unsafe { libc::signal(signum as i32, SIG_IGN) };
118 if handler != SIG_ERR {
119 unsafe { libc::signal(signum as i32, handler) };
120 }
121 let py_handler = if handler == SIG_DFL {
122 Some(sig_dfl.clone())
123 } else if handler == SIG_IGN {
124 Some(sig_ign.clone())
125 } else {
126 None
127 };
128 vm.signal_handlers.as_deref().unwrap().borrow_mut()[signum] = py_handler;
129 }
130
131 let int_handler = module
132 .get_attr("default_int_handler", vm)
133 .expect("_signal does not have this attr?");
134 if vm.state.settings.install_signal_handlers {
135 signal(libc::SIGINT, int_handler, vm).expect("Failed to set sigint handler");
136 }
137 }
138
139 #[cfg(not(any(unix, windows)))]
140 #[pyfunction]
141 pub fn signal(
142 _signalnum: i32,
143 _handler: PyObjectRef,
144 vm: &VirtualMachine,
145 ) -> PyResult<Option<PyObjectRef>> {
146 Err(vm.new_not_implemented_error("signal is not implemented on this platform".to_owned()))
147 }
148
149 #[cfg(any(unix, windows))]
150 #[pyfunction]
151 pub fn signal(
152 signalnum: i32,
153 handler: PyObjectRef,
154 vm: &VirtualMachine,
155 ) -> PyResult<Option<PyObjectRef>> {
156 signal::assert_in_range(signalnum, vm)?;
157 let signal_handlers = vm
158 .signal_handlers
159 .as_deref()
160 .ok_or_else(|| vm.new_value_error("signal only works in main thread".to_owned()))?;
161
162 let sig_handler =
163 match usize::try_from_borrowed_object(vm, &handler).ok() {
164 Some(SIG_DFL) => SIG_DFL,
165 Some(SIG_IGN) => SIG_IGN,
166 None if handler.is_callable() => run_signal as sighandler_t,
167 _ => return Err(vm.new_type_error(
168 "signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object"
169 .to_owned(),
170 )),
171 };
172 signal::check_signals(vm)?;
173
174 let old = unsafe { libc::signal(signalnum, sig_handler) };
175 if old == SIG_ERR {
176 return Err(vm.new_os_error("Failed to set signal".to_owned()));
177 }
178 #[cfg(all(unix, not(target_os = "redox")))]
179 unsafe {
180 siginterrupt(signalnum, 1);
181 }
182
183 let old_handler = std::mem::replace(
184 &mut signal_handlers.borrow_mut()[signalnum as usize],
185 Some(handler),
186 );
187 Ok(old_handler)
188 }
189
190 #[pyfunction]
191 fn getsignal(signalnum: i32, vm: &VirtualMachine) -> PyResult {
192 signal::assert_in_range(signalnum, vm)?;
193 let signal_handlers = vm
194 .signal_handlers
195 .as_deref()
196 .ok_or_else(|| vm.new_value_error("getsignal only works in main thread".to_owned()))?;
197 let handler = signal_handlers.borrow()[signalnum as usize]
198 .clone()
199 .unwrap_or_else(|| vm.ctx.none());
200 Ok(handler)
201 }
202
203 #[cfg(unix)]
204 #[pyfunction]
205 fn alarm(time: u32) -> u32 {
206 let prev_time = if time == 0 {
207 sig_alarm::cancel()
208 } else {
209 sig_alarm::set(time)
210 };
211 prev_time.unwrap_or(0)
212 }
213
214 #[pyfunction]
215 fn default_int_handler(
216 _signum: PyObjectRef,
217 _arg: PyObjectRef,
218 vm: &VirtualMachine,
219 ) -> PyResult {
220 Err(vm.new_exception_empty(vm.ctx.exceptions.keyboard_interrupt.to_owned()))
221 }
222
223 #[derive(FromArgs)]
224 struct SetWakeupFdArgs {
225 fd: WakeupFd,
226 #[pyarg(named, default = "true")]
227 warn_on_full_buffer: bool,
228 }
229
230 #[pyfunction]
231 fn set_wakeup_fd(args: SetWakeupFdArgs, vm: &VirtualMachine) -> PyResult<WakeupFdRaw> {
232 let _ = args.warn_on_full_buffer;
234 #[cfg(windows)]
235 let fd = args.fd.0;
236 #[cfg(not(windows))]
237 let fd = args.fd;
238
239 if vm.signal_handlers.is_none() {
240 return Err(vm.new_value_error("signal only works in main thread".to_owned()));
241 }
242
243 #[cfg(windows)]
244 let is_socket = if fd != INVALID_WAKEUP {
245 use windows_sys::Win32::Networking::WinSock;
246
247 crate::windows::init_winsock();
248 let mut res = 0i32;
249 let mut res_size = std::mem::size_of::<i32>() as i32;
250 let res = unsafe {
251 WinSock::getsockopt(
252 fd,
253 WinSock::SOL_SOCKET,
254 WinSock::SO_ERROR,
255 &mut res as *mut i32 as *mut _,
256 &mut res_size,
257 )
258 };
259 let is_socket = res == 0;
261 if !is_socket {
262 let err = std::io::Error::last_os_error();
263 if err.raw_os_error() != Some(WinSock::WSAENOTSOCK) {
265 return Err(err.into_pyexception(vm));
266 }
267 }
268 is_socket
269 } else {
270 false
271 };
272 #[cfg(unix)]
273 if fd != INVALID_WAKEUP {
274 use nix::fcntl;
275 let oflags = fcntl::fcntl(fd, fcntl::F_GETFL).map_err(|e| e.into_pyexception(vm))?;
276 let nonblock =
277 fcntl::OFlag::from_bits_truncate(oflags).contains(fcntl::OFlag::O_NONBLOCK);
278 if !nonblock {
279 return Err(vm.new_value_error(format!("the fd {fd} must be in non-blocking mode")));
280 }
281 }
282
283 let old_fd = WAKEUP.swap(fd, Ordering::Relaxed);
284 #[cfg(windows)]
285 WAKEUP_IS_SOCKET.store(is_socket, Ordering::Relaxed);
286
287 Ok(old_fd)
288 }
289
290 #[cfg(all(unix, not(target_os = "redox")))]
291 #[pyfunction(name = "siginterrupt")]
292 fn py_siginterrupt(signum: i32, flag: i32, vm: &VirtualMachine) -> PyResult<()> {
293 signal::assert_in_range(signum, vm)?;
294 let res = unsafe { siginterrupt(signum, flag) };
295 if res < 0 {
296 Err(crate::stdlib::os::errno_err(vm))
297 } else {
298 Ok(())
299 }
300 }
301
302 #[cfg(any(unix, windows))]
303 pub extern "C" fn run_signal(signum: i32) {
304 signal::TRIGGERS[signum as usize].store(true, Ordering::Relaxed);
305 signal::set_triggered();
306 let wakeup_fd = WAKEUP.load(Ordering::Relaxed);
307 if wakeup_fd != INVALID_WAKEUP {
308 let sigbyte = signum as u8;
309 #[cfg(windows)]
310 if WAKEUP_IS_SOCKET.load(Ordering::Relaxed) {
311 let _res = unsafe {
312 windows_sys::Win32::Networking::WinSock::send(
313 wakeup_fd,
314 &sigbyte as *const u8 as *const _,
315 1,
316 0,
317 )
318 };
319 return;
320 }
321 let _res = unsafe { libc::write(wakeup_fd as _, &sigbyte as *const u8 as *const _, 1) };
322 }
324 }
325}