Skip to main content

qemu_command_builder/
parser.rs

1use crate::args::accel::Accel;
2use crate::args::acpitable::AcpiTable;
3use crate::args::action::{Action, WatchdogAction};
4use crate::args::addfs::AddFd;
5use crate::args::audio::Audio;
6use crate::args::audiodev::AudioDev;
7use crate::args::blockdev::BlockDev;
8use crate::args::boot::Boot;
9use crate::args::chardev::CharDev;
10use crate::args::compact::Compact;
11use crate::args::device::Device;
12use crate::args::display::QemuDisplay;
13use crate::args::drive::Drive;
14use crate::args::fsdev::FsDev;
15use crate::args::fw_cfg::FwCfg;
16use crate::args::g::G;
17use crate::args::global::Global;
18use crate::args::icount::Icount;
19use crate::args::incoming::Incoming;
20use crate::args::iscsi::Iscsi;
21use crate::args::memory::Memory;
22use crate::args::mon::Mon;
23use crate::args::msg::Msg;
24use crate::args::name::Name;
25use crate::args::netdev::NetDev;
26use crate::args::network_compat::{LegacyNet, Nic};
27use crate::args::numa::NUMA;
28use crate::args::object::Object;
29use crate::args::overcommit::Overcommit;
30use crate::args::plugin::Plugin;
31use crate::args::rtc::Rtc;
32use crate::args::runwith::RunWith;
33use crate::args::sandbox::Sandbox;
34use crate::args::semihosting::SemihostingConfig;
35use crate::args::serial::SpecialDevice;
36use crate::args::set::Set;
37use crate::args::smbios::Smbios;
38use crate::args::smp::SMP;
39use crate::args::spice::Spice;
40use crate::args::tpmdev::TpmDev;
41use crate::args::trace::Trace;
42use crate::args::usb::USBDevice;
43use crate::args::vga::VGA;
44use crate::args::virtfs::Virtfs;
45use crate::args::vnc::VNC;
46use crate::parsers::{
47    ARG_ACCEL, ARG_ACPITABLE, ARG_ACTION, ARG_ADD_FD, ARG_APPEND, ARG_AUDIO, ARG_AUDIODEV, ARG_BIG_D, ARG_BIG_S, ARG_BIOS, ARG_BLOCKDEV, ARG_BOOT, ARG_CDROM, ARG_CHARDEV, ARG_CHROOT, ARG_COMPAT,
48    ARG_CPU, ARG_DAEMONIZE, ARG_DEBUGCON, ARG_DEVICE, ARG_DFILTER, ARG_DISPLAY, ARG_DRIVE, ARG_DTB, ARG_DUMP_VMSTATE, ARG_ECHR, ARG_ENABLE_KVM, ARG_ENABLE_SYNC_PROFILE, ARG_FDA, ARG_FDB, ARG_FSDEV,
49    ARG_FULL_SCREEN, ARG_FW_CFG, ARG_G, ARG_GDB, ARG_GLOBAL, ARG_HDA, ARG_HDB, ARG_HDC, ARG_HDD, ARG_ICOUNT, ARG_INCOMING, ARG_INITRD, ARG_ISCSI, ARG_JITDUMP, ARG_K, ARG_KERNEL, ARG_L, ARG_LITTLE_D,
50    ARG_LITTLE_S, ARG_LOADVM, ARG_MACHINE, ARG_MACHINE_ALIAS, ARG_MEM_PATH, ARG_MEM_PREALLOC, ARG_MEMORY, ARG_MON, ARG_MONITOR, ARG_MSG, ARG_MTDBLOCK, ARG_NAME, ARG_NET, ARG_NETDEV, ARG_NIC,
51    ARG_NO_FD_BOOTCHK, ARG_NO_REBOOT, ARG_NO_SHUTDOWN, ARG_NO_USER_CONFIG, ARG_NODEFAULTS, ARG_NOGRAPHIC, ARG_NUMA, ARG_OBJECT, ARG_ONLY_MIGRATABLE, ARG_OPTION_ROM, ARG_OVERCOMMIT, ARG_PARALLEL,
52    ARG_PERFMAP, ARG_PFLASH, ARG_PIDFILE, ARG_PLUGIN, ARG_PRECONFIG, ARG_PROM_ENV, ARG_QMP, ARG_QMP_PRETTY, ARG_QTEST, ARG_QTEST_LOG, ARG_READCONFIG, ARG_RTC, ARG_RUN_WITH, ARG_RUNAS, ARG_SANDBOX,
53    ARG_SD, ARG_SEED, ARG_SEMIHOSTING, ARG_SEMIHOSTING_CONFIG, ARG_SERIAL, ARG_SET, ARG_SHIM, ARG_SMBIOS, ARG_SMP, ARG_SNAPSHOT, ARG_SPICE, ARG_TPMDEV, ARG_TRACE, ARG_USB, ARG_USBDEVICE, ARG_UUID,
54    ARG_VGA, ARG_VIRTFS, ARG_VNC, ARG_WATCHDOG_ACTION, ARG_WIN2K_HACK, ARG_XEN_ATTACH, ARG_XEN_DOMID, ARG_XEN_DOMID_RESTRICT,
55};
56use crate::parsers::{ARG_MIGRATE_MODE_ENABLE, ARG_NO_HPET, ARG_ONLY_CPR_CAPABLE};
57use crate::shell_string::ShellString;
58use crate::{QEMU_BIN_AARCH64, QEMU_BIN_X86_64, QUuid, QemuCommand, QemuInstanceBase, QemuInstanceForAarch64, QemuInstanceForX86_64};
59use std::path::PathBuf;
60use std::str::FromStr;
61use winnow::Result;
62
63// tokens, ARG_ACCEL, Accel, q.accel
64#[macro_export]
65macro_rules! ff {
66    ($a:expr,$b:ident,$c:ty,$d:expr) => {
67        match $a.next() {
68            None => {
69                return Err(format!("Argument {} expected a token to parse, none found", $b));
70            }
71            Some(v) => match v.parse::<$c>() {
72                Ok(p) => {
73                    $d = Some(p);
74                }
75                Err(parse_err) => {
76                    return Err(format!("While trying to parse $b an error was seen: {}", parse_err));
77                }
78            },
79        }
80    };
81}
82#[macro_export]
83macro_rules! ffs {
84    ($a:expr,$b:ident,$c:ty,$d:expr) => {
85        match $a.next() {
86            None => {
87                return Err(format!("Argument {} expected a token to parse, none found", $b));
88            }
89            Some(v) => match v.parse::<$c>() {
90                Ok(p) => match $d {
91                    Some(ref mut vs) => vs.push(p),
92                    None => {
93                        $d = Some(vec![p]);
94                    }
95                },
96                Err(parse_err) => {
97                    return Err(format!("While trying to parse $b an error was seen: {}", parse_err));
98                }
99            },
100        }
101    };
102}
103
104fn parse_common_qemu_args<Machine: FromStr, Cpu: FromStr>(q: &mut QemuInstanceBase<Machine, Cpu>, tokens: impl IntoIterator<Item = String>) -> Result<(), String>
105where
106    <Cpu as FromStr>::Err: std::fmt::Display,
107    <Machine as FromStr>::Err: std::fmt::Display,
108{
109    let mut tokens = tokens.into_iter();
110    while let Some(token) = tokens.next() {
111        match token.as_str() {
112            // machine/cpu specific
113            ARG_CPU => ff!(tokens, ARG_CPU, Cpu, q.cpu),
114            ARG_MACHINE | ARG_MACHINE_ALIAS => ff!(tokens, ARG_MACHINE, Machine, q.machine),
115            // common options below
116            ARG_ACCEL => ff!(tokens, ARG_ACCEL, Accel, q.accel),
117            ARG_SMP => ff!(tokens, ARG_SMP, SMP, q.smp),
118            ARG_NUMA => ffs!(tokens, ARG_NUMA, NUMA, q.numa),
119            ARG_ADD_FD => ffs!(tokens, ARG_ADD_FD, AddFd, q.add_fd),
120            ARG_SET => ffs!(tokens, ARG_SET, Set, q.set),
121            ARG_GLOBAL => ffs!(tokens, ARG_GLOBAL, Global, q.global),
122            ARG_BOOT => ff!(tokens, ARG_BOOT, Boot, q.boot),
123            ARG_MEMORY => ff!(tokens, ARG_MEMORY, Memory, q.m),
124            ARG_MEM_PATH => ff!(tokens, ARG_MEM_PATH, PathBuf, q.mem_path),
125            ARG_MEM_PREALLOC => q.mem_prealloc = Some(true),
126            ARG_K => ff!(tokens, ARG_K, String, q.k),
127            ARG_AUDIO => ff!(tokens, ARG_AUDIO, Audio, q.audio),
128            ARG_AUDIODEV => ffs!(tokens, ARG_AUDIODEV, AudioDev, q.audiodev),
129            ARG_DEVICE => ffs!(tokens, ARG_DEVICE, Device, q.device),
130            ARG_NAME => ff!(tokens, ARG_NAME, Name, q.name),
131            ARG_UUID => ff!(tokens, ARG_UUID, newtype_uuid::TypedUuid<QUuid>, q.uuid),
132            ARG_FDA => ff!(tokens, ARG_FDA, PathBuf, q.fda),
133            ARG_FDB => ff!(tokens, ARG_FDB, PathBuf, q.fdb),
134            ARG_HDA => ff!(tokens, ARG_HDA, PathBuf, q.hda),
135            ARG_HDB => ff!(tokens, ARG_HDB, PathBuf, q.hdb),
136            ARG_HDC => ff!(tokens, ARG_HDC, PathBuf, q.hdc),
137            ARG_HDD => ff!(tokens, ARG_HDD, PathBuf, q.hdd),
138            ARG_CDROM => ff!(tokens, ARG_CDROM, PathBuf, q.cdrom),
139            ARG_BLOCKDEV => ffs!(tokens, ARG_BLOCKDEV, BlockDev, q.blockdev),
140            ARG_DRIVE => ffs!(tokens, ARG_DRIVE, Drive, q.drive),
141            ARG_MTDBLOCK => ff!(tokens, ARG_MTDBLOCK, PathBuf, q.mdtblock),
142            ARG_SD => ff!(tokens, ARG_SD, PathBuf, q.sd),
143            ARG_SNAPSHOT => q.snapshot = Some(true),
144            ARG_FSDEV => ffs!(tokens, ARG_FSDEV, FsDev, q.fsdev),
145            ARG_VIRTFS => ffs!(tokens, ARG_VIRTFS, Virtfs, q.virtfs),
146            ARG_ISCSI => ff!(tokens, ARG_ISCSI, Iscsi, q.iscsi),
147            ARG_USB => q.usb = Some(true),
148            ARG_USBDEVICE => ff!(tokens, ARG_USBDEVICE, USBDevice, q.usbdevice),
149            ARG_DISPLAY => ff!(tokens, ARG_DISPLAY, QemuDisplay, q.display),
150            ARG_NOGRAPHIC => q.nographic = Some(true),
151            ARG_SPICE => ff!(tokens, ARG_SPICE, Spice, q.spice),
152            ARG_VGA => ff!(tokens, ARG_VGA, VGA, q.vga),
153            ARG_FULL_SCREEN => q.full_screen = Some(true),
154            ARG_G => ff!(tokens, ARG_G, G, q.g),
155            ARG_VNC => ff!(tokens, ARG_VNC, VNC, q.vnc),
156            ARG_WIN2K_HACK => q.win2k_hack = Some(true),
157            ARG_NO_FD_BOOTCHK => q.no_fd_bootchk = Some(true),
158            ARG_ACPITABLE => ffs!(tokens, ARG_ACPITABLE, AcpiTable, q.acpitable),
159            ARG_SMBIOS => ffs!(tokens, ARG_SMBIOS, Smbios, q.smbios),
160            ARG_NETDEV => ffs!(tokens, ARG_NETDEV, NetDev, q.netdev),
161            ARG_NIC => ffs!(tokens, ARG_NIC, Nic, q.nic),
162            ARG_NET => ffs!(tokens, ARG_NET, LegacyNet, q.net),
163            ARG_CHARDEV => ffs!(tokens, ARG_CHARDEV, CharDev, q.chardev),
164            ARG_TPMDEV => ffs!(tokens, ARG_TPMDEV, TpmDev, q.tpmdev),
165            ARG_BIOS => ff!(tokens, ARG_BIOS, PathBuf, q.bios),
166            ARG_PFLASH => ff!(tokens, ARG_PFLASH, PathBuf, q.pflash),
167            ARG_KERNEL => ff!(tokens, ARG_KERNEL, PathBuf, q.kernel),
168            ARG_SHIM => ff!(tokens, ARG_SHIM, PathBuf, q.shim),
169            ARG_APPEND => ff!(tokens, ARG_APPEND, ShellString, q.append),
170            ARG_INITRD => ff!(tokens, ARG_INITRD, PathBuf, q.initrd),
171            ARG_DTB => ff!(tokens, ARG_DTB, PathBuf, q.dtb),
172            ARG_COMPAT => ff!(tokens, ARG_COMPAT, Compact, q.compact),
173            ARG_FW_CFG => ffs!(tokens, ARG_FW_CFG, FwCfg, q.fw_cfg),
174            ARG_SERIAL => ffs!(tokens, ARG_SERIAL, SpecialDevice, q.serial),
175            ARG_PARALLEL => ffs!(tokens, ARG_PARALLEL, SpecialDevice, q.parallel),
176            ARG_MONITOR => ffs!(tokens, ARG_MONITOR, SpecialDevice, q.monitor),
177            ARG_QMP => ffs!(tokens, ARG_QMP, SpecialDevice, q.qmp),
178            ARG_QMP_PRETTY => ffs!(tokens, ARG_QMP_PRETTY, SpecialDevice, q.qmp_pretty),
179            ARG_MON => ffs!(tokens, ARG_MON, Mon, q.mon),
180            ARG_DEBUGCON => ff!(tokens, ARG_DEBUGCON, SpecialDevice, q.debugcon),
181            ARG_PIDFILE => ff!(tokens, ARG_PIDFILE, PathBuf, q.pidfile),
182            ARG_PRECONFIG => q.preconfig = Some(true),
183            ARG_BIG_S => q.big_s = Some(true),
184            ARG_OVERCOMMIT => ff!(tokens, ARG_OVERCOMMIT, Overcommit, q.overcommit),
185            ARG_GDB => ff!(tokens, ARG_GDB, SpecialDevice, q.gdb),
186            ARG_LITTLE_S => q.s = Some(true),
187            ARG_LITTLE_D => ffs!(tokens, ARG_LITTLE_D, String, q.d),
188            ARG_BIG_D => ff!(tokens, ARG_BIG_D, PathBuf, q.big_d),
189            ARG_DFILTER => ffs!(tokens, ARG_DFILTER, String, q.dfilter),
190            ARG_SEED => ff!(tokens, ARG_SEED, usize, q.seed),
191            ARG_L => ff!(tokens, ARG_L, PathBuf, q.big_l),
192            ARG_ENABLE_KVM => q.enable_kvm = Some(true),
193            ARG_XEN_DOMID => ff!(tokens, ARG_XEN_DOMID, String, q.xen_domid),
194            ARG_XEN_ATTACH => q.xen_attach = Some(true),
195            ARG_XEN_DOMID_RESTRICT => q.xen_domid_restrict = Some(true),
196            ARG_NO_REBOOT => q.no_reboot = Some(true),
197            ARG_NO_HPET => q.no_hpet = Some(true),
198            ARG_NO_SHUTDOWN => q.no_shutdown = Some(true),
199            ARG_ACTION => ff!(tokens, ARG_ACTION, Action, q.action),
200            ARG_LOADVM => ff!(tokens, ARG_LOADVM, String, q.loadvm),
201            ARG_DAEMONIZE => q.daemonize = Some(true),
202            ARG_OPTION_ROM => ff!(tokens, ARG_OPTION_ROM, PathBuf, q.option_rom),
203            ARG_RTC => ff!(tokens, ARG_RTC, Rtc, q.rtc),
204            ARG_ICOUNT => ff!(tokens, ARG_ICOUNT, Icount, q.icount),
205            ARG_WATCHDOG_ACTION => ff!(tokens, ARG_WATCHDOG_ACTION, WatchdogAction, q.watchdog_action),
206            ARG_ECHR => ff!(tokens, ARG_ECHR, String, q.echr),
207            ARG_INCOMING => ffs!(tokens, ARG_INCOMING, Incoming, q.incoming),
208            ARG_ONLY_MIGRATABLE => q.only_migratable = Some(true),
209            ARG_ONLY_CPR_CAPABLE => q.only_cpr_capable = Some(true),
210            ARG_MIGRATE_MODE_ENABLE => ff!(tokens, ARG_MIGRATE_MODE_ENABLE, String, q.migrate_mode_enable),
211            ARG_NODEFAULTS => q.nodefaults = Some(true),
212            ARG_SANDBOX => ff!(tokens, ARG_SANDBOX, Sandbox, q.sandbox),
213            ARG_READCONFIG => ff!(tokens, ARG_READCONFIG, PathBuf, q.readconfig),
214            ARG_NO_USER_CONFIG => q.no_user_config = Some(true),
215            ARG_TRACE => ff!(tokens, ARG_TRACE, Trace, q.trace),
216            ARG_PLUGIN => ffs!(tokens, ARG_PLUGIN, Plugin, q.plugin),
217            ARG_SEMIHOSTING => q.semihosting = Some(true),
218            ARG_SEMIHOSTING_CONFIG => ff!(tokens, ARG_SEMIHOSTING_CONFIG, SemihostingConfig, q.semihosting_config),
219            ARG_QTEST => ff!(tokens, ARG_QTEST, String, q.qtest),
220            ARG_QTEST_LOG => ff!(tokens, ARG_QTEST_LOG, PathBuf, q.qtest_log),
221            ARG_PROM_ENV => ffs!(tokens, ARG_PROM_ENV, String, q.prom_env),
222            ARG_RUNAS => ff!(tokens, ARG_RUNAS, String, q.runas),
223            ARG_RUN_WITH => ff!(tokens, ARG_RUN_WITH, RunWith, q.run_with),
224            ARG_CHROOT => ff!(tokens, ARG_CHROOT, PathBuf, q.chroot),
225            ARG_MSG => ff!(tokens, ARG_MSG, Msg, q.msg),
226            ARG_DUMP_VMSTATE => ff!(tokens, ARG_DUMP_VMSTATE, PathBuf, q.dump_vmstate),
227            ARG_ENABLE_SYNC_PROFILE => q.enable_sync_profile = Some(true),
228            ARG_PERFMAP => ff!(tokens, ARG_PERFMAP, PathBuf, q.perfmap),
229            ARG_JITDUMP => ff!(tokens, ARG_JITDUMP, PathBuf, q.jitdump),
230            ARG_OBJECT => ffs!(tokens, ARG_OBJECT, Object, q.object),
231            other => {
232                return Err(format!("unsupported argument: {other}"));
233            }
234        }
235    }
236    Ok(())
237}
238
239fn _parse_qemu_command_line<Machine: std::str::FromStr, Cpu: std::str::FromStr>(bin_name: &str, s: &str, q: &mut QemuInstanceBase<Machine, Cpu>) -> Result<(), String>
240where
241    <Cpu as FromStr>::Err: std::fmt::Display,
242    <Machine as FromStr>::Err: std::fmt::Display,
243{
244    let mut tokens = shellish_parse::parse(s, shellish_parse::ParseOptions::new()).map_err(|e| e.to_string())?.into_iter();
245
246    let binary = tokens.next();
247    match binary {
248        None => {
249            return Err(String::from("No next token for determining binary name"));
250        }
251        Some(path) => {
252            if path.contains(bin_name) {
253                q.qemu_binary = PathBuf::from(path);
254            } else {
255                return Err(format!("{} not found in string {}", bin_name, path));
256            }
257        }
258    }
259
260    parse_common_qemu_args(q, tokens)?;
261
262    Ok(())
263}
264
265impl FromStr for QemuInstanceForX86_64 {
266    type Err = String;
267
268    fn from_str(s: &str) -> Result<Self, Self::Err> {
269        let mut q = QemuInstanceBase::builder().qemu_binary(PathBuf::from("")).build();
270        _parse_qemu_command_line(QEMU_BIN_X86_64, s, &mut q)?;
271        Ok(q)
272    }
273}
274
275impl FromStr for QemuInstanceForAarch64 {
276    type Err = String;
277
278    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
279        let mut q = QemuInstanceBase::builder().qemu_binary(PathBuf::from("")).build();
280        _parse_qemu_command_line(QEMU_BIN_AARCH64, s, &mut q)?;
281        Ok(q)
282    }
283}
284
285pub fn parse_qemu_command_line(cmd: &str) -> Result<QemuCommand, String> {
286    match cmd.split_once(" ") {
287        None => Err(String::from("No next token for determining binary name")),
288        Some((bin, _rest)) => {
289            if bin.ends_with(QEMU_BIN_AARCH64) {
290                return QemuInstanceForAarch64::from_str(cmd).map(QemuCommand::Aarch64);
291            }
292            if bin.ends_with(QEMU_BIN_X86_64) {
293                return QemuInstanceForX86_64::from_str(cmd).map(QemuCommand::X86_64);
294            }
295            Err(format!("binary {} is an unsupported architecture", bin))
296        }
297    }
298}