]> git.proxmox.com Git - pve-lxc-syscalld.git/blob - src/process/pid_fd.rs
674ebae49bc36573d1856ece8aa852b573e88930
[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 anyhow::{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 impl PidFd {
22 pub fn current() -> io::Result<Self> {
23 Self::open(unsafe { libc::getpid() })
24 }
25
26 pub fn open(pid: pid_t) -> io::Result<Self> {
27 let path = CString::new(format!("/proc/{}", pid)).unwrap();
28
29 let fd = c_try!(unsafe { libc::open(path.as_ptr(), libc::O_DIRECTORY | libc::O_CLOEXEC) });
30
31 Ok(Self(fd, pid))
32 }
33
34 /// Turn a valid pid file descriptor into a PidFd.
35 ///
36 /// # Safety
37 ///
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()?;
43 this.1 = pid;
44 Ok(this)
45 }
46
47 pub fn mount_namespace(&self) -> io::Result<NsFd<ns_type::Mount>> {
48 NsFd::openat(self.0, c_str!("ns/mnt"))
49 }
50
51 pub fn cgroup_namespace(&self) -> io::Result<NsFd<ns_type::Cgroup>> {
52 NsFd::openat(self.0, c_str!("ns/cgroup"))
53 }
54
55 pub fn user_namespace(&self) -> io::Result<NsFd<ns_type::User>> {
56 NsFd::openat(self.0, c_str!("ns/user"))
57 }
58
59 fn fd(&self, path: &CStr, flags: c_int, mode: c_int) -> io::Result<Fd> {
60 Ok(Fd(c_try!(unsafe {
61 libc::openat(
62 self.as_raw_fd(),
63 path.as_ptr() as *const _,
64 flags | libc::O_CLOEXEC,
65 mode,
66 )
67 })))
68 }
69
70 pub fn fd_cwd(&self) -> io::Result<Fd> {
71 self.fd(c_str!("cwd"), libc::O_DIRECTORY, 0)
72 }
73
74 pub fn fd_num(&self, num: RawFd, flags: c_int) -> io::Result<Fd> {
75 let path = format!("fd/{}\0", num);
76 self.fd(
77 unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) },
78 flags,
79 0,
80 )
81 }
82
83 pub fn enter_cwd(&self) -> io::Result<()> {
84 c_try!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) });
85 Ok(())
86 }
87
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 _) });
92 Ok(())
93 }
94
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()) })
98 }
99
100 #[inline]
101 fn open_buffered(&self, path: &CStr) -> io::Result<impl BufRead> {
102 Ok(BufReader::new(self.open_file(
103 path,
104 libc::O_RDONLY | libc::O_CLOEXEC,
105 0,
106 )?))
107 }
108
109 #[inline]
110 pub fn get_pid(&self) -> pid_t {
111 self.1
112 }
113
114 fn read_pid(&self) -> io::Result<pid_t> {
115 let reader = self.open_buffered(c_str!("status"))?;
116
117 for line in reader.lines() {
118 let line = line?;
119 let mut parts = line.split_ascii_whitespace();
120 if parts.next() == Some("Pid:") {
121 let pid = parts
122 .next()
123 .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "bad 'Pid:' line in proc"))?
124 .parse::<pid_t>()
125 .map_err(|_| {
126 io::Error::new(io::ErrorKind::Other, "failed to parse pid from proc")
127 })?;
128 return Ok(pid);
129 }
130 }
131
132 Err(io::ErrorKind::NotFound.into())
133 }
134
135 #[inline]
136 fn __check_uid_gid(value: Option<&str>) -> io::Result<libc::uid_t> {
137 value
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"))
141 }
142
143 pub fn get_status(&self) -> io::Result<ProcStatus> {
144 let reader = self.open_buffered(c_str!("status"))?;
145
146 #[inline]
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")
151 })?,
152 16,
153 )
154 .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?)
155 }
156
157 #[inline]
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")
162 })?,
163 8,
164 )
165 .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?)
166 }
167
168 let mut ids = Uids::default();
169 let mut caps = Capabilities::default();
170 let mut umask = 0o022;
171 for line in reader.lines() {
172 let line = line?;
173 let mut parts = line.split_ascii_whitespace();
174 match parts.next() {
175 Some("Uid:") => {
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())?;
180 }
181 Some("Gid:") => {
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())?;
186 }
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())?,
192 _ => continue,
193 }
194 }
195
196 Ok(ProcStatus {
197 uids: ids,
198 capabilities: caps,
199 umask,
200 })
201 }
202
203 pub fn get_cgroups(&self) -> Result<CGroups, Error> {
204 let reader = self.open_buffered(c_str!("cgroup"))?;
205
206 let mut cgroups = CGroups::new();
207
208 for line in reader.split(b'\n') {
209 let line = line?;
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);
216 }
217
218 let name = String::from_utf8(name.unwrap().to_vec())?;
219 let path = OsString::from_vec(path.unwrap().to_vec());
220
221 if name.is_empty() {
222 cgroups.v2 = Some(path);
223 } else {
224 for entry in name.split(',') {
225 cgroups.v1.insert(entry.to_string(), path.clone());
226 }
227 }
228 }
229
230 Ok(cgroups)
231 }
232
233 pub fn get_uid_gid_map(&self, file: &CStr) -> Result<IdMap, Error> {
234 let reader = self.open_buffered(file)?;
235
236 let mut entries = Vec::new();
237 for line in reader.lines() {
238 let line = line?;
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 });
244 }
245
246 Ok(IdMap::new(entries))
247 }
248
249 pub fn get_uid_map(&self) -> Result<IdMap, Error> {
250 self.get_uid_gid_map(c_str!("uid_map"))
251 }
252
253 pub fn get_gid_map(&self) -> Result<IdMap, Error> {
254 self.get_uid_gid_map(c_str!("gid_map"))
255 }
256
257 pub fn read_file(&self, file: &CStr) -> io::Result<Vec<u8>> {
258 use io::Read;
259
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)?;
263 Ok(out)
264 }
265
266 pub fn user_caps(&self) -> Result<UserCaps, Error> {
267 UserCaps::new(self)
268 }
269 }