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