1 //! pidfd helper functionality
3 use std
::ffi
::{CStr, CString, OsString}
;
4 use std
::io
::{self, BufRead, BufReader}
;
5 use std
::os
::raw
::c_int
;
6 use std
::os
::unix
::ffi
::OsStringExt
;
7 use std
::os
::unix
::io
::{AsRawFd, FromRawFd, IntoRawFd, RawFd}
;
9 use anyhow
::{bail, Error}
;
12 use crate::capability
::Capabilities
;
13 use crate::error
::io_err_other
;
14 use crate::nsfd
::{ns_type, NsFd}
;
17 use super::{CGroups, IdMap, IdMapEntry, ProcStatus, Uids, UserCaps}
;
19 pub struct PidFd(RawFd
, pid_t
);
20 file_descriptor_impl
!(PidFd
);
23 pub fn current() -> io
::Result
<Self> {
24 Self::open(unsafe { libc::getpid() }
)
27 pub fn open(pid
: pid_t
) -> io
::Result
<Self> {
28 let path
= CString
::new(format
!("/proc/{}", pid
)).unwrap();
30 let fd
= c_try
!(unsafe { libc::open(path.as_ptr(), libc::O_DIRECTORY | libc::O_CLOEXEC) }
);
35 /// Turn a valid pid file descriptor into a PidFd.
39 /// The file descriptor must already be a valid pidfd, this is not checked. This function only
40 /// fails if reading the pid from the pidfd's proc entry fails.
41 pub unsafe fn try_from_fd(fd
: Fd
) -> io
::Result
<Self> {
42 #[allow(clippy::unnecessary_cast)] // pid_t is a type alias
43 let mut this
= Self(fd
.into_raw_fd(), -1 as pid_t
);
44 let pid
= this
.read_pid()?
;
49 pub fn mount_namespace(&self) -> io
::Result
<NsFd
<ns_type
::Mount
>> {
50 NsFd
::openat(self.0, c_str
!("ns/mnt"))
53 pub fn cgroup_namespace(&self) -> io
::Result
<NsFd
<ns_type
::Cgroup
>> {
54 NsFd
::openat(self.0, c_str
!("ns/cgroup"))
57 pub fn user_namespace(&self) -> io
::Result
<NsFd
<ns_type
::User
>> {
58 NsFd
::openat(self.0, c_str
!("ns/user"))
61 fn fd(&self, path
: &CStr
, flags
: c_int
, mode
: c_int
) -> io
::Result
<Fd
> {
65 path
.as_ptr() as *const _
,
66 flags
| libc
::O_CLOEXEC
,
72 pub fn fd_cwd(&self) -> io
::Result
<Fd
> {
73 self.fd(c_str
!("cwd"), libc
::O_DIRECTORY
, 0)
76 pub fn fd_num(&self, num
: RawFd
, flags
: c_int
) -> io
::Result
<Fd
> {
77 let path
= format
!("fd/{}\0", num
);
79 unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) }
,
85 pub fn enter_cwd(&self) -> io
::Result
<()> {
86 c_try
!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) }
);
90 pub fn enter_chroot(&self) -> io
::Result
<()> {
91 c_try
!(unsafe { libc::fchdir(self.as_raw_fd()) }
);
92 c_try
!(unsafe { libc::chroot(b"root\0".as_ptr() as *const _) }
);
93 c_try
!(unsafe { libc::chdir(b"/\0".as_ptr() as *const _) }
);
97 // procfs files cannot be async, we cannot add them to epoll...
98 pub fn open_file(&self, path
: &CStr
, flags
: c_int
, mode
: c_int
) -> io
::Result
<std
::fs
::File
> {
99 Ok(unsafe { std::fs::File::from_raw_fd(self.fd(path, flags, mode)?.into_raw_fd()) }
)
103 fn open_buffered(&self, path
: &CStr
) -> io
::Result
<impl BufRead
> {
104 Ok(BufReader
::new(self.open_file(
106 libc
::O_RDONLY
| libc
::O_CLOEXEC
,
112 pub fn get_pid(&self) -> pid_t
{
116 fn read_pid(&self) -> io
::Result
<pid_t
> {
117 let reader
= self.open_buffered(c_str
!("status"))?
;
119 for line
in reader
.lines() {
121 let mut parts
= line
.split_ascii_whitespace();
122 if parts
.next() == Some("Pid:") {
125 .ok_or_else(|| io
::Error
::new(io
::ErrorKind
::Other
, "bad 'Pid:' line in proc"))?
128 io
::Error
::new(io
::ErrorKind
::Other
, "failed to parse pid from proc")
134 Err(io
::ErrorKind
::NotFound
.into())
138 fn __check_uid_gid(value
: Option
<&str>) -> io
::Result
<libc
::uid_t
> {
140 .ok_or_else(|| io
::Error
::new(io
::ErrorKind
::Other
, "bad 'Uid/Gid:' line in proc"))?
141 .parse
::<libc
::uid_t
>()
142 .map_err(|_
| io
::Error
::new(io
::ErrorKind
::Other
, "failed to parse uid from proc"))
145 pub fn get_status(&self) -> io
::Result
<ProcStatus
> {
146 let reader
= self.open_buffered(c_str
!("status"))?
;
149 fn check_u64_hex(value
: Option
<&str>) -> io
::Result
<u64> {
151 value
.ok_or_else(|| {
152 io
::Error
::new(io
::ErrorKind
::Other
, "bad numeric property line in proc")
156 .map_err(io_err_other
)
160 fn check_u32_oct(value
: Option
<&str>) -> io
::Result
<u32> {
162 value
.ok_or_else(|| {
163 io
::Error
::new(io
::ErrorKind
::Other
, "bad numeric property line in proc")
167 .map_err(io_err_other
)
170 let mut ids
= Uids
::default();
171 let mut caps
= Capabilities
::default();
172 let mut umask
= 0o022;
173 for line
in reader
.lines() {
175 let mut parts
= line
.split_ascii_whitespace();
178 ids
.ruid
= Self::__check_uid_gid(parts
.next())?
;
179 ids
.euid
= Self::__check_uid_gid(parts
.next())?
;
180 ids
.suid
= Self::__check_uid_gid(parts
.next())?
;
181 ids
.fsuid
= Self::__check_uid_gid(parts
.next())?
;
184 ids
.rgid
= Self::__check_uid_gid(parts
.next())?
;
185 ids
.egid
= Self::__check_uid_gid(parts
.next())?
;
186 ids
.sgid
= Self::__check_uid_gid(parts
.next())?
;
187 ids
.fsgid
= Self::__check_uid_gid(parts
.next())?
;
189 Some("CapInh:") => caps
.inheritable
= check_u64_hex(parts
.next())?
,
190 Some("CapPrm:") => caps
.permitted
= check_u64_hex(parts
.next())?
,
191 Some("CapEff:") => caps
.effective
= check_u64_hex(parts
.next())?
,
192 //Some("CapBnd:") => caps.bounding = check_u64_hex(parts.next())?,
193 Some("Umask:") => umask
= check_u32_oct(parts
.next())?
,
205 pub fn get_cgroups(&self) -> Result
<CGroups
, Error
> {
206 let reader
= self.open_buffered(c_str
!("cgroup"))?
;
208 let mut cgroups
= CGroups
::new();
210 for line
in reader
.split(b'
\n'
) {
212 let mut parts
= line
.splitn(3, |b
| *b
== b'
:'
);
213 let num
= parts
.next();
214 let name
= parts
.next();
215 let path
= parts
.next();
216 if num
.is_none() || name
.is_none() || path
.is_none() || parts
.next().is_some() {
217 bail
!("failed to parse cgroup line: {:?}", line
);
220 let name
= String
::from_utf8(name
.unwrap().to_vec())?
;
221 let path
= OsString
::from_vec(path
.unwrap().to_vec());
224 cgroups
.v2
= Some(path
);
226 for entry
in name
.split('
,'
) {
229 .get_or_insert_with(Default
::default)
230 .insert(entry
.to_string(), path
.clone());
238 pub fn get_uid_gid_map(&self, file
: &CStr
) -> Result
<IdMap
, Error
> {
239 let reader
= self.open_buffered(file
)?
;
241 let mut entries
= Vec
::new();
242 for line
in reader
.lines() {
244 let mut parts
= line
.split_ascii_whitespace();
245 let ns
= u64::from(Self::__check_uid_gid(parts
.next())?
);
246 let host
= u64::from(Self::__check_uid_gid(parts
.next())?
);
247 let range
= u64::from(Self::__check_uid_gid(parts
.next())?
);
248 entries
.push(IdMapEntry { ns, host, range }
);
251 Ok(IdMap
::new(entries
))
254 pub fn get_uid_map(&self) -> Result
<IdMap
, Error
> {
255 self.get_uid_gid_map(c_str
!("uid_map"))
258 pub fn get_gid_map(&self) -> Result
<IdMap
, Error
> {
259 self.get_uid_gid_map(c_str
!("gid_map"))
262 pub fn read_file(&self, file
: &CStr
) -> io
::Result
<Vec
<u8>> {
265 let mut reader
= self.open_file(file
, libc
::O_RDONLY
| libc
::O_CLOEXEC
, 0)?
;
266 let mut out
= Vec
::new();
267 reader
.read_to_end(&mut out
)?
;
271 pub fn user_caps(&self) -> Result
<UserCaps
, Error
> {