diff --git a/scripts/install_quickemu.py b/scripts/install_quickemu.py deleted file mode 100644 index efaba950..00000000 --- a/scripts/install_quickemu.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/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(): - 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) diff --git a/winapps-cli/Cargo.toml b/winapps-cli/Cargo.toml index 6837cf7b..bcb76f97 100644 --- a/winapps-cli/Cargo.toml +++ b/winapps-cli/Cargo.toml @@ -7,5 +7,8 @@ default-run = "winapps-cli" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = "4.3.11" +clap = "4.3" +time = "0.3" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["time"] } winapps = { path = "../winapps" } diff --git a/winapps-cli/src/main.rs b/winapps-cli/src/main.rs index 3fb78ee0..7840ff90 100644 --- a/winapps-cli/src/main.rs +++ b/winapps-cli/src/main.rs @@ -1,6 +1,6 @@ use clap::{arg, Command}; +use tracing::info; use winapps::freerdp::freerdp_back::Freerdp; -use winapps::quickemu::{create_vm, kill_vm, start_vm}; use winapps::{unwrap_or_panic, RemoteClient}; fn cli() -> Command { @@ -16,19 +16,15 @@ fn cli() -> Command { .about("Connects to app on remote") .arg(arg!( "App to open")), ) - .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() { + tracing_subscriber::fmt() + // .with_timer(tracing_subscriber::fmt::time::uptime()) + .without_time() + .with_target(false) + .init(); + let cli = cli(); let matches = cli.clone().get_matches(); @@ -37,48 +33,20 @@ fn main() { match matches.subcommand() { Some(("check", _)) => { - println!("Checking remote connection"); + info!("Checking remote connection"); client.check_depends(config); } Some(("connect", _)) => { - println!("Connecting to remote"); + info!("Connecting to remote"); client.run_app(config, None); } Some(("run", sub_matches)) => { - println!("Connecting to app on remote"); + info!("Connecting to app on remote"); client.run_app(config, sub_matches.get_one::("APP")); } - - 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((_, _)) => { - unwrap_or_panic!( - cli.about("Command not found, try existing ones!") - .print_help(), - "Couldn't print help" - ); - } - _ => unreachable!(), - }; - } - Some((_, _)) => { unwrap_or_panic!( cli.about("Command not found, try existing ones!") diff --git a/winapps/Cargo.toml b/winapps/Cargo.toml index 8d6d0a55..a712dbe7 100644 --- a/winapps/Cargo.toml +++ b/winapps/Cargo.toml @@ -6,10 +6,18 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0.75" -derive-new = "0.5.9" -home = "0.5.5" -serde = { version = "1.0.171", features = ["derive"] } -thiserror = "1.0.49" -toml = "0.8.2" -tracing = "0.1.37" +anyhow = "1.0" +sevenz-rust={version="0.2", features=["zstd"]} +derive-new = "0.5" +home = "0.5" +indicatif = { version="0.17", features = ["tokio"] } +reqwest = { version = "0.12", features = ["stream"] } +serde = { version = "1.0", features = ["derive"] } +tempfile = "3.11" +thiserror = "1.0" +tokio = { version = "1.37", features = ["full"] } +tokio-stream = "0.1" +tokio-util = {version="0.7", features=["io"]} +toml = "0.8" +tracing = "0.1" +tracing-subscriber = "0.3" diff --git a/winapps/src/freerdp.rs b/winapps/src/freerdp.rs index b88936aa..4865f684 100644 --- a/winapps/src/freerdp.rs +++ b/winapps/src/freerdp.rs @@ -1,21 +1,113 @@ pub mod freerdp_back { + use crate::{get_data_dir, unwrap_or_exit, Config, RemoteClient}; + use indicatif::ProgressBar; + use std::cmp::min; + use std::fs::File; use std::process::{Command, Stdio}; + use tokio_stream::StreamExt; use tracing::{info, warn}; - use crate::{unwrap_or_exit, Config, RemoteClient}; - pub struct Freerdp {} + impl Freerdp { + fn get_freerdp() -> Command { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(Freerdp::install_freerdp()); + Command::new(get_data_dir().join("usr/bin/xfreerdp")) + } + + async fn install_freerdp() { + if get_data_dir().join("usr/bin/xfreerdp").exists() { + return; + } + + let freerdp_file = "freerdp-3.6.3-1.fc41.x86_64.rpm"; + + let bar = ProgressBar::new(1); + + bar.set_style(indicatif::ProgressStyle::with_template( + "{spinner:.green} [{elapsed}] {wide_bar:.cyan/blue} {bytes}/{total_bytes} {bytes_per_sec} {msg} ({eta})", + ).unwrap().progress_chars("#>-")); + bar.set_message(format!("Starting {}", freerdp_file)); + + bar.tick(); + + let response = reqwest::get( + "https://kojipkgs.fedoraproject.org/packages/freerdp/3.6.3/1.fc41/x86_64/" + .to_owned() + + freerdp_file, + ) + .await + .unwrap(); + + let total_size = response.content_length().unwrap_or(0); + + bar.set_length(total_size); + bar.set_message(format!("Downloading {}", freerdp_file)); + + let mut downloaded: u64 = 0; + + let mut stream = response.bytes_stream().map(|result| { + result + .inspect(|result| { + let new = min(downloaded + (result.len() as u64), total_size); + downloaded = new; + bar.set_position(new); + }) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err)) + }); + + let mut file = tokio::fs::File::create(get_data_dir().join(freerdp_file)) + .await + .unwrap(); + + while let Some(item) = stream.next().await { + tokio::io::copy(&mut item.unwrap().as_ref(), &mut file) + .await + .unwrap(); + } + + let mut rpm2cpio = Command::new("rpm2cpio"); + rpm2cpio.stdin(Stdio::from( + File::open(get_data_dir().join(freerdp_file)).unwrap(), + )); + rpm2cpio.stdout(Stdio::piped()); + rpm2cpio.stderr(Stdio::null()); + rpm2cpio.current_dir(get_data_dir()); + + let rpm2cpio = unwrap_or_exit!( + rpm2cpio.spawn(), + "rpm2cpio execution failed! Check if rpm2cpio (rpm) is installed!", + ); + + let mut cpio = Command::new("cpio"); + cpio.stdin(Stdio::from(rpm2cpio.stdout.unwrap())); + cpio.stdout(Stdio::null()); + cpio.stderr(Stdio::null()); + cpio.current_dir(get_data_dir()); + cpio.arg("-idmv"); + + unwrap_or_exit!( + cpio.spawn(), + "cpio execution failed! Check if cpio is installed!", + ); + } + } + impl RemoteClient for Freerdp { fn check_depends(&self, config: Config) { - let mut xfreerdp = Command::new("xfreerdp"); + let mut xfreerdp = Freerdp::get_freerdp(); xfreerdp.stdout(Stdio::null()); xfreerdp.stderr(Stdio::null()); xfreerdp.args(["-h"]); unwrap_or_exit!( xfreerdp.spawn(), - "Freerdp execution failed! It needs to be installed!", + "Freerdp execution failed! Try to delete {}, to force a reinstall.", + get_data_dir().join("usr").display(), ); info!("Freerdp found!"); @@ -30,7 +122,7 @@ pub mod freerdp_back { } fn run_app(&self, config: Config, app: Option<&String>) { - let mut xfreerdp = Command::new("xfreerdp"); + let mut xfreerdp = Freerdp::get_freerdp(); xfreerdp.stdout(Stdio::null()); xfreerdp.stderr(Stdio::null()); match app { diff --git a/winapps/src/lib.rs b/winapps/src/lib.rs index 148f331e..1d2afda0 100644 --- a/winapps/src/lib.rs +++ b/winapps/src/lib.rs @@ -1,6 +1,5 @@ pub mod errors; pub mod freerdp; -pub mod quickemu; use crate::errors::WinappsError; use derive_new::new; @@ -27,16 +26,6 @@ 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)] @@ -49,11 +38,11 @@ pub struct HostConfig { pub struct RemoteConfig { #[new(value = "\"127.0.0.1\".to_string()")] host: String, - #[new(value = "\"WORKGROUP\".to_string()")] + #[new(value = "\"\".to_string()")] domain: String, - #[new(value = "\"Quickemu\".to_string()")] + #[new(value = "\"Docker\".to_string()")] username: String, - #[new(value = "\"quickemu\".to_string()")] + #[new(value = "\"\".to_string()")] password: String, } diff --git a/winapps/src/quickemu.rs b/winapps/src/quickemu.rs deleted file mode 100644 index d2cb54cd..00000000 --- a/winapps/src/quickemu.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::{get_data_dir, save_config, unwrap_or_exit, Config}; -use std::fs; -use std::process::Command; -use tracing::info; - -pub fn create_vm(mut config: Config) { - let data_dir = get_data_dir(); - - let output = unwrap_or_exit!( - Command::new("quickget") - .current_dir(data_dir) - .arg("windows") - .arg("10") - .output(), - "Failed to execute quickget: \n\ - Please make sure quickget is installed and in your PATH" - ); - - config.vm.name = "windows-10-22H2".to_string(); - config.vm.short_name = "windows-10".to_string(); - - unwrap_or_exit!(save_config(&config, None), "Failed to save config"); - - println!("{}", String::from_utf8_lossy(&output.stdout)); -} - -pub fn start_vm(config: Config) { - let data_dir = get_data_dir(); - - let command = unwrap_or_exit!( - Command::new("quickemu") - .current_dir(data_dir) - .args([ - "--ignore-msrs-always", - "--vm", - &format!("{}.conf", config.vm.name), - "--display", - "none", - ]) - .spawn(), - "Failed to execute quickemu: \n\ - Please make sure quickemu is installed and in your PATH" - ); - - let output = unwrap_or_exit!( - command.wait_with_output(), - "Failed to gather output from quickemu / stdout" - ); - - println!("{}", String::from_utf8_lossy(&output.stdout)); -} - -pub fn kill_vm(config: Config) { - let data_dir = get_data_dir(); - - let pid = unwrap_or_exit!( - fs::read_to_string( - data_dir.join(format!("{}/{}.pid", config.vm.short_name, config.vm.name)) - ), - "Failed to read PID file, is the VM running and the config correct?" - ); - - info!("Killing VM with PID {}", pid); - - unwrap_or_exit!( - Command::new("kill").arg(pid.trim()).spawn(), - "Failed to kill VM (execute kill)" - ); -}