]>
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; |
caeb9675 | 7 | use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; |
e420f6f9 | 8 | |
8150a439 | 9 | use anyhow::{bail, Error}; |
512f780a WB |
10 | use libc::pid_t; |
11 | ||
3bbd1db0 | 12 | use crate::capability::Capabilities; |
a03af34b | 13 | use crate::error::io_err_other; |
e420f6f9 | 14 | use crate::nsfd::{ns_type, NsFd}; |
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. | |
caeb9675 | 40 | pub unsafe fn try_from_fd(fd: OwnedFd) -> io::Result<Self> { |
a03af34b | 41 | #[allow(clippy::unnecessary_cast)] // pid_t is a type alias |
512f780a WB |
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) | |
e420f6f9 WB |
46 | } |
47 | ||
48 | pub fn mount_namespace(&self) -> io::Result<NsFd<ns_type::Mount>> { | |
1e80bab0 | 49 | NsFd::openat(self.0, c_str!("ns/mnt")) |
e420f6f9 WB |
50 | } |
51 | ||
52 | pub fn cgroup_namespace(&self) -> io::Result<NsFd<ns_type::Cgroup>> { | |
1e80bab0 | 53 | NsFd::openat(self.0, c_str!("ns/cgroup")) |
e420f6f9 WB |
54 | } |
55 | ||
56 | pub fn user_namespace(&self) -> io::Result<NsFd<ns_type::User>> { | |
1e80bab0 | 57 | NsFd::openat(self.0, c_str!("ns/user")) |
e420f6f9 WB |
58 | } |
59 | ||
caeb9675 WB |
60 | fn fd(&self, path: &CStr, flags: c_int, mode: c_int) -> io::Result<OwnedFd> { |
61 | Ok(unsafe { | |
62 | OwnedFd::from_raw_fd(c_try!(libc::openat( | |
e420f6f9 | 63 | self.as_raw_fd(), |
937921aa WB |
64 | path.as_ptr() as *const _, |
65 | flags | libc::O_CLOEXEC, | |
3bb4df0b | 66 | mode, |
caeb9675 WB |
67 | ))) |
68 | }) | |
e420f6f9 | 69 | } |
937921aa | 70 | |
caeb9675 | 71 | pub fn fd_cwd(&self) -> io::Result<OwnedFd> { |
1e80bab0 | 72 | self.fd(c_str!("cwd"), libc::O_DIRECTORY, 0) |
937921aa WB |
73 | } |
74 | ||
caeb9675 | 75 | pub fn fd_num(&self, num: RawFd, flags: c_int) -> io::Result<OwnedFd> { |
3bb4df0b | 76 | let path = format!("fd/{}\0", num); |
512f780a WB |
77 | self.fd( |
78 | unsafe { CStr::from_bytes_with_nul_unchecked(path.as_bytes()) }, | |
79 | flags, | |
80 | 0, | |
81 | ) | |
937921aa | 82 | } |
275009ec | 83 | |
bff40ab9 | 84 | pub fn enter_cwd(&self) -> io::Result<()> { |
7ca1a14c | 85 | c_try!(unsafe { libc::fchdir(self.fd_cwd()?.as_raw_fd()) }); |
bff40ab9 WB |
86 | Ok(()) |
87 | } | |
88 | ||
89 | pub fn enter_chroot(&self) -> io::Result<()> { | |
7ca1a14c WB |
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 _) }); | |
512f780a WB |
93 | Ok(()) |
94 | } | |
3bb4df0b WB |
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 | ||
512f780a WB |
101 | #[inline] |
102 | fn open_buffered(&self, path: &CStr) -> io::Result<impl BufRead> { | |
103 | Ok(BufReader::new(self.open_file( | |
104 | path, | |
3bb4df0b WB |
105 | libc::O_RDONLY | libc::O_CLOEXEC, |
106 | 0, | |
512f780a WB |
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> { | |
1e80bab0 | 116 | let reader = self.open_buffered(c_str!("status"))?; |
512f780a WB |
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 | ||
1349eed4 WB |
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 | ||
512f780a | 144 | pub fn get_status(&self) -> io::Result<ProcStatus> { |
1e80bab0 | 145 | let reader = self.open_buffered(c_str!("status"))?; |
512f780a | 146 | |
512f780a WB |
147 | #[inline] |
148 | fn check_u64_hex(value: Option<&str>) -> io::Result<u64> { | |
a03af34b | 149 | u64::from_str_radix( |
512f780a WB |
150 | value.ok_or_else(|| { |
151 | io::Error::new(io::ErrorKind::Other, "bad numeric property line in proc") | |
152 | })?, | |
153 | 16, | |
154 | ) | |
a03af34b | 155 | .map_err(io_err_other) |
512f780a WB |
156 | } |
157 | ||
158 | #[inline] | |
159 | fn check_u32_oct(value: Option<&str>) -> io::Result<u32> { | |
a03af34b | 160 | u32::from_str_radix( |
512f780a WB |
161 | value.ok_or_else(|| { |
162 | io::Error::new(io::ErrorKind::Other, "bad numeric property line in proc") | |
163 | })?, | |
164 | 8, | |
165 | ) | |
a03af34b | 166 | .map_err(io_err_other) |
512f780a | 167 | } |
3bb4df0b | 168 | |
512f780a WB |
169 | let mut ids = Uids::default(); |
170 | let mut caps = Capabilities::default(); | |
171 | let mut umask = 0o022; | |
3bb4df0b WB |
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:") => { | |
1349eed4 WB |
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())?; | |
3bb4df0b WB |
181 | } |
182 | Some("Gid:") => { | |
1349eed4 WB |
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())?; | |
3bb4df0b | 187 | } |
512f780a WB |
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())?, | |
3bb4df0b WB |
193 | _ => continue, |
194 | } | |
512f780a WB |
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> { | |
1e80bab0 | 205 | let reader = self.open_buffered(c_str!("cgroup"))?; |
512f780a WB |
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(); | |
f3cae2a7 | 215 | if num.is_none() || name.is_none() || path.is_none() || parts.next().is_some() { |
512f780a | 216 | bail!("failed to parse cgroup line: {:?}", line); |
3bb4df0b | 217 | } |
512f780a WB |
218 | |
219 | let name = String::from_utf8(name.unwrap().to_vec())?; | |
220 | let path = OsString::from_vec(path.unwrap().to_vec()); | |
221 | ||
9486338a | 222 | if name.is_empty() { |
512f780a WB |
223 | cgroups.v2 = Some(path); |
224 | } else { | |
225 | for entry in name.split(',') { | |
fe73c2fb WB |
226 | cgroups |
227 | .v1 | |
228 | .get_or_insert_with(Default::default) | |
229 | .insert(entry.to_string(), path.clone()); | |
512f780a WB |
230 | } |
231 | } | |
232 | } | |
233 | ||
234 | Ok(cgroups) | |
235 | } | |
236 | ||
1349eed4 WB |
237 | pub fn get_uid_gid_map(&self, file: &CStr) -> Result<IdMap, Error> { |
238 | let reader = self.open_buffered(file)?; | |
239 | ||
240 | let mut entries = Vec::new(); | |
241 | for line in reader.lines() { | |
242 | let line = line?; | |
243 | let mut parts = line.split_ascii_whitespace(); | |
9486338a WB |
244 | let ns = u64::from(Self::__check_uid_gid(parts.next())?); |
245 | let host = u64::from(Self::__check_uid_gid(parts.next())?); | |
246 | let range = u64::from(Self::__check_uid_gid(parts.next())?); | |
1349eed4 WB |
247 | entries.push(IdMapEntry { ns, host, range }); |
248 | } | |
249 | ||
3bbd1db0 | 250 | Ok(IdMap::new(entries)) |
1349eed4 WB |
251 | } |
252 | ||
253 | pub fn get_uid_map(&self) -> Result<IdMap, Error> { | |
1e80bab0 | 254 | self.get_uid_gid_map(c_str!("uid_map")) |
1349eed4 WB |
255 | } |
256 | ||
257 | pub fn get_gid_map(&self) -> Result<IdMap, Error> { | |
1e80bab0 | 258 | self.get_uid_gid_map(c_str!("gid_map")) |
1349eed4 WB |
259 | } |
260 | ||
42f25756 WB |
261 | pub fn read_file(&self, file: &CStr) -> io::Result<Vec<u8>> { |
262 | use io::Read; | |
263 | ||
264 | let mut reader = self.open_file(file, libc::O_RDONLY | libc::O_CLOEXEC, 0)?; | |
265 | let mut out = Vec::new(); | |
266 | reader.read_to_end(&mut out)?; | |
267 | Ok(out) | |
268 | } | |
269 | ||
512f780a WB |
270 | pub fn user_caps(&self) -> Result<UserCaps, Error> { |
271 | UserCaps::new(self) | |
272 | } | |
273 | } |