qemu_command_builder/
vnc.rs

1use crate::common::OnOff;
2use crate::to_command::ToArg;
3use crate::to_command::ToCommand;
4use bon::Builder;
5use std::path::PathBuf;
6
7#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
8pub enum VNCDisplay {
9    To(usize),
10    Host(usize),
11    Unix(PathBuf),
12    None,
13}
14
15#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
16pub enum AllowExclusiveForceSharedIgnore {
17    AllowExclusive,
18    ForceShared,
19    Ignore,
20}
21
22impl ToArg for AllowExclusiveForceSharedIgnore {
23    fn to_arg(&self) -> &str {
24        match self {
25            AllowExclusiveForceSharedIgnore::AllowExclusive => "allow-exclusive",
26            AllowExclusiveForceSharedIgnore::ForceShared => "force-shared",
27            AllowExclusiveForceSharedIgnore::Ignore => "ignore",
28        }
29    }
30}
31#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Builder)]
32pub struct VNC {
33    display: VNCDisplay,
34
35    /// Connect to a listening VNC client via a "reverse" connection.
36    /// The client is specified by the display. For reverse network
37    /// connections (host:d,``reverse``), the d argument is a TCP port
38    /// number, not a display number.    
39    reverse: Option<OnOff>,
40
41    /// Opens an additional TCP listening port dedicated to VNC
42    /// Websocket connections. If a bare websocket option is given, the
43    /// Websocket port is 5700+display. An alternative port can be
44    /// specified with the syntax ``websocket``\ =port.
45    ///
46    /// If host is specified connections will only be allowed from this
47    /// host. It is possible to control the websocket listen address
48    /// independently, using the syntax ``websocket``\ =host:port.
49    ///
50    /// Websocket could be allowed over UNIX domain socket, using the syntax
51    /// ``websocket``\ =unix:path, where path is the location of a unix socket
52    /// to listen for connections on.
53    ///
54    /// If no TLS credentials are provided, the websocket connection
55    /// runs in unencrypted mode. If TLS credentials are provided, the
56    /// websocket connection requires encrypted client connections.
57    websocket: Option<OnOff>,
58
59    /// Require that password based authentication is used for client
60    /// connections.
61    ///
62    /// The password must be set separately using the ``set_password``
63    /// command in the :ref:`QEMU monitor`. The
64    /// syntax to change your password is:
65    /// ``set_password <protocol> <password>`` where <protocol> could be
66    /// either "vnc" or "spice".
67    ///
68    /// If you would like to change <protocol> password expiration, you
69    /// should use ``expire_password <protocol> <expiration-time>``
70    /// where expiration time could be one of the following options:
71    ///     now, never, +seconds or UNIX time of expiration, e.g. +60 to
72    /// make password expire in 60 seconds, or 1335196800 to make
73    /// password expire on "Mon Apr 23 12:00:00 EDT 2012" (UNIX time for
74    /// this date and time).
75    ///
76    /// You can also use keywords "now" or "never" for the expiration
77    /// time to allow <protocol> password to expire immediately or never
78    /// expire.
79    password: Option<OnOff>,
80
81    /// Require that password based authentication is used for client
82    /// connections, using the password provided by the ``secret``
83    /// object identified by ``secret-id``.
84    password_secret: Option<String>,
85
86    /// Provides the ID of a set of TLS credentials to use to secure the
87    /// VNC server. They will apply to both the normal VNC server socket
88    /// and the websocket socket (if enabled). Setting TLS credentials
89    /// will cause the VNC server socket to enable the VeNCrypt auth
90    /// mechanism. The credentials should have been previously created
91    /// using the ``-object tls-creds`` argument.
92    tls_creds: Option<String>,
93
94    /// Provides the ID of the QAuthZ authorization object against which
95    /// the client's x509 distinguished name will validated. This object
96    /// is only resolved at time of use, so can be deleted and recreated
97    /// on the fly while the VNC server is active. If missing, it will
98    /// default to denying access.
99    tls_authz: Option<String>,
100
101    /// Require that the client use SASL to authenticate with the VNC
102    /// server. The exact choice of authentication method used is
103    /// controlled from the system / user's SASL configuration file for
104    /// the 'qemu' service. This is typically found in
105    /// /etc/sasl2/qemu.conf. If running QEMU as an unprivileged user,
106    /// an environment variable SASL\_CONF\_PATH can be used to make it
107    /// search alternate locations for the service config. While some
108    /// SASL auth methods can also provide data encryption (eg GSSAPI),
109    /// it is recommended that SASL always be combined with the 'tls'
110    /// and 'x509' settings to enable use of SSL and server
111    /// certificates. This ensures a data encryption preventing
112    /// compromise of authentication credentials. See the
113    /// :ref:`VNC security` section in the System Emulation Users Guide
114    /// for details on using SASL authentication.    
115    sasl: Option<OnOff>,
116
117    /// Provides the ID of the QAuthZ authorization object against which
118    /// the client's SASL username will validated. This object is only
119    /// resolved at time of use, so can be deleted and recreated on the
120    /// fly while the VNC server is active. If missing, it will default
121    /// to denying access.
122    sasl_authz: Option<String>,
123
124    /// Legacy method for enabling authorization of clients against the
125    /// x509 distinguished name and SASL username. It results in the
126    /// creation of two ``authz-list`` objects with IDs of
127    /// ``vnc.username`` and ``vnc.x509dname``. The rules for these
128    /// objects must be configured with the HMP ACL commands.
129    ///
130    /// This option is deprecated and should no longer be used. The new
131    /// ``sasl-authz`` and ``tls-authz`` options are a replacement.
132    acl: Option<OnOff>,
133
134    /// Enable lossy compression methods (gradient, JPEG, ...). If this
135    /// option is set, VNC client may receive lossy framebuffer updates
136    /// depending on its encoding settings. Enabling this option can
137    /// save a lot of bandwidth at the expense of quality.   
138    lossy: Option<OnOff>,
139
140    /// Disable adaptive encodings. Adaptive encodings are enabled by
141    /// default. An adaptive encoding will try to detect frequently
142    /// updated screen regions, and send updates in these regions using
143    /// a lossy encoding (like JPEG). This can be really helpful to save
144    /// bandwidth when playing videos. Disabling adaptive encodings
145    /// restores the original static behavior of encodings like Tight.
146    non_adaptive: Option<OnOff>,
147
148    /// Set display sharing policy. 'allow-exclusive' allows clients to
149    /// ask for exclusive access. As suggested by the rfb spec this is
150    /// implemented by dropping other connections. Connecting multiple
151    /// clients in parallel requires all clients asking for a shared
152    /// session (vncviewer: -shared switch). This is the default.
153    /// 'force-shared' disables exclusive client access. Useful for
154    /// shared desktop sessions, where you don't want someone forgetting
155    /// specify -shared disconnect everybody else. 'ignore' completely
156    /// ignores the shared flag and allows everybody connect
157    /// unconditionally. Doesn't conform to the rfb spec but is
158    /// traditional QEMU behavior.
159    share: Option<AllowExclusiveForceSharedIgnore>,
160
161    /// Set keyboard delay, for key down and key up events, in
162    /// milliseconds. Default is 10. Keyboards are low-bandwidth
163    /// devices, so this slowdown can help the device and guest to keep
164    /// up and not lose events in case events are arriving in bulk.
165    /// Possible causes for the latter are flaky network connections, or
166    /// scripts for automated testing.
167    key_delay_ms: Option<usize>,
168
169    /// Use the specified audiodev when the VNC client requests audio
170    /// transmission. When not using an -audiodev argument, this option
171    /// must be omitted, otherwise is must be present and specify a
172    /// valid audiodev.
173    audiodev: Option<String>,
174
175    /// Permit the remote client to issue shutdown, reboot or reset power
176    /// control requests.
177    power_control: Option<OnOff>,
178}
179
180impl ToCommand for VNC {
181    fn to_command(&self) -> Vec<String> {
182        let mut cmd = vec![];
183
184        cmd.push("-vnc".to_string());
185
186        let mut args = vec![];
187        match &self.display {
188            VNCDisplay::To(l) => {
189                args.push(format!("to={}", l));
190            }
191            VNCDisplay::Host(d) => {
192                args.push(format!("host={}", d));
193            }
194            VNCDisplay::Unix(path) => {
195                args.push(format!("unix={}", path.display()));
196            }
197            VNCDisplay::None => {
198                args.push("none".to_string());
199            }
200        }
201
202        if let Some(reverse) = &self.reverse {
203            args.push(format!("reverse={}", reverse.to_arg()));
204        }
205        if let Some(websocket) = &self.websocket {
206            args.push(format!("websocket={}", websocket.to_arg()));
207        }
208        if let Some(password) = &self.password {
209            args.push(format!("password={}", password.to_arg()));
210        }
211        if let Some(password_secret) = &self.password_secret {
212            args.push(format!("password-secret={}", password_secret));
213        }
214        if let Some(tls_creds) = &self.tls_creds {
215            args.push(format!("tls-creds={}", tls_creds));
216        }
217        if let Some(tls_authz) = &self.tls_authz {
218            args.push(format!("tls-authz={}", tls_authz));
219        }
220        if let Some(sasl) = &self.sasl {
221            args.push(format!("sasl={}", sasl.to_arg()));
222        }
223        if let Some(sasl_authz) = &self.sasl_authz {
224            args.push(format!("sasl-authz={}", sasl_authz));
225        }
226        if let Some(acl) = &self.acl {
227            args.push(format!("acl={}", acl.to_arg()));
228        }
229        if let Some(lossy) = &self.lossy {
230            args.push(format!("lossy={}", lossy.to_arg()));
231        }
232        if let Some(non_adaptive) = &self.non_adaptive {
233            args.push(format!("non-adaptive={}", non_adaptive.to_arg()));
234        }
235        if let Some(share) = &self.share {
236            args.push(format!("share={}", share.to_arg()));
237        }
238        if let Some(key_delay_ms) = &self.key_delay_ms {
239            args.push(format!("key-delay-ms={}", key_delay_ms));
240        }
241        if let Some(audiodev) = &self.audiodev {
242            args.push(format!("audiodev={}", audiodev));
243        }
244        if let Some(power_control) = &self.power_control {
245            args.push(format!("power-control={}", power_control.to_arg()));
246        }
247
248        cmd.push(args.join(","));
249
250        cmd
251    }
252}