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
);
22 pub fn current() -> io
::Result
<Self> {
23 let fd
= c_try
!(unsafe {
25 b
"/proc/self\0".as_ptr() as _
,
26 libc
::O_DIRECTORY
| libc
::O_CLOEXEC
,
30 Ok(Self(fd
, unsafe { libc::getpid() }
))
33 pub fn open(pid
: pid_t
) -> io
::Result
<Self> {
34 let path
= CString
::new(format
!("/proc/{}", pid
)).unwrap();
36 let fd
= c_try
!(unsafe { libc::open(path.as_ptr(), libc::O_DIRECTORY | libc::O_CLOEXEC) }
);
41 /// Turn a valid pid file descriptor into a PidFd.
45 /// The file descriptor must already be a valid pidfd, this is not checked. This function only
46 /// fails if reading the pid from the pidfd's proc entry fails.
47 pub unsafe fn try_from_fd(fd
: Fd
) -> io
::Result
<Self> {
48 let mut this
= Self(fd
.into_raw_fd(), -1 as pid_t
);
49 let pid
= this
.read_pid()?
;
54 pub fn mount_namespace(&self) -> io
::Result
<NsFd
<ns_type
::Mount
>> {
55 NsFd
::openat(self.0, c_str
!("ns/mnt"))
58 pub fn cgroup_namespace(&self) -> io
::Result
<NsFd
<ns_type
::Cgroup
>> {
59 NsFd
::openat(self.0, c_str
!("ns/cgroup"))
62 pub fn user_namespace(&self) -> io
::Result
<NsFd
<ns_type
::User
>> {
63 NsFd
::openat(self.0, c_str
!("ns/user"))
66 fn fd(&self, path
: &CStr
, flags
: c_int
, mode
: c_int
) -> io
::Result
<Fd
> {
70 path
.as_ptr() as *const _
,
71 flags
| libc
::O_CLOEXEC
,
77 pub fn fd_cwd(&self) -> io
::Result
<Fd
> {
78 self.fd(c_str
!("cwd"), libc
::O_DIRECTORY
, 0)
81 pub fn fd_num(&self, num
: RawFd
, flags
: c_int
) -> io
::Result
<Fd
> {
82 let path
= format
!("fd/{}\0", num
);
84 unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) }
,
90 pub fn enter_cwd(&self) -> io
::Result
<()> {
91 c_try
!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) }
);
95 pub fn enter_chroot(&self) -> io
::Result
<()> {
96 c_try
!(unsafe { libc::fchdir(self.as_raw_fd()) }
);
97 c_try
!(unsafe { libc::chroot(b"root\0".as_ptr() as *const _) }
);
98 c_try
!(unsafe { libc::chdir(b"/\0".as_ptr() as *const _) }
);
102 // procfs files cannot be async, we cannot add them to epoll...
103 pub fn open_file(&self, path
: &CStr
, flags
: c_int
, mode
: c_int
) -> io
::Result
<std
::fs
::File
> {
104 Ok(unsafe { std::fs::File::from_raw_fd(self.fd(path, flags, mode)?.into_raw_fd()) }
)
108 fn open_buffered(&self, path
: &CStr
) -> io
::Result
<impl BufRead
> {
109 Ok(BufReader
::new(self.open_file(
111 libc
::O_RDONLY
| libc
::O_CLOEXEC
,
117 pub fn get_pid(&self) -> pid_t
{
121 fn read_pid(&self) -> io
::Result
<pid_t
> {
122 let reader
= self.open_buffered(c_str
!("status"))?
;
124 for line
in reader
.lines() {
126 let mut parts
= line
.split_ascii_whitespace();
127 if parts
.next() == Some("Pid:") {
130 .ok_or_else(|| io
::Error
::new(io
::ErrorKind
::Other
, "bad 'Pid:' line in proc"))?
133 io
::Error
::new(io
::ErrorKind
::Other
, "failed to parse pid from proc")
139 Err(io
::ErrorKind
::NotFound
.into())
143 fn __check_uid_gid(value
: Option
<&str>) -> io
::Result
<libc
::uid_t
> {
145 .ok_or_else(|| io
::Error
::new(io
::ErrorKind
::Other
, "bad 'Uid/Gid:' line in proc"))?
146 .parse
::<libc
::uid_t
>()
147 .map_err(|_
| io
::Error
::new(io
::ErrorKind
::Other
, "failed to parse uid from proc"))
150 pub fn get_status(&self) -> io
::Result
<ProcStatus
> {
151 let reader
= self.open_buffered(c_str
!("status"))?
;
154 fn check_u64_hex(value
: Option
<&str>) -> io
::Result
<u64> {
155 Ok(u64::from_str_radix(
156 value
.ok_or_else(|| {
157 io
::Error
::new(io
::ErrorKind
::Other
, "bad numeric property line in proc")
161 .map_err(|e
| io
::Error
::new(io
::ErrorKind
::Other
, e
.to_string()))?
)
165 fn check_u32_oct(value
: Option
<&str>) -> io
::Result
<u32> {
166 Ok(u32::from_str_radix(
167 value
.ok_or_else(|| {
168 io
::Error
::new(io
::ErrorKind
::Other
, "bad numeric property line in proc")
172 .map_err(|e
| io
::Error
::new(io
::ErrorKind
::Other
, e
.to_string()))?
)
175 let mut ids
= Uids
::default();
176 let mut caps
= Capabilities
::default();
177 let mut umask
= 0o022;
178 for line
in reader
.lines() {
180 let mut parts
= line
.split_ascii_whitespace();
183 ids
.ruid
= Self::__check_uid_gid(parts
.next())?
;
184 ids
.euid
= Self::__check_uid_gid(parts
.next())?
;
185 ids
.suid
= Self::__check_uid_gid(parts
.next())?
;
186 ids
.fsuid
= Self::__check_uid_gid(parts
.next())?
;
189 ids
.rgid
= Self::__check_uid_gid(parts
.next())?
;
190 ids
.egid
= Self::__check_uid_gid(parts
.next())?
;
191 ids
.sgid
= Self::__check_uid_gid(parts
.next())?
;
192 ids
.fsgid
= Self::__check_uid_gid(parts
.next())?
;
194 Some("CapInh:") => caps
.inheritable
= check_u64_hex(parts
.next())?
,
195 Some("CapPrm:") => caps
.permitted
= check_u64_hex(parts
.next())?
,
196 Some("CapEff:") => caps
.effective
= check_u64_hex(parts
.next())?
,
197 //Some("CapBnd:") => caps.bounding = check_u64_hex(parts.next())?,
198 Some("Umask:") => umask
= check_u32_oct(parts
.next())?
,
210 pub fn get_cgroups(&self) -> Result
<CGroups
, Error
> {
211 let reader
= self.open_buffered(c_str
!("cgroup"))?
;
213 let mut cgroups
= CGroups
::new();
215 for line
in reader
.split(b'
\n'
) {
217 let mut parts
= line
.splitn(3, |b
| *b
== b'
:'
);
218 let num
= parts
.next();
219 let name
= parts
.next();
220 let path
= parts
.next();
221 if num
.is_none() || name
.is_none() || path
.is_none() || parts
.next().is_some() {
222 bail
!("failed to parse cgroup line: {:?}", line
);
225 let name
= String
::from_utf8(name
.unwrap().to_vec())?
;
226 let path
= OsString
::from_vec(path
.unwrap().to_vec());
229 cgroups
.v2
= Some(path
);
231 for entry
in name
.split('
,'
) {
232 cgroups
.v1
.insert(entry
.to_string(), path
.clone());
240 pub fn get_uid_gid_map(&self, file
: &CStr
) -> Result
<IdMap
, Error
> {
241 let reader
= self.open_buffered(file
)?
;
243 let mut entries
= Vec
::new();
244 for line
in reader
.lines() {
246 let mut parts
= line
.split_ascii_whitespace();
247 let ns
= u64::from(Self::__check_uid_gid(parts
.next())?
);
248 let host
= u64::from(Self::__check_uid_gid(parts
.next())?
);
249 let range
= u64::from(Self::__check_uid_gid(parts
.next())?
);
250 entries
.push(IdMapEntry { ns, host, range }
);
253 Ok(IdMap
::new(entries
))
256 pub fn get_uid_map(&self) -> Result
<IdMap
, Error
> {
257 self.get_uid_gid_map(c_str
!("uid_map"))
260 pub fn get_gid_map(&self) -> Result
<IdMap
, Error
> {
261 self.get_uid_gid_map(c_str
!("gid_map"))
264 pub fn read_file(&self, file
: &CStr
) -> io
::Result
<Vec
<u8>> {
267 let mut reader
= self.open_file(file
, libc
::O_RDONLY
| libc
::O_CLOEXEC
, 0)?
;
268 let mut out
= Vec
::new();
269 reader
.read_to_end(&mut out
)?
;
273 pub fn user_caps(&self) -> Result
<UserCaps
, Error
> {