]> git.proxmox.com Git - pve-lxc-syscalld.git/blob - src/process/pid_fd.rs
use pidfd_open
[pve-lxc-syscalld.git] / src / process / pid_fd.rs
1 //! pidfd helper functionality
2
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};
8
9 use failure::{bail, Error};
10 use libc::pid_t;
11
12 use crate::capability::Capabilities;
13 use crate::nsfd::{ns_type, NsFd};
14 use crate::tools::Fd;
15
16 use super::{CGroups, IdMap, IdMapEntry, ProcStatus, Uids, UserCaps};
17
18 pub struct PidFd(RawFd, pid_t);
19 file_descriptor_impl!(PidFd);
20
21 pub const SYS_pidfd_open: libc::c_long = 434; // asm-generic
22
23 impl PidFd {
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)
28 });
29 Ok(Self(fd, pid))
30 }
31
32 pub fn open(pid: pid_t) -> io::Result<Self> {
33 let path = CString::new(format!("/proc/{}", pid)).unwrap();
34
35 let fd = c_try!(unsafe { libc::open(path.as_ptr(), libc::O_DIRECTORY | libc::O_CLOEXEC) });
36
37 Ok(Self(fd, pid))
38 }
39
40 /// Turn a valid pid file descriptor into a PidFd.
41 ///
42 /// # Safety
43 ///
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()?;
49 this.1 = pid;
50 Ok(this)
51 }
52
53 pub fn mount_namespace(&self) -> io::Result<NsFd<ns_type::Mount>> {
54 NsFd::openat(self.0, c_str!("ns/mnt"))
55 }
56
57 pub fn cgroup_namespace(&self) -> io::Result<NsFd<ns_type::Cgroup>> {
58 NsFd::openat(self.0, c_str!("ns/cgroup"))
59 }
60
61 pub fn user_namespace(&self) -> io::Result<NsFd<ns_type::User>> {
62 NsFd::openat(self.0, c_str!("ns/user"))
63 }
64
65 fn fd(&self, path: &CStr, flags: c_int, mode: c_int) -> io::Result<Fd> {
66 Ok(Fd(c_try!(unsafe {
67 libc::openat(
68 self.as_raw_fd(),
69 path.as_ptr() as *const _,
70 flags | libc::O_CLOEXEC,
71 mode,
72 )
73 })))
74 }
75
76 pub fn fd_cwd(&self) -> io::Result<Fd> {
77 self.fd(c_str!("cwd"), libc::O_DIRECTORY, 0)
78 }
79
80 pub fn fd_num(&self, num: RawFd, flags: c_int) -> io::Result<Fd> {
81 let path = format!("fd/{}\0", num);
82 self.fd(
83 unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) },
84 flags,
85 0,
86 )
87 }
88
89 pub fn enter_cwd(&self) -> io::Result<()> {
90 c_try!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) });
91 Ok(())
92 }
93
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 _) });
98 Ok(())
99 }
100
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()) })
104 }
105
106 #[inline]
107 fn open_buffered(&self, path: &CStr) -> io::Result<impl BufRead> {
108 Ok(BufReader::new(self.open_file(
109 path,
110 libc::O_RDONLY | libc::O_CLOEXEC,
111 0,
112 )?))
113 }
114
115 #[inline]
116 pub fn get_pid(&self) -> pid_t {
117 self.1
118 }
119
120 fn read_pid(&self) -> io::Result<pid_t> {
121 let reader = self.open_buffered(c_str!("status"))?;
122
123 for line in reader.lines() {
124 let line = line?;
125 let mut parts = line.split_ascii_whitespace();
126 if parts.next() == Some("Pid:") {
127 let pid = parts
128 .next()
129 .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "bad 'Pid:' line in proc"))?
130 .parse::<pid_t>()
131 .map_err(|_| {
132 io::Error::new(io::ErrorKind::Other, "failed to parse pid from proc")
133 })?;
134 return Ok(pid);
135 }
136 }
137
138 Err(io::ErrorKind::NotFound.into())
139 }
140
141 #[inline]
142 fn __check_uid_gid(value: Option<&str>) -> io::Result<libc::uid_t> {
143 value
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"))
147 }
148
149 pub fn get_status(&self) -> io::Result<ProcStatus> {
150 let reader = self.open_buffered(c_str!("status"))?;
151
152 #[inline]
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")
157 })?,
158 16,
159 )
160 .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?)
161 }
162
163 #[inline]
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")
168 })?,
169 8,
170 )
171 .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?)
172 }
173
174 let mut ids = Uids::default();
175 let mut caps = Capabilities::default();
176 let mut umask = 0o022;
177 for line in reader.lines() {
178 let line = line?;
179 let mut parts = line.split_ascii_whitespace();
180 match parts.next() {
181 Some("Uid:") => {
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())?;
186 }
187 Some("Gid:") => {
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())?;
192 }
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())?,
198 _ => continue,
199 }
200 }
201
202 Ok(ProcStatus {
203 uids: ids,
204 capabilities: caps,
205 umask,
206 })
207 }
208
209 pub fn get_cgroups(&self) -> Result<CGroups, Error> {
210 let reader = self.open_buffered(c_str!("cgroup"))?;
211
212 let mut cgroups = CGroups::new();
213
214 for line in reader.split(b'\n') {
215 let line = line?;
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);
222 }
223
224 let name = String::from_utf8(name.unwrap().to_vec())?;
225 let path = OsString::from_vec(path.unwrap().to_vec());
226
227 if name.is_empty() {
228 cgroups.v2 = Some(path);
229 } else {
230 for entry in name.split(',') {
231 cgroups.v1.insert(entry.to_string(), path.clone());
232 }
233 }
234 }
235
236 Ok(cgroups)
237 }
238
239 pub fn get_uid_gid_map(&self, file: &CStr) -> Result<IdMap, Error> {
240 let reader = self.open_buffered(file)?;
241
242 let mut entries = Vec::new();
243 for line in reader.lines() {
244 let line = line?;
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 });
250 }
251
252 Ok(IdMap::new(entries))
253 }
254
255 pub fn get_uid_map(&self) -> Result<IdMap, Error> {
256 self.get_uid_gid_map(c_str!("uid_map"))
257 }
258
259 pub fn get_gid_map(&self) -> Result<IdMap, Error> {
260 self.get_uid_gid_map(c_str!("gid_map"))
261 }
262
263 pub fn read_file(&self, file: &CStr) -> io::Result<Vec<u8>> {
264 use io::Read;
265
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)?;
269 Ok(out)
270 }
271
272 pub fn user_caps(&self) -> Result<UserCaps, Error> {
273 UserCaps::new(self)
274 }
275 }