]>
Commit | Line | Data |
---|---|---|
e420f6f9 WB |
1 | //! pidfd helper functionality |
2 | ||
b2564ece | 3 | use std::ffi::{CStr, CString, OsString}; |
512f780a | 4 | use std::io::{self, BufRead, BufReader}; |
937921aa | 5 | use std::os::raw::c_int; |
3bbd1db0 | 6 | use std::os::unix::ffi::OsStringExt; |
e420f6f9 WB |
7 | use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; |
8 | ||
8150a439 | 9 | use anyhow::{bail, Error}; |
512f780a WB |
10 | use libc::pid_t; |
11 | ||
3bbd1db0 | 12 | use crate::capability::Capabilities; |
e420f6f9 WB |
13 | use crate::nsfd::{ns_type, NsFd}; |
14 | use crate::tools::Fd; | |
e420f6f9 | 15 | |
3bbd1db0 WB |
16 | use super::{CGroups, IdMap, IdMapEntry, ProcStatus, Uids, UserCaps}; |
17 | ||
512f780a | 18 | pub struct PidFd(RawFd, pid_t); |
9aa2a15a | 19 | file_descriptor_impl!(PidFd); |
512f780a | 20 | |
e420f6f9 | 21 | impl PidFd { |
42f25756 | 22 | pub fn current() -> io::Result<Self> { |
401ab6a2 | 23 | Self::open(unsafe { libc::getpid() }) |
42f25756 WB |
24 | } |
25 | ||
512f780a | 26 | pub fn open(pid: pid_t) -> io::Result<Self> { |
b2564ece WB |
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)) | |
512f780a WB |
32 | } |
33 | ||
92eface0 WB |
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. | |
512f780a WB |
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) | |
e420f6f9 WB |
45 | } |
46 | ||
47 | pub fn mount_namespace(&self) -> io::Result<NsFd<ns_type::Mount>> { | |
1e80bab0 | 48 | NsFd::openat(self.0, c_str!("ns/mnt")) |
e420f6f9 WB |
49 | } |
50 | ||
51 | pub fn cgroup_namespace(&self) -> io::Result<NsFd<ns_type::Cgroup>> { | |
1e80bab0 | 52 | NsFd::openat(self.0, c_str!("ns/cgroup")) |
e420f6f9 WB |
53 | } |
54 | ||
55 | pub fn user_namespace(&self) -> io::Result<NsFd<ns_type::User>> { | |
1e80bab0 | 56 | NsFd::openat(self.0, c_str!("ns/user")) |
e420f6f9 WB |
57 | } |
58 | ||
3bb4df0b | 59 | fn fd(&self, path: &CStr, flags: c_int, mode: c_int) -> io::Result<Fd> { |
7ca1a14c | 60 | Ok(Fd(c_try!(unsafe { |
e420f6f9 WB |
61 | libc::openat( |
62 | self.as_raw_fd(), | |
937921aa WB |
63 | path.as_ptr() as *const _, |
64 | flags | libc::O_CLOEXEC, | |
3bb4df0b | 65 | mode, |
e420f6f9 WB |
66 | ) |
67 | }))) | |
68 | } | |
937921aa WB |
69 | |
70 | pub fn fd_cwd(&self) -> io::Result<Fd> { | |
1e80bab0 | 71 | self.fd(c_str!("cwd"), libc::O_DIRECTORY, 0) |
937921aa WB |
72 | } |
73 | ||
74 | pub fn fd_num(&self, num: RawFd, flags: c_int) -> io::Result<Fd> { | |
3bb4df0b | 75 | let path = format!("fd/{}\0", num); |
512f780a WB |
76 | self.fd( |
77 | unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) }, | |
78 | flags, | |
79 | 0, | |
80 | ) | |
937921aa | 81 | } |
275009ec | 82 | |
bff40ab9 | 83 | pub fn enter_cwd(&self) -> io::Result<()> { |
7ca1a14c | 84 | c_try!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) }); |
bff40ab9 WB |
85 | Ok(()) |
86 | } | |
87 | ||
88 | pub fn enter_chroot(&self) -> io::Result<()> { | |
7ca1a14c WB |
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 _) }); | |
512f780a WB |
92 | Ok(()) |
93 | } | |
3bb4df0b WB |
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 | ||
512f780a WB |
100 | #[inline] |
101 | fn open_buffered(&self, path: &CStr) -> io::Result<impl BufRead> { | |
102 | Ok(BufReader::new(self.open_file( | |
103 | path, | |
3bb4df0b WB |
104 | libc::O_RDONLY | libc::O_CLOEXEC, |
105 | 0, | |
512f780a WB |
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> { | |
1e80bab0 | 115 | let reader = self.open_buffered(c_str!("status"))?; |
512f780a WB |
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 | ||
1349eed4 WB |
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 | ||
512f780a | 143 | pub fn get_status(&self) -> io::Result<ProcStatus> { |
1e80bab0 | 144 | let reader = self.open_buffered(c_str!("status"))?; |
512f780a | 145 | |
512f780a WB |
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 | } | |
3bb4df0b | 167 | |
512f780a WB |
168 | let mut ids = Uids::default(); |
169 | let mut caps = Capabilities::default(); | |
170 | let mut umask = 0o022; | |
3bb4df0b WB |
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:") => { | |
1349eed4 WB |
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())?; | |
3bb4df0b WB |
180 | } |
181 | Some("Gid:") => { | |
1349eed4 WB |
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())?; | |
3bb4df0b | 186 | } |
512f780a WB |
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())?, | |
3bb4df0b WB |
192 | _ => continue, |
193 | } | |
512f780a WB |
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> { | |
1e80bab0 | 204 | let reader = self.open_buffered(c_str!("cgroup"))?; |
512f780a WB |
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(); | |
f3cae2a7 | 214 | if num.is_none() || name.is_none() || path.is_none() || parts.next().is_some() { |
512f780a | 215 | bail!("failed to parse cgroup line: {:?}", line); |
3bb4df0b | 216 | } |
512f780a WB |
217 | |
218 | let name = String::from_utf8(name.unwrap().to_vec())?; | |
219 | let path = OsString::from_vec(path.unwrap().to_vec()); | |
220 | ||
9486338a | 221 | if name.is_empty() { |
512f780a WB |
222 | cgroups.v2 = Some(path); |
223 | } else { | |
224 | for entry in name.split(',') { | |
fe73c2fb WB |
225 | cgroups |
226 | .v1 | |
227 | .get_or_insert_with(Default::default) | |
228 | .insert(entry.to_string(), path.clone()); | |
512f780a WB |
229 | } |
230 | } | |
231 | } | |
232 | ||
233 | Ok(cgroups) | |
234 | } | |
235 | ||
1349eed4 WB |
236 | pub fn get_uid_gid_map(&self, file: &CStr) -> Result<IdMap, Error> { |
237 | let reader = self.open_buffered(file)?; | |
238 | ||
239 | let mut entries = Vec::new(); | |
240 | for line in reader.lines() { | |
241 | let line = line?; | |
242 | let mut parts = line.split_ascii_whitespace(); | |
9486338a WB |
243 | let ns = u64::from(Self::__check_uid_gid(parts.next())?); |
244 | let host = u64::from(Self::__check_uid_gid(parts.next())?); | |
245 | let range = u64::from(Self::__check_uid_gid(parts.next())?); | |
1349eed4 WB |
246 | entries.push(IdMapEntry { ns, host, range }); |
247 | } | |
248 | ||
3bbd1db0 | 249 | Ok(IdMap::new(entries)) |
1349eed4 WB |
250 | } |
251 | ||
252 | pub fn get_uid_map(&self) -> Result<IdMap, Error> { | |
1e80bab0 | 253 | self.get_uid_gid_map(c_str!("uid_map")) |
1349eed4 WB |
254 | } |
255 | ||
256 | pub fn get_gid_map(&self) -> Result<IdMap, Error> { | |
1e80bab0 | 257 | self.get_uid_gid_map(c_str!("gid_map")) |
1349eed4 WB |
258 | } |
259 | ||
42f25756 WB |
260 | pub fn read_file(&self, file: &CStr) -> io::Result<Vec<u8>> { |
261 | use io::Read; | |
262 | ||
263 | let mut reader = self.open_file(file, libc::O_RDONLY | libc::O_CLOEXEC, 0)?; | |
264 | let mut out = Vec::new(); | |
265 | reader.read_to_end(&mut out)?; | |
266 | Ok(out) | |
267 | } | |
268 | ||
512f780a WB |
269 | pub fn user_caps(&self) -> Result<UserCaps, Error> { |
270 | UserCaps::new(self) | |
271 | } | |
272 | } |