Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creating and running the VM via quickemu #26

Merged
merged 9 commits into from
Oct 9, 2023
110 changes: 110 additions & 0 deletions scripts/install_quickemu
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#!/usr/bin/env python3

import platform
import os
import shutil
import sys


def _(c: str):
"""Execute the command `c` and print it"""
print("> " + c)
os.system(c)


def clone_repo():
if os.path.exists(os.path.expanduser("~/.local/share/quickemu")):
print("📦 quickemu is already installed. Updating...")
update_quickemu()
return

print("📦 Cloning quickemu...")

_("git clone --filter=blob:none https://github.com/quickemu-project/quickemu ~/.local/share/quickemu")
_("mkdir -p ~/.local/bin")
_("ln -s ~/.local/share/quickemu/quickemu ~/.local/bin/quickemu")
_("ln -s ~/.local/share/quickemu/macrecovery ~/.local/bin/macrecovery")
_("ln -s ~/.local/share/quickemu/quickget ~/.local/bin/quickget")
_("ln -s ~/.local/share/quickemu/windowskey ~/.local/bin/windowskey")

print("Installation complete.")
print("⚠️ Make sure ~/.local/bin is in your PATH.")


def update_quickemu():
print("📦 Updating quickemu...")

_("cd ~/.local/share/quickemu")
_("git pull")

print("Update complete.")
print("⚠️ Make sure ~/.local/bin is in your PATH.")


def install_fedora():
oskardotglobal marked this conversation as resolved.
Show resolved Hide resolved
print("📦 Installing dependencies...")

_("sudo dnf install qemu bash coreutils edk2-tools grep jq lsb procps python3 genisoimage usbutils"
+ " util-linux sed spice-gtk-tools swtpm wget xdg-user-dirs xrandr unzip socat -y")

clone_repo()

sys.exit(0)


def install_deb():
print("📦 Installing dependencies...")

_("sudo apt update")
_("sudo apt install qemu bash coreutils ovmf grep jq lsb-base procps python3 genisoimage usbutils"
+ " util-linux sed spice-client-gtk libtss2-tcti-swtpm0 wget xdg-user-dirs zsync unzip socat -y")

clone_repo()

sys.exit(0)


def install_ubuntu():
print("⚠️ Adding ppa...")

_("sudo apt-add-repository ppa:flexiondotorg/quickemu")
_("sudo apt update")
_("sudo apt install quickemu -y")

sys.exit(0)


if __name__ == "__main__":
print("⚠️ This script requires elevated privileges (sudo). You will be asked for your password.")

os_release = platform.freedesktop_os_release()

distro_id = os_release.get("ID_LIKE")
distro_id_like = os_release.get("ID")

if not distro_id and not distro_id_like:
print("❌ Couldn't fetch distro, is os-release installed?")

if distro_id == "ubuntu" \
or distro_id_like == "ubuntu":
install_ubuntu()
elif distro_id == "debian" \
or distro_id_like == "debian" \
or shutil.which("apt"):
install_deb()
elif distro_id == "fedora" \
or distro_id_like == "fedora" \
or shutil.which("dnf"):
install_fedora()
else:
if distro_id:
print("❌ Unsupported distro: ", distro_id)
elif distro_id_like:
print("❌ Unsupported distro: ", distro_id_like)
else:
print("❌ Unsupported distro. Couldn't fetch data from os-release and couldn't find dnf or apt on PATH.")

sys.exit(1)

print("❌ Unsupported platform.")
sys.exit(1)
46 changes: 40 additions & 6 deletions winapps-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use clap::Command;
use winapps::RemoteClient;
use winapps::freerdp::freerdp_back::Freerdp;
use winapps::quickemu::{create_vm, kill_vm, start_vm};
use winapps::RemoteClient;

