]> git.proxmox.com Git - pve-lxc-syscalld.git/blob - src/process/pid_fd.rs
also use pidfd_open for explicit pids
[pve-lxc-syscalld.git] / src / process / pid_fd.rs
1 //! pidfd helper functionality
2
3 use std::ffi::{CStr, 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 Self::open(unsafe { libc::getpid() })
26 }
27
28 pub fn open(pid: pid_t) -> io::Result<Self> {
29 let fd = c_try!(unsafe {
30 libc::syscall(SYS_PIDFD_OPEN, pid, 0)
31 });
32 Ok(Self(fd as RawFd, pid))
33 }
34
35 /// Turn a valid pid file descriptor into a PidFd.
36 ///
37 /// # Safety
38 ///
39 /// The file descriptor must already be a valid pidfd, this is not checked. This function only
40 /// fails if reading the pid from the pidfd's proc entry fails.
41 pub unsafe fn try_from_fd(fd: Fd) -> io::Result<Self> {
42 let mut this = Self(fd.into_raw_fd(), -1 as pid_t);
43 let pid = this.read_pid()?;
44 this.1 = pid;
45 Ok(this)
46 }
47
48 pub fn mount_namespace(&self) -> io::Result<NsFd<ns_type::Mount>> {
49 NsFd::openat(self.0, c_str!("ns/mnt"))
50 }
51
52 pub fn cgroup_namespace(&self) -> io::Result<NsFd<ns_type::Cgroup>> {
53 NsFd::openat(self.0, c_str!("ns/cgroup"))
54 }
55
56 pub fn user_namespace(&self) -> io::Result<NsFd<ns_type::User>> {
57 NsFd::openat(self.0, c_str!("ns/user"))
58 }
59
60 fn fd(&self, path: &CStr, flags: c_int, mode: c_int) -> io::Result<Fd> {
61 Ok(Fd(c_try!(unsafe {
62 libc::openat(
63 self.as_raw_fd(),
64 path.as_ptr() as *const _,
65 flags | libc::O_CLOEXEC,
66 mode,
67 )
68 })))
69 }
70
71 pub fn fd_cwd(&self) -> io::Result<Fd> {
72 self.fd(c_str!("cwd"), libc::O_DIRECTORY, 0)
73 }
74
75 pub fn fd_num(&self, num: RawFd, flags: c_int) -> io::Result<Fd> {
76 let path = format!("fd/{}\0", num);
77 self.fd(
78 unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) },
79 flags,
80 0,
81 )
82 }
83
84 pub fn enter_cwd(&self) -> io::Result<()> {
85 c_try!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) });
86 Ok(())
87 }
88
89 pub fn enter_chroot(&self) -> io::Result<()> {
90 c_try!(unsafe { libc::fchdir(self.as_raw_fd()) });
91 c_try!(unsafe { libc::chroot(b"root\0".as_ptr() as *const _) });
92 c_try!(unsafe { libc::chdir(b"/\0".as_ptr() as *const _) });
93 Ok(())
94 }
95
96 // procfs files cannot be async, we cannot add them to epoll...
97 pub fn open_file(&self, path: &CStr, flags: c_int, mode: c_int) -> io::Result<std::fs::File> {
98 Ok(unsafe { std::fs::File::from_raw_fd(self.fd(path, flags, mode)?.into_raw_fd()) })
99 }
100
101 #[inline]
102 fn open_buffered(&self, path: &CStr) -> io::Result<impl BufRead> {
103 Ok(BufReader::new(self.open_file(
104 path,
105 libc::O_RDONLY | libc::O_CLOEXEC,
106 0,
107 )?))
108 }
109
110 #[inline]
111 pub fn get_pid(&self) -> pid_t {
112 self.1
113 }
114
115 fn read_pid(&self) -> io::Result<pid_t> {
116 let reader = self.open_buffered(c_str!("status"))?;
117
118 for line in reader.lines() {
119 let line = line?;
120 let mut parts = line.split_ascii_whitespace();
121 if parts.next() == Some("Pid:") {
122 let pid = parts
123 .next()
124 .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "bad 'Pid:' line in proc"))?
125 .parse::<pid_t>()
126 .map_err(|_| {
127 io::Error::new(io::ErrorKind::Other, "failed to parse pid from proc")
128 })?;
129 return Ok(pid);
130 }
131 }
132
133 Err(io::ErrorKind::NotFound.into())
134 }
135
136 #[inline]
137 fn __check_uid_gid(value: Option<&str>) -> io::Result<libc::uid_t> {
138 value
139 .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "bad 'Uid/Gid:' line in proc"))?
140 .parse::<libc::uid_t>()
141 .map_err(|_| io::Error::new(io::ErrorKind::Other, "failed to parse uid from proc"))
142 }
143
144 pub fn get_status(&self) -> io::Result<ProcStatus> {
145 let reader = self.open_buffered(c_str!("status"))?;
146
147 #[inline]
148 fn check_u64_hex(value: Option<&str>) -> io::Result<u64> {
149 Ok(u64::from_str_radix(
150 value.ok_or_else(|| {
151 io::Error::new(io::ErrorKind::Other, "bad numeric property line in proc")
152 })?,
153 16,
154 )
155 .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?)
156 }
157
158 #[inline]
159 fn check_u32_oct(value: Option<&str>) -> io::Result<u32> {
160 Ok(u32::from_str_radix(
161 value.ok_or_else(|| {
162 io::Error::new(io::ErrorKind::Other, "bad numeric property line in proc")
163 })?,
164 8,
165 )
166 .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?)
167 }
168
169 let mut ids = Uids::default();
170 let mut caps = Capabilities::default();
171 let mut umask = 0o022;
172 for line in reader.lines() {
173 let line = line?;
174 let mut parts = line.split_ascii_whitespace();
175 match parts.next() {
176 Some("Uid:") => {
177 ids.ruid = Self::__check_uid_gid(parts.next())?;
178 ids.euid = Self::__check_uid_gid(parts.next())?;
179 ids.suid = Self::__check_uid_gid(parts.next())?;
180 ids.fsuid = Self::__check_uid_gid(parts.next())?;
181 }
182 Some("Gid:") => {
183 ids.rgid = Self::__check_uid_gid(parts.next())?;
184 ids.egid = Self::__check_uid_gid(parts.next())?;
185 ids.sgid = Self::__check_uid_gid(parts.next())?;
186 ids.fsgid = Self::__check_uid_gid(parts.next())?;
187 }
188 Some("CapInh:") => caps.inheritable = check_u64_hex(parts.next())?,
189 Some("CapPrm:") => caps.permitted = check_u64_hex(parts.next())?,
190 Some("CapEff:") => caps.effective = check_u64_hex(parts.next())?,
191 //Some("CapBnd:") => caps.bounding = check_u64_hex(parts.next())?,
192 Some("Umask:") => umask = check_u32_oct(parts.next())?,
193 _ => continue,
194 }
195 }
196
197 Ok(ProcStatus {
198 uids: ids,
199 capabilities: caps,
200 umask,
201 })
202 }
203
204 pub fn get_cgroups(&self) -> Result<CGroups, Error> {
205 let reader = self.open_buffered(c_str!("cgroup"))?;
206
207 let mut cgroups = CGroups::new();
208
209 for line in reader.split(b'\n') {
210 let line = line?;
211 let mut parts = line.splitn(3, |b| *b == b':');
212 let num = parts.next();
213 let name = parts.next();
214 let path = parts.next();
215 if num.is_none() || name.is_none() || path.is_none() || parts.next().is_some() {
216 bail!("failed to parse cgroup line: {:?}", line);
217 }
218
219 let name = String::from_utf8(name.unwrap().to_vec())?;
220 let path = OsString::from_vec(path.unwrap().to_vec());
221
222 if name.is_empty() {
223 cgroups.v2 = Some(path);
224 } else {
225 for entry in name.split(',') {
226 cgroups.v1.insert(entry.to_string(), path.clone());
227 }
228 }
229 }
230
231 Ok(cgroups)
232 }
233
234 pub fn get_uid_gid_map(&self, file: &CStr) -> Result<IdMap, Error> {
235 let reader = self.open_buffered(file)?;
236
237 let mut entries = Vec::new();
238 for line in reader.lines() {
239 let line = line?;
240 let mut parts = line.split_ascii_whitespace();
241 let ns = u64::from(Self::__check_uid_gid(parts.next())?);
242 let host = u64::from(Self::__check_uid_gid(parts.next())?);
243 let range = u64::from(Self::__check_uid_gid(parts.next())?);
244 entries.push(IdMapEntry { ns, host, range });
245 }
246
247 Ok(IdMap::new(entries))
248 }
249
250 pub fn get_uid_map(&self) -> Result<IdMap, Error> {
251 self.get_uid_gid_map(c_str!("uid_map"))
252 }
253
254 pub fn get_gid_map(&self) -> Result<IdMap, Error> {
255 self.get_uid_gid_map(c_str!("gid_map"))
256 }
257
258 pub fn read_file(&self, file: &CStr) -> io::Result<Vec<u8>> {
259 use io::Read;
260
261 let mut reader = self.open_file(file, libc::O_RDONLY | libc::O_CLOEXEC, 0)?;
262 let mut out = Vec::new();
263 reader.read_to_end(&mut out)?;
264 Ok(out)
265 }
266
267 pub fn user_caps(&self) -> Result<UserCaps, Error> {
268 UserCaps::new(self)
269 }
270 }