qemu_plugin/install/
mod.rs1use crate::qemu_plugin_bool_parse;
4use qemu_plugin_sys::{
5 QEMU_PLUGIN_VERSION, qemu_info_t, qemu_info_t__bindgen_ty_1,
6 qemu_info_t__bindgen_ty_2__bindgen_ty_1, qemu_plugin_id_t,
7};
8use std::{
9 collections::HashMap,
10 ffi::{CStr, c_char, c_int},
11};
12
13use crate::{error::Error, plugin::PLUGIN};
14
15#[allow(non_upper_case_globals)]
16#[unsafe(no_mangle)]
17pub static qemu_plugin_version: c_int = QEMU_PLUGIN_VERSION as c_int;
19
20pub const PLUGIN_INSTALL_SUCCESS: c_int = 0;
22
23#[derive(Debug, Clone)]
24pub enum Value {
28 Bool(bool),
31 Integer(i64),
33 String(String),
35}
36
37impl Value {
38 fn new(key: &str, value: &str) -> Result<Self, Error> {
39 if let Ok(maybe_bool) = qemu_plugin_bool_parse(key, value) {
40 Ok(Self::Bool(maybe_bool))
41 } else if let Ok(int) = value.parse::<i64>() {
42 Ok(Self::Integer(int))
43 } else {
44 Ok(Self::String(value.to_string()))
45 }
46 }
47}
48
49#[derive(Debug, Clone)]
50pub struct Args {
54 pub raw: Vec<String>,
58 pub parsed: HashMap<String, Value>,
62}
63
64impl Args {
65 fn new(argc: c_int, value: *const *const c_char) -> Result<Self, Error> {
68 Ok(Self {
69 raw: (0..argc)
70 .map(|i| unsafe { CStr::from_ptr(*value.offset(i as isize)) })
71 .map(|cstr| cstr.to_string_lossy().into_owned())
72 .collect::<Vec<_>>(),
73 parsed: (0..argc)
74 .map(|i| unsafe { CStr::from_ptr(*value.offset(i as isize)) })
75 .map(|cstr| cstr.to_string_lossy().into_owned())
76 .map(|argument| {
77 let mut split = argument.splitn(2, '=');
78 let Some(key) = split.next() else {
79 return Err(Error::MissingArgKey { argument });
80 };
81 let Some(value) = split.next() else {
82 return Err(Error::MissingArgValue { argument });
83 };
84 Ok((key.to_string(), Value::new(key, value)?))
85 })
86 .collect::<Result<Vec<(_, _)>, Error>>()?
87 .into_iter()
88 .collect::<HashMap<_, _>>(),
89 })
90 }
91}
92
93#[derive(Debug, Clone)]
94pub struct Version {
96 pub current: i64,
98 pub mininum: i64,
100}
101
102impl From<&qemu_info_t__bindgen_ty_1> for Version {
103 fn from(value: &qemu_info_t__bindgen_ty_1) -> Self {
104 Self {
105 current: value.cur as i64,
106 mininum: value.min as i64,
107 }
108 }
109}
110
111#[derive(Debug, Clone)]
112pub struct System {
115 pub max_vcpus: i64,
117 pub smp_vcpus: i64,
119}
120
121impl From<&qemu_info_t__bindgen_ty_2__bindgen_ty_1> for System {
122 fn from(value: &qemu_info_t__bindgen_ty_2__bindgen_ty_1) -> Self {
123 Self {
124 max_vcpus: value.max_vcpus as i64,
125 smp_vcpus: value.smp_vcpus as i64,
126 }
127 }
128}
129
130#[derive(Debug, Clone)]
131pub struct Info {
134 pub target_name: String,
136 pub version: Version,
138 pub system: Option<System>,
141}
142
143impl Info {
144 unsafe fn try_from(value: *const qemu_info_t) -> Result<Self, Error> {
151 let target_name = unsafe { CStr::from_ptr((*value).target_name) }
152 .to_str()
153 .map_err(Error::from)?
154 .to_string();
155 let version = Version::from(unsafe { &(*value).version });
156 let system_emulation = unsafe { (*value).system_emulation };
157 let system = if system_emulation {
158 Some(System::from(unsafe { &(*value).__bindgen_anon_1.system }))
161 } else {
162 None
163 };
164
165 Ok(Self {
166 target_name,
167 version,
168 system,
169 })
170 }
171}
172
173#[unsafe(no_mangle)]
174pub unsafe extern "C" fn qemu_plugin_install(
183 id: qemu_plugin_id_t,
184 info: *const qemu_info_t,
185 argc: c_int,
186 argv: *const *const c_char,
187) -> c_int {
188 let args = Args::new(argc, argv).expect("Failed to parse arguments");
189 let info = unsafe { Info::try_from(info) }.expect("Failed to convert qemu_info_t");
190
191 let Some(plugin) = PLUGIN.get() else {
192 panic!("Plugin not set");
193 };
194
195 let Ok(mut plugin) = plugin.lock() else {
196 panic!("Failed to lock plugin");
197 };
198
199 plugin
200 .register_default(id, &args, &info)
201 .expect("Failed to register plugin");
202
203 PLUGIN_INSTALL_SUCCESS
204}