fn cli() -> Command {
Command::new("winapps-cli")
Expand All @@ -10,27 +11,60 @@ fn cli() -> Command {
.allow_external_subcommands(true)
.subcommand(Command::new("check").about("Checks remote connection"))
.subcommand(Command::new("connect").about("Connects to remote"))
.subcommand(
Command::new("vm")
.about("Manage a windows 10 vm using quickemu")
.subcommand_required(true)
.arg_required_else_help(true)
.allow_external_subcommands(true)
.subcommand(Command::new("create").about("Create a windows 10 vm using quickget"))
.subcommand(Command::new("start").about("Start the vm"))
.subcommand(Command::new("kill").about("Kill the running VM")),
)
}

fn main() {
let cli = cli();
let matches = cli.clone().get_matches();

let client: &dyn RemoteClient = &Freerdp{};
let config = winapps::load_config(None);
oskardotglobal marked this conversation as resolved.
Show resolved Hide resolved
let client: &dyn RemoteClient = &Freerdp {};

match matches.subcommand() {
Some(("check", _)) => {
println!("Checking remote connection");

let config = winapps::load_config(None);
client.check_depends(config);
}
Some(("connect", _)) => {
println!("Connecting to remote");

let config = winapps::load_config(None);
client.run_app(config, "explorer");
}

Some(("vm", command)) => {
match command.subcommand() {
Some(("create", _)) => {
println!("Creating windows 10 vm..");
create_vm(config);
}
Some(("start", _)) => {
println!("Starting vm..");
start_vm(config);
}

Some(("kill", _)) => {
println!("Killing vm..");
kill_vm(config);
}

Some((_, _)) => {
cli.about("Command not found, try existing ones!")
.print_help()
.expect("Couldn't print help");
}
_ => unreachable!(),
};
}

Some((_, _)) => {
cli.about("Command not found, try existing ones!")
.print_help()
Expand Down
1 change: 1 addition & 0 deletions winapps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ edition = "2021"

[dependencies]
derive-new = "0.5.9"
home = "0.5.5"
serde = { version = "1.0.171", features = ["derive"] }
toml = "0.7.6"
11 changes: 6 additions & 5 deletions winapps/src/freerdp.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
pub mod freerdp_back {
use std::process::{Command, Stdio};

use crate::{RemoteClient, Config};
use crate::{Config, RemoteClient};

pub struct Freerdp {}

impl RemoteClient for Freerdp {
fn check_depends(&self, _config: Config) {

let mut xfreerdp = Command::new("xfreerdp");
xfreerdp.stdout(Stdio::null());
xfreerdp.args(["-h"]);
xfreerdp.spawn().expect("Freerdp execution failed! It needs to be installed!");
xfreerdp
.spawn()
.expect("Freerdp execution failed! It needs to be installed!");
println!("Freerdp found!");

println!("Checks success!");
}

fn run_app(&self, config: Config, _app: &str) {
fn run_app(&self, _config: Config, _app: &str) {
todo!()
}
}
Expand Down
93 changes: 79 additions & 14 deletions winapps/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
pub mod quickemu;

use derive_new::new;
use home::home_dir;
use serde::{Deserialize, Serialize};
use std::io::Write;
use std::path::PathBuf;
use std::{
env,
fs::{self, File},
Expand All @@ -21,6 +25,16 @@ pub struct Config {
host: HostConfig,
#[new(value = "RemoteConfig::new()")]
rdp: RemoteConfig,
#[new(value = "VmConfig::new()")]
vm: VmConfig,
}

#[derive(new, Debug, Deserialize, Serialize)]
pub struct VmConfig {
#[new(value = "\"windows-10\".to_string()")]
short_name: String,
#[new(value = "\"windows-10-22H2\".to_string()")]
name: String,
}

#[derive(new, Debug, Deserialize, Serialize)]
Expand All @@ -41,35 +55,86 @@ pub struct RemoteConfig {
password: String,
}

pub fn load_config(path: Option<&str>) -> Config {
let config = env::var("XDG_CONFIG_HOME").expect("Could not find the home path!");
let default = &format!("{}{}", config, "/winapps/");
let path = Path::new(path.unwrap_or(default));
let config = Config::new();
pub fn get_config_file(path: Option<&str>) -> PathBuf {
let default = match env::var("XDG_CONFIG_HOME") {
Ok(dir) => PathBuf::from(dir).join("winapps"),
Err(_) => {
println!("Couldn't read XDG_CONFIG_HOME, falling back to ~/.config");
home_dir()
.expect("Could not find the home path!")
.join(".config/winapps")
}
};

let path = Path::new(path.unwrap_or(default.to_str().unwrap()));

if !path.exists() {
println!("{:?} does not exist! Creating...", path.to_str());
println!("{:?} does not exist! Creating...", path);
fs::create_dir_all(path).expect("Failed to create directory");
}

let config_file = path.join("config.toml");
if !path.is_dir() {
panic!("Config directory {:?} is not a directory!", path);
}

path.join("config.toml")
}

if !config_file.exists() {
let mut config_file =
File::create(&config_file).expect("Failed to create configuration file");
pub fn load_config(path: Option<&str>) -> Config {
let config = Config::new();
let config_path = get_config_file(path);

let gen_config =
toml::to_string(&config).expect("Failed to generate default configuration");
write!(config_file, "{}", gen_config).expect("Failed to write configuration file");
if !config_path.exists() {
save_config(&config, path).expect("Failed to write default configuration");
return config;
}

let config_file = fs::read_to_string(config_file).expect("Failed to read configuration file");
let config_file = fs::read_to_string(config_path).expect("Failed to read configuration file");
let config: Config =
toml::from_str(config_file.as_str()).expect("Failed to parse the configuration");

config
}

pub fn save_config(config: &Config, path: Option<&str>) -> std::io::Result<()> {
let config_path = get_config_file(path);
let serialized_config = toml::to_string(&config).expect("Failed to serialize configuration");

let mut config_file = match config_path.exists() {
true => File::open(&config_path).expect("Failed to open configuration file"),
false => File::create(&config_path).expect("Failed to create configuration file"),
};

write!(config_file, "{}", serialized_config)
}

pub fn get_data_dir() -> PathBuf {
let data_dir = match env::var("XDG_DATA_HOME") {
Ok(dir) => PathBuf::from(dir).join("winapps"),
Err(_) => {
println!("Couldn't read XDG_DATA_HOME, falling back to ~/.local/share");
home_dir()
.expect("Could not find the home path!")
.join(".local/share/winapps")
}
};

if !data_dir.exists() {
let dir = data_dir.clone();
println!(
"Data directory {:?} does not exist! Creating...",
dir.to_str()
);
fs::create_dir_all(dir).expect("Failed to create directory");
}

if !data_dir.is_dir() {
panic!("Data directory {:?} is not a directory!", data_dir);
}

data_dir
}

pub fn add(left: usize, right: usize) -> usize {
left + right
}
Loading