1use std::{path::PathBuf, process::Command, str::FromStr};
4
5use strum::EnumString;
6use which::which;
7
8use crate::Error;
9
10#[derive(Clone, Copy, Debug, strum::Display, EnumString, Eq, PartialEq)]
14#[strum(serialize_all = "lowercase")]
15pub enum SystemdDetectVirtVm {
16 Qemu,
18 Kvm,
20 Amazon,
22 Zvm,
24 Vmware,
26 Microsoft,
28 Oracle,
30 PowerVm,
32 Xen,
34 Bochs,
36 Uml,
38 Parallels,
40 Bhyve,
42 Qnx,
44 Acrn,
46 Apple,
48 Sre,
50 Google,
52}
53
54#[derive(Clone, Copy, Debug, strum::Display, EnumString, Eq, PartialEq)]
58#[strum(serialize_all = "lowercase")]
59pub enum SystemdDetectVirtContainer {
60 OpenVc,
62 Lxc,
64 #[strum(serialize = "lxc-libvirt")]
66 LxcLibvirt,
67 #[strum(serialize = "systemd-nspawn")]
69 SystemdNspawn,
70 Docker,
72 Podman,
74 Rkt,
76 Wsl,
78 Proot,
80 Pouch,
82}
83
84#[derive(Clone, Copy, Debug, strum::Display, EnumString, Eq, PartialEq)]
88#[strum(serialize_all = "lowercase")]
89pub enum ConfidentialVirtualizationTechnology {
90 Sev,
92 #[strum(serialize = "sev-es")]
94 SevEs,
95 #[strum(serialize = "sev-snp")]
97 SevSnp,
98 Tdx,
100 Protvirt,
102}
103
104#[derive(Clone, Copy, Debug, strum::Display, Eq, PartialEq)]
108#[strum(serialize_all = "lowercase")]
109pub enum SystemdDetectVirtOutput {
110 #[strum(to_string = "{0}")]
112 Cms(ConfidentialVirtualizationTechnology),
113 #[strum(to_string = "{0}")]
115 Vm(SystemdDetectVirtVm),
116 #[strum(to_string = "{0}")]
118 Container(SystemdDetectVirtContainer),
119 None,
121}
122
123impl SystemdDetectVirtOutput {
124 pub fn uses_namespaces(&self) -> bool {
126 match self {
127 Self::Vm(_) | Self::Cms(_) | Self::None => false,
128 Self::Container(container) => match container {
129 SystemdDetectVirtContainer::OpenVc
130 | SystemdDetectVirtContainer::Lxc
131 | SystemdDetectVirtContainer::LxcLibvirt
132 | SystemdDetectVirtContainer::SystemdNspawn
133 | SystemdDetectVirtContainer::Rkt
134 | SystemdDetectVirtContainer::Pouch
135 | SystemdDetectVirtContainer::Docker
136 | SystemdDetectVirtContainer::Podman => true,
137 SystemdDetectVirtContainer::Wsl | SystemdDetectVirtContainer::Proot => false,
138 },
139 }
140 }
141}
142
143impl FromStr for SystemdDetectVirtOutput {
144 type Err = Error;
145
146 fn from_str(s: &str) -> Result<Self, Self::Err> {
147 let s = s.strip_suffix("\n").unwrap_or(s);
149
150 Ok(
151 if let Ok(cms) = ConfidentialVirtualizationTechnology::from_str(s) {
152 SystemdDetectVirtOutput::Cms(cms)
153 } else if let Ok(vm) = SystemdDetectVirtVm::try_from(s) {
154 SystemdDetectVirtOutput::Vm(vm)
155 } else if let Ok(container) = SystemdDetectVirtContainer::try_from(s) {
156 SystemdDetectVirtOutput::Container(container)
157 } else if s == "none" {
158 SystemdDetectVirtOutput::None
159 } else {
160 return Err(Error::UnknownSystemdDetectVirtOutput {
161 output: s.to_string(),
162 });
163 },
164 )
165 }
166}
167
168pub fn detect_virt() -> Result<SystemdDetectVirtOutput, Error> {
184 let command_name = get_command("systemd-detect-virt")?;
185 let mut command = Command::new(command_name);
186 let output = command
187 .output()
188 .map_err(|source| crate::Error::CommandExec {
189 command: format!("{command:?}"),
190 source,
191 })?;
192
193 SystemdDetectVirtOutput::from_str(&String::from_utf8_lossy(&output.stdout))
194}
195
196pub fn get_command(command: &str) -> Result<PathBuf, Error> {
205 which(command).map_err(|source| Error::ExecutableNotFound {
206 executable: command.to_string(),
207 source,
208 })
209}
210
211#[cfg(test)]
212mod tests {
213 use rstest::rstest;
214 use testresult::TestResult;
215
216 use super::*;
217
218 #[rstest]
220 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Qemu), "qemu")]
221 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Kvm), "kvm")]
222 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Amazon), "amazon")]
223 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Zvm), "zvm")]
224 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Vmware), "vmware")]
225 #[case(
226 SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Microsoft),
227 "microsoft"
228 )]
229 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Oracle), "oracle")]
230 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::PowerVm), "powervm")]
231 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Xen), "xen")]
232 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Bochs), "bochs")]
233 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Uml), "uml")]
234 #[case(
235 SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Parallels),
236 "parallels"
237 )]
238 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Bhyve), "bhyve")]
239 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Qnx), "qnx")]
240 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Acrn), "acrn")]
241 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Apple), "apple")]
242 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Sre), "sre")]
243 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Google), "google")]
244 #[case(
245 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::OpenVc),
246 "openvc"
247 )]
248 #[case(
249 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Lxc),
250 "lxc"
251 )]
252 #[case(
253 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::LxcLibvirt),
254 "lxc-libvirt"
255 )]
256 #[case(
257 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::SystemdNspawn),
258 "systemd-nspawn"
259 )]
260 #[case(
261 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Docker),
262 "docker"
263 )]
264 #[case(
265 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Podman),
266 "podman"
267 )]
268 #[case(
269 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Rkt),
270 "rkt"
271 )]
272 #[case(
273 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Wsl),
274 "wsl"
275 )]
276 #[case(
277 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Proot),
278 "proot"
279 )]
280 #[case(
281 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Pouch),
282 "pouch"
283 )]
284 #[case(
285 SystemdDetectVirtOutput::Cms(ConfidentialVirtualizationTechnology::Sev),
286 "sev"
287 )]
288 #[case(
289 SystemdDetectVirtOutput::Cms(ConfidentialVirtualizationTechnology::SevEs),
290 "sev-es"
291 )]
292 #[case(
293 SystemdDetectVirtOutput::Cms(ConfidentialVirtualizationTechnology::SevSnp),
294 "sev-snp"
295 )]
296 #[case(
297 SystemdDetectVirtOutput::Cms(ConfidentialVirtualizationTechnology::Tdx),
298 "tdx"
299 )]
300 #[case(
301 SystemdDetectVirtOutput::Cms(ConfidentialVirtualizationTechnology::Protvirt),
302 "protvirt"
303 )]
304 #[case(SystemdDetectVirtOutput::None, "none")]
305 fn systemd_detect_virt_output_serialize_deserialize(
306 #[case] output: SystemdDetectVirtOutput,
307 #[case] to_string: &str,
308 ) -> TestResult {
309 assert_eq!(output.to_string(), to_string);
310 assert_eq!(SystemdDetectVirtOutput::from_str(to_string)?, output);
311 Ok(())
312 }
313
314 #[rstest]
316 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Qemu), false)]
317 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Kvm), false)]
318 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Amazon), false)]
319 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Zvm), false)]
320 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Vmware), false)]
321 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Microsoft), false)]
322 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Oracle), false)]
323 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::PowerVm), false)]
324 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Xen), false)]
325 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Bochs), false)]
326 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Uml), false)]
327 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Parallels), false)]
328 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Bhyve), false)]
329 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Qnx), false)]
330 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Acrn), false)]
331 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Apple), false)]
332 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Sre), false)]
333 #[case(SystemdDetectVirtOutput::Vm(SystemdDetectVirtVm::Google), false)]
334 #[case(
335 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::OpenVc),
336 true
337 )]
338 #[case(
339 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Lxc),
340 true
341 )]
342 #[case(
343 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::LxcLibvirt),
344 true
345 )]
346 #[case(
347 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::SystemdNspawn),
348 true
349 )]
350 #[case(
351 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Docker),
352 true
353 )]
354 #[case(
355 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Podman),
356 true
357 )]
358 #[case(
359 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Rkt),
360 true
361 )]
362 #[case(
363 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Wsl),
364 false
365 )]
366 #[case(
367 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Proot),
368 false
369 )]
370 #[case(
371 SystemdDetectVirtOutput::Container(SystemdDetectVirtContainer::Pouch),
372 true
373 )]
374 #[case(
375 SystemdDetectVirtOutput::Cms(ConfidentialVirtualizationTechnology::Sev),
376 false
377 )]
378 #[case(
379 SystemdDetectVirtOutput::Cms(ConfidentialVirtualizationTechnology::SevEs),
380 false
381 )]
382 #[case(
383 SystemdDetectVirtOutput::Cms(ConfidentialVirtualizationTechnology::SevSnp),
384 false
385 )]
386 #[case(
387 SystemdDetectVirtOutput::Cms(ConfidentialVirtualizationTechnology::Tdx),
388 false
389 )]
390 #[case(
391 SystemdDetectVirtOutput::Cms(ConfidentialVirtualizationTechnology::Protvirt),
392 false
393 )]
394 #[case(SystemdDetectVirtOutput::None, false)]
395 fn systemd_detect_virt_output_uses_namespaces(
396 #[case] output: SystemdDetectVirtOutput,
397 #[case] uses_namespaces: bool,
398 ) -> TestResult {
399 assert_eq!(output.uses_namespaces(), uses_namespaces);
400 Ok(())
401 }
402}