1 //! pidfd helper functionality
3 use std
::ffi
::{CStr, 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 failure
::{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
);
21 pub const SYS_PIDFD_OPEN
: libc
::c_long
= 434; // asm-generic
24 pub fn current() -> io
::Result
<Self> {
25 Self::open(unsafe { libc::getpid() }
)
28 pub fn open(pid
: pid_t
) -> io
::Result
<Self> {
29 let fd
= c_try
!(unsafe {
30 libc
::syscall(SYS_PIDFD_OPEN
, pid
, 0)
32 Ok(Self(fd
as RawFd
, pid
))
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 let mut this
= Self(fd
.into_raw_fd(), -1 as pid_t
);
43 let pid
= this
.read_pid()?
;
48 pub fn mount_namespace(&self) -> io
::Result
<NsFd
<ns_type
::Mount
>> {
49 NsFd
::openat(self.0, c_str
!("ns/mnt"))
52 pub fn cgroup_namespace(&self) -> io
::Result
<NsFd
<ns_type
::Cgroup
>> {
53 NsFd
::openat(self.0, c_str
!("ns/cgroup"))
56 pub fn user_namespace(&self) -> io
::Result
<NsFd
<ns_type
::User
>> {
57 NsFd
::openat(self.0, c_str
!("ns/user"))
60 fn fd(&self, path
: &CStr
, flags
: c_int
, mode
: c_int
) -> io
::Result
<Fd
> {
64 path
.as_ptr() as *const _
,
65 flags
| libc
::O_CLOEXEC
,
71 pub fn fd_cwd(&self) -> io
::Result
<Fd
> {
72 self.fd(c_str
!("cwd"), libc
::O_DIRECTORY
, 0)
75 pub fn fd_num(&self, num
: RawFd
, flags
: c_int
) -> io
::Result
<Fd
> {
76 let path
= format
!("fd/{}\0", num
);
78 unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) }
,
84 pub fn enter_cwd(&self) -> io
::Result
<()> {
85 c_try
!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) }
);
89 pub fn enter_chroot(&self) -> io
::Result
<()> {
90 c_try
!(unsafe { libc::fchdir(self.as_raw_fd()) }
);
91 c_try
!(unsafe { libc::chroot(b"root\0".as_ptr() as *const _) }
);
92 c_try
!(unsafe { libc::chdir(b"/\0".as_ptr() as *const _) }
);
96 // procfs files cannot be async, we cannot add them to epoll...
97 pub fn open_file(&self, path
: &CStr
, flags
: c_int
, mode
: c_int
) -> io
::Result
<std
::fs
::File
> {
98 Ok(unsafe { std::fs::File::from_raw_fd(self.fd(path, flags, mode)?.into_raw_fd()) }
)
102 fn open_buffered(&self, path
: &CStr
) -> io
::Result
<impl BufRead
> {
103 Ok(BufReader
::new(self.open_file(
105 libc
::O_RDONLY
| libc
::O_CLOEXEC
,
111 pub fn get_pid(&self) -> pid_t
{
115 fn read_pid(&self) -> io
::Result
<pid_t
> {
116 let reader
= self.open_buffered(c_str
!("status"))?
;
118 for line
in reader
.lines() {
120 let mut parts
= line
.split_ascii_whitespace();
121 if parts
.next() == Some("Pid:") {
124 .ok_or_else(|| io
::Error
::new(io
::ErrorKind
::Other
, "bad 'Pid:' line in proc"))?
127 io
::Error
::new(io
::ErrorKind
::Other
, "failed to parse pid from proc")
133 Err(io
::ErrorKind
::NotFound
.into())
137 fn __check_uid_gid(value
: Option
<&str>) -> io
::Result
<libc
::uid_t
> {
139 .ok_or_else(|| io
::Error
::new(io
::ErrorKind
::Other
, "bad 'Uid/Gid:' line in proc"))?
140 .parse
::<libc
::uid_t
>()
141 .map_err(|_
| io
::Error
::new(io
::ErrorKind
::Other
, "failed to parse uid from proc"))
144 pub fn get_status(&self) -> io
::Result
<ProcStatus
> {
145 let reader
= self.open_buffered(c_str
!("status"))?
;
148 fn check_u64_hex(value
: Option
<&str>) -> io
::Result
<u64> {
149 Ok(u64::from_str_radix(
150 value
.ok_or_else(|| {
151 io
::Error
::new(io
::ErrorKind
::Other
, "bad numeric property line in proc")
155 .map_err(|e
| io
::Error
::new(io
::ErrorKind
::Other
, e
.to_string()))?
)
159 fn check_u32_oct(value
: Option
<&str>) -> io
::Result
<u32> {
160 Ok(u32::from_str_radix(
161 value
.ok_or_else(|| {
162 io
::Error
::new(io
::ErrorKind
::Other
, "bad numeric property line in proc")
166 .map_err(|e
| io
::Error
::new(io
::ErrorKind
::Other
, e
.to_string()))?
)
169 let mut ids
= Uids
::default();
170 let mut caps
= Capabilities
::default();
171 let mut umask
= 0o022;
172 for line
in reader
.lines() {
174 let mut parts
= line
.split_ascii_whitespace();
177 ids
.ruid
= Self::__check_uid_gid(parts
.next())?
;
178 ids
.euid
= Self::__check_uid_gid(parts
.next())?
;
179 ids
.suid
= Self::__check_uid_gid(parts
.next())?
;
180 ids
.fsuid
= Self::__check_uid_gid(parts
.next())?
;
183 ids
.rgid
= Self::__check_uid_gid(parts
.next())?
;
184 ids
.egid
= Self::__check_uid_gid(parts
.next())?
;
185 ids
.sgid
= Self::__check_uid_gid(parts
.next())?
;
186 ids
.fsgid
= Self::__check_uid_gid(parts
.next())?
;
188 Some("CapInh:") => caps
.inheritable
= check_u64_hex(parts
.next())?
,
189 Some("CapPrm:") => caps
.permitted
= check_u64_hex(parts
.next())?
,
190 Some("CapEff:") => caps
.effective
= check_u64_hex(parts
.next())?
,
191 //Some("CapBnd:") => caps.bounding = check_u64_hex(parts.next())?,
192 Some("Umask:") => umask
= check_u32_oct(parts
.next())?
,
204 pub fn get_cgroups(&self) -> Result
<CGroups
, Error
> {
205 let reader
= self.open_buffered(c_str
!("cgroup"))?
;
207 let mut cgroups
= CGroups
::new();
209 for line
in reader
.split(b'
\n'
) {
211 let mut parts
= line
.splitn(3, |b
| *b
== b'
:'
);
212 let num
= parts
.next();
213 let name
= parts
.next();
214 let path
= parts
.next();
215 if num
.is_none() || name
.is_none() || path
.is_none() || parts
.next().is_some() {
216 bail
!("failed to parse cgroup line: {:?}", line
);
219 let name
= String
::from_utf8(name
.unwrap().to_vec())?
;
220 let path
= OsString
::from_vec(path
.unwrap().to_vec());
223 cgroups
.v2
= Some(path
);
225 for entry
in name
.split('
,'
) {
226 cgroups
.v1
.insert(entry
.to_string(), path
.clone());
234 pub fn get_uid_gid_map(&self, file
: &CStr
) -> Result
<IdMap
, Error
> {
235 let reader
= self.open_buffered(file
)?
;
237 let mut entries
= Vec
::new();
238 for line
in reader
.lines() {
240 let mut parts
= line
.split_ascii_whitespace();
241 let ns
= u64::from(Self::__check_uid_gid(parts
.next())?
);
242 let host
= u64::from(Self::__check_uid_gid(parts
.next())?
);
243 let range
= u64::from(Self::__check_uid_gid(parts
.next())?
);
244 entries
.push(IdMapEntry { ns, host, range }
);
247 Ok(IdMap
::new(entries
))
250 pub fn get_uid_map(&self) -> Result
<IdMap
, Error
> {
251 self.get_uid_gid_map(c_str
!("uid_map"))
254 pub fn get_gid_map(&self) -> Result
<IdMap
, Error
> {
255 self.get_uid_gid_map(c_str
!("gid_map"))
258 pub fn read_file(&self, file
: &CStr
) -> io
::Result
<Vec
<u8>> {
261 let mut reader
= self.open_file(file
, libc
::O_RDONLY
| libc
::O_CLOEXEC
, 0)?
;
262 let mut out
= Vec
::new();
263 reader
.read_to_end(&mut out
)?
;
267 pub fn user_caps(&self) -> Result
<UserCaps
, Error
> {