Feat: conditional permissions behavior (#28)
* Fix: don't need sudo if we're root + other aesthetics
* Heavy refactoring, see PR #28
* Fix: avoid silent fatalities
demo: https://tcp.ac/i/JMSUc.gif
* Fix: Inverse check on `IsRoot`
* D.R.Y: check for permissions error in `common.ErrorCheck`
Reduce cognitive complexity.
* Fix: Issue with copying
* Resolve https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646535918
* Resolve https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646606680 and https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646594105
* Revert "Resolve https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646606680 and https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646594105"
This reverts commit ce15213009
.
* Resolve https://github.com/HikariKnight/quickpassthrough/pull/28#discussion_r1646730751
This commit is contained in:
parent
4d0086df41
commit
6c48a35180
21 changed files with 604 additions and 255 deletions
|
@ -4,13 +4,14 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||
"github.com/gookit/color"
|
||||
|
||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
||||
"github.com/HikariKnight/quickpassthrough/internal/configs"
|
||||
lsiommu "github.com/HikariKnight/quickpassthrough/internal/lsiommu"
|
||||
"github.com/HikariKnight/quickpassthrough/internal/lsiommu"
|
||||
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
||||
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
||||
"github.com/HikariKnight/quickpassthrough/pkg/menu"
|
||||
"github.com/gookit/color"
|
||||
)
|
||||
|
||||
func SelectGPU(config *configs.Config) {
|
||||
|
@ -94,10 +95,10 @@ func viewGPU(config *configs.Config, ext ...int) {
|
|||
config.Gpu_IDs = lsiommu.GetIOMMU("-g", mode, "-i", config.Gpu_Group, "--id")
|
||||
|
||||
// If the kernel_args file already exists
|
||||
if fileio.FileExist(config.Path.CMDLINE) {
|
||||
if exists, _ := fileio.FileExist(config.Path.CMDLINE); exists {
|
||||
// Delete it as we will have to make a new one anyway
|
||||
err := os.Remove(config.Path.CMDLINE)
|
||||
errorcheck.ErrorCheck(err, fmt.Sprintf("Could not remove %s", config.Path.CMDLINE))
|
||||
common.ErrorCheck(err, fmt.Sprintf("Could not remove %s", config.Path.CMDLINE))
|
||||
}
|
||||
|
||||
// Write initial kernel_arg file
|
||||
|
@ -114,10 +115,10 @@ func viewGPU(config *configs.Config, ext ...int) {
|
|||
)
|
||||
|
||||
// If the kernel_args file already exists
|
||||
if fileio.FileExist(config.Path.CMDLINE) {
|
||||
if exists, _ := fileio.FileExist(config.Path.CMDLINE); exists {
|
||||
// Delete it as we will have to make a new one anyway
|
||||
err := os.Remove(config.Path.CMDLINE)
|
||||
errorcheck.ErrorCheck(err, fmt.Sprintf("Could not remove %s", config.Path.CMDLINE))
|
||||
common.ErrorCheck(err, fmt.Sprintf("Could not remove %s", config.Path.CMDLINE))
|
||||
}
|
||||
|
||||
// Write initial kernel_arg file
|
||||
|
|
|
@ -4,11 +4,12 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gookit/color"
|
||||
|
||||
"github.com/HikariKnight/quickpassthrough/internal/configs"
|
||||
lsiommu "github.com/HikariKnight/quickpassthrough/internal/lsiommu"
|
||||
"github.com/HikariKnight/quickpassthrough/internal/lsiommu"
|
||||
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
||||
"github.com/HikariKnight/quickpassthrough/pkg/menu"
|
||||
"github.com/gookit/color"
|
||||
)
|
||||
|
||||
func selectUSB(config *configs.Config) {
|
||||
|
|
|
@ -6,34 +6,34 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"golang.org/x/term"
|
||||
|
||||
"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/menu"
|
||||
"github.com/HikariKnight/quickpassthrough/pkg/uname"
|
||||
"github.com/gookit/color"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func prepModules(config *configs.Config) {
|
||||
// If we have files for modprobe
|
||||
if fileio.FileExist(config.Path.MODPROBE) {
|
||||
if exists, _ := fileio.FileExist(config.Path.MODPROBE); exists {
|
||||
// Configure modprobe
|
||||
configs.Set_Modprobe(config.Gpu_IDs)
|
||||
}
|
||||
|
||||
// If we have a folder for dracut
|
||||
if fileio.FileExist(config.Path.DRACUT) {
|
||||
if exists, _ := fileio.FileExist(config.Path.DRACUT); exists {
|
||||
// Configure dracut
|
||||
configs.Set_Dracut()
|
||||
}
|
||||
|
||||
// If we have a mkinitcpio.conf file
|
||||
if fileio.FileExist(config.Path.MKINITCPIO) {
|
||||
if exists, _ := fileio.FileExist(config.Path.MKINITCPIO); exists {
|
||||
configs.Set_Mkinitcpio()
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,43 @@ func prepModules(config *configs.Config) {
|
|||
finalize(config)
|
||||
}
|
||||
|
||||
func finalizeNotice(isRoot bool) {
|
||||
color.Print(`
|
||||
The configuration files have been generated and are located inside the "config" folder
|
||||
|
||||
* The "kernel_args" file contains kernel arguments that your bootloader needs
|
||||
* The "qemu" folder contains files that may be needed for passthrough
|
||||
* The files inside the "etc" folder must be copied to your system.
|
||||
|
||||
<red>Verify that these files are correctly formated/edited!</>
|
||||
|
||||
Once all files have been copied, the following steps must be taken:
|
||||
|
||||
* bootloader configuration must be updated
|
||||
* initramfs must be rebuilt
|
||||
|
||||
`)
|
||||
switch isRoot {
|
||||
case true:
|
||||
color.Print("This program can do this for you, if desired.\n")
|
||||
default:
|
||||
color.Print(`This program can do this for you, however your sudo password is required.
|
||||
To avoid this:
|
||||
|
||||
* press CTRL+C and perform the steps mentioned above manually.
|
||||
OR
|
||||
* run ` + os.Args[0] + ` as root.
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
color.Print(`
|
||||
If you want to go back and change something, choose Back.
|
||||
|
||||
NOTE: A backup of the original files from the first run can be found in the backup folder
|
||||
`)
|
||||
}
|
||||
|
||||
func finalize(config *configs.Config) {
|
||||
// Clear the screen
|
||||
command.Clear()
|
||||
|
@ -56,60 +93,43 @@ func finalize(config *configs.Config) {
|
|||
title := color.New(color.BgHiBlue, color.White, color.Bold)
|
||||
title.Println("Finalizing configuration")
|
||||
|
||||
color.Print(
|
||||
"The configuration files have been generated and are\n",
|
||||
"located inside the \"config\" folder\n",
|
||||
"\n",
|
||||
"* The \"kernel_args\" file contains kernel arguments that your bootloader needs\n",
|
||||
"* The \"qemu\" folder contains files that might be\n neccessary for passing through the GPU\n",
|
||||
"* The files inside the \"etc\" folder must be copied to your system.\n",
|
||||
" NOTE: Verify that these files are correctly formated/edited!\n",
|
||||
"* Once all files have been copied, you need to update your bootloader and rebuild\n",
|
||||
" your initramfs using the tools to do so by your system.\n",
|
||||
"\n",
|
||||
"This program can do this for you, however the program will have to\n",
|
||||
"type your password to sudo using STDIN, to avoid using STDIN press CTRL+C\n",
|
||||
"and copy the files, update your bootloader and rebuild your initramfs manually.\n",
|
||||
"If you want to go back and change something, choose Back\n",
|
||||
"\nNOTE: A backup of the original files from the first run can be found in the backup folder\n",
|
||||
)
|
||||
config.IsRoot = os.Getuid() == 0
|
||||
|
||||
// Make a choice of going next or back
|
||||
choice := menu.Next("Press Next to continue with sudo using STDIN, ESC to exit or Back to go back.")
|
||||
finalizeNotice(config.IsRoot)
|
||||
|
||||
// Parse the choice
|
||||
switch choice {
|
||||
// Make a choice of going next or back and parse the choice
|
||||
switch menu.Next("Press Next to continue with sudo using STDIN, ESC to exit or Back to go back.") {
|
||||
case "next":
|
||||
installPassthrough(config)
|
||||
|
||||
case "back":
|
||||
// Go back
|
||||
disableVideo(config)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func installPassthrough(config *configs.Config) {
|
||||
// Get the user data
|
||||
user, err := user.Current()
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
// Provide a password prompt
|
||||
fmt.Printf("[sudo] password for %s: ", user.Username)
|
||||
bytep, err := term.ReadPassword(int(syscall.Stdin))
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Print("\n")
|
||||
if !config.IsRoot {
|
||||
// Provide a password prompt
|
||||
fmt.Printf("[sudo] password for %s: ", currentUser.Username)
|
||||
bytep, err := term.ReadPassword(syscall.Stdin)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Print("\n")
|
||||
|
||||
// Elevate with sudo
|
||||
command.Elevate(
|
||||
base64.StdEncoding.EncodeToString(
|
||||
bytep,
|
||||
),
|
||||
)
|
||||
// Elevate with sudo
|
||||
command.Elevate(
|
||||
base64.StdEncoding.EncodeToString(
|
||||
bytep,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Make an output string
|
||||
var output string
|
||||
|
@ -120,22 +140,24 @@ func installPassthrough(config *configs.Config) {
|
|||
logger.Printf("Configuring systemd-boot using kernelstub\n")
|
||||
|
||||
// Configure kernelstub
|
||||
output = configs.Set_KernelStub()
|
||||
fmt.Printf("%s\n", output)
|
||||
// callee logs the output and checks for errors
|
||||
configs.Set_KernelStub(config.IsRoot)
|
||||
|
||||
} else if config.Bootloader == "grubby" {
|
||||
// Write to logger
|
||||
logger.Printf("Configuring bootloader using grubby\n")
|
||||
|
||||
// Configure kernelstub
|
||||
output = configs.Set_Grubby()
|
||||
output = configs.Set_Grubby(config.IsRoot)
|
||||
fmt.Printf("%s\n", output)
|
||||
|
||||
} else if config.Bootloader == "grub2" {
|
||||
// Write to logger
|
||||
logger.Printf("Applying grub2 changes\n")
|
||||
grub_output, _ := configs.Set_Grub2()
|
||||
fmt.Printf("%s\n", strings.Join(grub_output, "\n"))
|
||||
_ = configs.Set_Grub2(config.IsRoot) // note: we set config.IsRoot earlier
|
||||
|
||||
// we'll print the output in the [configs.Set_Grub2] method
|
||||
// fmt.Printf("%s\n", strings.Join(grub_output, "\n"))
|
||||
|
||||
} else {
|
||||
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
|
||||
|
@ -145,68 +167,62 @@ func installPassthrough(config *configs.Config) {
|
|||
// 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 = configs.CopyToSystem(modprobeFile, "/etc/modprobe.d/vfio.conf")
|
||||
fmt.Printf("%s\n", output)
|
||||
|
||||
// lets hope by now we've already handled any permissions issues...
|
||||
// TODO: verify that we actually can drop the errors on [fileio.FileExist] call below
|
||||
|
||||
if exists, _ := fileio.FileExist(modprobeFile); exists {
|
||||
// Copy initramfs-tools module to system, note that CopyToSystem will log the command and output
|
||||
// as well as check for errors
|
||||
configs.CopyToSystem(config.IsRoot, 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) {
|
||||
|
||||
initramFsExists, initramFsErr := fileio.FileExist(initramfsFile)
|
||||
dracutExists, dracutErr := fileio.FileExist(dracutFile)
|
||||
mkinitcpioExists, mkinitcpioErr := fileio.FileExist(config.Path.MKINITCPIO)
|
||||
|
||||
for _, err = range []error{initramFsErr, dracutErr, mkinitcpioErr} {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
// we know this error isn't ErrNotExist, so we should throw it and exit
|
||||
log.Fatalf("Failed to stat file: %s", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case initramFsExists:
|
||||
// Copy initramfs-tools module to system
|
||||
output = configs.CopyToSystem(initramfsFile, "/etc/initramfs-tools/modules")
|
||||
fmt.Printf("%s\n", output)
|
||||
configs.CopyToSystem(config.IsRoot, initramfsFile, "/etc/initramfs-tools/modules")
|
||||
|
||||
// Copy the modules file to /etc/modules
|
||||
output = configs.CopyToSystem(config.Path.ETCMODULES, "/etc/modules")
|
||||
fmt.Printf("%s\n", output)
|
||||
configs.CopyToSystem(config.IsRoot, config.Path.ETCMODULES, "/etc/modules")
|
||||
|
||||
// Write to logger
|
||||
logger.Printf("Executing: sudo update-initramfs -u\n")
|
||||
if err = command.ExecAndLogSudo(config.IsRoot, true, "update-initramfs", "-u"); err != nil {
|
||||
log.Fatalf("Failed to update initramfs: %s", err)
|
||||
}
|
||||
|
||||
// Update initramfs
|
||||
fmt.Println("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) {
|
||||
case dracutExists:
|
||||
// Copy dracut config to /etc/dracut.conf.d/vfio
|
||||
output = configs.CopyToSystem(dracutFile, "/etc/dracut.conf.d/vfio")
|
||||
fmt.Printf("%s\n", output)
|
||||
configs.CopyToSystem(config.IsRoot, dracutFile, "/etc/dracut.conf.d/vfio")
|
||||
|
||||
// Get systeminfo
|
||||
sysinfo := uname.New()
|
||||
|
||||
// Write to logger
|
||||
logger.Printf("Executing: sudo dracut -f -v --kver %s\n", sysinfo.Release)
|
||||
if err = command.ExecAndLogSudo(config.IsRoot, true, "dracut", "-f", "-v", "--kver", sysinfo.Release); err != nil {
|
||||
log.Fatalf("Failed to update initramfs: %s", err)
|
||||
}
|
||||
|
||||
// Update initramfs
|
||||
fmt.Printf("Executed: sudo dracut -f -v --kver %s\nSee debug.log for detailed output", sysinfo.Release)
|
||||
_, cmd_err, _ := command.RunErr("sudo", "dracut", "-f", "-v", "--kver", sysinfo.Release)
|
||||
|
||||
// Write to logger
|
||||
logger.Printf(strings.Join(cmd_err, "\n"))
|
||||
} else if fileio.FileExist(config.Path.MKINITCPIO) {
|
||||
case mkinitcpioExists:
|
||||
// Copy dracut config to /etc/dracut.conf.d/vfio
|
||||
output = configs.CopyToSystem(config.Path.MKINITCPIO, "/etc/mkinitcpio.conf")
|
||||
fmt.Printf("%s\n", output)
|
||||
configs.CopyToSystem(config.IsRoot, config.Path.MKINITCPIO, "/etc/mkinitcpio.conf")
|
||||
|
||||
// Write to logger
|
||||
logger.Printf("Executing: sudo mkinitcpio -P")
|
||||
|
||||
// Update initramfs
|
||||
fmt.Println("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"))
|
||||
if err = command.ExecAndLogSudo(config.IsRoot, true, "mkinitcpio", "-P"); err != nil {
|
||||
log.Fatalf("Failed to update initramfs: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure prompt end up on next line
|
||||
|
|
23
internal/pages/06_finalize_test.go
Normal file
23
internal/pages/06_finalize_test.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package pages
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFinalizeNotice(t *testing.T) {
|
||||
msg := "\n%s\nprinting the finalize notice for manual review, this test should always pass.\n%s\n\n"
|
||||
divider := strings.Repeat("-", len(msg)-12)
|
||||
t.Logf(msg, divider, divider)
|
||||
t.Log("\n\nWith isRoot == true:\n\n")
|
||||
|
||||
finalizeNotice(true)
|
||||
|
||||
println("\n\n")
|
||||
|
||||
t.Log("\n\nWith isRoot == false:\n\n")
|
||||
|
||||
finalizeNotice(false)
|
||||
|
||||
println("\n\n")
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue