• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Small system utility modules for usage by other modules.
6 
7 #[cfg(target_os = "android")]
8 mod android;
9 #[cfg(target_os = "android")]
10 use android as target_os;
11 #[cfg(target_os = "linux")]
12 mod linux;
13 #[cfg(target_os = "linux")]
14 use linux as target_os;
15 use log::warn;
16 #[macro_use]
17 pub mod handle_eintr;
18 #[macro_use]
19 pub mod ioctl;
20 #[macro_use]
21 pub mod syslog;
22 mod acpi_event;
23 mod capabilities;
24 mod descriptor;
25 mod event;
26 mod file;
27 mod file_flags;
28 pub mod file_traits;
29 mod get_filesystem_type;
30 mod mmap;
31 pub mod net;
32 mod netlink;
33 mod notifiers;
34 pub mod panic_handler;
35 pub mod platform_timer_resolution;
36 mod poll;
37 mod priority;
38 pub mod process;
39 mod sched;
40 pub mod scoped_signal_handler;
41 mod shm;
42 pub mod signal;
43 mod signalfd;
44 mod sock_ctrl_msg;
45 mod stream_channel;
46 mod terminal;
47 mod timer;
48 pub mod vsock;
49 mod write_zeroes;
50 
51 use std::ffi::CStr;
52 use std::fs::remove_file;
53 use std::fs::File;
54 use std::fs::OpenOptions;
55 use std::mem;
56 use std::ops::Deref;
57 use std::os::unix::io::AsRawFd;
58 use std::os::unix::io::FromRawFd;
59 use std::os::unix::io::RawFd;
60 use std::os::unix::net::UnixDatagram;
61 use std::os::unix::net::UnixListener;
62 use std::os::unix::process::ExitStatusExt;
63 use std::path::Path;
64 use std::path::PathBuf;
65 use std::process::ExitStatus;
66 use std::ptr;
67 use std::time::Duration;
68 
69 pub use acpi_event::*;
70 pub use capabilities::drop_capabilities;
71 pub use descriptor::*;
72 pub use event::EventExt;
73 pub(crate) use event::PlatformEvent;
74 pub use file::find_next_data;
75 pub use file::FileDataIterator;
76 pub use file_flags::*;
77 pub use get_filesystem_type::*;
78 pub use ioctl::*;
79 use libc::c_int;
80 use libc::c_long;
81 use libc::fcntl;
82 use libc::pipe2;
83 use libc::syscall;
84 use libc::sysconf;
85 use libc::waitpid;
86 use libc::SYS_getpid;
87 use libc::SYS_getppid;
88 use libc::SYS_gettid;
89 use libc::EINVAL;
90 use libc::F_GETFL;
91 use libc::F_SETFL;
92 use libc::O_CLOEXEC;
93 pub(crate) use libc::PROT_READ;
94 pub(crate) use libc::PROT_WRITE;
95 use libc::SIGKILL;
96 use libc::WNOHANG;
97 use libc::_SC_IOV_MAX;
98 use libc::_SC_PAGESIZE;
99 pub use mmap::Error as MmapError;
100 pub use mmap::*;
101 pub use netlink::*;
102 pub use poll::EventContext;
103 pub use priority::*;
104 pub use sched::*;
105 pub use scoped_signal_handler::*;
106 pub use shm::MemfdSeals;
107 pub use shm::SharedMemory;
108 pub use shm::Unix as SharedMemoryUnix;
109 pub use signal::*;
110 pub use signalfd::Error as SignalFdError;
111 pub use signalfd::*;
112 pub use sock_ctrl_msg::*;
113 pub use stream_channel::*;
114 pub use terminal::*;
115 pub use timer::*;
116 pub(crate) use write_zeroes::file_punch_hole;
117 pub(crate) use write_zeroes::file_write_zeroes_at;
118 
119 use crate::descriptor::FromRawDescriptor;
120 use crate::descriptor::SafeDescriptor;
121 pub use crate::descriptor_reflection::deserialize_with_descriptors;
122 pub use crate::descriptor_reflection::with_as_descriptor;
123 pub use crate::descriptor_reflection::with_raw_descriptor;
124 pub use crate::descriptor_reflection::FileSerdeWrapper;
125 pub use crate::descriptor_reflection::SerializeDescriptors;
126 pub use crate::errno::Error;
127 pub use crate::errno::Result;
128 pub use crate::errno::*;
129 
130 /// Re-export libc types that are part of the API.
131 pub type Pid = libc::pid_t;
132 pub type Uid = libc::uid_t;
133 pub type Gid = libc::gid_t;
134 pub type Mode = libc::mode_t;
135 
136 #[macro_export]
137 macro_rules! syscall {
138     ($e:expr) => {{
139         let res = $e;
140         if res < 0 {
141             $crate::platform::errno_result()
142         } else {
143             Ok(res)
144         }
145     }};
146 }
147 
148 /// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
149 #[inline(always)]
pagesize() -> usize150 pub fn pagesize() -> usize {
151     // Trivially safe
152     unsafe { sysconf(_SC_PAGESIZE) as usize }
153 }
154 
155 /// Safe wrapper for `sysconf(_SC_IOV_MAX)`.
iov_max() -> usize156 pub fn iov_max() -> usize {
157     // Trivially safe
158     unsafe { sysconf(_SC_IOV_MAX) as usize }
159 }
160 
161 /// Uses the system's page size in bytes to round the given value up to the nearest page boundary.
162 #[inline(always)]
round_up_to_page_size(v: usize) -> usize163 pub fn round_up_to_page_size(v: usize) -> usize {
164     let page_mask = pagesize() - 1;
165     (v + page_mask) & !page_mask
166 }
167 
168 /// This bypasses `libc`'s caching `getpid(2)` wrapper which can be invalid if a raw clone was used
169 /// elsewhere.
170 #[inline(always)]
getpid() -> Pid171 pub fn getpid() -> Pid {
172     // Safe because this syscall can never fail and we give it a valid syscall number.
173     unsafe { syscall(SYS_getpid as c_long) as Pid }
174 }
175 
176 /// Safe wrapper for the geppid Linux systemcall.
177 #[inline(always)]
getppid() -> Pid178 pub fn getppid() -> Pid {
179     // Safe because this syscall can never fail and we give it a valid syscall number.
180     unsafe { syscall(SYS_getppid as c_long) as Pid }
181 }
182 
183 /// Safe wrapper for the gettid Linux systemcall.
gettid() -> Pid184 pub fn gettid() -> Pid {
185     // Calling the gettid() sycall is always safe.
186     unsafe { syscall(SYS_gettid as c_long) as Pid }
187 }
188 
189 /// Safe wrapper for `getsid(2)`.
getsid(pid: Option<Pid>) -> Result<Pid>190 pub fn getsid(pid: Option<Pid>) -> Result<Pid> {
191     // Calling the getsid() sycall is always safe.
192     syscall!(unsafe { libc::getsid(pid.unwrap_or(0)) } as Pid)
193 }
194 
195 /// Wrapper for `setsid(2)`.
setsid() -> Result<Pid>196 pub fn setsid() -> Result<Pid> {
197     // Safe because the return code is checked.
198     syscall!(unsafe { libc::setsid() as Pid })
199 }
200 
201 /// Safe wrapper for `geteuid(2)`.
202 #[inline(always)]
geteuid() -> Uid203 pub fn geteuid() -> Uid {
204     // trivially safe
205     unsafe { libc::geteuid() }
206 }
207 
208 /// Safe wrapper for `getegid(2)`.
209 #[inline(always)]
getegid() -> Gid210 pub fn getegid() -> Gid {
211     // trivially safe
212     unsafe { libc::getegid() }
213 }
214 
215 /// Safe wrapper for chown(2).
216 #[inline(always)]
chown(path: &CStr, uid: Uid, gid: Gid) -> Result<()>217 pub fn chown(path: &CStr, uid: Uid, gid: Gid) -> Result<()> {
218     // Safe since we pass in a valid string pointer and check the return value.
219     syscall!(unsafe { libc::chown(path.as_ptr(), uid, gid) }).map(|_| ())
220 }
221 
222 /// Safe wrapper for fchmod(2).
223 #[inline(always)]
fchmod<A: AsRawFd>(fd: &A, mode: Mode) -> Result<()>224 pub fn fchmod<A: AsRawFd>(fd: &A, mode: Mode) -> Result<()> {
225     // Safe since the function does not operate on pointers and check the return value.
226     syscall!(unsafe { libc::fchmod(fd.as_raw_fd(), mode) }).map(|_| ())
227 }
228 
229 /// Safe wrapper for fchown(2).
230 #[inline(always)]
fchown<A: AsRawFd>(fd: &A, uid: Uid, gid: Gid) -> Result<()>231 pub fn fchown<A: AsRawFd>(fd: &A, uid: Uid, gid: Gid) -> Result<()> {
232     // Safe since the function does not operate on pointers and check the return value.
233     syscall!(unsafe { libc::fchown(fd.as_raw_fd(), uid, gid) }).map(|_| ())
234 }
235 
236 /// The operation to perform with `flock`.
237 pub enum FlockOperation {
238     LockShared,
239     LockExclusive,
240     Unlock,
241 }
242 
243 /// Safe wrapper for flock(2) with the operation `op` and optionally `nonblocking`. The lock will be
244 /// dropped automatically when `file` is dropped.
245 #[inline(always)]
flock(file: &dyn AsRawFd, op: FlockOperation, nonblocking: bool) -> Result<()>246 pub fn flock(file: &dyn AsRawFd, op: FlockOperation, nonblocking: bool) -> Result<()> {
247     let mut operation = match op {
248         FlockOperation::LockShared => libc::LOCK_SH,
249         FlockOperation::LockExclusive => libc::LOCK_EX,
250         FlockOperation::Unlock => libc::LOCK_UN,
251     };
252 
253     if nonblocking {
254         operation |= libc::LOCK_NB;
255     }
256 
257     // Safe since we pass in a valid fd and flock operation, and check the return value.
258     syscall!(unsafe { libc::flock(file.as_raw_fd(), operation) }).map(|_| ())
259 }
260 
261 /// The operation to perform with `fallocate`.
262 pub enum FallocateMode {
263     PunchHole,
264     ZeroRange,
265     Allocate,
266 }
267 
268 /// Safe wrapper for `fallocate()`.
fallocate( file: &dyn AsRawFd, mode: FallocateMode, keep_size: bool, offset: u64, len: u64, ) -> Result<()>269 pub fn fallocate(
270     file: &dyn AsRawFd,
271     mode: FallocateMode,
272     keep_size: bool,
273     offset: u64,
274     len: u64,
275 ) -> Result<()> {
276     let offset = if offset > libc::off64_t::max_value() as u64 {
277         return Err(Error::new(libc::EINVAL));
278     } else {
279         offset as libc::off64_t
280     };
281 
282     let len = if len > libc::off64_t::max_value() as u64 {
283         return Err(Error::new(libc::EINVAL));
284     } else {
285         len as libc::off64_t
286     };
287 
288     let mut mode = match mode {
289         FallocateMode::PunchHole => libc::FALLOC_FL_PUNCH_HOLE,
290         FallocateMode::ZeroRange => libc::FALLOC_FL_ZERO_RANGE,
291         FallocateMode::Allocate => 0,
292     };
293 
294     if keep_size {
295         mode |= libc::FALLOC_FL_KEEP_SIZE;
296     }
297 
298     // Safe since we pass in a valid fd and fallocate mode, validate offset and len,
299     // and check the return value.
300     syscall!(unsafe { libc::fallocate64(file.as_raw_fd(), mode, offset, len) }).map(|_| ())
301 }
302 
303 /// A trait used to abstract types that provide a process id that can be operated on.
304 pub trait AsRawPid {
as_raw_pid(&self) -> Pid305     fn as_raw_pid(&self) -> Pid;
306 }
307 
308 impl AsRawPid for Pid {
as_raw_pid(&self) -> Pid309     fn as_raw_pid(&self) -> Pid {
310         *self
311     }
312 }
313 
314 impl AsRawPid for std::process::Child {
as_raw_pid(&self) -> Pid315     fn as_raw_pid(&self) -> Pid {
316         self.id() as Pid
317     }
318 }
319 
320 /// A safe wrapper around waitpid.
321 ///
322 /// On success if a process was reaped, it will be returned as the first value.
323 /// The second returned value is the ExitStatus from the libc::waitpid() call.
324 ///
325 /// Note: this can block if libc::WNOHANG is not set and EINTR is not handled internally.
wait_for_pid<A: AsRawPid>(pid: A, options: c_int) -> Result<(Option<Pid>, ExitStatus)>326 pub fn wait_for_pid<A: AsRawPid>(pid: A, options: c_int) -> Result<(Option<Pid>, ExitStatus)> {
327     let pid = pid.as_raw_pid();
328     let mut status: c_int = 1;
329     // Safe because status is owned and the error is checked.
330     let ret = unsafe { libc::waitpid(pid, &mut status, options) };
331     if ret < 0 {
332         return errno_result();
333     }
334     Ok((
335         if ret == 0 { None } else { Some(ret) },
336         ExitStatus::from_raw(status),
337     ))
338 }
339 
340 /// Reaps a child process that has terminated.
341 ///
342 /// Returns `Ok(pid)` where `pid` is the process that was reaped or `Ok(0)` if none of the children
343 /// have terminated. An `Error` is with `errno == ECHILD` if there are no children left to reap.
344 ///
345 /// # Examples
346 ///
347 /// Reaps all child processes until there are no terminated children to reap.
348 ///
349 /// ```
350 /// fn reap_children() {
351 ///     loop {
352 ///         match base::platform::reap_child() {
353 ///             Ok(0) => println!("no children ready to reap"),
354 ///             Ok(pid) => {
355 ///                 println!("reaped {}", pid);
356 ///                 continue
357 ///             },
358 ///             Err(e) if e.errno() == libc::ECHILD => println!("no children left"),
359 ///             Err(e) => println!("error reaping children: {}", e),
360 ///         }
361 ///         break
362 ///     }
363 /// }
364 /// ```
reap_child() -> Result<Pid>365 pub fn reap_child() -> Result<Pid> {
366     // Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error.
367     let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
368     if ret == -1 {
369         errno_result()
370     } else {
371         Ok(ret)
372     }
373 }
374 
375 /// Kill all processes in the current process group.
376 ///
377 /// On success, this kills all processes in the current process group, including the current
378 /// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`.
kill_process_group() -> Result<()>379 pub fn kill_process_group() -> Result<()> {
380     unsafe { kill(0, SIGKILL) }?;
381     // Kill succeeded, so this process never reaches here.
382     unreachable!();
383 }
384 
385 /// Spawns a pipe pair where the first pipe is the read end and the second pipe is the write end.
386 ///
387 /// If `close_on_exec` is true, the `O_CLOEXEC` flag will be set during pipe creation.
pipe(close_on_exec: bool) -> Result<(File, File)>388 pub fn pipe(close_on_exec: bool) -> Result<(File, File)> {
389     let flags = if close_on_exec { O_CLOEXEC } else { 0 };
390     let mut pipe_fds = [-1; 2];
391     // Safe because pipe2 will only write 2 element array of i32 to the given pointer, and we check
392     // for error.
393     let ret = unsafe { pipe2(&mut pipe_fds[0], flags) };
394     if ret == -1 {
395         errno_result()
396     } else {
397         // Safe because both fds must be valid for pipe2 to have returned sucessfully and we have
398         // exclusive ownership of them.
399         Ok(unsafe {
400             (
401                 File::from_raw_fd(pipe_fds[0]),
402                 File::from_raw_fd(pipe_fds[1]),
403             )
404         })
405     }
406 }
407 
408 /// Sets the pipe signified with fd to `size`.
409 ///
410 /// Returns the new size of the pipe or an error if the OS fails to set the pipe size.
set_pipe_size(fd: RawFd, size: usize) -> Result<usize>411 pub fn set_pipe_size(fd: RawFd, size: usize) -> Result<usize> {
412     // Safe because fcntl with the `F_SETPIPE_SZ` arg doesn't touch memory.
413     syscall!(unsafe { fcntl(fd, libc::F_SETPIPE_SZ, size as c_int) }).map(|ret| ret as usize)
414 }
415 
416 /// Test-only function used to create a pipe that is full. The pipe is created, has its size set to
417 /// the minimum and then has that much data written to it. Use `new_pipe_full` to test handling of
418 /// blocking `write` calls in unit tests.
new_pipe_full() -> Result<(File, File)>419 pub fn new_pipe_full() -> Result<(File, File)> {
420     use std::io::Write;
421 
422     let (rx, mut tx) = pipe(true)?;
423     // The smallest allowed size of a pipe is the system page size on linux.
424     let page_size = set_pipe_size(tx.as_raw_fd(), round_up_to_page_size(1))?;
425 
426     // Fill the pipe with page_size zeros so the next write call will block.
427     let buf = vec![0u8; page_size];
428     tx.write_all(&buf)?;
429 
430     Ok((rx, tx))
431 }
432 
433 /// Used to attempt to clean up a named pipe after it is no longer used.
434 pub struct UnlinkUnixDatagram(pub UnixDatagram);
435 impl AsRef<UnixDatagram> for UnlinkUnixDatagram {
as_ref(&self) -> &UnixDatagram436     fn as_ref(&self) -> &UnixDatagram {
437         &self.0
438     }
439 }
440 impl Drop for UnlinkUnixDatagram {
drop(&mut self)441     fn drop(&mut self) {
442         if let Ok(addr) = self.0.local_addr() {
443             if let Some(path) = addr.as_pathname() {
444                 if let Err(e) = remove_file(path) {
445                     warn!("failed to remove control socket file: {}", e);
446                 }
447             }
448         }
449     }
450 }
451 
452 /// Used to attempt to clean up a named pipe after it is no longer used.
453 pub struct UnlinkUnixListener(pub UnixListener);
454 
455 impl AsRef<UnixListener> for UnlinkUnixListener {
as_ref(&self) -> &UnixListener456     fn as_ref(&self) -> &UnixListener {
457         &self.0
458     }
459 }
460 
461 impl Deref for UnlinkUnixListener {
462     type Target = UnixListener;
463 
deref(&self) -> &UnixListener464     fn deref(&self) -> &UnixListener {
465         &self.0
466     }
467 }
468 
469 impl Drop for UnlinkUnixListener {
drop(&mut self)470     fn drop(&mut self) {
471         if let Ok(addr) = self.0.local_addr() {
472             if let Some(path) = addr.as_pathname() {
473                 if let Err(e) = remove_file(path) {
474                     warn!("failed to remove control socket file: {}", e);
475                 }
476             }
477         }
478     }
479 }
480 
481 /// Verifies that |raw_descriptor| is actually owned by this process and duplicates it
482 /// to ensure that we have a unique handle to it.
validate_raw_descriptor(raw_descriptor: RawDescriptor) -> Result<RawDescriptor>483 pub fn validate_raw_descriptor(raw_descriptor: RawDescriptor) -> Result<RawDescriptor> {
484     validate_raw_fd(raw_descriptor)
485 }
486 
487 /// Verifies that |raw_fd| is actually owned by this process and duplicates it to ensure that
488 /// we have a unique handle to it.
validate_raw_fd(raw_fd: RawFd) -> Result<RawFd>489 pub fn validate_raw_fd(raw_fd: RawFd) -> Result<RawFd> {
490     // Checking that close-on-exec isn't set helps filter out FDs that were opened by
491     // crosvm as all crosvm FDs are close on exec.
492     // Safe because this doesn't modify any memory and we check the return value.
493     let flags = unsafe { libc::fcntl(raw_fd, libc::F_GETFD) };
494     if flags < 0 || (flags & libc::FD_CLOEXEC) != 0 {
495         return Err(Error::new(libc::EBADF));
496     }
497 
498     // Duplicate the fd to ensure that we don't accidentally close an fd previously
499     // opened by another subsystem.  Safe because this doesn't modify any memory and
500     // we check the return value.
501     let dup_fd = unsafe { libc::fcntl(raw_fd, libc::F_DUPFD_CLOEXEC, 0) };
502     if dup_fd < 0 {
503         return Err(Error::last());
504     }
505     Ok(dup_fd as RawFd)
506 }
507 
508 /// Utility function that returns true if the given FD is readable without blocking.
509 ///
510 /// On an error, such as an invalid or incompatible FD, this will return false, which can not be
511 /// distinguished from a non-ready to read FD.
poll_in(fd: &dyn AsRawFd) -> bool512 pub fn poll_in(fd: &dyn AsRawFd) -> bool {
513     let mut fds = libc::pollfd {
514         fd: fd.as_raw_fd(),
515         events: libc::POLLIN,
516         revents: 0,
517     };
518     // Safe because we give a valid pointer to a list (of 1) FD and check the return value.
519     let ret = unsafe { libc::poll(&mut fds, 1, 0) };
520     // An error probably indicates an invalid FD, or an FD that can't be polled. Returning false in
521     // that case is probably correct as such an FD is unlikely to be readable, although there are
522     // probably corner cases in which that is wrong.
523     if ret == -1 {
524         return false;
525     }
526     fds.revents & libc::POLLIN != 0
527 }
528 
529 /// Returns the file flags set for the given `RawFD`
530 ///
531 /// Returns an error if the OS indicates the flags can't be retrieved.
get_fd_flags(fd: RawFd) -> Result<c_int>532 fn get_fd_flags(fd: RawFd) -> Result<c_int> {
533     // Safe because no third parameter is expected and we check the return result.
534     syscall!(unsafe { fcntl(fd, F_GETFL) })
535 }
536 
537 /// Sets the file flags set for the given `RawFD`.
538 ///
539 /// Returns an error if the OS indicates the flags can't be retrieved.
set_fd_flags(fd: RawFd, flags: c_int) -> Result<()>540 fn set_fd_flags(fd: RawFd, flags: c_int) -> Result<()> {
541     // Safe because we supply the third parameter and we check the return result.
542     // fcntlt is trusted not to modify the memory of the calling process.
543     syscall!(unsafe { fcntl(fd, F_SETFL, flags) }).map(|_| ())
544 }
545 
546 /// Performs a logical OR of the given flags with the FD's flags, setting the given bits for the
547 /// FD.
548 ///
549 /// Returns an error if the OS indicates the flags can't be retrieved or set.
add_fd_flags(fd: RawFd, set_flags: c_int) -> Result<()>550 pub fn add_fd_flags(fd: RawFd, set_flags: c_int) -> Result<()> {
551     let start_flags = get_fd_flags(fd)?;
552     set_fd_flags(fd, start_flags | set_flags)
553 }
554 
555 /// Clears the given flags in the FD's flags.
556 ///
557 /// Returns an error if the OS indicates the flags can't be retrieved or set.
clear_fd_flags(fd: RawFd, clear_flags: c_int) -> Result<()>558 pub fn clear_fd_flags(fd: RawFd, clear_flags: c_int) -> Result<()> {
559     let start_flags = get_fd_flags(fd)?;
560     set_fd_flags(fd, start_flags & !clear_flags)
561 }
562 
563 /// Return a timespec filed with the specified Duration `duration`.
564 #[allow(clippy::useless_conversion)]
duration_to_timespec(duration: Duration) -> libc::timespec565 pub fn duration_to_timespec(duration: Duration) -> libc::timespec {
566     // nsec always fits in i32 because subsec_nanos is defined to be less than one billion.
567     let nsec = duration.subsec_nanos() as i32;
568     libc::timespec {
569         tv_sec: duration.as_secs() as libc::time_t,
570         tv_nsec: nsec.into(),
571     }
572 }
573 
574 /// Return the maximum Duration that can be used with libc::timespec.
max_timeout() -> Duration575 pub fn max_timeout() -> Duration {
576     Duration::new(libc::time_t::max_value() as u64, 999999999)
577 }
578 
579 /// If the given path is of the form /proc/self/fd/N for some N, returns `Ok(Some(N))`. Otherwise
580 /// returns `Ok(None)`.
safe_descriptor_from_path<P: AsRef<Path>>(path: P) -> Result<Option<SafeDescriptor>>581 pub fn safe_descriptor_from_path<P: AsRef<Path>>(path: P) -> Result<Option<SafeDescriptor>> {
582     let path = path.as_ref();
583     if path.parent() == Some(Path::new("/proc/self/fd")) {
584         let raw_descriptor = path
585             .file_name()
586             .and_then(|fd_osstr| fd_osstr.to_str())
587             .and_then(|fd_str| fd_str.parse::<RawFd>().ok())
588             .ok_or_else(|| Error::new(EINVAL))?;
589         let validated_fd = validate_raw_fd(raw_descriptor)?;
590         Ok(Some(
591             // Safe because nothing else has access to validated_fd after this call.
592             unsafe { SafeDescriptor::from_raw_descriptor(validated_fd) },
593         ))
594     } else {
595         Ok(None)
596     }
597 }
598 
599 /// Open the file with the given path, or if it is of the form `/proc/self/fd/N` then just use the
600 /// file descriptor.
601 ///
602 /// Note that this will not work properly if the same `/proc/self/fd/N` path is used twice in
603 /// different places, as the metadata (including the offset) will be shared between both file
604 /// descriptors.
open_file<P: AsRef<Path>>(path: P, options: &OpenOptions) -> Result<File>605 pub fn open_file<P: AsRef<Path>>(path: P, options: &OpenOptions) -> Result<File> {
606     let path = path.as_ref();
607     // Special case '/proc/self/fd/*' paths. The FD is already open, just use it.
608     Ok(if let Some(fd) = safe_descriptor_from_path(path)? {
609         fd.into()
610     } else {
611         options.open(path)?
612     })
613 }
614 
615 /// Get the max number of open files allowed by the environment.
get_max_open_files() -> Result<u64>616 pub fn get_max_open_files() -> Result<u64> {
617     let mut buf = mem::MaybeUninit::<libc::rlimit64>::zeroed();
618 
619     // Safe because this will only modify `buf` and we check the return value.
620     let res = unsafe { libc::prlimit64(0, libc::RLIMIT_NOFILE, ptr::null(), buf.as_mut_ptr()) };
621     if res == 0 {
622         // Safe because the kernel guarantees that the struct is fully initialized.
623         let limit = unsafe { buf.assume_init() };
624         Ok(limit.rlim_max)
625     } else {
626         errno_result()
627     }
628 }
629 
630 /// Returns the number of online logical cores on the system.
number_of_logical_cores() -> Result<usize>631 pub fn number_of_logical_cores() -> Result<usize> {
632     // Safe because we pass a flag for this call and the host supports this system call
633     Ok(unsafe { libc::sysconf(libc::_SC_NPROCESSORS_CONF) } as usize)
634 }
635 
636 /// Moves the requested PID/TID to a particular cgroup
637 ///
move_to_cgroup(cgroup_path: PathBuf, id_to_write: Pid, cgroup_file: &str) -> Result<()>638 pub fn move_to_cgroup(cgroup_path: PathBuf, id_to_write: Pid, cgroup_file: &str) -> Result<()> {
639     use std::io::Write;
640 
641     let gpu_cgroup_file = cgroup_path.join(cgroup_file);
642     let mut f = File::create(gpu_cgroup_file)?;
643     f.write_all(id_to_write.to_string().as_bytes())?;
644     Ok(())
645 }
646 
move_task_to_cgroup(cgroup_path: PathBuf, thread_id: Pid) -> Result<()>647 pub fn move_task_to_cgroup(cgroup_path: PathBuf, thread_id: Pid) -> Result<()> {
648     move_to_cgroup(cgroup_path, thread_id, "tasks")
649 }
650 
move_proc_to_cgroup(cgroup_path: PathBuf, process_id: Pid) -> Result<()>651 pub fn move_proc_to_cgroup(cgroup_path: PathBuf, process_id: Pid) -> Result<()> {
652     move_to_cgroup(cgroup_path, process_id, "cgroup.procs")
653 }
654 
655 #[cfg(test)]
656 mod tests {
657     use std::io::Write;
658 
659     use super::*;
660 
661     #[test]
pipe_size_and_fill()662     fn pipe_size_and_fill() {
663         let (_rx, mut tx) = new_pipe_full().expect("Failed to pipe");
664 
665         // To  check that setting the size worked, set the descriptor to non blocking and check that
666         // write returns an error.
667         add_fd_flags(tx.as_raw_fd(), libc::O_NONBLOCK).expect("Failed to set tx non blocking");
668         tx.write(&[0u8; 8])
669             .expect_err("Write after fill didn't fail");
670     }
671 }
672