mirror of
https://github.com/winapps-org/winapps.git
synced 2025-06-05 22:57:19 +02:00
Merge pull request #48 from oskardotglobal/feat-errors
feat: error handling
This commit is contained in:
commit
eed3c31537
@ -4,3 +4,4 @@ members = [
|
|||||||
"winapps-cli",
|
"winapps-cli",
|
||||||
"winapps-gui",
|
"winapps-gui",
|
||||||
]
|
]
|
||||||
|
resolver = "2"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use clap::{arg, Command};
|
use clap::{arg, Command};
|
||||||
use winapps::freerdp::freerdp_back::Freerdp;
|
use winapps::freerdp::freerdp_back::Freerdp;
|
||||||
use winapps::quickemu::{create_vm, kill_vm, start_vm};
|
use winapps::quickemu::{create_vm, kill_vm, start_vm};
|
||||||
use winapps::RemoteClient;
|
use winapps::{unwrap_or_panic, RemoteClient};
|
||||||
|
|
||||||
fn cli() -> Command {
|
fn cli() -> Command {
|
||||||
Command::new("winapps-cli")
|
Command::new("winapps-cli")
|
||||||
@ -69,18 +69,22 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Some((_, _)) => {
|
Some((_, _)) => {
|
||||||
cli.about("Command not found, try existing ones!")
|
unwrap_or_panic!(
|
||||||
.print_help()
|
cli.about("Command not found, try existing ones!")
|
||||||
.expect("Couldn't print help");
|
.print_help(),
|
||||||
|
"Couldn't print help"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((_, _)) => {
|
Some((_, _)) => {
|
||||||
cli.about("Command not found, try existing ones!")
|
unwrap_or_panic!(
|
||||||
.print_help()
|
cli.about("Command not found, try existing ones!")
|
||||||
.expect("Couldn't print help");
|
.print_help(),
|
||||||
|
"Couldn't print help"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1 @@
|
|||||||
fn main() {
|
fn main() {}
|
||||||
println!("Test lib: {}", winapps::add(1, 2));
|
|
||||||
}
|
|
||||||
|
@ -6,7 +6,10 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.75"
|
||||||
derive-new = "0.5.9"
|
derive-new = "0.5.9"
|
||||||
home = "0.5.5"
|
home = "0.5.5"
|
||||||
serde = { version = "1.0.171", features = ["derive"] }
|
serde = { version = "1.0.171", features = ["derive"] }
|
||||||
toml = "0.7.6"
|
thiserror = "1.0.49"
|
||||||
|
toml = "0.8.2"
|
||||||
|
tracing = "0.1.37"
|
||||||
|
158
winapps/src/errors.rs
Normal file
158
winapps/src/errors.rs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
/// This enum represents all possible errors that can occur in this crate.
|
||||||
|
/// It is used as a return type for most functions should they return an error.
|
||||||
|
/// There's 2 base variants: `Message` and `WithError`.
|
||||||
|
/// `Message` is used for simple errors that don't have an underlying cause.
|
||||||
|
/// `WithError` is used for errors that occur from another error.
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum WinappsError {
|
||||||
|
#[error("{0}")]
|
||||||
|
Message(String),
|
||||||
|
#[error("{0}\n{1}")]
|
||||||
|
WithError(#[source] anyhow::Error, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WinappsError {
|
||||||
|
/// This function prints the error to the console.
|
||||||
|
/// All lines are logged as seperate messages, and the source of the error is also logged if it exists.
|
||||||
|
fn error(&self) {
|
||||||
|
let messages: Vec<String> = self.to_string().split('\n').map(|s| s.into()).collect();
|
||||||
|
messages.iter().for_each(|s| tracing::error!("{}", s));
|
||||||
|
|
||||||
|
if self.source().is_some() {
|
||||||
|
tracing::error!("Caused by: {}", self.source().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function prints the error to the console and exits the program with an exit code of 1.
|
||||||
|
pub fn exit(&self) -> ! {
|
||||||
|
self.error();
|
||||||
|
|
||||||
|
tracing::error!("Unrecoverable error, exiting...");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function prints the error to the console and panics.
|
||||||
|
pub fn panic(&self) -> ! {
|
||||||
|
self.error();
|
||||||
|
|
||||||
|
panic!("Program crashed, see log above");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This macro is a shortcut for creating a `WinappsError` from a string.
|
||||||
|
/// You can use normal `format!` syntax inside the macro.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! error {
|
||||||
|
($($fmt:tt)*) => {
|
||||||
|
$crate::errors::WinappsError::Message(format!($($fmt)*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This macro is a shortcut for creating a `WinappsError` from a string.
|
||||||
|
/// The first argument is the source error.
|
||||||
|
/// You can use normal `format!` syntax inside the macro.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! error_from {
|
||||||
|
($err:expr, $($fmt:tt)*) => {
|
||||||
|
$crate::errors::WinappsError::WithError(anyhow::Error::new($err), format!($($fmt)*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This trait serves as a generic way to convert a `Result` or `Option` into a `WinappsError`.
|
||||||
|
pub trait IntoError<T> {
|
||||||
|
fn into_error(self, msg: String) -> Result<T, WinappsError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> IntoError<T> for Result<T, U>
|
||||||
|
where
|
||||||
|
T: Debug,
|
||||||
|
U: Error + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn into_error(self, msg: String) -> Result<T, WinappsError> {
|
||||||
|
if let Err(error) = self {
|
||||||
|
return Err(WinappsError::WithError(anyhow::Error::new(error), msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoError<T> for Option<T> {
|
||||||
|
fn into_error(self, msg: String) -> Result<T, WinappsError> {
|
||||||
|
if self.is_none() {
|
||||||
|
return Err(WinappsError::Message(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This macro creates a `Result<_, WinappsError>` from either a `Result` or an `Option`.
|
||||||
|
/// It also works for all other types that implement `IntoError`.
|
||||||
|
/// Used internally by `winapps::unwrap_or_exit!` and `winapps::unwrap_or_panic!`.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! into_error {
|
||||||
|
($val:expr) => {{
|
||||||
|
fn into_error_impl<T, U>(val: U) -> std::result::Result<T, $crate::errors::WinappsError>
|
||||||
|
where
|
||||||
|
T: std::marker::Sized + std::fmt::Debug,
|
||||||
|
U: $crate::errors::IntoError<T>,
|
||||||
|
{
|
||||||
|
val.into_error(
|
||||||
|
"Expected a value, got None / an Error. \
|
||||||
|
See log above for more detail."
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
into_error_impl($val)
|
||||||
|
}};
|
||||||
|
($val:expr, $msg:expr) => {{
|
||||||
|
fn into_error_impl<T, U>(
|
||||||
|
val: U,
|
||||||
|
msg: String,
|
||||||
|
) -> std::result::Result<T, $crate::errors::WinappsError>
|
||||||
|
where
|
||||||
|
T: std::marker::Sized + std::fmt::Debug,
|
||||||
|
U: $crate::errors::IntoError<T>,
|
||||||
|
{
|
||||||
|
val.into_error(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
into_error_impl($val, $msg.into())
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This macro unwraps a `Result` or `Option` and returns the value if it exists.
|
||||||
|
/// Should the value not exist, then the program will exit with exit code 1.
|
||||||
|
/// Optionally, a message can be passed to the function using standard `format!` syntax.
|
||||||
|
/// The result type has to implement `Debug` and `Sized`, and the source error type has to implement `Error`, `Send`, `Sync` and has to be `'static`.
|
||||||
|
/// See `winapps::unwrap_or_panic!` for a version that panics instead of exiting.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! unwrap_or_exit {
|
||||||
|
($expr:expr) => {{
|
||||||
|
$crate::into_error!($expr).unwrap_or_else(|e| e.exit())
|
||||||
|
}};
|
||||||
|
($expr:expr, $($fmt:tt)*) => {{
|
||||||
|
$crate::into_error!($expr, format!($($fmt)*)).unwrap_or_else(|e| e.exit())
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This macro unwraps a `Result` or `Option` and returns the value if it exists.
|
||||||
|
/// Should the value not exist, then the program will panic.
|
||||||
|
/// Optionally, a message can be passed to the function using standard `format!` syntax.
|
||||||
|
/// The result type has to implement `Debug` and `Sized`, and the error type has to implement `Error`, `Send`, `Sync` and has to be `'static`.
|
||||||
|
/// See `winapps::unwrap_or_exit!` for a version that exits instead of panicking.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! unwrap_or_panic {
|
||||||
|
($expr:expr) => {{
|
||||||
|
$crate::into_error!($expr).unwrap_or_else(|e| e.panic())
|
||||||
|
}};
|
||||||
|
($expr:expr, $($fmt:tt)*) => {{
|
||||||
|
$crate::into_error!($expr, format!($($fmt)*)).unwrap_or_else(|e| e.panic())
|
||||||
|
}};
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
pub mod freerdp_back {
|
pub mod freerdp_back {
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
use tracing::{info, warn};
|
||||||
|
|
||||||
use crate::{Config, RemoteClient};
|
use crate::{unwrap_or_exit, Config, RemoteClient};
|
||||||
|
|
||||||
pub struct Freerdp {}
|
pub struct Freerdp {}
|
||||||
|
|
||||||
@ -11,18 +12,21 @@ pub mod freerdp_back {
|
|||||||
xfreerdp.stdout(Stdio::null());
|
xfreerdp.stdout(Stdio::null());
|
||||||
xfreerdp.stderr(Stdio::null());
|
xfreerdp.stderr(Stdio::null());
|
||||||
xfreerdp.args(["-h"]);
|
xfreerdp.args(["-h"]);
|
||||||
xfreerdp
|
|
||||||
.spawn()
|
|
||||||
.expect("Freerdp execution failed! It needs to be installed!");
|
|
||||||
println!("Freerdp found!");
|
|
||||||
|
|
||||||
println!("All dependencies found!");
|
unwrap_or_exit!(
|
||||||
println!("Running explorer as test!");
|
xfreerdp.spawn(),
|
||||||
println!("Check yourself if it appears correctly!");
|
"Freerdp execution failed! It needs to be installed!",
|
||||||
|
);
|
||||||
|
|
||||||
|
info!("Freerdp found!");
|
||||||
|
|
||||||
|
info!("All dependencies found!");
|
||||||
|
info!("Running explorer as test!");
|
||||||
|
warn!("Check yourself if it appears correctly!");
|
||||||
|
|
||||||
self.run_app(config, Some(&"explorer.exe".to_string()));
|
self.run_app(config, Some(&"explorer.exe".to_string()));
|
||||||
|
|
||||||
println!("Test finished!");
|
info!("Test finished!");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_app(&self, config: Config, app: Option<&String>) {
|
fn run_app(&self, config: Config, app: Option<&String>) {
|
||||||
@ -56,7 +60,11 @@ pub mod freerdp_back {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xfreerdp.spawn().expect("Freerdp execution failed!");
|
|
||||||
|
unwrap_or_exit!(
|
||||||
|
xfreerdp.spawn(),
|
||||||
|
"Freerdp execution failed, check logs above!",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
pub mod errors;
|
||||||
|
pub mod freerdp;
|
||||||
pub mod quickemu;
|
pub mod quickemu;
|
||||||
|
|
||||||
|
use crate::errors::WinappsError;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use home::home_dir;
|
use home::home_dir;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -10,8 +13,7 @@ use std::{
|
|||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
use tracing::{info, warn};
|
||||||
pub mod freerdp;
|
|
||||||
|
|
||||||
pub trait RemoteClient {
|
pub trait RemoteClient {
|
||||||
fn check_depends(&self, config: Config);
|
fn check_depends(&self, config: Config);
|
||||||
@ -59,22 +61,24 @@ pub fn get_config_file(path: Option<&str>) -> PathBuf {
|
|||||||
let default = match env::var("XDG_CONFIG_HOME") {
|
let default = match env::var("XDG_CONFIG_HOME") {
|
||||||
Ok(dir) => PathBuf::from(dir).join("winapps"),
|
Ok(dir) => PathBuf::from(dir).join("winapps"),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Couldn't read XDG_CONFIG_HOME, falling back to ~/.config");
|
warn!("Couldn't read XDG_CONFIG_HOME, falling back to ~/.config");
|
||||||
home_dir()
|
unwrap_or_panic!(home_dir(), "Couldn't find the home directory").join(".config/winapps")
|
||||||
.expect("Could not find the home path!")
|
|
||||||
.join(".config/winapps")
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = Path::new(path.unwrap_or(default.to_str().unwrap()));
|
let path = Path::new(path.unwrap_or(unwrap_or_panic!(
|
||||||
|
default.to_str(),
|
||||||
|
"Couldn't convert path {:?} to string",
|
||||||
|
default
|
||||||
|
)));
|
||||||
|
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
println!("{:?} does not exist! Creating...", path);
|
info!("{:?} does not exist! Creating...", path);
|
||||||
fs::create_dir_all(path).expect("Failed to create directory");
|
fs::create_dir_all(path).expect("Failed to create directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !path.is_dir() {
|
if !path.is_dir() {
|
||||||
panic!("Config directory {:?} is not a directory!", path);
|
error!("Config directory {:?} is not a directory", path).panic();
|
||||||
}
|
}
|
||||||
|
|
||||||
path.join("config.toml")
|
path.join("config.toml")
|
||||||
@ -85,56 +89,74 @@ pub fn load_config(path: Option<&str>) -> Config {
|
|||||||
let config_path = get_config_file(path);
|
let config_path = get_config_file(path);
|
||||||
|
|
||||||
if !config_path.exists() {
|
if !config_path.exists() {
|
||||||
save_config(&config, path).expect("Failed to write default configuration");
|
unwrap_or_panic!(
|
||||||
|
save_config(&config, path),
|
||||||
|
"Failed to write default configuration"
|
||||||
|
);
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
let config_file = fs::read_to_string(config_path).expect("Failed to read configuration file");
|
let config_file = unwrap_or_panic!(
|
||||||
let config: Config =
|
fs::read_to_string(config_path),
|
||||||
toml::from_str(config_file.as_str()).expect("Failed to parse the configuration");
|
"Failed to read configuration file"
|
||||||
|
);
|
||||||
|
|
||||||
|
let config: Config = unwrap_or_panic!(
|
||||||
|
toml::from_str(config_file.as_str()),
|
||||||
|
"Failed to parse configuration file",
|
||||||
|
);
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_config(config: &Config, path: Option<&str>) -> std::io::Result<()> {
|
pub fn save_config(config: &Config, path: Option<&str>) -> Result<(), WinappsError> {
|
||||||
let config_path = get_config_file(path);
|
let config_path = get_config_file(path);
|
||||||
let serialized_config = toml::to_string(&config).expect("Failed to serialize configuration");
|
let serialized_config = unwrap_or_panic!(
|
||||||
|
toml::to_string(&config),
|
||||||
|
"Failed to serialize configuration"
|
||||||
|
);
|
||||||
|
|
||||||
let mut config_file = match config_path.exists() {
|
let mut config_file = match config_path.exists() {
|
||||||
true => File::open(&config_path).expect("Failed to open configuration file"),
|
true => unwrap_or_panic!(
|
||||||
false => File::create(&config_path).expect("Failed to create configuration file"),
|
File::open(&config_path),
|
||||||
|
"Failed to open configuration file"
|
||||||
|
),
|
||||||
|
false => unwrap_or_panic!(
|
||||||
|
File::create(&config_path),
|
||||||
|
"Failed to create configuration file"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(config_file, "{}", serialized_config)
|
if let Err(e) = write!(config_file, "{}", serialized_config) {
|
||||||
|
return Err(error_from!(e, "Failed to write configuration file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_data_dir() -> PathBuf {
|
pub fn get_data_dir() -> PathBuf {
|
||||||
let data_dir = match env::var("XDG_DATA_HOME") {
|
let path = match env::var("XDG_DATA_HOME") {
|
||||||
Ok(dir) => PathBuf::from(dir).join("winapps"),
|
Ok(dir) => PathBuf::from(dir).join("winapps"),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Couldn't read XDG_DATA_HOME, falling back to ~/.local/share");
|
warn!("Couldn't read XDG_DATA_HOME, falling back to ~/.local/share");
|
||||||
home_dir()
|
unwrap_or_panic!(home_dir(), "Couldn't find the home directory")
|
||||||
.expect("Could not find the home path!")
|
|
||||||
.join(".local/share/winapps")
|
.join(".local/share/winapps")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !data_dir.exists() {
|
if !path.exists() {
|
||||||
let dir = data_dir.clone();
|
let dir = path.clone();
|
||||||
println!(
|
info!(
|
||||||
"Data directory {:?} does not exist! Creating...",
|
"Data directory {:?} does not exist! Creating...",
|
||||||
dir.to_str()
|
dir.to_str()
|
||||||
);
|
);
|
||||||
fs::create_dir_all(dir).expect("Failed to create directory");
|
fs::create_dir_all(dir).expect("Failed to create directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !data_dir.is_dir() {
|
if !path.is_dir() {
|
||||||
panic!("Data directory {:?} is not a directory!", data_dir);
|
error!("Data directory {:?} is not a directory", path).panic();
|
||||||
}
|
}
|
||||||
|
|
||||||
data_dir
|
path
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(left: usize, right: usize) -> usize {
|
|
||||||
left + right
|
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,25 @@
|
|||||||
use crate::{get_data_dir, save_config, Config};
|
use crate::{get_data_dir, save_config, unwrap_or_exit, Config};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::process::exit;
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
pub fn create_vm(mut config: Config) {
|
pub fn create_vm(mut config: Config) {
|
||||||
let data_dir = get_data_dir();
|
let data_dir = get_data_dir();
|
||||||
|
|
||||||
let output = match Command::new("quickget")
|
let output = unwrap_or_exit!(
|
||||||
.current_dir(data_dir)
|
Command::new("quickget")
|
||||||
.arg("windows")
|
.current_dir(data_dir)
|
||||||
.arg("10")
|
.arg("windows")
|
||||||
.output()
|
.arg("10")
|
||||||
{
|
.output(),
|
||||||
Ok(o) => o,
|
"Failed to execute quickget: \n\
|
||||||
Err(e) => {
|
Please make sure quickget is installed and in your PATH"
|
||||||
println!("Failed to execute quickget: {}", e);
|
);
|
||||||
println!("Please make sure quickget is installed and in your PATH");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
config.vm.name = "windows-10-22H2".to_string();
|
config.vm.name = "windows-10-22H2".to_string();
|
||||||
config.vm.short_name = "windows-10".to_string();
|
config.vm.short_name = "windows-10".to_string();
|
||||||
|
|
||||||
save_config(&config, None).expect("Failed to save config, VM will not start properly");
|
unwrap_or_exit!(save_config(&config, None), "Failed to save config");
|
||||||
|
|
||||||
println!("{}", String::from_utf8_lossy(&output.stdout));
|
println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
}
|
}
|
||||||
@ -31,33 +27,25 @@ pub fn create_vm(mut config: Config) {
|
|||||||
pub fn start_vm(config: Config) {
|
pub fn start_vm(config: Config) {
|
||||||
let data_dir = get_data_dir();
|
let data_dir = get_data_dir();
|
||||||
|
|
||||||
let command = match Command::new("quickemu")
|
let command = unwrap_or_exit!(
|
||||||
.current_dir(data_dir)
|
Command::new("quickemu")
|
||||||
.args([
|
.current_dir(data_dir)
|
||||||
"--ignore-msrs-always",
|
.args([
|
||||||
"--vm",
|
"--ignore-msrs-always",
|
||||||
&format!("{}.conf", config.vm.name),
|
"--vm",
|
||||||
"--display",
|
&format!("{}.conf", config.vm.name),
|
||||||
"none",
|
"--display",
|
||||||
])
|
"none",
|
||||||
.spawn()
|
])
|
||||||
{
|
.spawn(),
|
||||||
Ok(c) => c,
|
"Failed to execute quickemu: \n\
|
||||||
Err(e) => {
|
Please make sure quickemu is installed and in your PATH"
|
||||||
println!("Failed to execute quickemu: {}", e);
|
);
|
||||||
println!("Please make sure quickemu is installed and in your PATH");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = match command.wait_with_output() {
|
let output = unwrap_or_exit!(
|
||||||
Ok(o) => o,
|
command.wait_with_output(),
|
||||||
Err(e) => {
|
"Failed to gather output from quickemu / stdout"
|
||||||
println!("Failed to gather output from quickemu: {}", e);
|
);
|
||||||
println!("Please make sure quickemu is installed and in your PATH");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("{}", String::from_utf8_lossy(&output.stdout));
|
println!("{}", String::from_utf8_lossy(&output.stdout));
|
||||||
}
|
}
|
||||||
@ -65,25 +53,17 @@ pub fn start_vm(config: Config) {
|
|||||||
pub fn kill_vm(config: Config) {
|
pub fn kill_vm(config: Config) {
|
||||||
let data_dir = get_data_dir();
|
let data_dir = get_data_dir();
|
||||||
|
|
||||||
match fs::read_to_string(
|
let pid = unwrap_or_exit!(
|
||||||
data_dir.join(format!("{}/{}.pid", config.vm.short_name, config.vm.name)),
|
fs::read_to_string(
|
||||||
) {
|
data_dir.join(format!("{}/{}.pid", config.vm.short_name, config.vm.name))
|
||||||
Ok(pid) => {
|
),
|
||||||
let pid = pid.trim();
|
"Failed to read PID file, is the VM running and the config correct?"
|
||||||
|
);
|
||||||
|
|
||||||
println!("Killing VM with PID {}", pid);
|
info!("Killing VM with PID {}", pid);
|
||||||
|
|
||||||
match Command::new("kill").arg(pid).spawn() {
|
unwrap_or_exit!(
|
||||||
Ok(_) => (),
|
Command::new("kill").arg(pid.trim()).spawn(),
|
||||||
Err(e) => {
|
"Failed to kill VM (execute kill)"
|
||||||
println!("Failed to kill VM: {}", e);
|
);
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to read PID file: {}", e);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user