it works!!!

main
eater 3 years ago
parent 7411bb5972
commit b5c4d38b77
Signed by: eater
GPG Key ID: AD2560A0F84F0759

@ -1,7 +1,13 @@
[machine] [machine]
name = "win10" name = "win10"
memory = "12G" memory = "12G"
features = ["uefi", "spice", "scream"] features = [
"uefi",
"spice",
"pulse",
# "scream",
"looking-glass"
]
[cpu] [cpu]
amount = 12 amount = 12
@ -18,23 +24,23 @@ path = "/dev/disk/by-id/wwn-0x500a0751f008e09d"
preset = "ssd" preset = "ssd"
path = "/dev/disk/by-id/wwn-0x5002538e4038852d" path = "/dev/disk/by-id/wwn-0x5002538e4038852d"
#[[vfio]] [[vfio]]
#vendor = 0x10de vendor = 0x10de
#device = 0x1b80 device = 0x1b80
#index = 1 index = 1
#
#graphics = true graphics = true
#
#[[vfio]] [[vfio]]
#vendor = 0x10de vendor = 0x10de
#device = 0x10f0 device = 0x10f0
#index = 1 index = 1
#
#[[vfio]] [[vfio]]
#vendor = 0x1022 vendor = 0x1022
#device = 0x149c device = 0x149c
#addr = "0b:00.3" addr = "0b:00.3"
#
#[looking-glass] [looking-glass]
#width = 2560 width = 2560
#height = 1080 height = 1080

