From 03284e6ad7f5fb54a8c80d30ca97bb3ebfc71622 Mon Sep 17 00:00:00 2001 From: qaidvoid <12017109+QaidVoid@users.noreply.github.com> Date: Fri, 30 Oct 2020 09:16:34 +0545 Subject: [PATCH] Most stuff should work --- Makefile | 20 ++++++++++++ README.md | 6 +++- include/iommu.h | 11 +++++++ include/libvirt.h | 44 +++++++++++++++++++++++++ include/shell.h | 4 +++ include/sysinfo.h | 9 +++++ include/tools.h | 10 ++++++ src/iommu.cpp | 83 +++++++++++++++++++++++++++++++++++++++++++++++ src/libvirt.cpp | 71 ++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 25 ++++++++++++++ src/shell.cpp | 16 +++++++++ src/sysinfo.cpp | 41 +++++++++++++++++++++++ src/tools.cpp | 58 +++++++++++++++++++++++++++++++++ 13 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 Makefile create mode 100644 include/iommu.h create mode 100644 include/libvirt.h create mode 100644 include/shell.h create mode 100644 include/sysinfo.h create mode 100644 include/tools.h create mode 100644 src/iommu.cpp create mode 100644 src/libvirt.cpp create mode 100644 src/main.cpp create mode 100644 src/shell.cpp create mode 100644 src/sysinfo.cpp create mode 100644 src/tools.cpp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..18dade2 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +CXX := g++ +CXX_FLAGS := -Wall -Wextra -std=c++17 + +BIN := bin +SRC := src +INCLUDE := include + +EXECUTABLE := passthrough + +all: $(EXECUTABLE) + +run: clean all + clear + ./$(EXECUTABLE) + +$(EXECUTABLE): $(SRC)/*.cpp + $(CXX) $(CXX_FLAGS) -I$(INCLUDE) $^ -o $@ + +clean: + -rm passthrough \ No newline at end of file diff --git a/README.md b/README.md index 3a58be0..a787c2f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ ## Automated Setup -#### ** TODO : NOT SURE ** +#### Install required tools +```sh +sudo pacman -S qemu libvirt edk2-ovmf virt-manager dnsmasq ebtables iptables +``` + ## Manual Setup Note: Replace win10 with your virtual machine's name on libvirt hooks and virsh commands. diff --git a/include/iommu.h b/include/iommu.h new file mode 100644 index 0000000..3739c54 --- /dev/null +++ b/include/iommu.h @@ -0,0 +1,11 @@ +#include +#include +#include +#include +#include +#include + +void check_vt_support(); +bool check_iommu(); +void enable_iommu(); +void configure_iommu(); \ No newline at end of file diff --git a/include/libvirt.h b/include/libvirt.h new file mode 100644 index 0000000..da5d329 --- /dev/null +++ b/include/libvirt.h @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include + +void libvirt_hook(); +void hook_begin(); +void hook_release(); +std::string begin_script(); +std::string end_script(); + +inline const char *hooks_path = "/etc/libvirt/hooks"; +inline const char *qemu_hook = "/etc/libvirt/hooks/qemu"; +inline const char *vm_path = "/etc/libvirt/hooks/qemu.d/win10"; + +inline const char *vm_prepare = "/etc/libvirt/hooks/qemu.d/win10/prepare"; +inline const char *vm_begin = "/etc/libvirt/hooks/qemu.d/win10/prepare/begin"; +inline const char *begin_sh = "/etc/libvirt/hooks/qemu.d/win10/prepare/begin/begin.sh"; + +inline const char *vm_release = "/etc/libvirt/hooks/qemu.d/win10/release"; +inline const char *vm_end = "/etc/libvirt/hooks/qemu.d/win10/release/end"; +inline const char *end_sh = "/etc/libvirt/hooks/qemu.d/win10/release/end/end.sh"; + +inline const std::string qemu_script = R"#(#!/bin/bash + +GUEST_NAME="$1" +HOOK_NAME="$2" +STATE_NAME="$3" +MISC="${@:4}" + +BASEDIR="$(dirname $0)" + +HOOKPATH="$BASEDIR/qemu.d/$GUEST_NAME/$HOOK_NAME/$STATE_NAME" +set -e # If a script exits with an error, we should as well. + +if [ -f "$HOOKPATH" ]; then + eval \""$HOOKPATH"\" "$@" +elif [ -d "$HOOKPATH" ]; then + while read file; do + eval \""$file"\" "$@" + done <<< "$(find -L "$HOOKPATH" -maxdepth 1 -type f -executable -print;)" +fi +)#"; \ No newline at end of file diff --git a/include/shell.h b/include/shell.h new file mode 100644 index 0000000..11de708 --- /dev/null +++ b/include/shell.h @@ -0,0 +1,4 @@ +#include +#include + +std::string shell_cmd(char const *cmd); diff --git a/include/sysinfo.h b/include/sysinfo.h new file mode 100644 index 0000000..963e56a --- /dev/null +++ b/include/sysinfo.h @@ -0,0 +1,9 @@ +#include +#include +#include +#include + +std::string get_cpu_vendor(); +std::string get_gpu_vendor(); +std::string get_vga_slot(); +std::string get_audio_slot(); \ No newline at end of file diff --git a/include/tools.h b/include/tools.h new file mode 100644 index 0000000..7e25963 --- /dev/null +++ b/include/tools.h @@ -0,0 +1,10 @@ +#include +#include +#include +#include +#include + +void install_tools(); +void enable_services(); +void user_mod(); +std::string get_init(); \ No newline at end of file diff --git a/src/iommu.cpp b/src/iommu.cpp new file mode 100644 index 0000000..0cf3f32 --- /dev/null +++ b/src/iommu.cpp @@ -0,0 +1,83 @@ +#include + +void configure_iommu() +{ + check_vt_support(); + enable_iommu(); +} + +void check_vt_support() +{ + std::cout << "\033[1;36mChecking system for virtualisation support \033[0m"; + std::string res = shell_cmd("lscpu | grep Virtualization"); + + if (res.empty()) + throw std::runtime_error("\n\033[1;31mVirtualisation is not enabled on your system!\033[0m"); + std::cout << "\033[1;32m\u2714\033[0m" << std::endl; +} + +bool check_iommu() +{ + std::cout << "\033[1;36mIs IOMMU enabled? \033[0m"; + std::string res = shell_cmd("(dmesg | grep 'IOMMU enabled') 2>&1"); + + if (res.find("IOMMU enabled") != std::string::npos) + std::cout << "\033[1;32m\u2714\033[0m" << std::endl; + else + { + std::cout << "\033[1;31m\u2718\033[0m" << std::endl; + return false; + } + return true; +} + +void enable_iommu() +{ + if (!check_iommu()) + { + std::ifstream ifs("/etc/default/grub"); + std::ofstream ofs("./tmp/grub"); + + if (ifs.fail()) + throw std::runtime_error("\033[1;31mGrubloader doesn't exist or I can't find it on your system.\033[0m"); + if (ofs.fail()) + throw std::runtime_error("\033[1;31mFailed to create temporary file.\033[0m"); + + std::string line; + while (getline(ifs, line)) + { + std::istringstream iss(line); + std::string key; + if (std::getline(iss, key, '=') && key.rfind("#", 0) != 0) + { + std::string value; + std::getline(iss, value); + if (key == "GRUB_CMDLINE_LINUX_DEFAULT") + { + if (get_cpu_vendor() == "intel") + { + if (value.find("intel_iommu=on") == std::string::npos) + value.insert(*value.cbegin(), " intel_iommu=on"); + } + else if (value.find("amd_iommu=on") == std::string::npos) + value.insert(*value.cbegin(), " amd_iommu=on"); + if (value.find("iommu=pt") == std::string::npos) + value.insert(*value.cbegin(), " iommu=pt"); + } + ofs << key << "=" << value << '\n'; + } + else + ofs << line << '\n'; + } + ifs.close(); + ofs.close(); + + std::string res = shell_cmd("(mv -f ./tmp/grub /etc/default/grub) 2>&1"); + system("grub-mkconfig -o /boot/grub/grub.cfg"); + + if (res.empty()) + throw std::runtime_error("\033[1;32mIOMMU enabled. Please reboot your system to continue.\033[0m"); + else + throw std::runtime_error("\033[1;31mFailed to enable IOMMU.\n" + res + "\033[0m"); + } +} \ No newline at end of file diff --git a/src/libvirt.cpp b/src/libvirt.cpp new file mode 100644 index 0000000..38d400c --- /dev/null +++ b/src/libvirt.cpp @@ -0,0 +1,71 @@ +#include + +void libvirt_hook() +{ + struct stat buf; + if (stat(hooks_path, &buf) != 0) + if (mkdir(hooks_path, 0755) == -1) + throw std::runtime_error(strerror(errno)); + if (stat(qemu_hook, &buf) != 0) + { + std::ofstream file(qemu_hook); + file << qemu_script; + file.close(); + if (chmod(qemu_hook, 0755) == -1) + throw std::runtime_error(strerror(errno)); + } +} + +void hook_begin() +{ + struct stat buf; + if (stat(vm_path, &buf) != 0) + if (mkdir(vm_path, 0755) == -1) + throw std::runtime_error(strerror(errno)); + if (stat(vm_prepare, &buf) != 0) + if (mkdir(vm_prepare, 0755) == -1) + throw std::runtime_error(strerror(errno)); + if (stat(vm_begin, &buf) != 0) + if (mkdir(vm_begin, 0755) == -1) + throw std::runtime_error(strerror(errno)); + if (stat(begin_sh, &buf) != 0) + { + std::ofstream file(begin_sh); + file << "#BEGIN"; + file.close(); + if (chmod(begin_sh, 0755) == -1) + throw std::runtime_error(strerror(errno)); + } +} + +void hook_release() +{ + struct stat buf; + if (stat(vm_path, &buf) != 0) + if (mkdir(vm_path, 0755) == -1) + throw std::runtime_error(strerror(errno)); + if (stat(vm_prepare, &buf) != 0) + if (mkdir(vm_prepare, 0755) == -1) + throw std::runtime_error(strerror(errno)); + if (stat(vm_begin, &buf) != 0) + if (mkdir(vm_begin, 0755) == -1) + throw std::runtime_error(strerror(errno)); + if (stat(end_sh, &buf) != 0) + { + std::ofstream file(end_sh); + file << "#END"; + file.close(); + if (chmod(end_sh, 0755) == -1) + throw std::runtime_error(strerror(errno)); + } +} + +std::string begin_script() +{ + return ""; +} + +std::string end_script() +{ + return ""; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e627a16 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +int main() +{ + try + { + char cnf; + configure_iommu(); + + std::cout << "Do you want to install required packages automatically? (y/n) "; + std::cin >> cnf; + if (tolower(cnf) == 'y') + install_tools(); + enable_services(); + user_mod(); + libvirt_hook(); + } + catch (const std::runtime_error &err) + { + std::cerr << err.what() << std::endl; + return -1; + } +} \ No newline at end of file diff --git a/src/shell.cpp b/src/shell.cpp new file mode 100644 index 0000000..64e67ef --- /dev/null +++ b/src/shell.cpp @@ -0,0 +1,16 @@ +#include + +std::string shell_cmd(char const *cmd) +{ + std::array buffer; + std::string resp; + std::unique_ptr pipe(popen(cmd, "r"), pclose); + if (!pipe) + throw std::runtime_error("\n\033[1;31mThat was unexpected!\033[0m"); + + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) + { + resp += buffer.data(); + } + return resp; +} \ No newline at end of file diff --git a/src/sysinfo.cpp b/src/sysinfo.cpp new file mode 100644 index 0000000..13b76c8 --- /dev/null +++ b/src/sysinfo.cpp @@ -0,0 +1,41 @@ +#include + +std::string get_cpu_vendor() +{ + std::string cpu = "intel"; + std::string res = shell_cmd("lspci | grep -i amd"); + + if (!res.empty()) + cpu = "amd"; + return cpu; +} + +std::string get_gpu_vendor() +{ + std::string gpu = "amd"; + std::string res = shell_cmd("lspci | grep -i nvidia"); + + if (!res.empty()) + gpu = "nvidia"; + return gpu; +} + +std::string get_vga_slot() +{ + std::string c = "lspci | grep -i vga.*" + get_gpu_vendor(); + std::string res = shell_cmd((char *)&c); + + if (res.empty()) + throw std::runtime_error("\n\033[1;31mCouldn't fetch VGA PCI slot!\033[0m"); + return "0000:" + res.substr(0, res.find(" ")); +} + +std::string get_audio_slot() +{ + std::string c = "lspci | grep -i audio.*" + get_gpu_vendor(); + std::string res = shell_cmd((char *)&c); + + if (res.empty()) + throw std::runtime_error("\n\033[1;31mCouldn't fetch Audio PCI slot!\033[0m"); + return "0000:" + res.substr(0, res.find(" ")); +} \ No newline at end of file diff --git a/src/tools.cpp b/src/tools.cpp new file mode 100644 index 0000000..164e799 --- /dev/null +++ b/src/tools.cpp @@ -0,0 +1,58 @@ +#include + +void install_tools() +{ + struct stat buf; + if (stat("/etc/arch-release", &buf) == 0) + system("pacman -S qemu libvirt edk2-ovmf virt-manager dnsmasq ebtables"); + else if (stat("/etc/gentoo-release", &buf) == 0) + system("emerge -a qemu libvirt virt-manager dnsmasq ebtables"); + else if (stat("/etc/debian_version", &buf) == 0) + system("apt install qemu-kvm libvirt-clients libvirt-daemon-system ovmf dnsmasq ebtables"); + else if (stat("/etc/fedora-release", &buf) == 0) + system("dnf install @virtualization"); + else + throw std::runtime_error("\n\033[1;31mUnsupported system. Install required tools manually.\033[0m"); +} + +void enable_services() +{ + std::string init_sys = get_init(); + if (init_sys == "systemd") + system("systemctl enable --now libvirtd"); + else // Other init systems? + std::cout << "Enable libvirtd service on your system manually..\n"; + std::string res = shell_cmd("virsh net-list --all | grep -i default"); + + if (res.empty()) + { + system("virsh net-start default"); + system("virsh net-autostart default"); + } +} + +std::string get_init() +{ + std::ifstream bin("/sbin/init", std::ios::binary); + std::string line; + while (std::getline(bin, line)) + { + if (line.find("/lib/systemd") != std::string::npos) + return "systemd"; + else if (line.find("sysvinit") != std::string::npos) + return "sysvinit"; + else if (line.find("upstart") != std::string::npos) + return "upstart"; + } + return ""; +} + +void user_mod() +{ + std::string res = shell_cmd("usermod -aG kvm,input,libvirt $(logname)"); + if (!res.empty()) + { + std::string emsg = "\n\033[1;31m" + res + "\033[0m"; + throw std::runtime_error(emsg); + } +} \ No newline at end of file