1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use {
    argh::{ArgsInfo, FromArgValue, FromArgs},
    std::fmt,
};

#[cfg(not(target_os = "fuchsia"))]
use ffx_core::ffx_command;

#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum GuestType {
    Debian,
    Termina,
    Zircon,
}

impl FromArgValue for GuestType {
    fn from_arg_value(value: &str) -> Result<Self, String> {
        match value {
            "debian" => Ok(Self::Debian),
            "termina" => Ok(Self::Termina),
            "zircon" => Ok(Self::Zircon),
            _ => Err(format!(
                "Unrecognized guest type \"{}\". Supported guest types are: \
                \"debian\", \"termina\", \"zircon\".",
                value
            )),
        }
    }
}

impl fmt::Display for GuestType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            GuestType::Debian => write!(f, "debian"),
            GuestType::Termina => write!(f, "termina"),
            GuestType::Zircon => write!(f, "zircon"),
        }
    }
}

impl GuestType {
    pub fn moniker(&self) -> &str {
        match self {
            GuestType::Debian => "/core/debian-guest-manager",
            GuestType::Termina => "/core/termina-guest-manager",
            GuestType::Zircon => "/core/zircon-guest-manager",
        }
    }

    pub fn guest_manager_interface(&self) -> &str {
        match *self {
            GuestType::Zircon => "fuchsia.virtualization.ZirconGuestManager",
            GuestType::Debian => "fuchsia.virtualization.DebianGuestManager",
            GuestType::Termina => "fuchsia.virtualization.TerminaGuestManager",
        }
    }

    pub fn gn_target_label(self) -> &'static str {
        match self {
            GuestType::Zircon => "//src/virtualization/bundles:zircon",
            GuestType::Debian => "//src/virtualization/bundles:debian",
            GuestType::Termina => "//src/virtualization/bundles:termina",
        }
    }

    pub fn gn_core_shard_label(&self) -> &'static str {
        match self {
            GuestType::Zircon => "//src/virtualization/bundles:zircon_core_shards",
            GuestType::Debian => "//src/virtualization/bundles:debian_core_shards",
            GuestType::Termina => "//src/virtualization/bundles:termina_core_shards",
        }
    }

    pub fn package_url(&self) -> &'static str {
        match self {
            GuestType::Zircon => "fuchsia-pkg://fuchsia.com/zircon_guest#meta/zircon_guest.cm",
            GuestType::Debian => "fuchsia-pkg://fuchsia.com/debian_guest#meta/debian_guest.cm",
            GuestType::Termina => "fuchsia-pkg://fuchsia.com/termina_guest#meta/termina_guest.cm",
        }
    }

    pub fn all_guests() -> Vec<GuestType> {
        vec![GuestType::Debian, GuestType::Termina, GuestType::Zircon]
    }
}

#[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
/// Top-level command.
pub struct GuestOptions {
    #[argh(subcommand)]
    pub nested: SubCommands,
}

#[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
pub enum SubCommands {
    Attach(crate::attach_args::AttachArgs),
    Launch(crate::launch_args::LaunchArgs),
    Stop(crate::stop_args::StopArgs),
    Balloon(crate::balloon_args::BalloonArgs),
    List(crate::list_args::ListArgs),
    Socat(crate::socat_args::SocatArgs),
    Vsh(VshArgs),
    VsockPerf(crate::vsockperf_args::VsockPerfArgs),
    Wipe(crate::wipe_args::WipeArgs),
    Mem(crate::mem_args::MemArgs),
}

