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