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::nsfd
::{ns_type, NsFd}
;
16 use super::{CGroups, IdMap, IdMapEntry, ProcStatus, Uids, UserCaps}
;
18 pub struct PidFd(RawFd
, pid_t
);
19 file_descriptor_impl
!(PidFd
);
22 pub fn current() -> io
::Result
<Self> {
23 Self::open(unsafe { libc::getpid() }
)
26 pub fn open(pid
: pid_t
) -> io
::Result
<Self> {
27 let path
= CString
::new(format
!("/proc/{}", pid
)).unwrap();
29 let fd
= c_try
!(unsafe { libc::open(path.as_ptr(), libc::O_DIRECTORY | libc::O_CLOEXEC) }
);
34 /// Turn a valid pid file descriptor into a PidFd.
38 /// The file descriptor must already be a valid pidfd, this is not checked. This function only
39 /// fails if reading the pid from the pidfd's proc entry fails.
40 pub unsafe fn try_from_fd(fd
: Fd
) -> io
::Result
<Self> {
41 let mut this
= Self(fd
.into_raw_fd(), -1 as pid_t
);
42 let pid
= this
.read_pid()?
;
47 pub fn mount_namespace(&self) -> io
::Result
<NsFd
<ns_type
::Mount
>> {
48 NsFd
::openat(self.0, c_str
!("ns/mnt"))
51 pub fn cgroup_namespace(&self) -> io
::Result
<NsFd
<ns_type
::Cgroup
>> {
52 NsFd
::openat(self.0, c_str
!("ns/cgroup"))
55 pub fn user_namespace(&self) -> io
::Result
<NsFd
<ns_type
::User
>> {
56 NsFd
::openat(self.0, c_str
!("ns/user"))
59 fn fd(&self, path
: &CStr
, flags
: c_int
, mode
: c_int
) -> io
::Result
<Fd
> {
63 path
.as_ptr() as *const _
,
64 flags
| libc
::O_CLOEXEC
,
70 pub fn fd_cwd(&self) -> io
::Result
<Fd
> {
71 self.fd(c_str
!("cwd"), libc
::O_DIRECTORY
, 0)
74 pub fn fd_num(&self, num
: RawFd
, flags
: c_int
) -> io
::Result
<Fd
> {
75 let path
= format
!("fd/{}\0", num
);
77 unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) }
,
83 pub fn enter_cwd(&self) -> io
::Result
<()> {
84 c_try
!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) }
);
88 pub fn enter_chroot(&self) -> io
::Result
<()> {
89 c_try
!(unsafe { libc::fchdir(self.as_raw_fd()) }
);
90 c_try
!(unsafe { libc::chroot(b"root\0".as_ptr() as *const _) }
);
91 c_try
!(unsafe { libc::chdir(b"/\0".as_ptr() as *const _) }
);
95 // procfs files cannot be async, we cannot add them to epoll...
96 pub fn open_file(&self, path
: &CStr
, flags
: c_int
, mode
: c_int
) -> io
::Result
<std
::fs
::File
> {
97 Ok(unsafe { std::fs::File::from_raw_fd(self.fd(path, flags, mode)?.into_raw_fd()) }
)
101 fn open_buffered(&self, path
: &CStr
) -> io
::Result
<impl BufRead
> {
102 Ok(BufReader
::new(self.open_file(
104 libc
::O_RDONLY
| libc
::O_CLOEXEC
,
110 pub fn get_pid(&self) -> pid_t
{
114 fn read_pid(&self) -> io
::Result
<pid_t
> {
115 let reader
= self.open_buffered(c_str
!("status"))?
;
117 for line
in reader
.lines() {
119 let mut parts
= line
.split_ascii_whitespace();
120 if parts
.next() == Some("Pid:") {
123 .ok_or_else(|| io
::Error
::new(io
::ErrorKind
::Other
, "bad 'Pid:' line in proc"))?
126 io
::Error
::new(io
::ErrorKind
::Other
, "failed to parse pid from proc")
132 Err(io
::ErrorKind
::NotFound
.into())
136 fn __check_uid_gid(value
: Option
<&str>) -> io
::Result
<libc
::uid_t
> {
138 .ok_or_else(|| io
::Error
::new(io
::ErrorKind
::Other
, "bad 'Uid/Gid:' line in proc"))?
139 .parse
::<libc
::uid_t
>()
140 .map_err(|_
| io
::Error
::new(io
::ErrorKind
::Other
, "failed to parse uid from proc"))
143 pub fn get_status(&self) -> io
::Result
<ProcStatus
> {
144 let reader
= self.open_buffered(c_str
!("status"))?
;
147 fn check_u64_hex(value
: Option
<&str>) -> io
::Result
<u64> {
148 Ok(u64::from_str_radix(
149 value
.ok_or_else(|| {
150 io
::Error
::new(io
::ErrorKind
::Other
, "bad numeric property line in proc")
154 .map_err(|e
| io
::Error
::new(io
::ErrorKind
::Other
, e
.to_string()))?
)
158 fn check_u32_oct(value
: Option
<&str>) -> io
::Result
<u32> {
159 Ok(u32::from_str_radix(
160 value
.ok_or_else(|| {
161 io
::Error
::new(io
::ErrorKind
::Other
, "bad numeric property line in proc")
165 .map_err(|e
| io
::Error
::new(io
::ErrorKind
::Other
, e
.to_string()))?
)
168 let mut ids
= Uids
::default();
169 let mut caps
= Capabilities
::default();
170 let mut umask
= 0o022;
171 for line
in reader
.lines() {
173 let mut parts
= line
.split_ascii_whitespace();
176 ids
.ruid
= Self::__check_uid_gid(parts
.next())?
;
177 ids
.euid
= Self::__check_uid_gid(parts
.next())?
;
178 ids
.suid
= Self::__check_uid_gid(parts
.next())?
;
179 ids
.fsuid
= Self::__check_uid_gid(parts
.next())?
;
182 ids
.rgid
= Self::__check_uid_gid(parts
.next())?
;
183 ids
.egid
= Self::__check_uid_gid(parts
.next())?
;
184 ids
.sgid
= Self::__check_uid_gid(parts
.next())?
;
185 ids
.fsgid
= Self::__check_uid_gid(parts
.next())?
;
187 Some("CapInh:") => caps
.inheritable
= check_u64_hex(parts
.next())?
,
188 Some("CapPrm:") => caps
.permitted
= check_u64_hex(parts
.next())?
,
189 Some("CapEff:") => caps
.effective
= check_u64_hex(parts
.next())?
,
190 //Some("CapBnd:") => caps.bounding = check_u64_hex(parts.next())?,
191 Some("Umask:") => umask
= check_u32_oct(parts
.next())?
,
203 pub fn get_cgroups(&self) -> Result
<CGroups
, Error
> {
204 let reader
= self.open_buffered(c_str
!("cgroup"))?
;
206 let mut cgroups
= CGroups
::new();
208 for line
in reader
.split(b'
\n'
) {
210 let mut parts
= line
.splitn(3, |b
| *b
== b'
:'
);
211 let num
= parts
.next();
212 let name
= parts
.next();
213 let path
= parts
.next();
214 if num
.is_none() || name
.is_none() || path
.is_none() || parts
.next().is_some() {
215 bail
!("failed to parse cgroup line: {:?}", line
);
218 let name
= String
::from_utf8(name
.unwrap().to_vec())?
;
219 let path
= OsString
::from_vec(path
.unwrap().to_vec());
222 cgroups
.v2
= Some(path
);
224 for entry
in name
.split('
,'
) {
225 cgroups
.v1
.insert(entry
.to_string(), path
.clone());
233 pub fn get_uid_gid_map(&self, file
: &CStr
) -> Result
<IdMap
, Error
> {
234 let reader
= self.open_buffered(file
)?
;
236 let mut entries
= Vec
::new();
237 for line
in reader
.lines() {
239 let mut parts
= line
.split_ascii_whitespace();
240 let ns
= u64::from(Self::__check_uid_gid(parts
.next())?
);
241 let host
= u64::from(Self::__check_uid_gid(parts
.next())?
);
242 let range
= u64::from(Self::__check_uid_gid(parts
.next())?
);
243 entries
.push(IdMapEntry { ns, host, range }
);
246 Ok(IdMap
::new(entries
))
249 pub fn get_uid_map(&self) -> Result
<IdMap
, Error
> {
250 self.get_uid_gid_map(c_str
!("uid_map"))
253 pub fn get_gid_map(&self) -> Result
<IdMap
, Error
> {
254 self.get_uid_gid_map(c_str
!("gid_map"))
257 pub fn read_file(&self, file
: &CStr
) -> io
::Result
<Vec
<u8>> {
260 let mut reader
= self.open_file(file
, libc
::O_RDONLY
| libc
::O_CLOEXEC
, 0)?
;
261 let mut out
= Vec
::new();
262 reader
.read_to_end(&mut out
)?
;
266 pub fn user_caps(&self) -> Result
<UserCaps
, Error
> {