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