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::fd::AsRawFd;
49use std::os::fd::BorrowedFd;
50use std::os::unix::io::FromRawFd;
51use std::os::unix::io::RawFd;
52use std::os::unix::net::UnixDatagram;
53use std::os::unix::net::UnixListener;
54use std::os::unix::process::ExitStatusExt;
55use std::path::Path;
56use std::path::PathBuf;
57use std::process::ExitStatus;
58use std::ptr;
59use std::sync::OnceLock;
60use std::time::Duration;
61
62pub use capabilities::drop_capabilities;
63pub use event::EventExt;
64pub(crate) use event::PlatformEvent;
65pub use file::find_next_data;
66pub use file::FileDataIterator;
67pub(crate) use file_traits::lib::*;
68pub use ioctl::*;
69use libc::c_int;
70use libc::c_long;
71use libc::fcntl;
72use libc::pipe2;
73use libc::prctl;
74use libc::syscall;
75use libc::waitpid;
76use libc::SYS_getpid;
77use libc::SYS_getppid;
78use libc::SYS_gettid;
79use libc::EINVAL;
80use libc::O_CLOEXEC;
81use libc::PR_SET_NAME;
82use libc::SIGKILL;
83use libc::WNOHANG;
84pub use mmap::*;
85pub(in crate::sys) use net::sendmsg_nosignal as sendmsg;
86pub(in crate::sys) use net::sockaddr_un;
87pub(in crate::sys) use net::sockaddrv4_to_lib_c;
88pub(in crate::sys) use net::sockaddrv6_to_lib_c;
89pub use poll::EventContext;
90pub use priority::*;
91pub use sched::*;
92pub use shm::MemfdSeals;
93pub use shm::SharedMemoryLinux;
94pub use signal::*;
95pub use signalfd::Error as SignalFdError;
96pub use signalfd::*;
97pub use terminal::*;
98pub(crate) use write_zeroes::file_punch_hole;
99pub(crate) use write_zeroes::file_write_zeroes_at;
100
101use crate::descriptor::FromRawDescriptor;
102use crate::descriptor::SafeDescriptor;
103pub use crate::errno::Error;
104pub use crate::errno::Result;
105pub use crate::errno::*;
106use crate::number_of_logical_cores;
107use crate::round_up_to_page_size;
108pub use crate::sys::unix::descriptor::*;
109use crate::syscall;
110use crate::AsRawDescriptor;
111use crate::IoBuf;
112use crate::IoBufMut;
113use crate::Pid;
114
115pub type Uid = libc::uid_t;
117pub type Gid = libc::gid_t;
118pub type Mode = libc::mode_t;
119
120const CPU_DIR: &str = "/sys/devices/system/cpu";
122
123#[inline(always)]
125pub fn set_thread_name(name: &str) -> Result<()> {
126 let name = CString::new(name).or(Err(Error::new(EINVAL)))?;
127 let ret = unsafe { prctl(PR_SET_NAME, name.as_c_str()) };
129 if ret == 0 {
130 Ok(())
131 } else {
132 errno_result()
133 }
134}
135
136#[inline(always)]
139pub fn getpid() -> Pid {
140 unsafe { syscall(SYS_getpid as c_long) as Pid }
143}
144
145#[inline(always)]
147pub fn getppid() -> Pid {
148 unsafe { syscall(SYS_getppid as c_long) as Pid }
151}
152
153pub fn gettid() -> Pid {
155 unsafe { syscall(SYS_gettid as c_long) as Pid }
158}
159
160#[inline(always)]
162pub fn geteuid() -> Uid {
163 unsafe { libc::geteuid() }
166}
167
168#[inline(always)]
170pub fn getegid() -> Gid {
171 unsafe { libc::getegid() }
174}
175
176pub enum FlockOperation {
178 LockShared,
179 LockExclusive,
180 Unlock,
181}
182
183#[inline(always)]
186pub fn flock<F: AsRawDescriptor>(file: &F, op: FlockOperation, nonblocking: bool) -> Result<()> {
187 let mut operation = match op {
188 FlockOperation::LockShared => libc::LOCK_SH,
189 FlockOperation::LockExclusive => libc::LOCK_EX,
190 FlockOperation::Unlock => libc::LOCK_UN,
191 };
192
193 if nonblocking {
194 operation |= libc::LOCK_NB;
195 }
196
197 syscall!(unsafe { libc::flock(file.as_raw_descriptor(), operation) }).map(|_| ())
200}
201
202pub enum FallocateMode {
204 PunchHole,
205 ZeroRange,
206 Allocate,
207}
208
209impl From<FallocateMode> for i32 {
210 fn from(value: FallocateMode) -> Self {
211 match value {
212 FallocateMode::Allocate => libc::FALLOC_FL_KEEP_SIZE,
213 FallocateMode::PunchHole => libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE,
214 FallocateMode::ZeroRange => libc::FALLOC_FL_ZERO_RANGE | libc::FALLOC_FL_KEEP_SIZE,
215 }
216 }
217}
218
219impl From<FallocateMode> for u32 {
220 fn from(value: FallocateMode) -> Self {
221 Into::<i32>::into(value) as u32
222 }
223}
224
225pub fn fallocate<F: AsRawDescriptor>(
227 file: &F,
228 mode: FallocateMode,
229 offset: u64,
230 len: u64,
231) -> Result<()> {
232 let offset = if offset > libc::off64_t::MAX as u64 {
233 return Err(Error::new(libc::EINVAL));
234 } else {
235 offset as libc::off64_t
236 };
237
238 let len = if len > libc::off64_t::MAX as u64 {
239 return Err(Error::new(libc::EINVAL));
240 } else {
241 len as libc::off64_t
242 };
243
244 syscall!(unsafe { libc::fallocate64(file.as_raw_descriptor(), mode.into(), offset, len) })
248 .map(|_| ())
249}
250
251#[repr(C)]
253#[derive(Default, Debug, Copy, Clone)]
254pub struct open_how {
255 pub flags: u64,
257 pub mode: u64,
259 pub resolve: u64,
261}
262
263pub fn openat2<D: AsRawDescriptor>(dir: &D, name: &std::ffi::CStr, how: &open_how) -> Result<File> {
265 syscall!(unsafe {
268 libc::syscall(
269 libc::SYS_openat2,
270 dir.as_raw_descriptor(),
271 name.as_ptr(),
272 how as *const open_how,
273 std::mem::size_of::<open_how>() as libc::size_t,
274 )
275 })
276 .map(|fd| unsafe { File::from_raw_descriptor(fd as RawFd) })
277}
278
279pub fn fstat<F: AsRawDescriptor>(f: &F) -> Result<libc::stat64> {
281 let mut st = MaybeUninit::<libc::stat64>::zeroed();
282
283 syscall!(unsafe { libc::fstat64(f.as_raw_descriptor(), st.as_mut_ptr()) })?;
287
288 Ok(unsafe { st.assume_init() })
291}
292
293pub fn is_block_file<F: AsRawDescriptor>(file: &F) -> Result<bool> {
295 let stat = fstat(file)?;
296 Ok((stat.st_mode & libc::S_IFMT) == libc::S_IFBLK)
297}
298
299const BLOCK_IO_TYPE: u32 = 0x12;
300ioctl_io_nr!(BLKDISCARD, BLOCK_IO_TYPE, 119);
301
302pub fn discard_block<F: AsRawDescriptor>(file: &F, offset: u64, len: u64) -> Result<()> {
304 let range: [u64; 2] = [offset, len];
305 syscall!(unsafe { libc::ioctl(file.as_raw_descriptor(), BLKDISCARD, &range) }).map(|_| ())
312}
313
314pub trait AsRawPid {
316 fn as_raw_pid(&self) -> Pid;
317}
318
319impl AsRawPid for Pid {
320 fn as_raw_pid(&self) -> Pid {
321 *self
322 }
323}
324
325impl AsRawPid for std::process::Child {
326 fn as_raw_pid(&self) -> Pid {
327 self.id() as Pid
328 }
329}
330
331pub fn wait_for_pid<A: AsRawPid>(pid: A, options: c_int) -> Result<(Option<Pid>, ExitStatus)> {
338 let pid = pid.as_raw_pid();
339 let mut status: c_int = 1;
340 let ret = unsafe { libc::waitpid(pid, &mut status, options) };
343 if ret < 0 {
344 return errno_result();
345 }
346 Ok((
347 if ret == 0 { None } else { Some(ret) },
348 ExitStatus::from_raw(status),
349 ))
350}
351
352pub fn reap_child() -> Result<Pid> {
378 let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
381 if ret == -1 {
382 errno_result()
383 } else {
384 Ok(ret)
385 }
386}
387
388pub fn kill_process_group() -> Result<()> {
393 unsafe { kill(0, SIGKILL) }?;
395 unreachable!();
397}
398
399pub fn pipe() -> Result<(File, File)> {
403 let mut pipe_fds = [-1; 2];
404 let ret = unsafe { pipe2(&mut pipe_fds[0], O_CLOEXEC) };
408 if ret == -1 {
409 errno_result()
410 } else {
411 Ok(unsafe {
415 (
416 File::from_raw_fd(pipe_fds[0]),
417 File::from_raw_fd(pipe_fds[1]),
418 )
419 })
420 }
421}
422
423pub fn set_pipe_size(fd: RawFd, size: usize) -> Result<usize> {
427 syscall!(unsafe { fcntl(fd, libc::F_SETPIPE_SZ, size as c_int) }).map(|ret| ret as usize)
430}
431
432pub fn new_pipe_full() -> Result<(File, File)> {
436 use std::io::Write;
437
438 let (rx, mut tx) = pipe()?;
439 let page_size = set_pipe_size(tx.as_raw_descriptor(), round_up_to_page_size(1))?;
441
442 let buf = vec![0u8; page_size];
444 tx.write_all(&buf)?;
445
446 Ok((rx, tx))
447}
448
449pub struct UnlinkUnixDatagram(pub UnixDatagram);
451impl AsRef<UnixDatagram> for UnlinkUnixDatagram {
452 fn as_ref(&self) -> &UnixDatagram {
453 &self.0
454 }
455}
456impl Drop for UnlinkUnixDatagram {
457 fn drop(&mut self) {
458 if let Ok(addr) = self.0.local_addr() {
459 if let Some(path) = addr.as_pathname() {
460 if let Err(e) = remove_file(path) {
461 warn!("failed to remove control socket file: {}", e);
462 }
463 }
464 }
465 }
466}
467
468pub struct UnlinkUnixListener(pub UnixListener);
470
471impl AsRef<UnixListener> for UnlinkUnixListener {
472 fn as_ref(&self) -> &UnixListener {
473 &self.0
474 }
475}
476
477impl Deref for UnlinkUnixListener {
478 type Target = UnixListener;
479
480 fn deref(&self) -> &UnixListener {
481 &self.0
482 }
483}
484
485impl Drop for UnlinkUnixListener {
486 fn drop(&mut self) {
487 if let Ok(addr) = self.0.local_addr() {
488 if let Some(path) = addr.as_pathname() {
489 if let Err(e) = remove_file(path) {
490 warn!("failed to remove control socket file: {}", e);
491 }
492 }
493 }
494 }
495}
496
497pub fn validate_raw_descriptor(raw_descriptor: RawDescriptor) -> Result<RawDescriptor> {
500 validate_raw_fd(&raw_descriptor)
501}
502
503pub fn validate_raw_fd(raw_fd: &RawFd) -> Result<RawFd> {
506 let flags = unsafe { libc::fcntl(*raw_fd, libc::F_GETFD) };
511 if flags < 0 || (flags & libc::FD_CLOEXEC) != 0 {
512 return Err(Error::new(libc::EBADF));
513 }
514
515 let dup_fd = unsafe { libc::fcntl(*raw_fd, libc::F_DUPFD_CLOEXEC, 0) };
520 if dup_fd < 0 {
521 return Err(Error::last());
522 }
523 Ok(dup_fd as RawFd)
524}
525
526pub fn poll_in<F: AsRawDescriptor>(fd: &F) -> bool {
531 let mut fds = libc::pollfd {
532 fd: fd.as_raw_descriptor(),
533 events: libc::POLLIN,
534 revents: 0,
535 };
536 let ret = unsafe { libc::poll(&mut fds, 1, 0) };
539 if ret == -1 {
543 return false;
544 }
545 fds.revents & libc::POLLIN != 0
546}
547
548pub fn max_timeout() -> Duration {
550 Duration::new(libc::time_t::MAX as u64, 999999999)
551}
552
553pub fn safe_descriptor_from_path<P: AsRef<Path>>(path: P) -> Result<Option<SafeDescriptor>> {
556 let path = path.as_ref();
557 if path.parent() == Some(Path::new("/proc/self/fd")) {
558 let raw_descriptor = path
559 .file_name()
560 .and_then(|fd_osstr| fd_osstr.to_str())
561 .and_then(|fd_str| fd_str.parse::<RawFd>().ok())
562 .ok_or_else(|| Error::new(EINVAL))?;
563 let validated_fd = validate_raw_fd(&raw_descriptor)?;
564 Ok(Some(
565 unsafe { SafeDescriptor::from_raw_descriptor(validated_fd) },
568 ))
569 } else {
570 Ok(None)
571 }
572}
573
574pub fn safe_descriptor_from_cmdline_fd(fd: &RawFd) -> Result<SafeDescriptor> {
578 let validated_fd = validate_raw_fd(fd)?;
579 Ok(
580 unsafe { SafeDescriptor::from_raw_descriptor(validated_fd) },
583 )
584}
585
586pub fn open_file_or_duplicate<P: AsRef<Path>>(path: P, options: &OpenOptions) -> Result<File> {
593 let path = path.as_ref();
594 Ok(if let Some(fd) = safe_descriptor_from_path(path)? {
596 fd.into()
597 } else {
598 options.open(path)?
599 })
600}
601
602pub fn max_open_files() -> Result<libc::rlimit64> {
604 let mut buf = mem::MaybeUninit::<libc::rlimit64>::zeroed();
605
606 let res = unsafe { libc::prlimit64(0, libc::RLIMIT_NOFILE, ptr::null(), buf.as_mut_ptr()) };
609 if res == 0 {
610 let limit = unsafe { buf.assume_init() };
613 Ok(limit)
614 } else {
615 errno_result()
616 }
617}
618
619pub fn call_with_extended_max_files<T, E>(
622 callback: impl FnOnce() -> std::result::Result<T, E>,
623) -> Result<std::result::Result<T, E>> {
624 let cur_limit = max_open_files()?;
625 let new_limit = libc::rlimit64 {
626 rlim_cur: cur_limit.rlim_max,
627 ..cur_limit
628 };
629 let needs_extension = cur_limit.rlim_cur < new_limit.rlim_cur;
630 if needs_extension {
631 set_max_open_files(new_limit)?;
632 }
633
634 let r = callback();
635
636 if needs_extension {
638 set_max_open_files(cur_limit)?;
639 }
640
641 Ok(r)
642}
643
644fn set_max_open_files(limit: libc::rlimit64) -> Result<()> {
646 let res = unsafe { libc::setrlimit64(libc::RLIMIT_NOFILE, &limit) };
649 if res == 0 {
650 Ok(())
651 } else {
652 errno_result()
653 }
654}
655
656pub fn move_to_cgroup(cgroup_path: PathBuf, id_to_write: Pid, cgroup_file: &str) -> Result<()> {
658 use std::io::Write;
659
660 let gpu_cgroup_file = cgroup_path.join(cgroup_file);
661 let mut f = File::create(gpu_cgroup_file)?;
662 f.write_all(id_to_write.to_string().as_bytes())?;
663 Ok(())
664}
665
666pub fn move_task_to_cgroup(cgroup_path: PathBuf, thread_id: Pid) -> Result<()> {
667 move_to_cgroup(cgroup_path, thread_id, "tasks")
668}
669
670pub fn move_proc_to_cgroup(cgroup_path: PathBuf, process_id: Pid) -> Result<()> {
671 move_to_cgroup(cgroup_path, process_id, "cgroup.procs")
672}
673
674fn read_sysfs_cpu_info_in_dir(cpu_dir: &str, cpu_id: usize, property: &str) -> Result<String> {
675 let path = Path::new(cpu_dir)
676 .join(format!("cpu{cpu_id}"))
677 .join(property);
678
679 std::fs::read_to_string(path).map_err(|e| e.into())
680}
681
682fn parse_sysfs_cpu_info_vec(cpu_id: usize, property: &str) -> Result<Vec<u32>> {
684 parse_sysfs_cpu_info_vec_in_dir(CPU_DIR, cpu_id, property)
685}
686
687fn parse_sysfs_cpu_info_vec_in_dir(
688 cpu_dir: &str,
689 cpu_id: usize,
690 property: &str,
691) -> Result<Vec<u32>> {
692 read_sysfs_cpu_info_in_dir(cpu_dir, cpu_id, property)?
693 .split_whitespace()
694 .map(|x| x.parse().map_err(|_| Error::new(libc::EINVAL)))
695 .collect()
696}
697
698pub fn logical_core_frequencies_khz(cpu_id: usize) -> Result<Vec<u32>> {
700 parse_sysfs_cpu_info_vec(cpu_id, "cpufreq/scaling_available_frequencies")
701}
702
703fn parse_sysfs_cpu_info(cpu_id: usize, property: &str) -> Result<u32> {
705 parse_sysfs_cpu_info_in_dir(CPU_DIR, cpu_id, property)
706}
707
708fn parse_sysfs_cpu_info_in_dir(cpu_dir: &str, cpu_id: usize, property: &str) -> Result<u32> {
709 read_sysfs_cpu_info_in_dir(cpu_dir, cpu_id, property)?
710 .trim()
711 .parse()
712 .map_err(|_| Error::new(libc::EINVAL))
713}
714
715pub fn logical_core_capacity(cpu_id: usize) -> Result<u32> {
717 static CPU_MAX_FREQS: OnceLock<Option<Vec<u32>>> = OnceLock::new();
718
719 let cpu_capacity = parse_sysfs_cpu_info(cpu_id, "cpu_capacity")?;
720
721 let cpu_max_freqs = CPU_MAX_FREQS.get_or_init(|| {
725 (0..number_of_logical_cores().ok()?)
726 .map(|cpu_id| logical_core_max_freq_khz(cpu_id).ok())
727 .collect()
728 });
729
730 if let Some(cpu_max_freqs) = cpu_max_freqs {
731 let largest_max_freq = *cpu_max_freqs.iter().max().ok_or(Error::new(EINVAL))?;
732 let cpu_max_freq = *cpu_max_freqs.get(cpu_id).ok_or(Error::new(EINVAL))?;
733 let normalized_cpu_capacity = (u64::from(cpu_capacity) * u64::from(largest_max_freq))
734 .checked_div(u64::from(cpu_max_freq))
735 .ok_or(Error::new(EINVAL))?;
736 normalized_cpu_capacity
737 .try_into()
738 .map_err(|_| Error::new(EINVAL))
739 } else {
740 Ok(cpu_capacity)
742 }
743}
744
745pub fn logical_core_cluster_id(cpu_id: usize) -> Result<u32> {
747 parse_sysfs_cpu_info(cpu_id, "topology/physical_package_id")
748}
749
750pub fn logical_core_max_freq_khz(cpu_id: usize) -> Result<u32> {
752 parse_sysfs_cpu_info(cpu_id, "cpufreq/cpuinfo_max_freq")
753}
754
755fn parse_online_cpu_range(content: &str) -> std::collections::BTreeSet<usize> {
757 let mut cpus = std::collections::BTreeSet::new();
758 for part in content.trim().split(',') {
759 let part = part.trim();
760 if part.is_empty() {
761 continue;
762 }
763 if let Some((start_str, end_str)) = part.split_once('-') {
764 if let (Ok(start), Ok(end)) = (start_str.parse::<usize>(), end_str.parse::<usize>()) {
765 for i in start..=end {
766 cpus.insert(i);
767 }
768 }
769 } else if let Ok(cpu) = part.parse::<usize>() {
770 cpus.insert(cpu);
771 }
772 }
773 cpus
774}
775
776pub fn is_cpu_online(cpu_id: usize) -> bool {
778 static ONLINE_CPUS: OnceLock<std::collections::BTreeSet<usize>> = OnceLock::new();
779
780 let online_cpus = ONLINE_CPUS.get_or_init(|| {
781 let mut cpus = std::collections::BTreeSet::new();
782 let path = Path::new(CPU_DIR).join("online");
783 match std::fs::read_to_string(&path) {
784 Ok(content) => {
785 cpus = parse_online_cpu_range(&content);
786 }
787 Err(_) => {
788 if let Ok(total_cores) = crate::number_of_logical_cores() {
791 for id in 0..total_cores {
792 match parse_sysfs_cpu_info(id, "online") {
793 Ok(1) => {
794 cpus.insert(id);
795 }
796 Ok(_) => {}
797 Err(e) => {
798 warn!(
800 "Assuming CPU {} is online because we couldn't read the sys file: {}",
801 id, e
802 );
803 cpus.insert(id);
804 }
805 }
806 }
807 }
808 }
809 }
810 cpus
811 });
812
813 online_cpus.contains(&cpu_id)
814}
815
816pub fn preadv2(
822 fd: BorrowedFd<'_>,
823 iovs: &mut [IoBufMut],
824 offset: libc::off_t,
825 flags: libc::c_int,
826) -> libc::ssize_t {
827 if iovs.is_empty() {
828 return 0;
829 }
830 let pos_l = offset as libc::c_ulong;
831 let pos_h = 0;
832 unsafe {
836 libc::syscall(
837 libc::SYS_preadv2,
838 fd.as_raw_fd(),
839 iovs.as_mut_ptr() as *mut libc::iovec,
840 iovs.len(),
841 pos_l,
842 pos_h,
843 flags,
844 ) as libc::ssize_t
845 }
846}
847
848pub fn pwritev2(
854 fd: BorrowedFd<'_>,
855 iovs: &[IoBuf],
856 offset: libc::off_t,
857 flags: libc::c_int,
858) -> libc::ssize_t {
859 if iovs.is_empty() {
860 return 0;
861 }
862 let pos_l = offset as libc::c_ulong;
863 let pos_h = 0;
864 unsafe {
868 libc::syscall(
869 libc::SYS_pwritev2,
870 fd.as_raw_fd(),
871 iovs.as_ptr(),
872 iovs.len(),
873 pos_l,
874 pos_h,
875 flags,
876 ) as libc::ssize_t
877 }
878}
879
880#[repr(C)]
881pub struct sched_attr {
882 pub size: u32,
883
884 pub sched_policy: u32,
885 pub sched_flags: u64,
886 pub sched_nice: i32,
887
888 pub sched_priority: u32,
889
890 pub sched_runtime: u64,
891 pub sched_deadline: u64,
892 pub sched_period: u64,
893
894 pub sched_util_min: u32,
895 pub sched_util_max: u32,
896}
897
898impl Default for sched_attr {
899 fn default() -> Self {
900 Self {
901 size: std::mem::size_of::<sched_attr>() as u32,
902 sched_policy: 0,
903 sched_flags: 0,
904 sched_nice: 0,
905 sched_priority: 0,
906 sched_runtime: 0,
907 sched_deadline: 0,
908 sched_period: 0,
909 sched_util_min: 0,
910 sched_util_max: 0,
911 }
912 }
913}
914
915pub fn sched_setattr(pid: Pid, attr: &mut sched_attr, flags: u32) -> Result<()> {
916 let ret = unsafe {
918 libc::syscall(
919 libc::SYS_sched_setattr,
920 pid as usize,
921 attr as *mut sched_attr as usize,
922 flags as usize,
923 )
924 };
925
926 if ret < 0 {
927 return Err(Error::last());
928 }
929 Ok(())
930}
931
932#[cfg(test)]
933mod tests {
934 use std::fs::create_dir_all;
935 use std::fs::File;
936 use std::io::Write;
937 use std::os::fd::AsRawFd;
938
939 use tempfile::TempDir;
940
941 use super::*;
942 use crate::unix::add_fd_flags;
943
944 fn create_temp_file(path: &Path, content: &str) {
945 if let Some(parent) = path.parent() {
946 create_dir_all(parent).unwrap();
947 }
948 let mut file = File::create(path).unwrap();
949 file.write_all(content.as_bytes()).unwrap();
950 }
951
952 #[test]
953 fn test_parse_online_cpu_range() {
954 let set = parse_online_cpu_range("0-3,5-7");
955 assert_eq!(set.len(), 7);
956 assert!(set.contains(&0));
957 assert!(set.contains(&1));
958 assert!(set.contains(&2));
959 assert!(set.contains(&3));
960 assert!(!set.contains(&4));
961 assert!(set.contains(&5));
962 assert!(set.contains(&6));
963 assert!(set.contains(&7));
964
965 let set = parse_online_cpu_range("0");
966 assert_eq!(set.len(), 1);
967 assert!(set.contains(&0));
968
969 let set = parse_online_cpu_range("0,2,4");
970 assert_eq!(set.len(), 3);
971 assert!(set.contains(&0));
972 assert!(set.contains(&2));
973 assert!(set.contains(&4));
974
975 let set = parse_online_cpu_range(" 0-1, 3 ");
976 assert_eq!(set.len(), 3);
977 assert!(set.contains(&0));
978 assert!(set.contains(&1));
979 assert!(set.contains(&3));
980
981 let set = parse_online_cpu_range("");
982 assert!(set.is_empty());
983 }
984
985 #[test]
986 fn pipe_size_and_fill() {
987 let (_rx, mut tx) = new_pipe_full().expect("Failed to pipe");
988
989 add_fd_flags(tx.as_raw_fd(), libc::O_NONBLOCK).expect("Failed to set tx non blocking");
992 tx.write(&[0u8; 8])
993 .expect_err("Write after fill didn't fail");
994 }
995
996 #[test]
997 fn test_parse_sysfs_cpu_info() {
998 let temp_dir = TempDir::new().unwrap();
999 let root = temp_dir.path();
1000 let cpu_dir = root.join("sys/devices/system/cpu");
1001 let cpu = 0;
1002 let property = "cpufreq/cpuinfo_max_freq";
1003 create_temp_file(
1004 &root.join("sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"),
1005 "1000",
1006 );
1007
1008 assert_eq!(
1009 parse_sysfs_cpu_info_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap(),
1010 1000
1011 );
1012 }
1013
1014 #[test]
1015 fn test_parse_sysfs_cpu_info_error() {
1016 let temp_dir = TempDir::new().unwrap();
1017 let root = temp_dir.path();
1018 let cpu_dir = root.join("sys/devices/system/cpu");
1019 let cpu = 0;
1020 let property = "cpufreq/cpuinfo_max_freq";
1021 let err =
1024 parse_sysfs_cpu_info_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap_err();
1025 assert_eq!(err, Error::new(libc::ENOENT));
1026 }
1027
1028 #[test]
1029 fn test_parse_sysfs_cpu_info_vec() {
1030 let temp_dir = TempDir::new().unwrap();
1031 let root = temp_dir.path();
1032 let cpu_dir = root.join("sys/devices/system/cpu");
1033 let cpu = 0;
1034 let property = "cpufreq/scaling_available_frequencies";
1035 create_temp_file(
1036 &root.join("sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies"),
1037 "1000 2000",
1038 );
1039
1040 assert_eq!(
1041 parse_sysfs_cpu_info_vec_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap(),
1042 vec![1000, 2000]
1043 );
1044 }
1045
1046 #[test]
1047 fn test_parse_sysfs_cpu_info_vec_error() {
1048 let temp_dir = TempDir::new().unwrap();
1049 let root = temp_dir.path();
1050 let cpu_dir = root.join("sys/devices/system/cpu");
1051 let cpu = 0;
1052 let property = "cpufreq/scaling_available_frequencies";
1053 let err =
1056 parse_sysfs_cpu_info_vec_in_dir(cpu_dir.to_str().unwrap(), cpu, property).unwrap_err();
1057 assert_eq!(err, Error::new(libc::ENOENT));
1058 }
1059}