1#[cfg(target_os = "android")]
8mod android;
9#[cfg(target_os = "android")]
10use android as target_os;
11#[cfg(target_os = "linux")]
12#[allow(clippy::module_inception)]
13mod linux;
14#[cfg(target_os = "linux")]
15use linux as target_os;
16use log::warn;
17#[macro_use]
18pub mod ioctl;
19#[macro_use]
20pub mod syslog;
21mod capabilities;
22mod descriptor;
23mod event;
24mod file;
25mod file_traits;
26mod mmap;
27mod net;
28mod notifiers;
29pub mod platform_timer_resolution;
30mod poll;
31mod priority;
32mod sched;
33mod shm;
34pub mod signal;
35mod signalfd;
36mod terminal;
37mod timer;
38pub mod vsock;
39mod write_zeroes;
40
41use std::ffi::CString;
42use std::fs::remove_file;
43use std::fs::File;
44use std::fs::OpenOptions;
45use std::mem;
46use std::mem::MaybeUninit;
47use std::ops::Deref;
48use std::os::unix::io::FromRawFd;
49use std::os::unix::io::RawFd;
50use std::os::unix::net::UnixDatagram;
51use std::os::unix::net::UnixListener;
52use std::os::unix::process::ExitStatusExt;
53use std::path::Path;
54use std::path::PathBuf;
55use std::process::ExitStatus;
56use std::ptr;
57use std::sync::OnceLock;
58use std::time::Duration;
59
60pub use capabilities::drop_capabilities;
61pub use event::EventExt;
62pub(crate) use event::PlatformEvent;
63pub use file::find_next_data;
64pub use file::FileDataIterator;
65pub(crate) use file_traits::lib::*;
66pub use ioctl::*;
67use libc::c_int;
68use libc::c_long;
69use libc::fcntl;
70use libc::pipe2;
71use libc::prctl;
72use libc::syscall;
73use libc::waitpid;
74use libc::SYS_getpid;
75use libc::SYS_getppid;
76use libc::SYS_gettid;
77use libc::EINVAL;
78use libc::O_CLOEXEC;
79use libc::PR_SET_NAME;
80use libc::SIGKILL;
81use libc::WNOHANG;
82pub use mmap::*;
83pub(in crate::sys) use net::sendmsg_nosignal as sendmsg;
84pub(in crate::sys) use net::sockaddr_un;
85pub(in crate::sys) use net::sockaddrv4_to_lib_c;
86pub(in crate::sys) use net::sockaddrv6_to_lib_c;
87pub use poll::EventContext;
88pub use priority::*;
89pub use sched::*;
90pub use shm::MemfdSeals;
91pub use shm::SharedMemoryLinux;
92pub use signal::*;
93pub use signalfd::Error as SignalFdError;
94pub use signalfd::*;
95pub use terminal::*;
96pub(crate) use write_zeroes::file_punch_hole;
97pub(crate) use write_zeroes::file_write_zeroes_at;
98
99use crate::descriptor::FromRawDescriptor;
100use crate::descriptor::SafeDescriptor;
101pub use crate::errno::Error;
102pub use crate::errno::Result;
103pub use crate::errno::*;
104use crate::number_of_logical_cores;
105use crate::round_up_to_page_size;
106pub use crate::sys::unix::descriptor::*;
107use crate::syscall;
108use crate::AsRawDescriptor;
109use crate::Pid;
110
111pub type Uid = libc::uid_t;
113pub type Gid = libc::gid_t;
114pub type Mode = libc::mode_t;
115
116const CPU_DIR: &str = "/sys/devices/system/cpu";
118
119#[inline(always)]
121pub fn set_thread_name(name: &str) -> Result<()> {
122 let name = CString::new(name).or(Err(Error::new(EINVAL)))?;
123 let ret = unsafe { prctl(PR_SET_NAME, name.as_c_str()) };
125 if ret == 0 {
126 Ok(())
127 } else {
128 errno_result()
129 }
130}
131
132#[inline(always)]
135pub fn getpid() -> Pid {
136 unsafe { syscall(SYS_getpid as c_long) as Pid }
139}
140
141#[inline(always)]
143pub fn getppid() -> Pid {
144 unsafe { syscall(SYS_getppid as c_long) as Pid }
147}
148
149pub fn gettid() -> Pid {
151 unsafe { syscall(SYS_gettid as c_long) as Pid }
154}
155
156#[inline(always)]
158pub fn geteuid() -> Uid {
159 unsafe { libc::geteuid() }
162}
163
164#[inline(always)]
166pub fn getegid() -> Gid {
167 unsafe { libc::getegid() }
170}
171
172pub enum FlockOperation {
174 LockShared,
175 LockExclusive,
176 Unlock,
177}
178
179#[inline(always)]
182pub fn flock<F: AsRawDescriptor>(file: &F, op: FlockOperation, nonblocking: bool) -> Result<()> {
183 let mut operation = match op {
184 FlockOperation::LockShared => libc::LOCK_SH,
185 FlockOperation::LockExclusive => libc::LOCK_EX,
186 FlockOperation::Unlock => libc::LOCK_UN,
187 };
188
189 if nonblocking {
190 operation |= libc::LOCK_NB;
191 }
192
193 syscall!(unsafe { libc::flock(file.as_raw_descriptor(), operation) }).map(|_| ())
196}
197
198pub enum FallocateMode {
200 PunchHole,
201 ZeroRange,
202 Allocate,
203}
204
205impl From<FallocateMode> for i32 {
206 fn from(value: FallocateMode) -> Self {
207 match value {
208 FallocateMode::Allocate => libc::FALLOC_FL_KEEP_SIZE,
209 FallocateMode::PunchHole => libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE,
210 FallocateMode::ZeroRange => libc::FALLOC_FL_ZERO_RANGE | libc::FALLOC_FL_KEEP_SIZE,
211 }
212 }
213}
214
215impl From<FallocateMode> for u32 {
216 fn from(value: FallocateMode) -> Self {
217 Into::<i32>::into(value) as u32
218 }
219}
220
221pub fn fallocate<F: AsRawDescriptor>(
223 file: &F,
224 mode: FallocateMode,
225 offset: u64,
226 len: u64,
227) -> Result<()> {
228 let offset = if offset > libc::off64_t::MAX as u64 {
229 return Err(Error::new(libc::EINVAL));
230 } else {
231 offset as libc::off64_t
232 };
233
234 let len = if len > libc::off64_t::MAX as u64 {
235 return Err(Error::new(libc::EINVAL));
236 } else {
237 len as libc::off64_t
238 };
239
240 syscall!(unsafe { libc::fallocate64(file.as_raw_descriptor(), mode.into(), offset, len) })
244 .map(|_| ())
245}
246
247#[repr(C)]
249#[derive(Default, Debug, Copy, Clone)]
250pub struct open_how {
251 pub flags: u64,
253 pub mode: u64,
255 pub resolve: u64,
257}
258
259pub fn openat2<D: AsRawDescriptor>(dir: &D, name: &std::ffi::CStr, how: &open_how) -> Result<File> {
261 syscall!(unsafe {
264 libc::syscall(
265 libc::SYS_openat2,
266 dir.as_raw_descriptor(),
267 name.as_ptr(),
268 how as *const open_how,
269 std::mem::size_of::<open_how>() as libc::size_t,
270 )
271 })
272 .map(|fd| unsafe { File::from_raw_descriptor(fd as RawFd) })
273}
274
275pub fn fstat<F: AsRawDescriptor>(f: &F) -> Result<libc::stat64> {
277 let mut st = MaybeUninit::<libc::stat64>::zeroed();
278
279 syscall!(unsafe { libc::fstat64(f.as_raw_descriptor(), st.as_mut_ptr()) })?;
283
284 Ok(unsafe { st.assume_init() })
287}
288
289pub fn is_block_file<F: AsRawDescriptor>(file: &F) -> Result<bool> {
291 let stat = fstat(file)?;
292 Ok((stat.st_mode & libc::S_IFMT) == libc::S_IFBLK)
293}
294
295const BLOCK_IO_TYPE: u32 = 0x12;
296ioctl_io_nr!(BLKDISCARD, BLOCK_IO_TYPE, 119);
297
298pub fn discard_block<F: AsRawDescriptor>(file: &F, offset: u64, len: u64) -> Result<()> {
300 let range: [u64; 2] = [offset, len];
301 syscall!(unsafe { libc::ioctl(file.as_raw_descriptor(), BLKDISCARD, &range) }).map(|_| ())
308}
309
310pub trait AsRawPid {
312 fn as_raw_pid(&self) -> Pid;
313}
314
315impl AsRawPid for Pid {
316 fn as_raw_pid(&self) -> Pid {
317 *self
318 }
319}
320
321impl AsRawPid for std::process::Child {
322 fn as_raw_pid(&self) -> Pid {
323 self.id() as Pid
324 }
325}
326
327pub fn wait_for_pid<A: AsRawPid>(pid: A, options: c_int) -> Result<(Option<Pid>, ExitStatus)> {
334 let pid = pid.as_raw_pid();
335 let mut status: c_int = 1;
336 let ret = unsafe { libc::waitpid(pid, &mut status, options) };
339 if ret < 0 {
340 return errno_result();
341 }
342 Ok((
343 if ret == 0 { None } else { Some(ret) },
344 ExitStatus::from_raw(status),
345 ))
346}
347
348pub fn reap_child() -> Result<Pid> {
374 let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
377 if ret == -1 {
378 errno_result()
379 } else {
380 Ok(ret)
381 }
382}
383
384pub fn kill_process_group() -> Result<()> {
389 unsafe { kill(0, SIGKILL) }?;
391 unreachable!();
393}
394
395pub fn pipe() -> Result<(File, File)> {
399 let mut pipe_fds = [-1; 2];
400 let ret = unsafe { pipe2(&mut pipe_fds[0], O_CLOEXEC) };
404 if ret == -1 {
405 errno_result()
406 } else {
407 Ok(unsafe {
411 (
412 File::from_raw_fd(pipe_fds[0]),
413 File::from_raw_fd(pipe_fds[1]),
414 )
415 })
416 }
417}
418
419pub fn set_pipe_size(fd: RawFd, size: usize) -> Result<usize> {
423 syscall!(unsafe { fcntl(fd, libc::F_SETPIPE_SZ, size as c_int) }).map(|ret| ret as usize)
426}
427
428pub fn new_pipe_full() -> Result<(File, File)> {
432 use std::io::Write;
433
434 let (rx, mut tx) = pipe()?;
435 let page_size = set_pipe_size(tx.as_raw_descriptor(), round_up_to_page_size(1))?;
437
438 let buf = vec![0u8; page_size];
440 tx.write_all(&buf)?;
441
442 Ok((rx, tx))
443}
444
445pub struct UnlinkUnixDatagram(pub UnixDatagram);
447impl AsRef<UnixDatagram> for UnlinkUnixDatagram {
448 fn as_ref(&self) -> &UnixDatagram {
449 &self.0
450 }
451}
452impl Drop for UnlinkUnixDatagram {
453 fn drop(&mut self) {
454 if let Ok(addr) = self.0.local_addr() {
455 if let Some(path) = addr.as_pathname() {
456 if let Err(e) = remove_file(path) {
457 warn!("failed to remove control socket file: {}", e);
458 }
459 }
460 }
461 }
462}
463
464pub struct UnlinkUnixListener(pub UnixListener);
466
467impl AsRef<UnixListener> for UnlinkUnixListener {
468 fn as_ref(&self) -> &UnixListener {
469 &self.0
470 }
471}
472
473impl Deref for UnlinkUnixListener {
474 type Target = UnixListener;
475
476 fn deref(&self) -> &UnixListener {
477 &self.0
478 }
479}
480
481impl Drop for UnlinkUnixListener {
482 fn drop(&mut self) {
483 if let Ok(addr) = self.0.local_addr() {
484 if let Some(path) = addr.as_pathname() {
485 if let Err(e) = remove_file(path) {
486 warn!("failed to remove control socket file: {}", e);
487 }
488 }
489 }
490 }
491}
492
493pub fn validate_raw_descriptor(raw_descriptor: RawDescriptor) -> Result<RawDescriptor> {
496 validate_raw_fd(&raw_descriptor)
497}
498
499pub fn validate_raw_fd(raw_fd: &RawFd) -> Result<RawFd> {
502 let flags = unsafe { libc::fcntl(*raw_fd, libc::F_GETFD) };
507 if flags < 0 || (flags & libc::FD_CLOEXEC) != 0 {
508 return Err(Error::new(libc::EBADF));
509 }
510
511 let dup_fd = unsafe { libc::fcntl(*raw_fd, libc::F_DUPFD_CLOEXEC, 0) };
516 if dup_fd < 0 {
517 return Err(Error::last());
518 }
519 Ok(dup_fd as RawFd)
520}
521
522pub fn poll_in<F: AsRawDescriptor>(fd: &F) -> bool {
527 let mut fds = libc::pollfd {
528 fd: fd.as_raw_descriptor(),
529 events: libc::POLLIN,
530 revents: 0,
531 };
532 let ret = unsafe { libc::poll(&mut fds, 1, 0) };
535 if ret == -1 {
539 return false;
540 }
541 fds.revents & libc::POLLIN != 0
542}
543
544pub fn max_timeout() -> Duration {
546 Duration::new(libc::time_t::MAX as u64, 999999999)
547}
548
549pub fn safe_descriptor_from_path<P: AsRef<Path>>(path: P) -> Result<Option<SafeDescriptor>> {
552 let path = path.as_ref();
553 if path.parent() == Some(Path::new("/proc/self/fd")) {
554 let raw_descriptor = path
555 .file_name()
556 .and_then(|fd_osstr| fd_osstr.to_str())
557 .and_then(|fd_str| fd_str.parse::<RawFd>().ok())
558 .ok_or_else(|| Error::new(EINVAL))?;
559 let validated_fd = validate_raw_fd(&raw_descriptor)?;
560 Ok(Some(
561 unsafe { SafeDescriptor::from_raw_descriptor(validated_fd) },
564 ))
565 } else {
566 Ok(None)
567 }
568}
569
570pub fn safe_descriptor_from_cmdline_fd(fd: &RawFd) -> Result<SafeDescriptor> {
574 let validated_fd = validate_raw_fd(fd)?;
575 Ok(
576 unsafe { SafeDescriptor::from_raw_descriptor(validated_fd) },
579 )
580}
581
582pub fn open_file_or_duplicate<P: AsRef<Path>>(path: P, options: &OpenOptions) -> Result<File> {
589 let path = path.as_ref();
590 Ok(if let Some(fd) = safe_descriptor_from_path(path)? {
592 fd.into()
593 } else {
594 options.open(path)?
595 })
596}
597
598pub fn max_open_files() -> Result<libc::rlimit64> {
600 let mut buf = mem::MaybeUninit::<libc::rlimit64>::zeroed();
601
602 let res = unsafe { libc::prlimit64(0, libc::RLIMIT_NOFILE, ptr::null(), buf.as_mut_ptr()) };
605 if res == 0 {
606 let limit = unsafe { buf.assume_init() };
609 Ok(limit)
610 } else {
611 errno_result()
612 }
613}
614
615pub fn call_with_extended_max_files<T, E>(
618 callback: impl FnOnce() -> std::result::Result<T, E>,
619) -> Result<std::result::Result<T, E>> {
620 let cur_limit = max_open_files()?;
621 let new_limit = libc::rlimit64 {
622 rlim_cur: cur_limit.rlim_max,
623 ..cur_limit
624 };
625 let needs_extension = cur_limit.rlim_cur < new_limit.rlim_cur;
626 if needs_extension {
627 set_max_open_files(new_limit)?;
628 }
629
630 let r = callback();
631
632 if needs_extension {
634 set_max_open_files(cur_limit)?;
635 }
636
637 Ok(r)
638}
639
640fn set_max_open_files(limit: libc::rlimit64) -> Result<()> {
642 let res = unsafe { libc::setrlimit64(libc::RLIMIT_NOFILE, &limit) };
645 if res == 0 {
646 Ok(())
647 } else {
648 errno_result()
649 }
650}
651
652pub fn move_to_cgroup(cgroup_path: PathBuf, id_to_write: Pid, cgroup_file: &str) -> Result<()> {
654 use std::io::Write;
655
656 let gpu_cgroup_file = cgroup_path.join(cgroup_file);
657 let mut f = File::create(gpu_cgroup_file)?;
658 f.write_all(id_to_write.to_string().as_bytes())?;
659 Ok(())
660}
661
662pub fn move_task_to_cgroup(cgroup_path: PathBuf, thread_id: Pid) -> Result<()> {
663 move_to_cgroup(cgroup_path, thread_id, "tasks")
664}
665
666pub fn move_proc_to_cgroup(cgroup_path: PathBuf, process_id: Pid) -> Result<()> {
667 move_to_cgroup(cgroup_path, process_id, "cgroup.procs")
668}
669
670fn read_sysfs_cpu_info_in_dir(cpu_dir: &str, cpu_id: usize, property: &str) -> Result<String> {
671 let path = Path::new(cpu_dir)
672 .join(format!("cpu{cpu_id}"))
673 .join(property);
674
675 std::fs::read_to_string(path).map_err(|e| e.into())
676}
677
678fn parse_sysfs_cpu_info_vec(cpu_id: usize, property: &str) -> Result<Vec<u32>> {
680 parse_sysfs_cpu_info_vec_in_dir(CPU_DIR, cpu_id, property)
681}
682
683fn parse_sysfs_cpu_info_vec_in_dir(
684 cpu_dir: &str,
685 cpu_id: usize,
686 property: &str,
687) -> Result<Vec<u32>> {
688 read_sysfs_cpu_info_in_dir(cpu_dir, cpu_id, property)?
689 .split_whitespace()
690 .map(|x| x.parse().map_err(|_| Error::new(libc::EINVAL)))
691 .collect()
692}
693
694pub fn logical_core_frequencies_khz(cpu_id: usize) -> Result<Vec<u32>> {
696 parse_sysfs_cpu_info_vec(cpu_id, "cpufreq/scaling_available_frequencies")
697}
698
699fn parse_sysfs_cpu_info(cpu_id: usize, property: &str) -> Result<u32> {
701 parse_sysfs_cpu_info_in_dir(CPU_DIR, cpu_id, property)
702}
703
704fn parse_sysfs_cpu_info_in_dir(cpu_dir: &str, cpu_id: usize, property: &str) -> Result<u32> {
705 read_sysfs_cpu_info_in_dir(cpu_dir, cpu_id, property)?
706 .trim()
707 .parse()
708 .map_err(|_| Error::new(libc::EINVAL))
709}
710
711pub fn logical_core_capacity(cpu_id: usize) -> Result<u32> {
713 static CPU_MAX_FREQS: OnceLock<Option<Vec<u32>>> = OnceLock::new();
714
715 let cpu_capacity = parse_sysfs_cpu_info(cpu_id, "cpu_capacity")?;
716
717 let cpu_max_freqs = CPU_MAX_FREQS.get_or_init(|| {
721 (0..number_of_logical_cores().ok()?)
722 .map(|cpu_id| logical_core_max_freq_khz(cpu_id).ok())
723 .collect()
724 });
725
726 if let Some(cpu_max_freqs) = cpu_max_freqs {
727 let largest_max_freq = *cpu_max_freqs.iter().max().ok_or(Error::new(EINVAL))?;
728 let cpu_max_freq = *cpu_max_freqs.get(cpu_id).ok_or(Error::new(EINVAL))?;
729 let normalized_cpu_capacity = (u64::from(cpu_capacity) * u64::from(largest_max_freq))
730 .checked_div(u64::from(cpu_max_freq))
731 .ok_or(Error::new(EINVAL))?;
732 normalized_cpu_capacity
733 .try_into()
734 .map_err(|_| Error::new(EINVAL))
735 } else {
736 Ok(cpu_capacity)
738 }
739}
740
741pub fn logical_core_cluster_id(cpu_id: usize) -> Result<u32> {
743 parse_sysfs_cpu_info(cpu_id, "topology/physical_package_id")
744}
745
746pub fn logical_core_max_freq_khz(cpu_id: usize) -> Result<u32> {
748 parse_sysfs_cpu_info(cpu_id, "cpufreq/cpuinfo_max_freq")
749}
750
751fn parse_online_cpu_range(content: &str) -> std::collections::BTreeSet<usize> {
753 let mut cpus = std::collections::BTreeSet::new();
754 for part in content.trim().split(',') {
755 let part = part.trim();
756 if part.is_empty() {
757 continue;
758 }
759 if let Some((start_str, end_str)) = part.split_once('-') {
760 if let (Ok(start), Ok(end)) = (start_str.parse::<usize>(), end_str.parse::<usize>()) {
761 for i in start..=end {
762 cpus.insert(i);
763 }
764 }
765 } else if let Ok(cpu) = part.parse::<usize>() {
766 cpus.insert(cpu);
767 }
768 }
769 cpus
770}
771
772pub fn is_cpu_online(cpu_id: usize) -> bool {
774 static ONLINE_CPUS: OnceLock<std::collections::BTreeSet<usize>> = OnceLock::new();
775
776 let online_cpus = ONLINE_CPUS.get_or_init(|| {
777 let mut cpus = std::collections::BTreeSet::new();
778 let path = Path::new(CPU_DIR).join("online");
779 match std::fs::read_to_string(&path) {
780 Ok(content) => {
781 cpus = parse_online_cpu_range(&content);
782 }
783 Err(_) => {
784 if let Ok(total_cores) = crate::number_of_logical_cores() {
787 for id in 0..total_cores {
788 match parse_sysfs_cpu_info(id, "online") {
789 Ok(1) => {
790 cpus.insert(id);
791 }
792 Ok(_) => {}
793 Err(e) => {
794 warn!(
796 "Assuming CPU {} is online because we couldn't read the sys file: {}",
797 id, e
798 );
799 cpus.insert(id);
800 }
801 }
802 }
803 }
804 }
805 }
806 cpus
807 });
808
809 online_cpus.contains(&cpu_id)
810}
811
812#[repr(C)]
813pub struct sched_attr {
814 pub size: u32,
815
816 pub sched_policy: u32,
817 pub sched_flags: u64,
818 pub sched_nice: i32,
819
820 pub sched_priority: u32,
821
822 pub sched_runtime: u64,
823 pub sched_deadline: u64,
824 pub sched_period: u64,
825
826 pub sched_util_min: u32,
827 pub sched_util_max: u32,
828}
829
830impl Default for sched_attr {
831 fn default() -> Self {
832 Self {
833 size: std::mem::size_of::<sched_attr>() as u32,
834 sched_policy: 0,
835 sched_flags: 0,
836 sched_nice: 0,
837 sched_priority: 0,
838 sched_runtime: 0,
839 sched_deadline: 0,
840 sched_period: 0,
841 sched_util_min: 0,
842 sched_util_max: 0,
843 }
844 }
845}
846
847pub fn sched_setattr(pid: Pid, attr: &mut sched_attr, flags: u32) -> Result<()> {
848 let ret = unsafe {
850 libc::syscall(
851 libc::SYS_sched_setattr,
852 pid as usize,
853 attr as *mut sched_attr as usize,
854 flags as usize,
855 )
856 };
857
858 if ret < 0 {
859 return Err(Error::last());
860 }
861 Ok(())
862}
863
864#[cfg(test)]
865mod tests {
866 use std::fs::create_dir_all;
867 use std::fs::File;
868 use std::io::Write;
869 use std::os::fd::AsRawFd;
870
871 use tempfile::TempDir;
872
873 use super::*;
874 use crate::unix::add_fd_flags;
875
876 fn create_temp_file(path: &Path, content: &str) {
877 if let Some(parent) = path.parent() {
878 create_dir_all(parent).unwrap();
879 }
880 let mut file = File::create(path).unwrap();
881 file.write_all(content.as_bytes()).unwrap();
882 }
883
884 #[test]
885 fn test_parse_online_cpu_range() {
886 let set = parse_online_cpu_range("0-3,5-7");
887 assert_eq!(set.len(), 7);
888 assert!(set.contains(&0));
889 assert!(set.contains(&1));
890 assert!(set.contains(&2));
891 assert!(set.contains(&3));
892 assert!(!set.contains(&4));
893 assert!(set.contains(&5));
894 assert!(set.contains(&6));
895 assert!(set.contains(&7));
896
897 let set = parse_online_cpu_range("0");
898 assert_eq!(set.len(), 1);
899 assert!(set.contains(&0));
900
901 let set = parse_online_cpu_range("0,2,4");
902 assert_eq!(set.len(), 3);
903 assert!(set.contains(&0));
904 assert!(set.contains(&2));
905 assert!(set.contains(&4));
906
907 let set = parse_online_cpu_range(" 0-1, 3 ");
908 assert_eq!(set.len(), 3);
909 assert!(set.contains(&0));
910 assert!(set.contains(&1));
911 assert!(set.contains(&3));
912
913 let set = parse_online_cpu_range("");
914 assert!(set.is_empty());
915 }
916
917 #[test]
918 fn pipe_size_and_fill() {
919 let (_rx, mut tx) = new_pipe_full().expect("Failed to pipe");
920
921 add_fd_flags(tx.as_raw_fd(), libc::O_NONBLOCK).expect("Failed to set tx non blocking");
924 tx.write(&[0u8; 8])
925 .expect_err("Write after fill didn't fail");
926 }
927
928 #[test]
929 fn test_parse_sysfs_cpu_info() {
930 let temp_dir = TempDir::new().unwrap();
931 let root = temp_dir.path();
932 let cpu_dir = root.join("sys/devices/system/cpu");
933 let cpu = 0;
934 let property = "cpufreq/cpuinfo_max_freq";
935 create_temp_file(
936 &root.join("sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"),
937 "1000",
938 );
939
940 assert_eq!(
941 parse_sysfs_cpu_info_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap(),
942 1000
943 );
944 }
945
946 #[test]
947 fn test_parse_sysfs_cpu_info_error() {
948 let temp_dir = TempDir::new().unwrap();
949 let root = temp_dir.path();
950 let cpu_dir = root.join("sys/devices/system/cpu");
951 let cpu = 0;
952 let property = "cpufreq/cpuinfo_max_freq";
953 let err =
956 parse_sysfs_cpu_info_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap_err();
957 assert_eq!(err, Error::new(libc::ENOENT));
958 }
959
960 #[test]
961 fn test_parse_sysfs_cpu_info_vec() {
962 let temp_dir = TempDir::new().unwrap();
963 let root = temp_dir.path();
964 let cpu_dir = root.join("sys/devices/system/cpu");
965 let cpu = 0;
966 let property = "cpufreq/scaling_available_frequencies";
967 create_temp_file(
968 &root.join("sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies"),
969 "1000 2000",
970 );
971
972 assert_eq!(
973 parse_sysfs_cpu_info_vec_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap(),
974 vec![1000, 2000]
975 );
976 }
977
978 #[test]
979 fn test_parse_sysfs_cpu_info_vec_error() {
980 let temp_dir = TempDir::new().unwrap();
981 let root = temp_dir.path();
982 let cpu_dir = root.join("sys/devices/system/cpu");
983 let cpu = 0;
984 let property = "cpufreq/scaling_available_frequencies";
985 let err =
988 parse_sysfs_cpu_info_vec_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap_err();
989 assert_eq!(err, Error::new(libc::ENOENT));
990 }
991}