pub mod mem_args {
    use super::*;
    /// Interact with the guest virtio-mem. Usage: guest mem sub-command [ request-plugged or stats]
    #[derive(ArgsInfo, FromArgs, Debug, PartialEq)]
    #[argh(subcommand, name = "mem")]
    #[cfg_attr(not(target_os = "fuchsia"), ffx_command())]
    pub struct MemArgs {
        #[argh(subcommand)]
        pub mem_cmd: MemCommands,
    }
    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    #[argh(subcommand)]
    pub enum MemCommands {
        RequestPluggedMem(RequestPluggedMem),
        StatsMem(StatsMem),
    }

    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Modify the requested amount of the dynamically plugged memory. Usage: guest mem request-plugged guest-type size
    #[argh(subcommand, name = "request-plugged")]
    pub struct RequestPluggedMem {
        #[argh(positional)]
        /// type of the guest
        pub guest_type: GuestType,
        #[argh(positional)]
        /// target amount of memory to be dynamically plugged
        pub size: u64,
    }
    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// See the stats of a guest's virtio-mem. Usage: guest mem stats guest-type
    #[argh(subcommand, name = "stats")]
    pub struct StatsMem {
        #[argh(positional)]
        /// type of the guest
        pub guest_type: GuestType,
    }
}

pub mod balloon_args {
    use super::*;
    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Interact with the guest memory balloon. Usage: guest balloon sub-command guest-type ...
    #[argh(subcommand, name = "balloon")]
    #[cfg_attr(not(target_os = "fuchsia"), ffx_command())]
    pub struct BalloonArgs {
        #[argh(subcommand)]
        pub balloon_cmd: BalloonCommands,
    }

    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    #[argh(subcommand)]
    pub enum BalloonCommands {
        Set(BalloonSet),
        Stats(BalloonStats),
    }

    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Modify the size of a memory balloon. Usage: guest balloon set guest-type num-pages
    #[argh(subcommand, name = "set")]
    pub struct BalloonSet {
        #[argh(positional)]
        /// type of the guest
        pub guest_type: GuestType,
        #[argh(positional)]
        /// number of pages guest balloon will have after use.
        pub num_pages: u32,
    }

    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// See the stats of a guest's memory balloon. Usage: guest balloon stats guest-type
    #[argh(subcommand, name = "stats")]
    pub struct BalloonStats {
        #[argh(positional)]
        /// type of the guest
        pub guest_type: GuestType,
    }
}

pub mod list_args {
    use super::*;
    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// List available guest environments.
    #[argh(subcommand, name = "list")]
    #[cfg_attr(not(target_os = "fuchsia"), ffx_command())]
    pub struct ListArgs {
        #[argh(positional)]
        /// optional guest type to get detailed information about
        pub guest_type: Option<GuestType>,
    }
}

pub mod socat_args {
    use super::*;
    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Interact with the guest via socat. See the sub-command help for details.
    #[argh(subcommand, name = "socat")]
    #[cfg_attr(not(target_os = "fuchsia"), ffx_command())]
    pub struct SocatArgs {
        #[argh(subcommand)]
        pub socat_cmd: SocatCommands,
    }

    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    #[argh(subcommand)]
    pub enum SocatCommands {
        Listen(SocatListen),
        Connect(SocatConnect),
    }

    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Create a socat connection on the specified port. Usage: guest socat connect guest-type port
    #[argh(subcommand, name = "connect")]
    pub struct SocatConnect {
        #[argh(positional)]
        /// type of the guest
        pub guest_type: GuestType,
        #[argh(positional)]
        /// guest port number to attempt to connect to
        pub guest_port: u32,
    }

    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Listen through socat on the specified port. Usage: guest socat listen guest-type host-port
    #[argh(subcommand, name = "listen")]
    pub struct SocatListen {
        #[argh(positional)]
        /// type of the guest
        pub guest_type: GuestType,
        #[argh(positional)]
        /// host port number to accept incoming guest connections on
        pub host_port: u32,
    }
}

#[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
/// Create virtual shell for a guest or connect via virtual shell.
#[argh(subcommand, name = "vsh")]
pub struct VshArgs {
    #[argh(option)]
    /// port of a vsh socket to connect to.
    pub port: Option<u32>,
    #[argh(switch, short = 'c')]
    /// connect to the container within the VM
    pub container: bool,
    #[argh(positional)]
    /// list of arguments to run non-interactively on launch.
    pub args: Vec<String>,
}

