From 4c99baeed81074557f2d3e863da879e73bbe6ebd Mon Sep 17 00:00:00 2001 From: HikariKnight <2557889+HikariKnight@users.noreply.github.com> Date: Wed, 12 Apr 2023 01:19:02 +0200 Subject: [PATCH] Finish writing the apply changes functions --- internal/configs/config_bootloaders.go | 26 +++++---- internal/configs/configs.go | 18 ++++++ internal/ui_main_events.go | 11 ++-- internal/ui_main_functions.go | 77 ++++++++++++++++++++++++-- internal/ui_main_view.go | 12 +++- internal/ui_model.go | 13 ++++- 6 files changed, 133 insertions(+), 24 deletions(-) diff --git a/internal/configs/config_bootloaders.go b/internal/configs/config_bootloaders.go index 0ba4128..3e1e25e 100644 --- a/internal/configs/config_bootloaders.go +++ b/internal/configs/config_bootloaders.go @@ -86,7 +86,7 @@ func Set_KernelStub() string { errorcheck.ErrorCheck(err, "Error, kernelstub command returned exit code 1") // Return what we did - return fmt.Sprintf("sudo kernelstub -a \"%s\"", kernel_args) + return fmt.Sprintf("Executed: sudo kernelstub -a \"%s\"", kernel_args) } // Configures grub2 and/or systemd-boot using grubby @@ -105,7 +105,7 @@ func Set_Grubby() string { errorcheck.ErrorCheck(err, "Error, grubby command returned exit code 1") // Return what we did - return fmt.Sprintf("sudo grubby --update-kernel=ALL --args=\"%s\"", kernel_args) + return fmt.Sprintf("Executed: sudo grubby --update-kernel=ALL --args=\"%s\"", kernel_args) } func Configure_Grub2() { @@ -206,21 +206,23 @@ func Set_Grub2() ([]string, error) { // Get the conf file conffile := fmt.Sprintf("%s/grub", config.Path.DEFAULT) + // Get the sysfile + sysfile_re := regexp.MustCompile(`^config`) + sysfile := sysfile_re.ReplaceAllString(conffile, "") + // Write to logger - logger.Printf("Executing command:\nsudo cp -v \"%s\" /etc/default/grub", conffile) + logger.Printf("Executing command:\nsudo cp -v \"%s\" %s", conffile, sysfile) - // Since we should be elevated with our sudo token we will copy with cp - // (using built in functions will not work as we are running as the normal user) - output, err := command.Run("sudo", "cp", "-v", conffile, "/etc/default/grub") - errorcheck.ErrorCheck(err, fmt.Sprintf("Failed to copy %s to /etc/default/grub", conffile)) + // Make our output slice + var output []string - // Write output to logger - logger.Printf(strings.Join(output, "\n")) + // Copy files to system + output = append(output, CopyToSystem(conffile, sysfile)) // Set a variable for the mkconfig command mkconfig := "grub-mkconfig" // Check for grub-mkconfig - _, err = command.Run("which", "grub-mkconfig") + _, err := command.Run("which", "grub-mkconfig") if err == nil { // Set binary as grub-mkconfig mkconfig = "grub-mkconfig" @@ -230,12 +232,12 @@ func Set_Grub2() ([]string, error) { // Update grub.cfg if fileio.FileExist("/boot/grub/grub.cfg") { - output = append(output, fmt.Sprintf("sudo %s -o /boot/grub/grub.cfg", mkconfig)) + output = append(output, fmt.Sprintf("Executed: sudo %s -o /boot/grub/grub.cfg\nSee debug.log for more detailed output", mkconfig)) mklog, err := command.Run("sudo", mkconfig, "-o", "/boot/grub/grub.cfg") logger.Printf(strings.Join(mklog, "\n")) errorcheck.ErrorCheck(err, "Failed to update /boot/grub/grub.cfg") } else { - output = append(output, fmt.Sprintf("sudo %s -o /boot/grub/grub.cfg\nSee debug.log for more detailed output", mkconfig)) + output = append(output, fmt.Sprintf("Executed: sudo %s -o /boot/grub/grub.cfg\nSee debug.log for more detailed output", mkconfig)) mklog, err := command.Run("sudo", mkconfig, "-o", "/boot/grub2/grub.cfg") logger.Printf(strings.Join(mklog, "\n")) errorcheck.ErrorCheck(err, "Failed to update /boot/grub/grub.cfg") diff --git a/internal/configs/configs.go b/internal/configs/configs.go index dc0096f..acd7077 100644 --- a/internal/configs/configs.go +++ b/internal/configs/configs.go @@ -7,6 +7,7 @@ import ( "github.com/HikariKnight/ls-iommu/pkg/errorcheck" "github.com/HikariKnight/quickpassthrough/internal/logger" + "github.com/HikariKnight/quickpassthrough/pkg/command" "github.com/HikariKnight/quickpassthrough/pkg/fileio" "github.com/HikariKnight/quickpassthrough/pkg/uname" "github.com/klauspost/cpuid/v2" @@ -218,3 +219,20 @@ func makeBackupDir(dest string) { err := os.MkdirAll(fmt.Sprintf("backup/%s", dest), os.ModePerm) errorcheck.ErrorCheck(err, "Error making backup/ folder") } + +// Copy a file to the system, make sure you have run command.Elevate() recently +func CopyToSystem(conffile, sysfile string) string { + // Since we should be elevated with our sudo token we will copy with cp + // (using built in functions will not work as we are running as the normal user) + output, _ := command.Run("sudo", "cp", "-v", conffile, sysfile) + + // Clean the output + clean_re := regexp.MustCompile(`\n`) + clean_output := clean_re.ReplaceAllString(output[0], "") + + // Write output to logger + logger.Printf(clean_output) + + // Return the output + return fmt.Sprintf("Copying: %s", clean_output) +} diff --git a/internal/ui_main_events.go b/internal/ui_main_events.go index c85259f..b85e1ee 100644 --- a/internal/ui_main_events.go +++ b/internal/ui_main_events.go @@ -2,6 +2,7 @@ package internal import ( "encoding/base64" + "fmt" "github.com/HikariKnight/quickpassthrough/internal/logger" "github.com/HikariKnight/quickpassthrough/pkg/command" @@ -52,7 +53,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "enter": // If we are on the INSTALL dialog - if m.focused == INSTALL { + if m.focused == INSTALL && m.authDialog.Value() != "" { // Write to logger logger.Printf("Getting authentication token by elevating with sudo once") @@ -71,14 +72,16 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Blank the password field m.authDialog.SetValue("") + fmt.Println("Working... Application frozen until done, check debug.log for progress") + // Start installation and send the password to the command - m.installOutput = m.install() + m.install() // Move to the DONE dialog - m.focused++ + m.focused = DONE // Exit the alt screen as the output on the done dialog needs to be scrollable - //return m, tea.ExitAltScreen + return m, tea.ClearScreen } else { // Quit the application if we are on a different view diff --git a/internal/ui_main_functions.go b/internal/ui_main_functions.go index 265d722..2b66a28 100644 --- a/internal/ui_main_functions.go +++ b/internal/ui_main_functions.go @@ -4,11 +4,14 @@ import ( "fmt" "os" "regexp" + "strings" "github.com/HikariKnight/ls-iommu/pkg/errorcheck" "github.com/HikariKnight/quickpassthrough/internal/configs" "github.com/HikariKnight/quickpassthrough/internal/logger" + "github.com/HikariKnight/quickpassthrough/pkg/command" "github.com/HikariKnight/quickpassthrough/pkg/fileio" + "github.com/HikariKnight/quickpassthrough/pkg/uname" ) // This function processes the enter event @@ -151,7 +154,7 @@ func (m *model) processSelection() bool { // This function starts the install process // It takes 1 auth string as variable -func (m *model) install() []string { +func (m *model) install() { // Get the config config := configs.GetConfig() @@ -159,7 +162,7 @@ func (m *model) install() []string { var output []string // Based on the bootloader, setup the configuration - if config.Bootloader != "kernelstub" { + if config.Bootloader == "kernelstub" { // Write to logger logger.Printf("Configuring systemd-boot using kernelstub") @@ -173,7 +176,7 @@ func (m *model) install() []string { // Configure kernelstub output = append(output, configs.Set_Grubby()) - } else if config.Bootloader != "grub2" { + } else if config.Bootloader == "grub2" { // Write to logger logger.Printf("Configuring grub2 manually") grub_output, _ := configs.Set_Grub2() @@ -184,5 +187,71 @@ func (m *model) install() []string { logger.Printf("Unsupported bootloader, please add the below line to your bootloaders kernel arguments\n%s", kernel_args) } - return output + // A lot of linux systems support modprobe along with their own module system + // So copy the modprobe files if we have them + modprobeFile := fmt.Sprintf("%s/vfio.conf", config.Path.MODPROBE) + if fileio.FileExist(modprobeFile) { + // Copy initramfs-tools module to system + output = append(output, configs.CopyToSystem(modprobeFile, "/etc/modprobe.d/vfio.conf")) + } + + // Copy the config files for the system we have + initramfsFile := fmt.Sprintf("%s/modules", config.Path.INITRAMFS) + dracutFile := fmt.Sprintf("%s/vfio.conf", config.Path.DRACUT) + if fileio.FileExist(initramfsFile) { + // Copy initramfs-tools module to system + output = append(output, configs.CopyToSystem(initramfsFile, "/etc/initramfs-tools/modules")) + + // Copy the modules file to /etc/modules + output = append(output, configs.CopyToSystem(config.Path.ETCMODULES, "/etc/modules")) + + // Write to logger + logger.Printf("Executing: sudo update-initramfs -u") + + // Update initramfs + output = append(output, "Executed: sudo update-initramfs -u\nSee debug.log for detailed output") + cmd_out, cmd_err, _ := command.RunErr("sudo", "update-initramfs", "-u") + + cmd_out = append(cmd_out, cmd_err...) + + // Write to logger + logger.Printf(strings.Join(cmd_out, "\n")) + } else if fileio.FileExist(dracutFile) { + // Copy dracut config to /etc/dracut.conf.d/vfio + output = append(output, configs.CopyToSystem(dracutFile, "/etc/dracut.conf.d/vfio")) + + // Get systeminfo + sysinfo := uname.New() + + // Write to logger + logger.Printf("Executing: sudo dracut -f -v --kver %s", sysinfo.Release) + + // Update initramfs + output = append(output, fmt.Sprintf("Executed: sudo dracut -f -v --kver %s\nSee debug.log for detailed output", sysinfo.Release)) + cmd_out, cmd_err, _ := command.RunErr("sudo", "dracut", "-f", "-v", "--kver", sysinfo.Release) + + cmd_out = append(cmd_out, cmd_err...) + + // Write to logger + logger.Printf(strings.Join(cmd_out, "\n")) + } else if fileio.FileExist(config.Path.MKINITCPIO) { + // Copy dracut config to /etc/dracut.conf.d/vfio + output = append(output, configs.CopyToSystem(config.Path.MKINITCPIO, "/etc/mkinitcpio.conf")) + + // Write to logger + logger.Printf("Executing: sudo mkinitcpio -P") + + // Update initramfs + output = append(output, "Executed: sudo mkinitcpio -P\nSee debug.log for detailed output") + cmd_out, cmd_err, _ := command.RunErr("sudo", "mkinitcpio", "-P") + + cmd_out = append(cmd_out, cmd_err...) + + // Write to logger + logger.Printf(strings.Join(cmd_out, "\n")) + } + + m.installOutput = output + m.focused++ + } diff --git a/internal/ui_main_view.go b/internal/ui_main_view.go index b4c52ea..a02e6f7 100644 --- a/internal/ui_main_view.go +++ b/internal/ui_main_view.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) @@ -20,7 +21,7 @@ func (m model) View() string { titleStyle.MarginLeft(0).Render("Welcome to QuickPassthrough!"), "\n\n", "This script is meant to make it easier to setup GPU passthrough for\n", - "Qemu based systems.\n", + "Qemu based systems. WITH 2 GPUS ON THE HOST SYSTEM\n", "However due to the complexity of GPU passthrough\n", "This script assumes you know how to do (or have done) the following.\n\n", "* You have already enabled IOMMU, VT-d, SVM and/or AMD-v\n inside your UEFI/BIOS advanced settings.\n", @@ -138,9 +139,16 @@ func (m model) View() string { view = m.authDialog.View() + case WORKING: + title = titleStyle.Render("Applying configurations!") + view = "" + + m.authDialog.Update(tea.KeyEnter) + tea.Batch() + case DONE: title = titleStyle.Render("Applying configurations!") - view = dialogStyle.Render(strings.Join(m.installOutput, "\n")) + view = dialogStyle.Render(fmt.Sprintf("%s\n\nPress Enter to Exit.", strings.Join(m.installOutput, "\n"))) } //return listStyle.SetString(fmt.Sprintf("%s\n\n", title)).Render(m.lists[m.focused].View()) return lipgloss.JoinVertical(lipgloss.Left, fmt.Sprintf("%s\n%s\n", title, view)) diff --git a/internal/ui_model.go b/internal/ui_model.go index 257019d..f3aea70 100644 --- a/internal/ui_model.go +++ b/internal/ui_model.go @@ -49,6 +49,7 @@ const ( USB USB_GROUP INSTALL + WORKING DONE ) @@ -99,14 +100,15 @@ func (m *model) initLists(width, height int) { defaultList, choiceList, choiceList, + choiceList, } // Configure offsets for sizing m.offsetx = []int{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } m.offsety = []int{ - 18, 2, 3, 13, 5, 2, 3, 12, 0, + 18, 2, 3, 13, 5, 2, 3, 12, 0, 0, } // Update the styles with the correct width @@ -167,6 +169,13 @@ func (m *model) initLists(width, height int) { m.lists[VIDEO].SetItems(items) m.lists[VIDEO].SetSize(m.width-m.offsetx[VIDEO], m.height-m.offsety[VIDEO]) + // Init DONE choises + items = []list.Item{ + item{title: "WAITING"}, + } + m.lists[WORKING].SetItems(items) + m.lists[WORKING].SetSize(m.width-m.offsetx[WORKING], m.height-m.offsety[WORKING]) + // Init DONE choises items = []list.Item{ item{title: "FINISH"},