1use std::io::{BufRead, Read};
2use std::{fs, io};
3
4use rustix::thread::*;
5
6#[test]
7fn test_get_keep_capabilities() {
8    dbg!(get_keep_capabilities().unwrap());
9}
10
11#[test]
12fn test_name() {
13    dbg!(name().unwrap());
14}
15
16#[test]
17fn test_is_in_capability_bounding_set() {
18    dbg!(is_in_capability_bounding_set(Capability::ChangeOwnership).unwrap());
19}
20
21#[test]
22fn test_capabilities_secure_bits() {
23    dbg!(capabilities_secure_bits().unwrap());
24}
25
26#[test]
27fn test_current_timer_slack() {
28    dbg!(current_timer_slack().unwrap());
29}
30
31#[test]
32fn test_no_new_privs() {
33    dbg!(no_new_privs().unwrap());
34}
35
36#[test]
37fn test_capability_is_in_ambient_capability_set() {
38    dbg!(capability_is_in_ambient_capability_set(Capability::ChangeOwnership).unwrap());
39}
40
41#[cfg(target_arch = "aarch64")]
42#[test]
43fn test_sve_vector_length_configuration() {
44    dbg!(sve_vector_length_configuration().unwrap());
45}
46
47#[cfg(target_arch = "aarch64")]
48#[test]
49fn test_current_tagged_address_mode() {
50    dbg!(current_tagged_address_mode().unwrap());
51}
52
53#[test]
54#[ignore = "?"]
55fn test_transparent_huge_pages_are_disabled() {
56    dbg!(transparent_huge_pages_are_disabled().unwrap());
57}
58
59/*
60#[test]
61#[ignore = "Might result in SIGKILL"]
62fn test_secure_computing_mode() {
63    if !linux_kernel_config_item_is_enabled("CONFIG_SECCOMP").unwrap_or(false) {
64        eprintln!("test_secure_computing_mode: Test skipped due to missing kernel feature: CONFIG_SECCOMP.");
65        return;
66    }
67
68    dbg!(secure_computing_mode().unwrap());
69}
70*/
71
72#[test]
73fn test_get_clear_child_tid_address() {
74    if !linux_kernel_config_item_is_enabled("CONFIG_CHECKPOINT_RESTORE").unwrap_or(false) {
75        eprintln!("test_get_clear_child_tid_address: Test skipped due to missing kernel feature: CONFIG_CHECKPOINT_RESTORE.");
76        return;
77    }
78
79    match get_clear_child_tid_address() {
80        Ok(address) => println!("get_clear_child_tid_address = {:?}", address),
81
82        Err(r) => {
83            let errno = r.raw_os_error();
84            assert!(errno == libc::ENODEV || errno == libc::EINVAL);
85            eprintln!("test_get_clear_child_tid_address: Test unsupported: {}", r);
86        }
87    }
88}
89
90#[test]
91fn test_core_scheduling_cookie() {
92    if !linux_kernel_config_item_is_enabled("CONFIG_SCHED_CORE").unwrap_or(false) {
93        eprintln!("test_core_scheduling_cookie: Test skipped due to missing kernel feature: CONFIG_SCHED_CORE.");
94        return;
95    }
96
97    match core_scheduling_cookie(rustix::thread::gettid(), CoreSchedulingScope::Thread) {
98        Ok(cookie) => println!("core_scheduling_cookie = {:?}", cookie),
99
100        Err(r) => {
101            let errno = r.raw_os_error();
102            assert!(errno == libc::ENODEV || errno == libc::EINVAL);
103            eprintln!("test_core_scheduling_cookie: Test unsupported: {}", r);
104        }
105    }
106}
107
108/*
109 * Helper functions.
110 */
111
112fn load_linux_kernel_config() -> io::Result<Vec<u8>> {
113    if let Ok(compressed_bytes) = fs::read("/proc/config.gz") {
114        let mut decoder = flate2::bufread::GzDecoder::new(compressed_bytes.as_slice());
115        let mut bytes = Vec::default();
116        decoder.read_to_end(&mut bytes)?;
117        return Ok(bytes);
118    }
119
120    let info = rustix::process::uname();
121    let release = info
122        .release()
123        .to_str()
124        .map_err(|_r| io::Error::from(io::ErrorKind::InvalidData))?;
125
126    fs::read(format!("/boot/config-{}", release))
127}
128
129fn is_linux_kernel_config_item_enabled(config: &[u8], name: &str) -> io::Result<bool> {
130    for line in io::Cursor::new(config).lines() {
131        let line = line?;
132        let line = line.trim();
133        if line.is_empty() || line.starts_with('#') {
134            continue;
135        }
136
137        let mut iter = line.splitn(2, '=');
138        if let Some(current_name) = iter.next().map(str::trim) {
139            if current_name == name {
140                if let Some(mut value) = iter.next().map(str::trim) {
141                    if value.starts_with('"') && value.ends_with('"') {
142                        // Just remove the quotes, but don't bother unescaping the inner string
143                        // because we are only trying to find out if the option is an true boolean.
144                        value = &value[1..(value.len() - 2)];
145                    }
146
147                    return Ok(value == "y" || value == "m");
148                }
149            }
150        }
151    }
152    Ok(false)
153}
154
155pub(crate) fn linux_kernel_config_item_is_enabled(name: &str) -> io::Result<bool> {
156    let config = load_linux_kernel_config()?;
157    is_linux_kernel_config_item_enabled(&config, name)
158}
159