1use super::Pid;
20use crate::io::Fd;
21use crate::signal;
22#[cfg(doc)]
23use crate::system::Concurrent;
24use crate::system::{
25 Disposition, Result, Sigaction, Sigmask, SigmaskOp, Signals, Sigset as _, TcSetPgrp,
26};
27
28pub trait RunBlocking: Signals {
42 fn run_blocking<F, T>(
54 &self,
55 signal: signal::Number,
56 f: F,
57 ) -> impl Future<Output = Result<T>> + use<'_, Self, F, T>
58 where
59 F: AsyncFnOnce() -> Result<T>;
60}
61
62impl<S> RunBlocking for S
63where
64 S: Sigmask + ?Sized,
65{
66 async fn run_blocking<F, T>(&self, signal: signal::Number, f: F) -> Result<T>
67 where
68 F: AsyncFnOnce() -> Result<T>,
69 {
70 let mut old_mask = S::Sigset::new();
71 self.sigmask(
72 Some((SigmaskOp::Add, &S::Sigset::from_signals([signal])?)),
73 Some(&mut old_mask),
74 )
75 .await?;
76
77 let main_result = f().await;
78
79 let restore_result = self.sigmask(Some((SigmaskOp::Set, &old_mask)), None).await;
80 if main_result.is_ok() {
81 restore_result?;
82 }
83
84 main_result
85 }
86}
87
88pub async fn tcsetpgrp_with_block<S>(system: &S, fd: Fd, pgid: Pid) -> Result<()>
101where
102 S: RunBlocking + TcSetPgrp + ?Sized,
103{
104 system
105 .run_blocking(S::SIGTTOU, || system.tcsetpgrp(fd, pgid))
106 .await
107}
108
109pub trait RunUnblocking: Signals {
124 fn run_unblocking<F, T>(
147 &self,
148 signal: signal::Number,
149 f: F,
150 ) -> impl Future<Output = Result<T>> + use<'_, Self, F, T>
151 where
152 F: AsyncFnOnce() -> Result<T>;
153}
154
155impl<S> RunUnblocking for S
156where
157 S: Sigmask + Sigaction + ?Sized,
158{
159 async fn run_unblocking<F, T>(&self, signal: signal::Number, f: F) -> Result<T>
160 where
161 F: AsyncFnOnce() -> Result<T>,
162 {
163 if signal == S::SIGKILL || signal == S::SIGSTOP {
164 return f().await;
166 }
167
168 let sigset = S::Sigset::from_signals([signal])?;
169
170 let old_handling = self.sigaction(signal, Disposition::Default)?;
171
172 let mut old_mask = S::Sigset::new();
173 let unblock_result = self
174 .sigmask(Some((SigmaskOp::Remove, &sigset)), Some(&mut old_mask))
175 .await;
176 if let Err(e) = unblock_result {
177 _ = self.sigaction(signal, old_handling);
178 return Err(e);
179 }
180
181 let main_result = f().await;
182
183 let restore_mask_result = self.sigmask(Some((SigmaskOp::Set, &old_mask)), None).await;
184 let restore_action_result = self.sigaction(signal, old_handling);
185
186 if main_result.is_ok() {
187 restore_mask_result?;
188 restore_action_result?;
189 }
190 main_result
191 }
192}
193
194pub async fn tcsetpgrp_without_block<S>(system: &S, fd: Fd, pgid: Pid) -> Result<()>
217where
218 S: RunUnblocking + TcSetPgrp + ?Sized,
219{
220 system
221 .run_unblocking(S::SIGTTOU, || system.tcsetpgrp(fd, pgid))
222 .await
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 use crate::signal;
229 use crate::system::r#virtual::{
230 SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGIOT,
231 SIGKILL, SIGPIPE, SIGPROF, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTERM, SIGTRAP, SIGTSTP,
232 SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH, SIGXCPU, SIGXFSZ,
233 };
234 use crate::system::{Errno, GetSigaction};
235 use futures_util::FutureExt as _;
236 use std::cell::{Cell, RefCell};
237 use std::collections::{BTreeMap, BTreeSet};
238 use std::ops::RangeInclusive;
239
240 #[derive(Clone, Debug, Default, Eq, PartialEq)]
241 struct TestSigset(BTreeSet<signal::Number>);
242
243 impl crate::system::Sigset for TestSigset {
244 fn full() -> Self {
245 unimplemented!("not needed for tests")
246 }
247
248 fn insert(&mut self, signal: signal::Number) -> Result<()> {
249 self.0.insert(signal);
250 Ok(())
251 }
252
253 fn remove(&mut self, signal: signal::Number) -> Result<()> {
254 self.0.remove(&signal);
255 Ok(())
256 }
257
258 fn contains(&self, signal: signal::Number) -> Result<bool> {
259 Ok(self.0.contains(&signal))
260 }
261 }
262
263 #[derive(Default)]
264 struct MockSystem {
265 mask: RefCell<TestSigset>,
266 dispositions: RefCell<BTreeMap<signal::Number, Disposition>>,
267 sigmask_errors: RefCell<BTreeMap<usize, Errno>>,
268 sigaction_errors: RefCell<BTreeMap<usize, Errno>>,
269 sigmask_call_count: Cell<usize>,
270 sigaction_call_count: Cell<usize>,
271 }
272
273 impl MockSystem {
274 fn set_mask(&self, mask: TestSigset) {
275 self.mask.replace(mask);
276 }
277
278 fn set_disposition(&self, signal: signal::Number, disposition: Disposition) {
279 self.dispositions.borrow_mut().insert(signal, disposition);
280 }
281
282 fn disposition_of(&self, signal: signal::Number) -> Disposition {
283 self.dispositions
284 .borrow()
285 .get(&signal)
286 .copied()
287 .unwrap_or_default()
288 }
289
290 fn is_blocked(&self, signal: signal::Number) -> bool {
291 self.mask
292 .borrow()
293 .contains(signal)
294 .expect("signals in tests are always valid")
295 }
296
297 fn set_sigmask_error_on_call(&self, call: usize, error: Errno) {
298 self.sigmask_errors.borrow_mut().insert(call, error);
299 }
300
301 fn set_sigaction_error_on_call(&self, call: usize, error: Errno) {
302 self.sigaction_errors.borrow_mut().insert(call, error);
303 }
304 }
305
306 impl Signals for MockSystem {
307 const SIGABRT: signal::Number = SIGABRT;
308 const SIGALRM: signal::Number = SIGALRM;
309 const SIGBUS: signal::Number = SIGBUS;
310 const SIGCHLD: signal::Number = SIGCHLD;
311 const SIGCLD: Option<signal::Number> = None;
312 const SIGCONT: signal::Number = SIGCONT;
313 const SIGEMT: Option<signal::Number> = None;
314 const SIGFPE: signal::Number = SIGFPE;
315 const SIGHUP: signal::Number = SIGHUP;
316 const SIGILL: signal::Number = SIGILL;
317 const SIGINFO: Option<signal::Number> = None;
318 const SIGINT: signal::Number = SIGINT;
319 const SIGIO: Option<signal::Number> = None;
320 const SIGIOT: signal::Number = SIGIOT;
321 const SIGKILL: signal::Number = SIGKILL;
322 const SIGLOST: Option<signal::Number> = None;
323 const SIGPIPE: signal::Number = SIGPIPE;
324 const SIGPOLL: Option<signal::Number> = None;
325 const SIGPROF: signal::Number = SIGPROF;
326 const SIGPWR: Option<signal::Number> = None;
327 const SIGQUIT: signal::Number = SIGQUIT;
328 const SIGSEGV: signal::Number = SIGSEGV;
329 const SIGSTKFLT: Option<signal::Number> = None;
330 const SIGSTOP: signal::Number = SIGSTOP;
331 const SIGSYS: signal::Number = SIGSYS;
332 const SIGTERM: signal::Number = SIGTERM;
333 const SIGTHR: Option<signal::Number> = None;
334 const SIGTRAP: signal::Number = SIGTRAP;
335 const SIGTSTP: signal::Number = SIGTSTP;
336 const SIGTTIN: signal::Number = SIGTTIN;
337 const SIGTTOU: signal::Number = SIGTTOU;
338 const SIGURG: signal::Number = SIGURG;
339 const SIGUSR1: signal::Number = SIGUSR1;
340 const SIGUSR2: signal::Number = SIGUSR2;
341 const SIGVTALRM: signal::Number = SIGVTALRM;
342 const SIGWINCH: signal::Number = SIGWINCH;
343 const SIGXCPU: signal::Number = SIGXCPU;
344 const SIGXFSZ: signal::Number = SIGXFSZ;
345
346 fn sigrt_range(&self) -> Option<RangeInclusive<signal::Number>> {
347 None
348 }
349 }
350
351 impl Sigmask for MockSystem {
352 type Sigset = TestSigset;
353
354 fn sigmask(
355 &self,
356 op: Option<(SigmaskOp, &Self::Sigset)>,
357 old_mask: Option<&mut Self::Sigset>,
358 ) -> impl Future<Output = Result<()>> + use<> {
359 let call_count = self.sigmask_call_count.get() + 1;
360 self.sigmask_call_count.set(call_count);
361
362 if let Some(error) = self.sigmask_errors.borrow_mut().remove(&call_count) {
363 return std::future::ready(Err(error));
364 }
365
366 let result = {
367 let mut mask = self.mask.borrow_mut();
368 if let Some(old_mask) = old_mask {
369 old_mask.clone_from(&mask);
370 }
371
372 if let Some((op, signals)) = op {
373 match op {
374 SigmaskOp::Add => {
375 for &signal in &signals.0 {
376 mask.insert(signal).unwrap();
377 }
378 }
379 SigmaskOp::Remove => {
380 for &signal in &signals.0 {
381 mask.remove(signal).unwrap();
382 }
383 }
384 SigmaskOp::Set => {
385 *mask = signals.clone();
386 }
387 }
388 }
389
390 Ok(())
391 };
392
393 std::future::ready(result)
394 }
395 }
396
397 impl GetSigaction for MockSystem {
398 fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition> {
399 Ok(self.disposition_of(signal))
400 }
401 }
402
403 impl Sigaction for MockSystem {
404 fn sigaction(&self, signal: signal::Number, action: Disposition) -> Result<Disposition> {
405 let call_count = self.sigaction_call_count.get() + 1;
406 self.sigaction_call_count.set(call_count);
407
408 if let Some(error) = self.sigaction_errors.borrow_mut().remove(&call_count) {
409 return Err(error);
410 }
411
412 Ok(self
413 .dispositions
414 .borrow_mut()
415 .insert(signal, action)
416 .unwrap_or_default())
417 }
418 }
419
420 #[test]
421 fn run_blocking_blocks_signal_and_restores_mask_on_success() {
422 let system = MockSystem::default();
423 let called = Cell::new(false);
424
425 let result = system
426 .run_blocking(MockSystem::SIGTTOU, async || {
427 called.set(true);
428 assert!(system.is_blocked(MockSystem::SIGTTOU));
429 Ok(())
430 })
431 .now_or_never()
432 .unwrap();
433
434 assert_eq!(result, Ok(()));
435 assert!(called.get());
436 assert!(!system.is_blocked(MockSystem::SIGTTOU));
437 }
438
439 #[test]
440 fn run_blocking_does_not_run_function_when_initial_sigmask_fails() {
441 let system = MockSystem::default();
442 system.set_sigmask_error_on_call(1, Errno::EINVAL);
443
444 let result = system
445 .run_blocking(MockSystem::SIGTTOU, async || -> Result<()> {
446 unreachable!("closure should not be called")
447 })
448 .now_or_never()
449 .unwrap();
450
451 assert_eq!(result, Err(Errno::EINVAL));
452 }
453
454 #[test]
455 fn run_blocking_discards_restore_error_when_function_returns_error() {
456 let system = MockSystem::default();
457 system.set_sigmask_error_on_call(2, Errno::EPERM);
458
459 let result = system
460 .run_blocking(MockSystem::SIGTTOU, async || Err::<(), _>(Errno::EINTR))
461 .now_or_never()
462 .unwrap();
463
464 assert_eq!(result, Err(Errno::EINTR));
465 }
466
467 #[test]
468 fn run_blocking_propagates_restore_error_when_function_succeeds() {
469 let system = MockSystem::default();
470 system.set_sigmask_error_on_call(2, Errno::EPERM);
471
472 let result = system
473 .run_blocking(MockSystem::SIGTTOU, async || Ok(()))
474 .now_or_never()
475 .unwrap();
476
477 assert_eq!(result, Err(Errno::EPERM));
478 }
479
480 #[test]
481 fn run_unblocking_for_sigkill_runs_function_without_sigaction_or_sigmask() {
482 let system = MockSystem::default();
483 let called = Cell::new(false);
484
485 let result = system
486 .run_unblocking(MockSystem::SIGKILL, async || {
487 called.set(true);
488 Err::<(), _>(Errno::EINTR)
489 })
490 .now_or_never()
491 .unwrap();
492
493 assert_eq!(result, Err(Errno::EINTR));
494 assert!(called.get());
495 assert_eq!(system.sigmask_call_count.get(), 0);
496 assert_eq!(system.sigaction_call_count.get(), 0);
497 }
498
499 #[test]
500 fn run_unblocking_for_sigstop_runs_function_without_sigaction_or_sigmask() {
501 let system = MockSystem::default();
502 let called = Cell::new(false);
503
504 let result = system
505 .run_unblocking(MockSystem::SIGSTOP, async || {
506 called.set(true);
507 Err::<(), _>(Errno::EINTR)
508 })
509 .now_or_never()
510 .unwrap();
511
512 assert_eq!(result, Err(Errno::EINTR));
513 assert!(called.get());
514 assert_eq!(system.sigmask_call_count.get(), 0);
515 assert_eq!(system.sigaction_call_count.get(), 0);
516 }
517
518 #[test]
519 fn run_unblocking_sets_default_unblocks_and_restores_on_success() {
520 let system = MockSystem::default();
521 let called = Cell::new(false);
522 let mut initial_mask = TestSigset::new();
523 initial_mask.insert(MockSystem::SIGTTOU).unwrap();
524 system.set_mask(initial_mask.clone());
525 system.set_disposition(MockSystem::SIGTTOU, Disposition::Ignore);
526
527 let result = system
528 .run_unblocking(MockSystem::SIGTTOU, async || {
529 called.set(true);
530 assert_eq!(
531 system.disposition_of(MockSystem::SIGTTOU),
532 Disposition::Default
533 );
534 assert!(!system.is_blocked(MockSystem::SIGTTOU));
535 Ok(())
536 })
537 .now_or_never()
538 .unwrap();
539
540 assert_eq!(result, Ok(()));
541 assert!(called.get());
542 assert!(system.is_blocked(MockSystem::SIGTTOU));
543 assert_eq!(
544 system.disposition_of(MockSystem::SIGTTOU),
545 Disposition::Ignore
546 );
547 }
548
549 #[test]
550 fn run_unblocking_returns_error_when_unblock_sigmask_fails_and_restores_disposition() {
551 let system = MockSystem::default();
552 system.set_disposition(MockSystem::SIGTTOU, Disposition::Ignore);
553 system.set_sigmask_error_on_call(1, Errno::EPERM);
554
555 let result = system
556 .run_unblocking(MockSystem::SIGTTOU, async || -> Result<()> {
557 unreachable!("closure should not be called")
558 })
559 .now_or_never()
560 .unwrap();
561
562 assert_eq!(result, Err(Errno::EPERM));
563 assert_eq!(
564 system.disposition_of(MockSystem::SIGTTOU),
565 Disposition::Ignore
566 );
567 }
568
569 #[test]
570 fn run_unblocking_discards_restore_errors_when_function_returns_error() {
571 let system = MockSystem::default();
572 let called = Cell::new(false);
573 system.set_sigmask_error_on_call(2, Errno::EPERM);
574 system.set_sigaction_error_on_call(2, Errno::EINVAL);
575
576 let result = system
577 .run_unblocking(MockSystem::SIGTTOU, async || {
578 called.set(true);
579 Err::<(), _>(Errno::EINTR)
580 })
581 .now_or_never()
582 .unwrap();
583
584 assert_eq!(result, Err(Errno::EINTR));
585 assert!(called.get());
586 }
587
588 #[test]
589 fn run_unblocking_propagates_restore_mask_error_when_function_succeeds() {
590 let system = MockSystem::default();
591 system.set_sigmask_error_on_call(2, Errno::EPERM);
592
593 let result = system
594 .run_unblocking(MockSystem::SIGTTOU, async || Ok(()))
595 .now_or_never()
596 .unwrap();
597
598 assert_eq!(result, Err(Errno::EPERM));
599 }
600
601 #[test]
602 fn run_unblocking_restores_sigaction_on_sigmask_restoration_error() {
603 let system = MockSystem::default();
604 system.set_disposition(MockSystem::SIGTTOU, Disposition::Ignore);
605 system.set_sigmask_error_on_call(2, Errno::EPERM);
606
607 let result = system
608 .run_unblocking(MockSystem::SIGTTOU, async || Ok(()))
609 .now_or_never()
610 .unwrap();
611
612 assert_eq!(result, Err(Errno::EPERM));
613 assert_eq!(
614 system.disposition_of(MockSystem::SIGTTOU),
615 Disposition::Ignore
616 );
617 }
618
619 #[test]
620 fn run_unblocking_propagates_restore_action_error_when_function_succeeds() {
621 let system = MockSystem::default();
622 system.set_sigaction_error_on_call(2, Errno::EINVAL);
623
624 let result = system
625 .run_unblocking(MockSystem::SIGTTOU, async || Ok(()))
626 .now_or_never()
627 .unwrap();
628
629 assert_eq!(result, Err(Errno::EINVAL));
630 }
631}