summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/unix/process/process_common/tests.rs
blob: d176b3401c03c0d65afb2d07a37e11765fff0c54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use super::*;

use crate::ffi::OsStr;
use crate::mem;
use crate::ptr;
use crate::sys::{cvt, cvt_nz};

macro_rules! t {
    ($e:expr) => {
        match $e {
            Ok(t) => t,
            Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
        }
    };
}

#[test]
#[cfg_attr(
    any(
        // See #14232 for more information, but it appears that signal delivery to a
        // newly spawned process may just be raced in the macOS, so to prevent this
        // test from being flaky we ignore it on macOS.
        target_os = "macos",
        // When run under our current QEMU emulation test suite this test fails,
        // although the reason isn't very clear as to why. For now this test is
        // ignored there.
        target_arch = "arm",
        target_arch = "aarch64",
        target_arch = "riscv64",
    ),
    ignore
)]
fn test_process_mask() {
    unsafe {
        // Test to make sure that a signal mask does not get inherited.
        let mut cmd = Command::new(OsStr::new("cat"));

        let mut set = mem::MaybeUninit::<libc::sigset_t>::uninit();
        let mut old_set = mem::MaybeUninit::<libc::sigset_t>::uninit();
        t!(cvt(sigemptyset(set.as_mut_ptr())));
        t!(cvt(sigaddset(set.as_mut_ptr(), libc::SIGINT)));
        t!(cvt_nz(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), old_set.as_mut_ptr())));

        cmd.stdin(Stdio::MakePipe);
        cmd.stdout(Stdio::MakePipe);

        let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
        let stdin_write = pipes.stdin.take().unwrap();
        let stdout_read = pipes.stdout.take().unwrap();

        t!(cvt_nz(libc::pthread_sigmask(libc::SIG_SETMASK, old_set.as_ptr(), ptr::null_mut())));

        t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT)));
        // We need to wait until SIGINT is definitely delivered. The
        // easiest way is to write something to cat, and try to read it
        // back: if SIGINT is unmasked, it'll get delivered when cat is
        // next scheduled.
        let _ = stdin_write.write(b"Hello");
        drop(stdin_write);

        // Either EOF or failure (EPIPE) is okay.
        let mut buf = [0; 5];
        if let Ok(ret) = stdout_read.read(&mut buf) {
            assert_eq!(ret, 0);
        }

        t!(cat.wait());
    }
}

#[test]
#[cfg_attr(
    any(
        // See test_process_mask
        target_os = "macos",
        target_arch = "arm",
        target_arch = "aarch64",
        target_arch = "riscv64",
    ),
    ignore
)]
fn test_process_group_posix_spawn() {
    unsafe {
        // Spawn a cat subprocess that's just going to hang since there is no I/O.
        let mut cmd = Command::new(OsStr::new("cat"));
        cmd.pgroup(0);
        cmd.stdin(Stdio::MakePipe);
        cmd.stdout(Stdio::MakePipe);
        let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true));

        // Check that we can kill its process group, which means there *is* one.
        t!(cvt(libc::kill(-(cat.id() as libc::pid_t), libc::SIGINT)));

        t!(cat.wait());
    }
}

#[test]
#[cfg_attr(
    any(
        // See test_process_mask
        target_os = "macos",
        target_arch = "arm",
        target_arch = "aarch64",
        target_arch = "riscv64",
    ),
    ignore
)]
fn test_process_group_no_posix_spawn() {
    unsafe {
        // Same as above, create hang-y cat. This time, force using the non-posix_spawnp path.
        let mut cmd = Command::new(OsStr::new("cat"));
        cmd.pgroup(0);
        cmd.pre_exec(Box::new(|| Ok(()))); // pre_exec forces fork + exec
        cmd.stdin(Stdio::MakePipe);
        cmd.stdout(Stdio::MakePipe);
        let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true));

        // Check that we can kill its process group, which means there *is* one.
        t!(cvt(libc::kill(-(cat.id() as libc::pid_t), libc::SIGINT)));

        t!(cat.wait());
    }
}

#[test]
fn test_program_kind() {
    let vectors = &[
        ("foo", ProgramKind::PathLookup),
        ("foo.out", ProgramKind::PathLookup),
        ("./foo", ProgramKind::Relative),
        ("../foo", ProgramKind::Relative),
        ("dir/foo", ProgramKind::Relative),
        // Note that paths on Unix can't contain / in them, so this is actually the directory "fo\\"
        // followed by the file "o".
        ("fo\\/o", ProgramKind::Relative),
        ("/foo", ProgramKind::Absolute),
        ("/dir/../foo", ProgramKind::Absolute),
    ];

    for (program, expected_kind) in vectors {
        assert_eq!(
            ProgramKind::new(program.as_ref()),
            *expected_kind,
            "actual != expected program kind for input {program}",
        );
    }
}