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 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 let pid
= unsafe { libc::getpid() }
;
26 let fd
= c_try
!(unsafe {
27 libc
::syscall(SYS_pidfd_open
, pid
, 0)
32 pub fn open(pid
: pid_t
) -> io
::Result
<Self> {
33 let path
= CString
::new(format
!("/proc/{}", pid
)).unwrap();
35 let fd
= c_try
!(unsafe { libc::open(path.as_ptr(), libc::O_DIRECTORY | libc::O_CLOEXEC) }
);
40 /// Turn a valid pid file descriptor into a PidFd.
44 /// The file descriptor must already be a valid pidfd, this is not checked. This function only
45 /// fails if reading the pid from the pidfd's proc entry fails.
46 pub unsafe fn try_from_fd(fd
: Fd
) -> io
::Result
<Self> {
47 let mut this
= Self(fd
.into_raw_fd(), -1 as pid_t
);
48 let pid
= this
.read_pid()?
;
53 pub fn mount_namespace(&self) -> io
::Result
<NsFd
<ns_type
::Mount
>> {
54 NsFd
::openat(self.0, c_str
!("ns/mnt"))
57 pub fn cgroup_namespace(&self) -> io
::Result
<NsFd
<ns_type
::Cgroup
>> {
58 NsFd
::openat(self.0, c_str
!("ns/cgroup"))
61 pub fn user_namespace(&self) -> io
::Result
<NsFd
<ns_type
::User
>> {
62 NsFd
::openat(self.0, c_str
!("ns/user"))
65 fn fd(&self, path
: &CStr
, flags
: c_int
, mode
: c_int
) -> io
::Result
<Fd
> {
69 path
.as_ptr() as *const _
,
70 flags
| libc
::O_CLOEXEC
,
76 pub fn fd_cwd(&self) -> io
::Result
<Fd
> {
77 self.fd(c_str
!("cwd"), libc
::O_DIRECTORY
, 0)
80 pub fn fd_num(&self, num
: RawFd
, flags
: c_int
) -> io
::Result
<Fd
> {
81 let path
= format
!("fd/{}\0", num
);
83 unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) }
,
89 pub fn enter_cwd(&self) -> io
::Result
<()> {
90 c_try
!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) }
);
94 pub fn enter_chroot(&self) -> io
::Result
<()> {
95 c_try
!(unsafe { libc::fchdir(self.as_raw_fd()) }
);
96 c_try
!(unsafe { libc::chroot(b"root\0".as_ptr() as *const _) }
);
97 c_try
!(unsafe { libc::chdir(b"/\0".as_ptr() as *const _) }
);
101 // procfs files cannot be async, we cannot add them to epoll...
102 pub fn open_file(&self, path
: &CStr
, flags
: c_int
, mode
: c_int
) -> io
::Result
<std
::fs
::File
> {
103 Ok(unsafe { std::fs::File::from_raw_fd(self.fd(path, flags, mode)?.into_raw_fd()) }
)
107 fn open_buffered(&self, path
: &CStr
) -> io
::Result
<impl BufRead
> {
108 Ok(BufReader
::new(self.open_file(
110 libc
::O_RDONLY
| libc
::O_CLOEXEC
,
116 pub fn get_pid(&self) -> pid_t
{
120 fn read_pid(&self) -> io
::Result
<pid_t
> {
121 let reader
= self.open_buffered(c_str
!("status"))?
;
123 for line
in reader
.lines() {
125 let mut parts
= line
.split_ascii_whitespace();
126 if parts
.next() == Some("Pid:") {
129 .ok_or_else(|| io
::Error
::new(io
::ErrorKind
::Other
, "bad 'Pid:' line in proc"))?
132 io
::Error
::new(io
::ErrorKind
::Other
, "failed to parse pid from proc")
138 Err(io
::ErrorKind
::NotFound
.into())
142 fn __check_uid_gid(value
: Option
<&str>) -> io
::Result
<libc
::uid_t
> {
144 .ok_or_else(|| io
::Error
::new(io
::ErrorKind
::Other
, "bad 'Uid/Gid:' line in proc"))?
145 .parse
::<libc
::uid_t
>()
146 .map_err(|_
| io
::Error
::new(io
::ErrorKind
::Other
, "failed to parse uid from proc"))
149 pub fn get_status(&self) -> io
::Result
<ProcStatus
> {
150 let reader
= self.open_buffered(c_str
!("status"))?
;
153 fn check_u64_hex(value
: Option
<&str>) -> io
::Result
<u64> {
154 Ok(u64::from_str_radix(
155 value
.ok_or_else(|| {
156 io
::Error
::new(io
::ErrorKind
::Other
, "bad numeric property line in proc")
160 .map_err(|e
| io
::Error
::new(io
::ErrorKind
::Other
, e
.to_string()))?
)
164 fn check_u32_oct(value
: Option
<&str>) -> io
::Result
<u32> {
165 Ok(u32::from_str_radix(
166 value
.ok_or_else(|| {
167 io
::Error
::new(io
::ErrorKind
::Other
, "bad numeric property line in proc")
171 .map_err(|e
| io
::Error
::new(io
::ErrorKind
::Other
, e
.to_string()))?
)
174 let mut ids
= Uids
::default();
175 let mut caps
= Capabilities
::default();
176 let mut umask
= 0o022;
177 for line
in reader
.lines() {
179 let mut parts
= line
.split_ascii_whitespace();
182 ids
.ruid
= Self::__check_uid_gid(parts
.next())?
;
183 ids
.euid
= Self::__check_uid_gid(parts
.next())?
;
184 ids
.suid
= Self::__check_uid_gid(parts
.next())?
;
185 ids
.fsuid
= Self::__check_uid_gid(parts
.next())?
;
188 ids
.rgid
= Self::__check_uid_gid(parts
.next())?
;
189 ids
.egid
= Self::__check_uid_gid(parts
.next())?
;
190 ids
.sgid
= Self::__check_uid_gid(parts
.next())?
;
191 ids
.fsgid
= Self::__check_uid_gid(parts
.next())?
;
193 Some("CapInh:") => caps
.inheritable
= check_u64_hex(parts
.next())?
,
194 Some("CapPrm:") => caps
.permitted
= check_u64_hex(parts
.next())?
,
195 Some("CapEff:") => caps
.effective
= check_u64_hex(parts
.next())?
,
196 //Some("CapBnd:") => caps.bounding = check_u64_hex(parts.next())?,
197 Some("Umask:") => umask
= check_u32_oct(parts
.next())?
,
209 pub fn get_cgroups(&self) -> Result
<CGroups
, Error
> {
210 let reader
= self.open_buffered(c_str
!("cgroup"))?
;
212 let mut cgroups
= CGroups
::new();
214 for line
in reader
.split(b'
\n'
) {
216 let mut parts
= line
.splitn(3, |b
| *b
== b'
:'
);
217 let num
= parts
.next();
218 let name
= parts
.next();
219 let path
= parts
.next();
220 if num
.is_none() || name
.is_none() || path
.is_none() || parts
.next().is_some() {
221 bail
!("failed to parse cgroup line: {:?}", line
);
224 let name
= String
::from_utf8(name
.unwrap().to_vec())?
;
225 let path
= OsString
::from_vec(path
.unwrap().to_vec());
228 cgroups
.v2
= Some(path
);
230 for entry
in name
.split('
,'
) {
231 cgroups
.v1
.insert(entry
.to_string(), path
.clone());
239 pub fn get_uid_gid_map(&self, file
: &CStr
) -> Result
<IdMap
, Error
> {
240 let reader
= self.open_buffered(file
)?
;
242 let mut entries
= Vec
::new();
243 for line
in reader
.lines() {
245 let mut parts
= line
.split_ascii_whitespace();
246 let ns
= u64::from(Self::__check_uid_gid(parts
.next())?
);
247 let host
= u64::from(Self::__check_uid_gid(parts
.next())?
);
248 let range
= u64::from(Self::__check_uid_gid(parts
.next())?
);
249 entries
.push(IdMapEntry { ns, host, range }
);
252 Ok(IdMap
::new(entries
))
255 pub fn get_uid_map(&self) -> Result
<IdMap
, Error
> {
256 self.get_uid_gid_map(c_str
!("uid_map"))
259 pub fn get_gid_map(&self) -> Result
<IdMap
, Error
> {
260 self.get_uid_gid_map(c_str
!("gid_map"))
263 pub fn read_file(&self, file
: &CStr
) -> io
::Result
<Vec
<u8>> {
266 let mut reader
= self.open_file(file
, libc
::O_RDONLY
| libc
::O_CLOEXEC
, 0)?
;
267 let mut out
= Vec
::new();
268 reader
.read_to_end(&mut out
)?
;
272 pub fn user_caps(&self) -> Result
<UserCaps
, Error
> {