pub mod wipe_args {
    use super::*;
    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Clears the stateful data for the target guest. Currently only termina is supported.
    #[argh(subcommand, name = "wipe")]
    #[cfg_attr(not(target_os = "fuchsia"), ffx_command())]
    pub struct WipeArgs {
        #[argh(positional)]
        /// type of the guest
        pub guest_type: GuestType,
    }
}

pub mod vsockperf_args {
    use super::*;
    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Perform a vsock micro benchmark on the target guest. Only Debian is supported.
    #[argh(subcommand, name = "vsock-perf")]
    #[cfg_attr(not(target_os = "fuchsia"), ffx_command())]
    pub struct VsockPerfArgs {
        #[argh(positional)]
        /// type of the guest
        pub guest_type: GuestType,
    }
}

pub mod launch_args {
    use super::*;
    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Launch a guest image. Usage: guest launch guest_type [--cmdline-add <arg>...] [--default-net <bool>] [--memory <memory-size>] [--cpus <num-cpus>] [--virtio-* <bool>]
    #[argh(subcommand, name = "launch")]
    #[cfg_attr(not(target_os = "fuchsia"), ffx_command())]
    pub struct LaunchArgs {
        #[argh(positional)]
        /// guest type to launch e.g. 'zircon'.
        pub guest_type: GuestType,
        /// adds provided strings to the existing kernel command line
        #[argh(option)]
        pub cmdline_add: Vec<String>,
        /// enable a default net device
        #[argh(option)]
        pub default_net: Option<bool>,
        /// allocate 'bytes' of memory for the guest
        #[argh(option)]
        pub memory: Option<u64>,
        /// number of virtual cpus available for the guest
        #[argh(option)]
        pub cpus: Option<u8>,
        /// enable virtio-balloon
        #[argh(option)]
        pub virtio_balloon: Option<bool>,
        /// enable virtio-console
        #[argh(option)]
        pub virtio_console: Option<bool>,
        /// enable virtio-gpu and virtio-input
        #[argh(option)]
        pub virtio_gpu: Option<bool>,
        /// enable virtio-rng
        #[argh(option)]
        pub virtio_rng: Option<bool>,
        /// enable virtio-sound
        #[argh(option)]
        pub virtio_sound: Option<bool>,
        /// enable virtio-sound-input
        #[argh(option)]
        pub virtio_sound_input: Option<bool>,
        /// enable virtio-vsock
        #[argh(option)]
        pub virtio_vsock: Option<bool>,
        /// enable virtio-mem to allow dynamically (un)plug memory to the guest
        #[argh(option)]
        pub virtio_mem: Option<bool>,
        /// virtio-mem pluggable region size
        #[argh(option)]
        pub virtio_mem_region_size: Option<u64>,
        /// virtio-mem pluggable region alignment
        #[argh(option)]
        pub virtio_mem_region_alignment: Option<u64>,
        /// virtio-mem pluggable blocksize
        #[argh(option)]
        pub virtio_mem_block_size: Option<u64>,
        /// detach from a guest allowing it to run in the background
        #[argh(switch, short = 'd')]
        pub detach: bool,
    }
}

pub mod stop_args {
    use super::*;
    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Stop a running guest. Usage: guest stop guest_type [-f]
    #[argh(subcommand, name = "stop")]
    #[cfg_attr(not(target_os = "fuchsia"), ffx_command())]
    pub struct StopArgs {
        /// guest type to stop e.g. 'zircon'
        #[argh(positional)]
        pub guest_type: GuestType,
        /// force stop the guest
        #[argh(switch, short = 'f')]
        pub force: bool,
    }
}

pub mod attach_args {
    use super::*;
    #[derive(ArgsInfo, FromArgs, PartialEq, Debug)]
    /// Attach console and serial to a running guest. Usage: guest attach guest_type
    #[argh(subcommand, name = "attach")]
    #[cfg_attr(not(target_os = "fuchsia"), ffx_command())]
    pub struct AttachArgs {
        /// guest type to attach to e.g. 'debian'
        #[argh(positional)]
        pub guest_type: GuestType,
        /// attach via serial instead of virtio-console
        #[argh(switch)]
        pub serial: bool,
    }
}