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