// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. use anyhow::{bail, Context, Result}; use crate::{ config::{parse_trace_options, ChardevType, CmdParser, MachineType, VmConfig}, qmp::qmp_socket::QmpSocketPath, temp_cleaner::TempCleaner, }; use util::file::clear_file; use util::unix::limit_permission; use util::{ arg_parser::{Arg, ArgMatches, ArgParser}, socket::SocketListener, }; /// This macro is to run struct $z 's function $s whose arg is $x 's inner member. /// There is a multi-macro-cast in cases of vec and bool. /// /// # Examples /// /// ```text /// add_args_to_config!(name, vm_cfg, update_name); /// add_args_to_config!(name, vm_cfg, update_name, vec); /// add_args_to_config!(name, vm_cfg, update_name, bool); /// ``` macro_rules! add_args_to_config { ( $x:tt, $z:expr, $s:tt ) => { if let Some(temp) = &$x { $z.$s(temp)?; } }; ( $x:tt, $z:expr, $s:tt, vec ) => { if let Some(temp) = &$x { $z.$s(&temp) } }; ( $x:tt, $z:expr, $s:tt, bool ) => { if ($x) { $z.$s(); } }; } /// This macro is to run struct $z 's function $s whose arg is $x 's every inner /// member. /// /// # Examples /// /// ```text /// add_args_to_config_multi!(drive, vm_cfg, update_drive); /// ``` macro_rules! add_args_to_config_multi { ( $x:tt, $z:expr, $s:tt ) => { if let Some(temps) = &$x { for temp in temps { $z.$s(temp)?; } } }; } /// This function is to define all commandline arguments. pub fn create_args_parser<'a>() -> ArgParser<'a> { let parser = ArgParser::new("StratoVirt") .version(util::VERSION) .author("The StratoVirt Project Developers") .about("A light kvm-based hypervisor.") .arg( Arg::with_name("name") .long("name") .value_name("[vm_name]") .help("set the name of the guest.") .takes_value(true), ) .arg( Arg::with_name("machine") .long("machine") .value_name("[type=]<name>[,dump_guest_core=on|off][,mem-share=on|off]") .help("'type' selects emulated machine type and set properties. \ 'dump_guest_core' includes guest memory in a core dump. \ 'mem-share' sets guest memory is shareable.") .takes_value(true), ) .arg( Arg::with_name("accel") .long("accel") .value_name("[accel]") .help("select accelerator, only 'kvm' is supported now.") .takes_value(true), ) .arg( Arg::with_name("smp") .long("smp") .value_name("[cpus=]<n>[,maxcpus=<cpus>][,sockets=<sockets>][,dies=<dies>][,clusters=<clusters>][,cores=<cores>][,threads=<threads>]") .help("'cpus' sets the number of CPUs to 'n' (default: 1). 'maxcpus' sets number of total CPUs, including online and offline CPUs. \ 'sockets' is the number of sockets on the machine. \ 'dies' is the number of dies in one socket. \ 'clusters' is the number of clusters in one die. \ 'cores' is the number of cores in one cluster. \ 'threads' is the number of threads in one core") .takes_value(true), ) .arg( Arg::with_name("cpu") .long("cpu") .value_name("host[,pmu=on|off][,sve=on|off]") .help("set CPU model and features.") .can_no_value(false) .takes_value(true) ) .arg( Arg::with_name("freeze_cpu") .short("S") .long("freeze") .help("freeze CPU at startup") .takes_value(false) .required(false), ) .arg( Arg::with_name("memory") .long("m") .value_name("[size=]<megs>[m|M|g|G]") .help("configure guest RAM(default unit: MiB).") .takes_value(true), ) .arg( Arg::with_name("mem-path") .long("mem-path") .value_name("<filebackend file path>") .help("configure file path that backs guest memory.") .takes_value(true), ) .arg( Arg::with_name("mem-prealloc") .long("mem-prealloc") .help("Prealloc memory for VM") .takes_value(false) .required(false), ) .arg( Arg::with_name("numa") .multiple(true) .long("numa") .value_name("<parameters>") .help("\n\t\tset numa node: -numa node,nodeid=<0>,cpus=<0-1>,memdev=<mem0>; \ \n\t\tset numa distance: -numa dist,src=<0>,dst=<1>,val=<20> ") .takes_values(true), ) .arg( Arg::with_name("kernel") .long("kernel") .value_name("<kernel_path>") .help("use uncompressed kernel image") .takes_value(true), ) .arg( Arg::with_name("kernel-cmdline") .multiple(true) .long("append") .value_name("<kernel cmdline parameters>") .help("use 'cmdline' as kernel command line") .takes_values(true), ) .arg( Arg::with_name("initrd-file") .long("initrd") .value_name("<initrd_path>") .help("use 'initrd-file' as initial ram disk") .takes_value(true), ) .arg( Arg::with_name("qmp") .long("qmp") .value_name("<parameters>") .help("\n\t\tset unix socket path: unix:<socket_path>,server,nowait; \ \n\t\tset tcp socket path: tcp:ip:port,server,nowait") .takes_value(true) ) .arg( Arg::with_name("mod-test") .long("mod-test") .value_name("unix:socket_path") .help("set module test's unixsocket path") .takes_value(true) ) .arg( Arg::with_name("drive") .multiple(true) .long("drive") .value_name("<parameters>") .help("\n\t\tset block drive image: -drive id=<drive_id>,file=<path_on_host>[,readonly=on|off][,direct=on|off][,throttling.iops-total=<200>]; \ \n\t\tset pflash drive image: -drive file=<pflash_path>,if=pflash,unit=0|1[,readonly=true|false]; \ \n\t\tset scsi drive image: -drive id=<drive-scsi0-0-0-0>,file=<path_on_host>[,readonly=true|false]") .takes_values(true), ) .arg( Arg::with_name("netdev") .multiple(true) .long("netdev") .value_name( "tap,id=<str>,ifname=<tap_name>[,vhost=on|off][,queue=<N>]", ) .help("configure a host TAP network with ID 'str'") .takes_values(true), ) .arg( Arg::with_name("chardev") .multiple(true) .long("chardev") .value_name("<parameters>") .help("\n\t\tadd standard i/o device: -chardev stdio,id=<char_id>; \ \n\t\tadd pseudo-terminal: -chardev pty,id=<char_id>; \ \n\t\tadd file: -chardev file,id=<char_id>,path=<path>; \ \n\t\tadd unix-socket: -chardev socket,id=<char_id>,path=<path>[,server][,nowait]; \ \n\t\tadd tcp-socket: -chardev socket,id=<char_id>,port=<port>[,host=host][,server][,nowait];") .takes_values(true), ) .arg( Arg::with_name("device") .multiple(true) .long("device") .value_name("<parameters>") .help("\n\t\tadd virtio mmio block: -device virtio-blk-device,id=<blk_id>,drive=<drive_id>[,iothread=<iothread1>][,serial=<serial_num>]; \ \n\t\tadd virtio pci block: -device virtio-blk-pci,id=<blk_id>,drive=<drive_id>,bus=<pcie.0>,addr=<0x3>[,multifunction=on|off][,iothread=<iothread1>][,serial=<serial_num>][,num-queues=<N>][,bootindex=<N>]; \ \n\t\tadd vhost user pci block: -device vhost-user-blk-pci,id=<blk_id>,chardev=<chardev_id>,bus=<pcie.0>,addr=<0x3>[,num-queues=<N>][,bootindex=<N>]; \ \n\t\tadd virtio mmio net: -device virtio-net-device,id=<net_id>,netdev=<netdev_id>[,iothread=<iothread1>][,mac=<12:34:56:78:9A:BC>]; \ \n\t\tadd virtio pci net: -device virtio-net-pci,id=<net_id>,netdev=<netdev_id>,bus=<pcie.0>,addr=<0x2>[,multifunction=on|off][,iothread=<iothread1>][,mac=<12:34:56:78:9A:BC>][,mq=on|off]; \ \n\t\tadd vhost mmio net: -device virtio-net-device,id=<net_id>,netdev=<netdev_id>[,iothread=<iothread1>][,mac=<12:34:56:78:9A:BC>]; \ \n\t\tadd vhost pci net: -device virtio-net-pci,id=<net_id>,netdev=<netdev_id>,bus=<pcie.0>,addr=<0x2>[,multifunction=on|off][,iothread=<iothread1>][,mac=<12:34:56:78:9A:BC>][,mq=on|off]; \ \n\t\tadd virtio mmio console: -device virtio-serial-device[,id=<virtio-serial0>] -device virtconsole,id=console_id,chardev=<virtioconsole1>; \ \n\t\tadd virtio pci console: -device virtio-serial-pci,id=<virtio-serial0>,bus=<pcie.0>,addr=<0x3>[,multifunction=on|off] -device virtconsole,id=<console_id>,chardev=<virtioconsole1>; \ \n\t\tadd vhost mmio vsock: -device vhost-vsock-device,id=<vsock_id>,guest-cid=<N>; \ \n\t\tadd vhost pci vsock: -device vhost-vsock-pci,id=<vsock_id>,guest-cid=<N>,bus=<pcie.0>,addr=<0x3>[,multifunction=on|off]; \ \n\t\tadd virtio mmio balloon: -device virtio-balloon-device[,deflate-on-oom=true|false][,free-page-reporting=true|false]; \ \n\t\tadd virtio pci balloon: -device virtio-balloon-pci,id=<balloon_id>,bus=<pcie.0>,addr=<0x4>[,deflate-on-oom=true|false][,free-page-reporting=true|false][,multifunction=on|off]; \ \n\t\tadd virtio mmio rng: -device virtio-rng-device,rng=<objrng0>,max-bytes=<1234>,period=<1000>; \ \n\t\tadd virtio pci rng: -device virtio-rng-pci,id=<rng_id>,rng=<objrng0>,max-bytes=<1234>,period=<1000>,bus=<pcie.0>,addr=<0x1>[,multifunction=on|off]; \ \n\t\tadd pcie root port: -device pcie-root-port,id=<pcie.1>,port=<0x1>,bus=<pcie.0>,addr=<0x1>[,multifunction=on|off]; \ \n\t\tadd vfio pci: -device vfio-pci,id=<vfio_id>,host=<0000:1a:00.3>,bus=<pcie.0>,addr=<0x03>[,multifunction=on|off]; \ \n\t\tadd usb controller: -device nec-usb-xhci,id=<xhci>,bus=<pcie.0>,addr=<0xa>; \ \n\t\tadd usb keyboard: -device usb-kbd,id=<kbd>; \ \n\t\tadd usb tablet: -device usb-tablet,id=<tablet>; \ \n\t\tadd usb storage: -device usb-storage,id=<storage>,drive=<drive_id>; \ \n\t\tadd scsi controller: -device virtio-scsi-pci,id=<scsi_id>,bus=<pcie.0>,addr=<0x3>[,multifunction=on|off][,iothread=<iothread1>][,num-queues=<N>]; \ \n\t\tadd scsi hard disk: -device scsi-hd,scsi-id=<0>,bus=<scsi0.0>,lun=<0>,drive=<drive-scsi0-0-0-0>,id=<scsi0-0-0-0>; \ \n\t\tadd vhost user fs: -device vhost-user-fs-pci,id=<device_id>,chardev=<chardev_id>,tag=<mount_tag>; \ \n\t\tadd pvpanic: -device pvpanic,id=<pvpanic_pci>,bus=<pcie.0>,addr=<0x7>[,supported-features=<0|1|2|3>];") .takes_values(true), ) .arg( Arg::with_name("serial") .long("serial") .value_name("<parameters>") .help("\n\t\tuse chardev device: -serial chardev:<char_id>; \ \n\t\tuse standard i/o device: -serial stdio; \ \n\t\tuse pseudo-terminal: -serial pty; \ \n\t\tuse file: -serial file,path=<path>; \ \n\t\tuse unix-socket: -serial socket,path=<path>[,server][,nowait]; \ \n\t\tuse tcp-socket: -serial socket,port=<port>[,host=<host>][,server][,nowait]; \ ") .takes_value(true), ) .arg( Arg::with_name("display log") .long("D") .value_name("[log path]") .help("output log to logfile (default stderr)") .takes_value(true) .can_no_value(true), ) .arg( Arg::with_name("pidfile") .long("pidfile") .value_name("<pidfile path>") .help("write PID to 'file'") .takes_value(true), ) .arg( Arg::with_name("daemonize") .long("daemonize") .value_name("") .help("daemonize StratoVirt after initializing") .takes_value(false) .required(false), ) .arg( Arg::with_name("disable-seccomp") .long("disable-seccomp") .value_name("") .help("not use seccomp sandbox for StratoVirt") .takes_value(false) .required(false), ) .arg( Arg::with_name("incoming") .long("incoming") .value_name("<parameters>") .help("\n\t\tdo the migration using tcp socket: -incoming tcp:<ip>:<port>; \ \n\t\tdo the migration using unix socket: -incoming unix:<socket path>; \ \n\t\tdo the virtual machine snapshot: -incoming file:<file path>") .takes_value(true), ) .arg( Arg::with_name("object") .multiple(true) .long("object") .value_name("<parameters>") .help("\n\t\tadd memory backend ram object: -object memory-backend-ram,size=<size>,id=<memid>[,policy=<bind>] [,host-nodes=<0>][,mem-prealloc=<true|false>][,dump-guest-core=<true|false>][,share=<on|off>]; \ \n\t\tadd memory backend file object: -object memory-backend-file,size=<size>,id=<memid>[,host-nodes=<0-1>] \ [,policy=bind][,mem-path=<path/to/file>][,dump-guest-core=<true|false>][,mem-prealloc=<true|false>][,share=<on|off>] \ \n\t\tadd memory backend memfd object: -object memory-backend-memfd,size=<size>,id=<memid>[,host-nodes=0-1][,policy=bind] \ [,mem-prealloc=<true|false>][,dump-guest-core=<true|false>][,share=<on|off>]; \ \n\t\tadd iothread object: -object iothread,id=<iothread_id>; \ \n\t\tadd rng object: -object rng-random,id=<rng_id>,filename=<file_path>; \ \n\t\tadd vnc tls object: -object tls-creds-x509,id=<vnc_id>,dir=</etc/pki/vnc>; \ \n\t\tadd authz object: -object authz-simple,id=<authz_id>,identity=<username>") .takes_values(true), ) .arg( Arg::with_name("mon") .long("mon") .value_name("chardev=<chardev_id>,id=<mon_id>[,mode=control]") .help("-mon is another way to create qmp channel. To use it, the chardev should be specified") .takes_value(true), ) .arg( Arg::with_name("overcommit") .long("overcommit") .value_name("[mem-lock=off]") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("uuid") .long("uuid") .value_name("[uuid]") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("no-user-config") .long("no-user-config") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("nodefaults") .long("nodefaults") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("sandbox") .long("sandbox") .value_name("[on,obsolete=deny]") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("msg") .long("msg") .value_name("[timestamp=on]") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("rtc") .long("rtc") .value_name("[base=utc]") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("no-shutdown") .long("no-shutdown") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("battery") .long("battery") .value_name("") .help("enable battery and power adapter devices") .takes_value(false) .required(false), ) .arg( Arg::with_name("boot") .long("boot") .value_name("[strict=on]") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("nographic") .long("nographic") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("realtime") .long("realtime") .value_name("[malock=off]") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("display") .long("display") .value_name("[none]") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("usb") .long("usb") .hidden(true) .can_no_value(true) .takes_value(true), ) .arg( Arg::with_name("trace") .multiple(false) .long("trace") .value_name("file=<file>") .help("specify the file lists trace state to enable") .takes_value(true), ) .arg( Arg::with_name("global") .multiple(true) .long("global") .value_name("[key=<value>]") .help("set global config") .takes_values(true) .required(false), ) .arg( Arg::with_name("smbios") .multiple(true) .long("smbios") .value_name("<parameters>") .help("\n\t\tadd type0 table: -smbios type=0[,vendor=str][,version=str][,date=str]; \ \n\t\tadd type1 table: -smbios type=1[,manufacturer=str][,version=str][,product=str][,serial=str][,uuid=str][,sku=str][,family=str]; \ \n\t\tadd type2 table: -smbios type=2[,manufacturer=str][,product=str][,version=str][,serial=str][,asset=str][,location=str]; \ \n\t\tadd type3 table: -smbios type=3[,manufacturer=str][,version=str][,serial=str][,asset=str][,sku=str]; \ \n\t\tadd type4 table: -smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str][,max-speed=%d][,current-speed=%d]; \ \n\t\tadd type17 table: -smbios type=17[,loc_pfx=str][,bank=str][,manufacturer=str][,serial=str][,asset=str][,part=str][,speed=%d]") .takes_values(true), ); #[cfg(feature = "usb_camera")] let parser = parser.arg( Arg::with_name("cameradev") .multiple(true) .long("cameradev") .value_name("<parameters>") .help("set cameradev: -cameradev v4l2,id=<testCam>,path=</dev/video0>") .takes_values(true), ); #[cfg(feature = "gtk")] let parser = parser.arg( Arg::with_name("display") .multiple(false) .long("display") .value_name("gtk") .help("set display for virtual machine: currently only supports gtk") .takes_value(true), ); #[cfg(feature = "vnc")] let parser = parser.arg( Arg::with_name("vnc") .multiple(false) .long("vnc") .value_name("ip:port") .help("specify the ip and port for vnc") .takes_value(true), ); #[cfg(feature = "windows_emu_pid")] let parser = parser.arg( Arg::with_name("windows_emu_pid") .multiple(false) .long("windows_emu_pid") .value_name("pid") .help("watch on the external windows emu pid") .takes_value(true), ); parser } /// Create `VmConfig` from `ArgMatches`'s arg. /// /// When accepted cmdline arguments, `StratoVirt` will parse useful arguments and /// transform them to VM's configuration structure -- `VmConfig`. /// /// # Arguments /// /// - * `args` - The structure accepted input cmdline arguments. /// /// # Errors /// /// Input arguments is illegal for `VmConfig` or `VmConfig`'s health check /// failed -- with this unhealthy `VmConfig`, VM will not boot successfully. pub fn create_vmconfig(args: &ArgMatches) -> Result<VmConfig> { // Parse config-file json. // VmConfig can be transformed by json file which described VmConfig // directly. let mut vm_cfg = VmConfig::default(); // Parse cmdline args which need to set in VmConfig add_args_to_config!((args.value_of("name")), vm_cfg, add_name); add_args_to_config!((args.value_of("machine")), vm_cfg, add_machine); add_args_to_config!((args.value_of("accel")), vm_cfg, add_accel); add_args_to_config!((args.value_of("memory")), vm_cfg, add_memory); add_args_to_config!((args.value_of("mem-path")), vm_cfg, add_mem_path); add_args_to_config!((args.value_of("smp")), vm_cfg, add_cpu); add_args_to_config!((args.value_of("cpu")), vm_cfg, add_cpu_feature); add_args_to_config!((args.value_of("kernel")), vm_cfg, add_kernel); add_args_to_config!((args.value_of("initrd-file")), vm_cfg, add_initrd); add_args_to_config!((args.value_of("serial")), vm_cfg, add_serial); add_args_to_config!((args.value_of("incoming")), vm_cfg, add_incoming); #[cfg(feature = "vnc")] add_args_to_config!((args.value_of("vnc")), vm_cfg, add_vnc); #[cfg(any(feature = "gtk", feature = "ohui_srv"))] add_args_to_config!((args.value_of("display")), vm_cfg, add_display); #[cfg(feature = "windows_emu_pid")] add_args_to_config!( (args.value_of("windows_emu_pid")), vm_cfg, add_windows_emu_pid ); add_args_to_config!( (args.is_present("no-shutdown")), vm_cfg, add_no_shutdown, bool ); add_args_to_config!((args.is_present("battery")), vm_cfg, add_battery, bool); add_args_to_config!( (args.is_present("mem-prealloc")), vm_cfg, enable_mem_prealloc, bool ); add_args_to_config!( (args.values_of("kernel-cmdline")), vm_cfg, add_kernel_cmdline, vec ); add_args_to_config_multi!((args.values_of("drive")), vm_cfg, add_drive); add_args_to_config_multi!((args.values_of("object")), vm_cfg, add_object); add_args_to_config_multi!((args.values_of("netdev")), vm_cfg, add_netdev); add_args_to_config_multi!((args.values_of("chardev")), vm_cfg, add_chardev); add_args_to_config_multi!((args.values_of("device")), vm_cfg, add_device); add_args_to_config_multi!((args.values_of("global")), vm_cfg, add_global_config); add_args_to_config_multi!((args.values_of("numa")), vm_cfg, add_numa); #[cfg(feature = "usb_camera")] add_args_to_config_multi!((args.values_of("cameradev")), vm_cfg, add_camera_backend); add_args_to_config_multi!((args.values_of("smbios")), vm_cfg, add_smbios); if let Some(opt) = args.value_of("trace") { parse_trace_options(&opt)?; } // Check the mini-set for Vm to start is ok if vm_cfg.machine_config.mach_type != MachineType::None { vm_cfg .check_vmconfig(args.is_present("daemonize")) .with_context(|| "Precheck failed, VmConfig is unhealthy, stop running")?; } Ok(vm_cfg) } /// This function is to parse qmp socket path and type. /// /// # Arguments /// /// * `args` - The structure accepted input cmdline arguments. /// /// # Errors /// /// The value of `qmp` is illegel. pub fn check_api_channel( args: &ArgMatches, vm_config: &mut VmConfig, ) -> Result<Vec<SocketListener>> { let mut sock_paths = Vec::new(); if let Some(qmp_config) = args.value_of("qmp") { let mut cmd_parser = CmdParser::new("qmp"); cmd_parser.push("").push("server").push("nowait"); cmd_parser.parse(&qmp_config)?; if let Some(uri) = cmd_parser.get_value::<String>("")? { let sock_path = QmpSocketPath::new(uri).with_context(|| "Failed to parse qmp socket path")?; sock_paths.push(sock_path); } else { bail!("No uri found for qmp"); } if cmd_parser.get_value::<String>("server")?.is_none() { bail!("Argument \'server\' is needed for qmp"); } if cmd_parser.get_value::<String>("nowait")?.is_none() { bail!("Argument \'nowait\' is needed for qmp"); } } if let Some(mon_config) = args.value_of("mon") { let mut cmd_parser = CmdParser::new("monitor"); cmd_parser.push("id").push("mode").push("chardev"); cmd_parser.parse(&mon_config)?; let chardev = cmd_parser .get_value::<String>("chardev")? .with_context(|| "Argument \'chardev\' is missing for \'mon\'")?; if let Some(mode) = cmd_parser.get_value::<String>("mode")? { if mode != *"control" { bail!("Invalid \'mode\' parameter: {:?} for monitor", &mode); } } else { bail!("Argument \'mode\' of \'mon\' should be set to \'control\'."); } if let Some(cfg) = vm_config.chardev.remove(&chardev) { if let ChardevType::UnixSocket { path, server, nowait, } = cfg.backend { if !server || !nowait { bail!( "Argument \'server\' and \'nowait\' are both required for chardev \'{}\'", path ); } sock_paths.push(QmpSocketPath::Unix { path }); } else if let ChardevType::TcpSocket { host, port, server, nowait, } = cfg.backend { if !server || !nowait { bail!( "Argument \'server\' and \'nowait\' are both required for chardev \'{}:{}\'", host, port ); } sock_paths.push(QmpSocketPath::Tcp { host, port }); } else { bail!("Only chardev of unix-socket type can be used for monitor"); } } else { bail!("No chardev found: {}", &chardev); } } if sock_paths.is_empty() { bail!("Please use \'-qmp\' or \'-mon\' to give a qmp path for Unix socket"); } let mut listeners = Vec::new(); for sock_path in sock_paths { listeners.push(bind_socket(&sock_path).with_context(|| { format!( "Failed to bind socket for path: {:?}", sock_path.to_string() ) })?) } Ok(listeners) } fn bind_socket(path: &QmpSocketPath) -> Result<SocketListener> { match path { QmpSocketPath::Tcp { host, port } => { let listener = SocketListener::bind_by_tcp(host, *port) .with_context(|| format!("Failed to bind tcp socket {}:{}", &host, &port))?; Ok(listener) } QmpSocketPath::Unix { path } => { clear_file(path.clone())?; let listener = SocketListener::bind_by_uds(path) .with_context(|| format!("Failed to bind socket file {}", &path))?; // Add file to temporary pool, so it could be cleaned when vm exits. TempCleaner::add_path(path.clone()); limit_permission(path) .with_context(|| format!("Failed to limit permission for socket file {}", &path))?; Ok(listener) } } }