Skip to main content

qemu_command_builder/args/
boot.rs

1use crate::parsers::ARG_BOOT;
2use std::str::FromStr;
3
4use bon::Builder;
5use proptest_derive::Arbitrary;
6use winnow::Result;
7use winnow::ascii::{alphanumeric1, dec_uint};
8use winnow::combinator::opt;
9use winnow::prelude::*;
10use winnow::token::literal;
11
12use crate::common::OnOff;
13use crate::parsers::DELIM_COMMA;
14use crate::shell_string::{ShellString, ShellStringError, shell_string_until_comma};
15use crate::to_command::ToCommand;
16use crate::{pco0, ppo0, qao};
17
18const KEY_ORDER: &str = "order=";
19const KEY_ONCE: &str = "once=";
20const KEY_MENU: &str = "menu=";
21const KEY_SPLASH: &str = "splash=";
22const KEY_SPLASH_TIME: &str = "splash-time=";
23const KEY_REBOOT_TIMEOUT: &str = "reboot-timeout=";
24const KEY_STRICT: &str = "strict=";
25
26/// Specify boot order drives as a string of drive letters. Valid drive
27/// letters depend on the target architecture. The x86 PC uses: a, b
28/// (floppy 1 and 2), c (first hard disk), d (first CD-ROM), n-p
29/// (Etherboot from network adapter 1-4), hard disk boot is the default.
30/// To apply a particular boot order only on the first startup, specify
31/// it via ``once``. Note that the ``order`` or ``once`` parameter
32/// should not be used together with the ``bootindex`` property of
33/// devices, since the firmware implementations normally do not support
34/// both at the same time.
35///
36/// Interactive boot menus/prompts can be enabled via ``menu=on`` as far
37/// as firmware/BIOS supports them. The default is non-interactive boot.
38///
39/// A splash picture could be passed to bios, enabling user to show it
40/// as logo, when option splash=sp\_name is given and menu=on, If
41/// firmware/BIOS supports them. Currently Seabios for X86 system
42/// support it. limitation: The splash file could be a jpeg file or a
43/// BMP file in 24 BPP format(true color). The resolution should be
44/// supported by the SVGA mode, so the recommended is 320x240, 640x480,
45/// 800x640.
46///
47/// A timeout could be passed to bios, guest will pause for rb\_timeout
48/// ms when boot failed, then reboot. If rb\_timeout is '-1', guest will
49/// not reboot, qemu passes '-1' to bios by default. Currently Seabios
50/// for X86 system support it.
51///
52/// Do strict boot via ``strict=on`` as far as firmware/BIOS supports
53/// it. This only effects when boot priority is changed by bootindex
54/// options. The default is non-strict boot.
55#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder, Arbitrary)]
56pub struct Boot {
57    order: Option<ShellString>,
58    once: Option<ShellString>,
59    menu: Option<OnOff>,
60    splash: Option<ShellString>,
61    splash_time: Option<u64>,
62    reboot_timeout: Option<u64>,
63    strict: Option<OnOff>,
64}
65
66impl ToCommand for Boot {
67    fn has_args(&self) -> bool {
68        self.order.is_some() || self.once.is_some() || self.menu.is_some() || self.splash.is_some() || self.splash_time.is_some() || self.reboot_timeout.is_some() || self.strict.is_some()
69    }
70
71    fn command(&self) -> String {
72        ARG_BOOT.to_string()
73    }
74    fn to_args(&self) -> Vec<String> {
75        let mut args = vec![];
76
77        if let Some(order) = &self.order {
78            args.push(format!("{}{}", KEY_ORDER, order.as_ref()));
79        }
80        if let Some(once) = &self.once {
81            args.push(format!("{}{}", KEY_ONCE, once.as_ref()));
82        }
83        qao!(&self.menu, args, KEY_MENU);
84        if let Some(splash) = &self.splash {
85            args.push(format!("{}{}", KEY_SPLASH, splash.as_ref()));
86        }
87        qao!(&self.splash_time, args, KEY_SPLASH_TIME);
88        qao!(&self.reboot_timeout, args, KEY_REBOOT_TIMEOUT);
89        qao!(&self.strict, args, KEY_STRICT);
90
91        vec![args.join(DELIM_COMMA)]
92    }
93}
94
95impl FromStr for Boot {
96    type Err = ShellStringError;
97
98    fn from_str(s: &str) -> Result<Self, Self::Err> {
99        let mut value = Self {
100            order: None,
101            once: None,
102            menu: None,
103            splash: None,
104            splash_time: None,
105            reboot_timeout: None,
106            strict: None,
107        };
108        for part in s.split(DELIM_COMMA) {
109            let (key, raw) = part.split_once('=').ok_or_else(|| ShellStringError::new(format!("invalid -boot option: {part}")))?;
110            match key {
111                "order" => value.order = Some(raw.parse::<ShellString>().map_err(ShellStringError::new)?),
112                "once" => value.once = Some(raw.parse::<ShellString>().map_err(ShellStringError::new)?),
113                "menu" => value.menu = Some(raw.parse::<OnOff>().map_err(|_| ShellStringError::new(format!("invalid menu value: {raw}")))?),
114                "splash" => value.splash = Some(raw.parse::<ShellString>().map_err(ShellStringError::new)?),
115                "splash-time" => value.splash_time = Some(raw.parse::<u64>().map_err(|e| ShellStringError::new(e.to_string()))?),
116                "reboot-timeout" => value.reboot_timeout = Some(raw.parse::<u64>().map_err(|e| ShellStringError::new(e.to_string()))?),
117                "strict" => value.strict = Some(raw.parse::<OnOff>().map_err(|_| ShellStringError::new(format!("invalid strict value: {raw}")))?),
118                other => return Err(ShellStringError::new(format!("unsupported -boot option: {other}"))),
119            }
120        }
121        Ok(value)
122    }
123}
124
125pco0!(order, shell_string_until_comma, ShellString, KEY_ORDER);
126pco0!(once, shell_string_until_comma, ShellString, KEY_ONCE);
127pco0!(menu, alphanumeric1, OnOff, KEY_MENU);
128pco0!(splash, shell_string_until_comma, ShellString, KEY_SPLASH);
129ppo0!(splash_time, dec_uint, u64, KEY_SPLASH_TIME);
130ppo0!(reboot_timeout, dec_uint, u64, KEY_REBOOT_TIMEOUT);
131pco0!(strict, alphanumeric1, OnOff, KEY_STRICT);
132
133pub fn boot(s: &mut &str) -> ModalResult<Boot> {
134    let order = opt(order).parse_next(s)?;
135    let once = opt(once).parse_next(s)?;
136    let menu = opt(menu).parse_next(s)?;
137    let splash = opt(splash).parse_next(s)?;
138    let splash_time = opt(splash_time).parse_next(s)?;
139    let reboot_timeout = opt(reboot_timeout).parse_next(s)?;
140    let strict = opt(strict).parse_next(s)?;
141
142    Ok(Boot {
143        order,
144        once,
145        menu,
146        splash,
147        splash_time,
148        reboot_timeout,
149        strict,
150    })
151}