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