@ -125,14 +125,20 @@ vore:set_build_command(function(instance, vm)
vm:arg("-spice", "unix,addr=" .. instance.spice.socket_path .. ",disable-ticketing=on,seamless-migration=on") vm:arg("-spice", "unix,addr=" .. instance.spice.socket_path .. ",disable-ticketing=on,seamless-migration=on")
end end
if instance.pulse.enabled then
vm:arg("-device", "intel-hda", "-device", "hda-duplex")
vm:arg("-audiodev", "pa,server=/run/user/1000/pulse/native,id=pa0")
end
vm:arg( vm:arg(
"-machine", "-machine",
"q35,accel=kvm,usb=off,vmport=off,dump-guest-core=off,kernel_irqchip=on" "q35,accel=kvm,usb=off,vmport=off,dump-guest-core=off,kernel_irqchip=on"
) )
-- Pls update
vm:arg( vm:arg(
"-cpu", "-cpu",
"host,migratable=on,hv-time,hv-relaxed,hv-vapic,hv-spinlocks=0x1fff,hv-vendor-id=whatever,kvm=off" "host,hv-time,hv-relaxed,hv-vapic,hv-spinlocks=0x1fff,hv-vendor-id=whatever,kvm=off,+topoext"
) )
return vm return vm

@ -83,6 +83,9 @@ end
---@field enabled boolean ---@field enabled boolean
---@field socket_path string ---@field socket_path string
---@class Pulse
---@field enabled boolean
---@class Instance ---@class Instance
---@field name string ---@field name string
---@field kvm boolean ---@field kvm boolean
@ -96,6 +99,7 @@ end
---@field looking_glass LookingGlass ---@field looking_glass LookingGlass
---@field scream Scream ---@field scream Scream
---@field spice Spice ---@field spice Spice
---@field pulse Pulse
---- ----
---Add a disk definition to the argument list ---Add a disk definition to the argument list

@ -1,6 +1,6 @@
use anyhow::{Context, Error}; use anyhow::{Context, Error};
use config::{Config, File, FileFormat, Value}; use config::{Config, File, FileFormat, Value};
use serde::de::Visitor; use serde::de::{Visitor};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
@ -19,6 +19,7 @@ pub struct InstanceConfig {
pub vfio: Vec<VfioConfig>, pub vfio: Vec<VfioConfig>,
pub looking_glass: LookingGlassConfig, pub looking_glass: LookingGlassConfig,
pub scream: ScreamConfig, pub scream: ScreamConfig,
pub pulse: PulseConfig,
pub spice: SpiceConfig, pub spice: SpiceConfig,
} }
@ -75,15 +76,15 @@ impl InstanceConfig {
instance_config.looking_glass = LookingGlassConfig::from_table( instance_config.looking_glass = LookingGlassConfig::from_table(
config.get_table("looking-glass").unwrap_or_default(), config.get_table("looking-glass").unwrap_or_default(),
&instance_config.name,
)?; )?;
instance_config.scream = ScreamConfig::from_table( instance_config.scream = ScreamConfig::from_table(
config.get_table("scream").unwrap_or_default(), config.get_table("scream").unwrap_or_default(),
&instance_config.name,
)?; )?;
instance_config.spice = instance_config.spice =
SpiceConfig::from_table(config.get_table("spice").unwrap_or_default())?; SpiceConfig::from_table(config.get_table("spice").unwrap_or_default())?;
instance_config.pulse = PulseConfig::from_table(config.get_table("pulse").unwrap_or_default())?;
if let Ok(features) = config.get::<Vec<String>>("machine.features") { if let Ok(features) = config.get::<Vec<String>>("machine.features") {
for feature in features { for feature in features {
match feature.as_str() { match feature.as_str() {
@ -91,6 +92,7 @@ impl InstanceConfig {
"spice" => instance_config.spice.enabled = true, "spice" => instance_config.spice.enabled = true,
"scream" => instance_config.scream.enabled = true, "scream" => instance_config.scream.enabled = true,
"uefi" => instance_config.uefi.enabled = true, "uefi" => instance_config.uefi.enabled = true,
"pulse" => instance_config.pulse.enabled = true,
_ => {} _ => {}
} }
} }
@ -115,6 +117,7 @@ impl Default for InstanceConfig {
vfio: vec![], vfio: vec![],
looking_glass: Default::default(), looking_glass: Default::default(),
scream: Default::default(), scream: Default::default(),
pulse: Default::default(),
spice: Default::default(), spice: Default::default(),
} }
} }
@ -285,7 +288,6 @@ pub struct ScreamConfig {
impl ScreamConfig { impl ScreamConfig {
pub fn from_table( pub fn from_table(
table: HashMap<String, Value>, table: HashMap<String, Value>,
name: &str,
) -> Result<ScreamConfig, anyhow::Error> { ) -> Result<ScreamConfig, anyhow::Error> {
let mut cfg = ScreamConfig::default(); let mut cfg = ScreamConfig::default();
if let Some(enabled) = table.get("enabled").cloned() { if let Some(enabled) = table.get("enabled").cloned() {
@ -294,8 +296,6 @@ impl ScreamConfig {
if let Some(mem_path) = table.get("mem-path").cloned() { if let Some(mem_path) = table.get("mem-path").cloned() {
cfg.mem_path = mem_path.into_str()?; cfg.mem_path = mem_path.into_str()?;
} else {
cfg.mem_path = format!("/dev/shm/{}-scream", name);
} }
if let Some(buffer_size) = table.get("buffer-size").cloned() { if let Some(buffer_size) = table.get("buffer-size").cloned() {
@ -370,7 +370,6 @@ impl LookingGlassConfig {
pub fn from_table( pub fn from_table(
table: HashMap<String, Value>, table: HashMap<String, Value>,
name: &str,
) -> Result<LookingGlassConfig, anyhow::Error> { ) -> Result<LookingGlassConfig, anyhow::Error> {
let mut cfg = LookingGlassConfig::default(); let mut cfg = LookingGlassConfig::default();
@ -380,8 +379,6 @@ impl LookingGlassConfig {
if let Some(mem_path) = table.get("mem-path").cloned() { if let Some(mem_path) = table.get("mem-path").cloned() {
cfg.mem_path = mem_path.into_str()?; cfg.mem_path = mem_path.into_str()?;
} else {
cfg.mem_path = format!("/dev/shm/{}/looking-glass", name);
} }
match (table.get("buffer-size").cloned(), table.get("width").cloned(), table.get("height").cloned()) { match (table.get("buffer-size").cloned(), table.get("width").cloned(), table.get("height").cloned()) {
@ -613,6 +610,26 @@ impl VfioConfig {
} }
} }
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct PulseConfig {
pub enabled: bool,
}
impl PulseConfig {
pub fn from_table(table: HashMap<String, Value>) -> Result<PulseConfig, anyhow::Error> {
let mut cfg = PulseConfig {
enabled: false,
};
if let Some(enabled) = table.get("enabled").cloned() {
cfg.enabled = enabled.into_bool()?;
}
Ok(cfg)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, Default)] #[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct SpiceConfig { pub struct SpiceConfig {
pub enabled: bool, pub enabled: bool,
@ -623,13 +640,17 @@ impl SpiceConfig {
pub fn from_table(table: HashMap<String, Value>) -> Result<SpiceConfig, anyhow::Error> { pub fn from_table(table: HashMap<String, Value>) -> Result<SpiceConfig, anyhow::Error> {
let mut cfg = SpiceConfig { let mut cfg = SpiceConfig {
enabled: false, enabled: false,
socket_path: "/tmp/win10.sock".to_string(), socket_path: "".to_string(),
}; };
if let Some(enabled) = table.get("enabled").cloned() { if let Some(enabled) = table.get("enabled").cloned() {
cfg.enabled = enabled.into_bool()?; cfg.enabled = enabled.into_bool()?;
} }
if let Some(socket_path) = table.get("socket-path").cloned() {
cfg.socket_path = socket_path.into_str()?;
}
Ok(cfg) Ok(cfg)
} }
} }
@ -655,6 +676,11 @@ impl<'de> Deserialize<'de> for PCIAddress {
formatter.write_str("Expecting a string") formatter.write_str("Expecting a string")
} }
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where
E: de::Error, {
Ok(v.to_string())
}
fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> { fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
Ok(v) Ok(v)
} }

@ -223,11 +223,11 @@ impl QemuCommandBuilder {
global: &GlobalConfig, global: &GlobalConfig,
working_dir: PathBuf, working_dir: PathBuf,
) -> Result<QemuCommandBuilder, anyhow::Error> { ) -> Result<QemuCommandBuilder, anyhow::Error> {
let lua = Path::new(GLOBAL_CONFIG_LOCATION).join(&global.qemu.script); let lua = Path::new(GLOBAL_CONFIG_LOCATION).parent().unwrap().join(&global.qemu.script);
let builder = QemuCommandBuilder { let builder = QemuCommandBuilder {
lua: Lua::new(), lua: Lua::new(),
script: fs::read_to_string(lua)?, script: fs::read_to_string(&lua).with_context(|| format!("Failed to load lua qemu command build script ({:?})", lua))?,
storage: VoreLuaStorage::new(working_dir), storage: VoreLuaStorage::new(working_dir),
}; };

@ -13,7 +13,7 @@ macro_rules! define_requests {
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged, rename_all = "snake_case")] #[serde(tag = "answer", rename_all = "snake_case")]
pub enum AllResponses { pub enum AllResponses {
$($name(paste! { [<$name Response >] })),+ $($name(paste! { [<$name Response >] })),+
} }
@ -98,4 +98,8 @@ define_requests! {
Unload({ Unload({
pub name: String, pub name: String,
}, {}) }, {})
Kill({
pub name: String,
}, {})
} }

@ -43,6 +43,7 @@ impl CommandCenter {
} }
pub fn read_answer<R: Request>(answer: &str) -> Result<(u64, R::Response), CommandError> { pub fn read_answer<R: Request>(answer: &str) -> Result<(u64, R::Response), CommandError> {
log::debug!("Reading answer: {}", answer);
let answer_obj: Answer<R::Response> = serde_json::from_str(answer).map_err(|err| CommandError::InternalError(err.into()))?; let answer_obj: Answer<R::Response> = serde_json::from_str(answer).map_err(|err| CommandError::InternalError(err.into()))?;
match answer_obj.data { match answer_obj.data {

@ -18,7 +18,7 @@ pub struct Answer<R: Response> {
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)] #[serde(tag = "status", rename_all = "snake_case")]
pub enum AnswerResult<R: Response> { pub enum AnswerResult<R: Response> {
Error(AnswerError), Error(AnswerError),
#[serde(bound = "R: Response")] #[serde(bound = "R: Response")]

@ -2,7 +2,7 @@ use crate::{GlobalConfig, InstanceConfig, QemuCommandBuilder};
use anyhow::{Context, Error}; use anyhow::{Context, Error};
use beau_collector::BeauCollector; use beau_collector::BeauCollector;
use qapi::qmp::{QMP, Event}; use qapi::qmp::{QMP, Event};
use qapi::{Qmp, ExecuteError}; use qapi::{Qmp};
use std::{fmt, mem}; use std::{fmt, mem};
use std::fmt::{Debug, Formatter, Display}; use std::fmt::{Debug, Formatter, Display};
use std::fs::{read_link, OpenOptions, read_dir}; use std::fs::{read_link, OpenOptions, read_dir};
@ -76,7 +76,7 @@ impl Debug for ControlSocket {
} }
} }
const AUTO_UNBIND_BLACKLIST: &[&str] = &["nvidia"]; const AUTO_UNBIND_BLACKLIST: &[&str] = &["nvidia", "amdgpu"];
impl VirtualMachine { impl VirtualMachine {
pub fn new<P: AsRef<Path>>( pub fn new<P: AsRef<Path>>(
@ -111,6 +111,8 @@ impl VirtualMachine {
let mut results = vec![]; let mut results = vec![];
results.extend(self.prepare_disks()); results.extend(self.prepare_disks());
results.extend(self.prepare_vfio(execute_fixes, force)); results.extend(self.prepare_vfio(execute_fixes, force));
results.extend(self.prepare_shm());
results.extend(self.prepare_sockets());
results results
.into_iter() .into_iter()
.bcollect::<()>() .bcollect::<()>()
@ -122,6 +124,52 @@ impl VirtualMachine {
Ok(()) Ok(())
} }
pub fn prepare_shm(&mut self) -> Vec<Result<(), anyhow::Error>> {
let mut shm = vec![];
if self.config.looking_glass.enabled {
if self.config.looking_glass.mem_path.is_empty() {
self.config.looking_glass.mem_path = format!("/dev/shm/vore/{}/looking-glass", self.config.name);
}
shm.push(&self.config.looking_glass.mem_path);
}
if self.config.scream.enabled {
if self.config.scream.mem_path.is_empty() {
self.config.scream.mem_path = format!("/dev/shm/vore/{}/scream", self.config.name);
}
shm.push(&self.config.scream.mem_path);
}
shm
.into_iter()
.map(|x| Path::new(x))
.filter_map(|x| x.parent())
.filter(|x| !x.is_dir())
.map(|x| std::fs::create_dir_all(&x).with_context(|| format!("Failed creating directories for shared memory ({:?})", x)))
.collect()
}
pub fn prepare_sockets(&mut self) -> Vec<Result<(), anyhow::Error>> {
let mut sockets = vec![];
if self.config.spice.enabled {
if self.config.spice.socket_path.is_empty() {
self.config.spice.socket_path = self.working_dir.join("spice.sock").to_str().unwrap().to_string();
}
sockets.push(&self.config.spice.socket_path);
}
sockets
.into_iter()
.map(|x| Path::new(x))
.filter_map(|x| x.parent())
.filter(|x| !x.is_dir())
.map(|x| std::fs::create_dir_all(&x).with_context(|| format!("Failed creating directories for shared memory ({:?})", x)))
.collect()
}
/// ///
/// Doesn't really prepare them, but mostly checks if the user has permissions to read them /// Doesn't really prepare them, but mostly checks if the user has permissions to read them
/// ///
@ -305,6 +353,14 @@ impl VirtualMachine {
Ok(()) Ok(())
} }
pub fn boop(&mut self) -> Result<(), anyhow::Error> {
if let Some(qmp) = self.control_socket.as_mut() {
qmp.qmp.nop()?;
}
self.process_qmp_events()
}
fn process_qmp_events(&mut self) -> Result<(), anyhow::Error> { fn process_qmp_events(&mut self) -> Result<(), anyhow::Error> {
let events = if let Some(qmp) = self.control_socket.as_mut() { let events = if let Some(qmp) = self.control_socket.as_mut() {
// While we could iter, we keep hold of the mutable reference, so it's easier to just collect the events // While we could iter, we keep hold of the mutable reference, so it's easier to just collect the events
@ -366,18 +422,6 @@ impl VirtualMachine {
Ok(()) Ok(())
} }
pub fn stop_now(&mut self) -> Result<(), anyhow::Error> {
self.stop()?;
if let Some(mut process) = self.process.take() {
self.wait(Some(Duration::from_secs(30)), VirtualMachineState::Stopped)?;
self.quit()?;
process.wait()?;
}
Ok(())
}
pub fn wait_till_stopped(&mut self) -> Result<(), anyhow::Error> { pub fn wait_till_stopped(&mut self) -> Result<(), anyhow::Error> {
self.wait(None, VirtualMachineState::Stopped)?; self.wait(None, VirtualMachineState::Stopped)?;
Ok(()) Ok(())
@ -388,19 +432,12 @@ impl VirtualMachine {
return Ok(()); return Ok(());
} }
self.send_qmp_command(&qapi_qmp::quit {}) match self.send_qmp_command(&qapi_qmp::quit {}) {
.map(|_| ()) Err(err) if err.downcast_ref::<io::Error>().map_or(false, |x| x.kind() == io::ErrorKind::UnexpectedEof) => {}
.or_else(|x| err => { err?; }
if let Some(ExecuteError::Io(err)) = x.downcast_ref::<ExecuteError>() { }
if err.kind() == ErrorKind::UnexpectedEof {
Ok(()) Ok(())
} else {
Err(x)
}
} else {
Err(x)
})
.map_err(From::from)
} }
fn wait(&mut self, duration: Option<Duration>, target_state: VirtualMachineState) -> Result<bool, anyhow::Error> { fn wait(&mut self, duration: Option<Duration>, target_state: VirtualMachineState) -> Result<bool, anyhow::Error> {
@ -438,8 +475,12 @@ impl VirtualMachine {
} }
} }
if self.state == VirtualMachineState::Loaded {
self.prepare(true, false)?
}
let mut command = Command::new("qemu-system-x86_64"); let mut command = Command::new("qemu-system-x86_64");
command.args(self.get_cmd_line()?); command.args(self.get_cmd_line().context("Failed to generate qemu command line")?);
self.process = Some(command.spawn()?); self.process = Some(command.spawn()?);
let mut res = || { let mut res = || {
@ -477,7 +518,7 @@ impl VirtualMachine {
_info: handshake, _info: handshake,
}; };
// self.pin_qemu_threads()?; self.pin_qemu_threads()?;
control_socket control_socket
.qmp .qmp
@ -495,7 +536,7 @@ impl VirtualMachine {
let result_ = res(); let result_ = res();
if result_.is_err() { if result_.is_err() {
if let Some(mut qemu) = self.process.take() { if let Some(mut qemu) = self.process.take() {
qemu.kill()?; let _ = qemu.kill();
qemu.wait()?; qemu.wait()?;
} }
} }
@ -547,4 +588,4 @@ impl Write for CloneableUnixStream {
fn flush(&mut self) -> io::Result<()> { fn flush(&mut self) -> io::Result<()> {
self.lock()?.flush() self.lock()?.flush()
} }
} }

@ -54,4 +54,14 @@ impl Client {
self.send(PrepareRequest { name: vm, cdroms })?; self.send(PrepareRequest { name: vm, cdroms })?;
Ok(()) Ok(())
} }
pub fn start(&mut self, vm: String, cdroms: Vec<String>) -> anyhow::Result<()> {
self.send(StartRequest { name: vm, cdroms })?;
Ok(())
}
pub fn stop(&mut self, vm: String) -> anyhow::Result<()> {
self.send(StopRequest { name: vm })?;
Ok(())
}
} }

@ -1,11 +1,13 @@
mod client; mod client;
use vore_core::{init_logging}; use vore_core::{init_logging, VirtualMachineInfo};
use crate::client::Client; use crate::client::Client;
use clap::{App, ArgMatches}; use clap::{App, ArgMatches};
use std::fs; use std::{fs, mem};
use anyhow::Context; use anyhow::Context;
use std::option::Option::Some; use std::option::Option::Some;
use std::process::Command;
use std::os::unix::process::CommandExt;
fn main() { fn main() {
init_logging(); init_logging();
@ -38,6 +40,18 @@ fn main_res() -> anyhow::Result<()> {
vore.prepare(args)?; vore.prepare(args)?;
} }
("start", Some(args)) => {
vore.start(args)?;
}
("stop", Some(args)) => {
vore.stop(args)?;
}
("looking-glass", Some(args)) => {
vore.looking_glass(args)?;
}
("daemon", Some(args)) => { ("daemon", Some(args)) => {
match args.subcommand() { match args.subcommand() {
("version", _) => { ("version", _) => {
@ -60,7 +74,7 @@ fn main_res() -> anyhow::Result<()> {
struct LoadVMOptions { struct LoadVMOptions {
config: String, config: String,
cdroms: Vec<String>, cd_roms: Vec<String>,
save: bool, save: bool,
} }
@ -71,7 +85,7 @@ fn get_load_vm_options(args: &ArgMatches) -> anyhow::Result<LoadVMOptions> {
Ok(LoadVMOptions { Ok(LoadVMOptions {
config, config,
cdroms: args.values_of("cdrom").map_or(vec![], |x| x.map(|x| x.to_string()).collect::<Vec<_>>()), cd_roms: args.values_of("cdrom").map_or(vec![], |x| x.map(|x| x.to_string()).collect::<Vec<_>>()),
save: args.is_present("save"), save: args.is_present("save"),
}) })
} }
@ -82,12 +96,17 @@ struct VoreApp {
impl VoreApp { impl VoreApp {
fn get_vm_name(&mut self, args: &ArgMatches) -> anyhow::Result<String> { fn get_vm_name(&mut self, args: &ArgMatches) -> anyhow::Result<String> {
self.get_vm(args).map(|x| x.name)
}
pub fn get_vm(&mut self, args: &ArgMatches) -> anyhow::Result<VirtualMachineInfo> {
let mut items = self.client.list_vms()?;
if let Some(vm_name) = args.value_of("vm-name") { if let Some(vm_name) = args.value_of("vm-name") {
Ok(vm_name.to_string()) items.into_iter().find(|x| x.name == vm_name)
.with_context(|| format!("Couldn't find VM with the name '{}'", vm_name))
} else { } else {
let mut items = self.client.list_vms()?;
match (items.len(), items.pop()) { match (items.len(), items.pop()) {
(amount, Some(x)) if amount == 1 => return Ok(x.name), (amount, Some(x)) if amount == 1 => return Ok(x),
(0, None) => anyhow::bail!("There are no VM's loaded"), (0, None) => anyhow::bail!("There are no VM's loaded"),
_ => anyhow::bail!("Multiple VM's are loaded, please specify one"), _ => anyhow::bail!("Multiple VM's are loaded, please specify one"),
} }
@ -103,7 +122,7 @@ impl VoreApp {
fn load(&mut self, args: &ArgMatches) -> anyhow::Result<()> { fn load(&mut self, args: &ArgMatches) -> anyhow::Result<()> {
let vm_options = get_load_vm_options(args)?; let vm_options = get_load_vm_options(args)?;
let vm_info = self.client.load_vm(&vm_options.config, vm_options.save, vm_options.cdroms)?; let vm_info = self.client.load_vm(&vm_options.config, vm_options.save, vm_options.cd_roms)?;
log::info!("Loaded VM {}", vm_info.name); log::info!("Loaded VM {}", vm_info.name);
Ok(()) Ok(())
} }
@ -123,4 +142,38 @@ impl VoreApp {
self.client.prepare(name, args.values_of("cdrom").map_or(vec![], |x| x.map(|x| x.to_string()).collect::<Vec<_>>()))?; self.client.prepare(name, args.values_of("cdrom").map_or(vec![], |x| x.map(|x| x.to_string()).collect::<Vec<_>>()))?;
Ok(()) Ok(())
} }
fn start(&mut self, args: &ArgMatches) -> anyhow::Result<()> {
let name = self.get_vm_name(args)?;
self.client.start(name, args.values_of("cdrom").map_or(vec![], |x| x.map(|x| x.to_string()).collect::<Vec<_>>()))?;
Ok(())
}
fn looking_glass(mut self, args: &ArgMatches) -> anyhow::Result<()> {
let vm = self.get_vm(args)?;
if !vm.config.looking_glass.enabled {
anyhow::bail!("VM '{}' has no looking glass", vm.name);
}
let mut command = Command::new(std::env::var("LOOKING_GLASS").unwrap_or("looking-glass-client".to_string()));
if vm.config.spice.enabled {
command.args(&["-c", &vm.config.spice.socket_path, "-p", "0"]);
} else {
command.args(&["-s", "no"]);
}
command.args(&["-f", &vm.config.looking_glass.mem_path]);
command.args(args.values_of("looking-glass-args").map_or(vec![], |x| x.into_iter().collect::<Vec<_>>()));
mem::drop(self);
command.exec();
Ok(())
}
fn stop(&mut self, args: &ArgMatches) -> anyhow::Result<()> {
let name = self.get_vm_name(args)?;
self.client.stop(name)?;
Ok(())
}
} }

@ -95,7 +95,7 @@ impl RPCConnection {
#[derive(Clone, Eq, PartialEq, Debug)] #[derive(Clone, Eq, PartialEq, Debug)]
enum EventTarget { enum EventTarget {
RPCListener, RPCListener,
_Machine(String), Machine(String),
RPCConnection(usize), RPCConnection(usize),
None, None,
} }
@ -236,15 +236,43 @@ impl Daemon {
rpc::PrepareResponse {}.into_enum() rpc::PrepareResponse {}.into_enum()
} }
AllRequests::Start(_) => { AllRequests::Start(val) => {
anyhow::bail!("Unimplemented"); let cloned = if let Some(machine) = self.machines.get_mut(&val.name) {
machine.start()?;
machine.control_stream().cloned()
} else {
anyhow::bail!("No machine with the name {} exists", val.name);
};
if let Some(cloned) = cloned {
let new_id = self.add_target(EventTarget::Machine(val.name.clone()));
self.poller.add(&cloned, Event::readable(new_id))?;
}
rpc::StartResponse {}.into_enum()
} }
AllRequests::Stop(_) => { AllRequests::Stop(val) => {
anyhow::bail!("Unimplemented"); if let Some(machine) = self.machines.get_mut(&val.name) {
machine.stop()?;
} else {
anyhow::bail!("No machine with the name {} exists", val.name);
}
rpc::StartResponse {}.into_enum()
} }
AllRequests::Unload(_) => { AllRequests::Unload(_) => {
anyhow::bail!("Unimplemented"); anyhow::bail!("Unimplemented");
} }
AllRequests::Kill(val) => {
if let Some(machine) = self.machines.get_mut(&val.name) {
machine.quit()?;
} else {
anyhow::bail!("No machine with the name {} exists", val.name);
}
rpc::StartResponse {}.into_enum()
}
}; };
Ok(resp) Ok(resp)
@ -273,8 +301,12 @@ impl Daemon {
self.poller.modify(&self.rpc_listener, Event::readable(event.key))?; self.poller.modify(&self.rpc_listener, Event::readable(event.key))?;
self.accept_rpc_connections()?; self.accept_rpc_connections()?;
} }
EventTarget::_Machine(name) if self.machines.contains_key(&name) => { EventTarget::Machine(name) if self.machines.contains_key(&name) => {
if let Some(control_socket) = self.machines[&name].control_stream() { if let Some(machine) = self.machines.get_mut(&name) {
machine.boop()?;
}
if let Some(control_socket) = self.machines.get(&name).and_then(|x| x.control_stream()) {
self.poller.modify(control_socket, Event::readable(event.key))?; self.poller.modify(control_socket, Event::readable(event.key))?;
} }
} }

Loading…
Cancel
Save