Compare commits
No commits in common. "main" and "2.0.5" have entirely different histories.
26 changed files with 282 additions and 643 deletions
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
@ -1,6 +1,6 @@
|
||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: HikariKnight # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
patreon: # Replace with patreon username
|
patreon: # Replace with patreon username
|
||||||
open_collective: # Replace with a single Open Collective username
|
open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: HikariKnight
|
ko_fi: HikariKnight
|
||||||
|
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
|
@ -1,4 +1,4 @@
|
||||||
name: Build & Release
|
name: goreleaser
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -6,5 +6,4 @@ bin/
|
||||||
dist/
|
dist/
|
||||||
main
|
main
|
||||||
quickpassthrough
|
quickpassthrough
|
||||||
debug.log
|
debug.log
|
||||||
quickpassthrough_debug.log
|
|
13
README.md
13
README.md
|
@ -5,7 +5,7 @@ A project to simplify setting up GPU passthrough on your Linux host for libvirt/
|
||||||
|
|
||||||
You can use it by simply downloading the latest [release](https://github.com/HikariKnight/quickpassthrough/releases/) and run it inside a terminal or by downloading and compiling it yourself with the commands below.
|
You can use it by simply downloading the latest [release](https://github.com/HikariKnight/quickpassthrough/releases/) and run it inside a terminal or by downloading and compiling it yourself with the commands below.
|
||||||
|
|
||||||
This project is aimed at **systems with 2 GPUs** and **headless servers**, where the only GPU is not needed.
|
This project is aimed at **desktops with 2 GPUs** and **headless servers**, where the only GPU is not needed.
|
||||||
|
|
||||||
**Note:** Quickpassthrough is not designed to be installed by a package manager! As you would usually have to only run it once, unless you change the GPU.
|
**Note:** Quickpassthrough is not designed to be installed by a package manager! As you would usually have to only run it once, unless you change the GPU.
|
||||||
|
|
||||||
|
@ -47,18 +47,16 @@ go get -u ./cmd
|
||||||
CGO_ENABLED=0 go build -ldflags="-X github.com/HikariKnight/quickpassthrough/internal/version.Version=$(git rev-parse --short HEAD)" -o quickpassthrough cmd/main.go
|
CGO_ENABLED=0 go build -ldflags="-X github.com/HikariKnight/quickpassthrough/internal/version.Version=$(git rev-parse --short HEAD)" -o quickpassthrough cmd/main.go
|
||||||
```
|
```
|
||||||
|
|
||||||
## Does this work on atomic or immutable systems?
|
## Does this work on immutable systems?
|
||||||
Currently no, however [Bazzite](https://bazzite.gg), [Bluefin](https://projectbluefin.io) and [Aurora](https://getaurora.dev) has an `ujust` command that does a very similar job.
|
Currently no, however [Bazzite](https://bazzite.gg) has an `ujust` command that does a very similar job.
|
||||||
|
In Bazzite you run `ujust setup-virtualization` and follow the prompts to `Enable Virtualization` and `Enable VFIO drivers`.
|
||||||
In Bazzite you run `ujust setup-virtualization` and follow the prompts to `Enable Virtualization` and `Enable VFIO drivers`.<br>
|
|
||||||
In Bluefin and Aurora you run `ujust setup-vfio` and follow the prompts.
|
|
||||||
|
|
||||||
## How do I undo the changes?
|
## How do I undo the changes?
|
||||||
There is a `backup/` folder generated on the first run that will have a copy of all your files (and their paths) from before we edited anything.
|
There is a `backup/` folder generated on the first run that will have a copy of all your files (and their paths) from before we edited anything.
|
||||||
Compare that folder with the `config/` folder to see which files you need to delete in addition to copying the files from `backup/` to your system before rebuilding your initramfs and updating your bootloader config.
|
Compare that folder with the `config/` folder to see which files you need to delete in addition to copying the files from `backup/` to your system before rebuilding your initramfs and updating your bootloader config.
|
||||||
|
|
||||||
## How do I just disable vfio for 1 boot?
|
## How do I just disable vfio for 1 boot?
|
||||||
Remove the vfio kernel arguments from your bootloader by pressing E on the boot menu. The kernel arguments added to the bootloader can be found in the `config/kernel_args` file. <br>
|
Remove the vfio kernel arguments from your bootloader by pressing E on the boot menu. The kernel arguments added to the bootloader can be found in the config/kernel_args file. <br>
|
||||||
NOTE: You can also just remove them from your bootloader permanently and update your bootloader if you want to keep the config files on your system.
|
NOTE: You can also just remove them from your bootloader permanently and update your bootloader if you want to keep the config files on your system.
|
||||||
|
|
||||||
## What this project does NOT do
|
## What this project does NOT do
|
||||||
|
@ -66,6 +64,7 @@ NOTE: You can also just remove them from your bootloader permanently and update
|
||||||
* Optimize your Virtual Machine for Passthrough (again this is your job)
|
* Optimize your Virtual Machine for Passthrough (again this is your job)
|
||||||
* Optimize your host machine for Passthrough or Virtualization (out of this projects scope)
|
* Optimize your host machine for Passthrough or Virtualization (out of this projects scope)
|
||||||
* Setup and configure GPU Passthrough on systems with 1 graphic card (iGPU counts as 1 Graphic Card by itself, so iGPU with another GPU will work)
|
* Setup and configure GPU Passthrough on systems with 1 graphic card (iGPU counts as 1 Graphic Card by itself, so iGPU with another GPU will work)
|
||||||
|
* Does not configure passthrough of 3D controllers, [as it will not work](https://lantian.pub/en/article/modify-computer/laptop-intel-nvidia-optimus-passthrough.lantian/) (this is most gaming laptops so do not even think about it). If you try run this on a laptop with a 3D controller, the "2nd GPU" will not show up.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
<img src="https://user-images.githubusercontent.com/2557889/156038229-4e70352f-9182-4474-8e32-d14d3ad67566.png" width="250px">
|
<img src="https://user-images.githubusercontent.com/2557889/156038229-4e70352f-9182-4474-8e32-d14d3ad67566.png" width="250px">
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
|
||||||
"github.com/gookit/color"
|
|
||||||
)
|
|
||||||
|
|
||||||
const PermissionNotice = `
|
|
||||||
<yellowB>Permissions error occured during file operations.</>
|
|
||||||
|
|
||||||
<blue_b>Hint</>:
|
|
||||||
|
|
||||||
If you initially ran QuickPassthrough as root or using sudo,
|
|
||||||
but are now running it as a normal user, this is expected behavior.
|
|
||||||
|
|
||||||
<us>Try running QuickPassthrough as root or using sudo if so.</>
|
|
||||||
|
|
||||||
If this does not work, double check your filesystem's permissions,
|
|
||||||
and be sure to check the debug log for more information.
|
|
||||||
`
|
|
||||||
|
|
||||||
// ErrorCheck serves as a wrapper for HikariKnight/ls-iommu/pkg/common.ErrorCheck that allows for visibile error messages
|
|
||||||
func ErrorCheck(err error, msg ...string) {
|
|
||||||
_, _ = os.Stdout.WriteString("\033[H\033[2J") // clear the screen
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if errors.Is(err, os.ErrPermission) {
|
|
||||||
color.Printf(PermissionNotice)
|
|
||||||
}
|
|
||||||
oneMsg := ""
|
|
||||||
if len(msg) < 1 {
|
|
||||||
oneMsg = ""
|
|
||||||
} else {
|
|
||||||
for _, v := range msg {
|
|
||||||
oneMsg += v + "\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
color.Printf("\n<red_b>FATAL</>: %s\n%s\nAborting", err.Error(), oneMsg)
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
print(".")
|
|
||||||
}
|
|
||||||
print("\n")
|
|
||||||
errorcheck.ErrorCheck(err, msg...)
|
|
||||||
}
|
|
|
@ -1,19 +1,16 @@
|
||||||
package configs
|
package configs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/klauspost/cpuid/v2"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
||||||
|
"github.com/klauspost/cpuid/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This function just adds what bootloader the system has to our config.bootloader value
|
// This function just adds what bootloader the system has to our config.bootloader value
|
||||||
|
@ -73,32 +70,39 @@ func Set_Cmdline(gpu_IDs []string) {
|
||||||
fileio.AppendContent(fmt.Sprintf(" vfio_pci.ids=%s", strings.Join(gpu_IDs, ",")), config.Path.CMDLINE)
|
fileio.AppendContent(fmt.Sprintf(" vfio_pci.ids=%s", strings.Join(gpu_IDs, ",")), config.Path.CMDLINE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set_KernelStub configures systemd-boot using kernelstub.
|
// Configures systemd-boot using kernelstub
|
||||||
func Set_KernelStub(isRoot bool) {
|
func Set_KernelStub() string {
|
||||||
// Get the config
|
// Get the config
|
||||||
config := GetConfig()
|
config := GetConfig()
|
||||||
|
|
||||||
// Get the kernel args
|
// Get the kernel args
|
||||||
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
|
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
|
||||||
|
|
||||||
// Run and log, check for errors
|
// Write to logger
|
||||||
common.ErrorCheck(
|
logger.Printf("Running command:\nsudo kernelstub -a \"%s\"\n", kernel_args)
|
||||||
command.ExecAndLogSudo(isRoot, true, "kernelstub", "-a", kernel_args),
|
|
||||||
"Error, kernelstub command returned exit code 1",
|
// Run the command
|
||||||
)
|
_, err := command.Run("sudo", "kernelstub", "-a", kernel_args)
|
||||||
|
errorcheck.ErrorCheck(err, "Error, kernelstub command returned exit code 1")
|
||||||
|
|
||||||
|
// Return what we did
|
||||||
|
return fmt.Sprintf("Executed: sudo kernelstub -a \"%s\"", kernel_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set_Grubby configures grub2 and/or systemd-boot using grubby
|
// Configures grub2 and/or systemd-boot using grubby
|
||||||
func Set_Grubby(isRoot bool) string {
|
func Set_Grubby() string {
|
||||||
// Get the config
|
// Get the config
|
||||||
config := GetConfig()
|
config := GetConfig()
|
||||||
|
|
||||||
// Get the kernel args
|
// Get the kernel args
|
||||||
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
|
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
|
||||||
|
|
||||||
// Run and log, check for errors
|
// Write to logger
|
||||||
err := command.ExecAndLogSudo(isRoot, true, "grubby", "--update-kernel=ALL", fmt.Sprintf("--args=%s", kernel_args))
|
logger.Printf("Running command:\nsudo grubby --update-kernel=ALL --args=\"%s\"\n", kernel_args)
|
||||||
common.ErrorCheck(err, "Error, grubby command returned exit code 1")
|
|
||||||
|
// Run the command
|
||||||
|
_, err := command.Run("sudo", "grubby", "--update-kernel=ALL", fmt.Sprintf("--args=%s", kernel_args))
|
||||||
|
errorcheck.ErrorCheck(err, "Error, grubby command returned exit code 1")
|
||||||
|
|
||||||
// Return what we did
|
// Return what we did
|
||||||
return fmt.Sprintf("Executed: sudo grubby --update-kernel=ALL --args=\"%s\"", kernel_args)
|
return fmt.Sprintf("Executed: sudo grubby --update-kernel=ALL --args=\"%s\"", kernel_args)
|
||||||
|
@ -112,8 +116,8 @@ func Configure_Grub2() {
|
||||||
conffile := fmt.Sprintf("%s/grub", config.Path.DEFAULT)
|
conffile := fmt.Sprintf("%s/grub", config.Path.DEFAULT)
|
||||||
|
|
||||||
// Make sure we start from scratch by deleting any old file
|
// Make sure we start from scratch by deleting any old file
|
||||||
if exists, _ := fileio.FileExist(conffile); exists {
|
if fileio.FileExist(conffile) {
|
||||||
_ = os.Remove(conffile)
|
os.Remove(conffile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a regex to get the system path instead of the config path
|
// Make a regex to get the system path instead of the config path
|
||||||
|
@ -197,8 +201,8 @@ func clean_Grub2_Args(old_kernel_args []string) []string {
|
||||||
return clean_kernel_args
|
return clean_kernel_args
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set_Grub2 copies our config to /etc/default/grub and updates grub
|
// This function copies our config to /etc/default/grub and updates grub
|
||||||
func Set_Grub2(isRoot bool) error {
|
func Set_Grub2() ([]string, error) {
|
||||||
// Get the config
|
// Get the config
|
||||||
config := GetConfig()
|
config := GetConfig()
|
||||||
|
|
||||||
|
@ -209,45 +213,38 @@ func Set_Grub2(isRoot bool) error {
|
||||||
sysfile_re := regexp.MustCompile(`^config`)
|
sysfile_re := regexp.MustCompile(`^config`)
|
||||||
sysfile := sysfile_re.ReplaceAllString(conffile, "")
|
sysfile := sysfile_re.ReplaceAllString(conffile, "")
|
||||||
|
|
||||||
// [CopyToSystem] will log the operation
|
// Write to logger
|
||||||
// logger.Printf("Executing command:\nsudo cp -v \"%s\" %s\n", conffile, sysfile)
|
logger.Printf("Executing command:\nsudo cp -v \"%s\" %s\n", conffile, sysfile)
|
||||||
|
|
||||||
// Copy files to system, logging and error checking is done in the function
|
// Make our output slice
|
||||||
CopyToSystem(isRoot, conffile, sysfile)
|
var output []string
|
||||||
|
|
||||||
|
// Copy files to system
|
||||||
|
output = append(output, CopyToSystem(conffile, sysfile))
|
||||||
|
|
||||||
// Set a variable for the mkconfig command
|
// Set a variable for the mkconfig command
|
||||||
var mkconfig string
|
mkconfig := "grub-mkconfig"
|
||||||
var grubPath = "/boot/grub/grub.cfg"
|
|
||||||
var lpErr error
|
|
||||||
|
|
||||||
// Check for grub-mkconfig
|
// Check for grub-mkconfig
|
||||||
mkconfig, lpErr = exec.LookPath("grub-mkconfig")
|
_, err := command.Run("which", "grub-mkconfig")
|
||||||
switch {
|
if err == nil {
|
||||||
case errors.Is(lpErr, exec.ErrNotFound) || mkconfig == "":
|
// Set binary as grub-mkconfig
|
||||||
// Check for grub2-mkconfig
|
mkconfig = "grub-mkconfig"
|
||||||
mkconfig, lpErr = exec.LookPath("grub2-mkconfig")
|
} else {
|
||||||
if lpErr == nil && mkconfig != "" {
|
mkconfig = "grub2-mkconfig"
|
||||||
grubPath = "/boot/grub2/grub.cfg"
|
|
||||||
break // skip below, we found grub2-mkconfig
|
|
||||||
}
|
|
||||||
if lpErr == nil {
|
|
||||||
// we know mkconfig is empty despite no error;
|
|
||||||
// so set an error for [common.ErrorCheck].
|
|
||||||
lpErr = errors.New("neither grub-mkconfig or grub2-mkconfig found")
|
|
||||||
}
|
|
||||||
common.ErrorCheck(lpErr, lpErr.Error()+"\n")
|
|
||||||
return lpErr // note: unreachable as [common.ErrorCheck] calls fatal
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, mklog, err := command.RunErrSudo(isRoot, mkconfig, "-o", grubPath)
|
// Update grub.cfg
|
||||||
|
if fileio.FileExist("/boot/grub/grub.cfg") {
|
||||||
|
output = append(output, fmt.Sprintf("Executed: sudo %s -o /boot/grub/grub.cfg\nSee debug.log for more detailed output", mkconfig))
|
||||||
|
_, mklog, err := command.RunErr("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("Executed: sudo %s -o /boot/grub/grub.cfg\nSee debug.log for more detailed output", mkconfig))
|
||||||
|
_, mklog, err := command.RunErr("sudo", mkconfig, "-o", "/boot/grub2/grub.cfg")
|
||||||
|
logger.Printf(strings.Join(mklog, "\n"))
|
||||||
|
errorcheck.ErrorCheck(err, "Failed to update /boot/grub/grub.cfg")
|
||||||
|
}
|
||||||
|
|
||||||
// tabulate the output, [command.RunErrSudo] logged the execution.
|
return output, err
|
||||||
logger.Printf("\t" + strings.Join(mklog, "\n\t"))
|
|
||||||
common.ErrorCheck(err, "Failed to update /boot/grub/grub.cfg")
|
|
||||||
|
|
||||||
// always returns nil as [common.ErrorCheck] calls fatal
|
|
||||||
// keeping the ret signature, as we should consider passing down errors
|
|
||||||
// but that's a massive rabbit hole to go down for this codebase as a whole
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set_Dracut writes a dracut configuration file for `/etc/dracut.conf.d/`.
|
// This function writes a dracut configuration file for /etc/dracut.conf.d/
|
||||||
func Set_Dracut() {
|
func Set_Dracut() {
|
||||||
config := GetConfig()
|
config := GetConfig()
|
||||||
|
|
||||||
|
@ -17,23 +17,23 @@ func Set_Dracut() {
|
||||||
dracutConf := fmt.Sprintf("%s/vfio.conf", config.Path.DRACUT)
|
dracutConf := fmt.Sprintf("%s/vfio.conf", config.Path.DRACUT)
|
||||||
|
|
||||||
// If the file already exists then delete it
|
// If the file already exists then delete it
|
||||||
if exists, _ := fileio.FileExist(dracutConf); exists {
|
if fileio.FileExist(dracutConf) {
|
||||||
_ = os.Remove(dracutConf)
|
os.Remove(dracutConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to logger
|
// Write to logger
|
||||||
logger.Printf("Writing to %s:\nforce_drivers+=\" %s \"\n", dracutConf, strings.Join(vfio_modules(), " "))
|
logger.Printf("Writing to %s:\nadd_drivers+=\" %s \"\n", dracutConf, strings.Join(vfio_modules(), " "))
|
||||||
|
|
||||||
// Write the dracut config file
|
// Write the dracut config file
|
||||||
fileio.AppendContent(fmt.Sprintf("force_drivers+=\" %s \"\n", strings.Join(vfio_modules(), " ")), dracutConf)
|
fileio.AppendContent(fmt.Sprintf("add_drivers+=\" %s \"\n", strings.Join(vfio_modules(), " ")), dracutConf)
|
||||||
|
|
||||||
// Get the current kernel arguments we have generated
|
// Get the current kernel arguments we have generated
|
||||||
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
|
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
|
||||||
|
|
||||||
// If the kernel argument is not already in the file
|
// If the kernel argument is not already in the file
|
||||||
if !strings.Contains(kernel_args, "rd.driver.pre=vfio-pci") {
|
if !strings.Contains(kernel_args, "rd.driver.pre=vfio_pci") {
|
||||||
// Add to our kernel arguments file that vfio_pci should load early (dracut does this using kernel arguments)
|
// Add to our kernel arguments file that vfio_pci should load early (dracut does this using kernel arguments)
|
||||||
fileio.AppendContent(" rd.driver.pre=vfio-pci", config.Path.CMDLINE)
|
fileio.AppendContent(" rd.driver.pre=vfio_pci", config.Path.CMDLINE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a backup of dracutConf if there is one there
|
// Make a backup of dracutConf if there is one there
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import (
|
||||||
func initramfs_readHeader(lines int, fileName string) string {
|
func initramfs_readHeader(lines int, fileName string) string {
|
||||||
// Open the file
|
// Open the file
|
||||||
f, err := os.Open(fileName)
|
f, err := os.Open(fileName)
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Error opening %s", fileName))
|
errorcheck.ErrorCheck(err, fmt.Sprintf("Error opening %s", fileName))
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
header_re := regexp.MustCompile(`^#`)
|
header_re := regexp.MustCompile(`^#`)
|
||||||
|
@ -50,7 +50,7 @@ func initramfs_addModules(conffile string) {
|
||||||
|
|
||||||
// Open the system file for reading
|
// Open the system file for reading
|
||||||
sysfile, err := os.Open(syspath)
|
sysfile, err := os.Open(syspath)
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Error opening file for reading %s", syspath))
|
errorcheck.ErrorCheck(err, fmt.Sprintf("Error opening file for reading %s", syspath))
|
||||||
defer sysfile.Close()
|
defer sysfile.Close()
|
||||||
|
|
||||||
// Check if user has vendor-reset installed/enabled and make sure that is first
|
// Check if user has vendor-reset installed/enabled and make sure that is first
|
||||||
|
|
|
@ -10,14 +10,14 @@ import (
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set_Mkinitcpio copies the content of /etc/mkinitcpio.conf to the config folder and does an inline replace/insert on the MODULES=() line
|
// This function copies the content of /etc/mkinitcpio.conf to the config folder and does an inline replace/insert on the MODULES=() line
|
||||||
func Set_Mkinitcpio() {
|
func Set_Mkinitcpio() {
|
||||||
// Get the config struct
|
// Get the config struct
|
||||||
config := GetConfig()
|
config := GetConfig()
|
||||||
|
|
||||||
// Make sure we start from scratch by deleting any old file
|
// Make sure we start from scratch by deleting any old file
|
||||||
if exists, _ := fileio.FileExist(config.Path.MKINITCPIO); exists {
|
if fileio.FileExist(config.Path.MKINITCPIO) {
|
||||||
_ = os.Remove(config.Path.MKINITCPIO)
|
os.Remove(config.Path.MKINITCPIO)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a regex to get the system path instead of the config path
|
// Make a regex to get the system path instead of the config path
|
||||||
|
|
|
@ -30,9 +30,9 @@ func Set_Modprobe(gpu_IDs []string) {
|
||||||
conffile := fmt.Sprintf("%s/vfio.conf", config.Path.MODPROBE)
|
conffile := fmt.Sprintf("%s/vfio.conf", config.Path.MODPROBE)
|
||||||
|
|
||||||
// If the file exists
|
// If the file exists
|
||||||
if exists, _ := fileio.FileExist(conffile); exists {
|
if fileio.FileExist(conffile) {
|
||||||
// Delete the old file
|
// Delete the old file
|
||||||
_ = os.Remove(conffile)
|
os.Remove(conffile)
|
||||||
}
|
}
|
||||||
|
|
||||||
content := fmt.Sprint(
|
content := fmt.Sprint(
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ func GenerateVBIOSDumper(vbios_path string) {
|
||||||
vbios_script_template := fmt.Sprint(
|
vbios_script_template := fmt.Sprint(
|
||||||
"#!/bin/bash\n",
|
"#!/bin/bash\n",
|
||||||
"# THIS FILE IS AUTO GENERATED!\n",
|
"# THIS FILE IS AUTO GENERATED!\n",
|
||||||
"# IF YOU HAVE CHANGED THE GPU, PLEASE RE-RUN QUICKPASSTHROUGH!\n",
|
"# IF YOU HAVE CHANGED GPU, PLEASE RE-RUN QUICKPASSTHROUGH!\n",
|
||||||
"mkdir -p \"%s\"\n",
|
"mkdir -p \"%s\"\n",
|
||||||
"echo Attempting to enable reading from rom\n",
|
"echo Attempting to enable reading from rom\n",
|
||||||
"echo 1 | sudo tee %s\n",
|
"echo 1 | sudo tee %s\n",
|
||||||
|
@ -43,24 +43,24 @@ func GenerateVBIOSDumper(vbios_path string) {
|
||||||
|
|
||||||
vbios_script := fmt.Sprintf(
|
vbios_script := fmt.Sprintf(
|
||||||
vbios_script_template,
|
vbios_script_template,
|
||||||
config.Path.QEMU,
|
config.Path.QUICKEMU,
|
||||||
vbios_path,
|
vbios_path,
|
||||||
vbios_path,
|
vbios_path,
|
||||||
scriptdir,
|
scriptdir,
|
||||||
config.Path.QEMU,
|
config.Path.QUICKEMU,
|
||||||
scriptdir,
|
scriptdir,
|
||||||
config.Path.QEMU,
|
config.Path.QUICKEMU,
|
||||||
vbios_path,
|
vbios_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Make the script file
|
// Make the script file
|
||||||
scriptfile, err := os.Create("utils/dump_vbios.sh")
|
scriptfile, err := os.Create("utils/dump_vbios.sh")
|
||||||
common.ErrorCheck(err, "Cannot create file \"utils/dump_vbios.sh\"")
|
errorcheck.ErrorCheck(err, "Cannot create file \"utils/dump_vbios.sh\"")
|
||||||
defer scriptfile.Close()
|
defer scriptfile.Close()
|
||||||
|
|
||||||
// Make the script executable
|
// Make the script executable
|
||||||
scriptfile.Chmod(0775)
|
scriptfile.Chmod(0775)
|
||||||
common.ErrorCheck(err, "Could not change permissions of \"utils/dump_vbios.sh\"")
|
errorcheck.ErrorCheck(err, "Could not change permissions of \"utils/dump_vbios.sh\"")
|
||||||
|
|
||||||
// Write to logger
|
// Write to logger
|
||||||
logger.Printf("Writing utils/dump_vbios.sh\n")
|
logger.Printf("Writing utils/dump_vbios.sh\n")
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
||||||
)
|
)
|
||||||
|
@ -26,7 +26,7 @@ func DisableVFIOVideo(i int) {
|
||||||
if strings.Contains(kernel_args, "vfio_pci.disable_vga") {
|
if strings.Contains(kernel_args, "vfio_pci.disable_vga") {
|
||||||
// Remove the old file
|
// Remove the old file
|
||||||
err := os.Remove(config.Path.CMDLINE)
|
err := os.Remove(config.Path.CMDLINE)
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Could not rewrite %s", config.Path.CMDLINE))
|
errorcheck.ErrorCheck(err, fmt.Sprintf("Could not rewrite %s", config.Path.CMDLINE))
|
||||||
|
|
||||||
// Enable or disable the VGA based on our given value
|
// Enable or disable the VGA based on our given value
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
package configs
|
package configs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/klauspost/cpuid/v2"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/uname"
|
"github.com/HikariKnight/quickpassthrough/pkg/uname"
|
||||||
|
"github.com/klauspost/cpuid/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Path struct {
|
type Path struct {
|
||||||
|
@ -22,7 +19,7 @@ type Path struct {
|
||||||
INITRAMFS string
|
INITRAMFS string
|
||||||
ETCMODULES string
|
ETCMODULES string
|
||||||
DEFAULT string
|
DEFAULT string
|
||||||
QEMU string
|
QUICKEMU string
|
||||||
DRACUT string
|
DRACUT string
|
||||||
MKINITCPIO string
|
MKINITCPIO string
|
||||||
}
|
}
|
||||||
|
@ -33,10 +30,9 @@ type Config struct {
|
||||||
Path *Path
|
Path *Path
|
||||||
Gpu_Group string
|
Gpu_Group string
|
||||||
Gpu_IDs []string
|
Gpu_IDs []string
|
||||||
IsRoot bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConfigPaths retrieves the path to all the config files.
|
// Gets the path to all the config files
|
||||||
func GetConfigPaths() *Path {
|
func GetConfigPaths() *Path {
|
||||||
Paths := &Path{
|
Paths := &Path{
|
||||||
CMDLINE: "config/kernel_args",
|
CMDLINE: "config/kernel_args",
|
||||||
|
@ -44,7 +40,7 @@ func GetConfigPaths() *Path {
|
||||||
INITRAMFS: "config/etc/initramfs-tools",
|
INITRAMFS: "config/etc/initramfs-tools",
|
||||||
ETCMODULES: "config/etc/modules",
|
ETCMODULES: "config/etc/modules",
|
||||||
DEFAULT: "config/etc/default",
|
DEFAULT: "config/etc/default",
|
||||||
QEMU: "config/qemu",
|
QUICKEMU: "config/quickemu",
|
||||||
DRACUT: "config/etc/dracut.conf.d",
|
DRACUT: "config/etc/dracut.conf.d",
|
||||||
MKINITCPIO: "config/etc/mkinitcpio.conf",
|
MKINITCPIO: "config/etc/mkinitcpio.conf",
|
||||||
}
|
}
|
||||||
|
@ -52,7 +48,7 @@ func GetConfigPaths() *Path {
|
||||||
return Paths
|
return Paths
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConfig retrieves all the configs and returns the struct.
|
// Gets all the configs and returns the struct
|
||||||
func GetConfig() *Config {
|
func GetConfig() *Config {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
Bootloader: "unknown",
|
Bootloader: "unknown",
|
||||||
|
@ -68,7 +64,7 @@ func GetConfig() *Config {
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitConfigs constructs the empty config files and folders based on what exists on the system
|
// Constructs the empty config files and folders based on what exists on the system
|
||||||
func InitConfigs() {
|
func InitConfigs() {
|
||||||
config := GetConfig()
|
config := GetConfig()
|
||||||
|
|
||||||
|
@ -81,17 +77,10 @@ func InitConfigs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove old config
|
// Remove old config
|
||||||
if err := os.RemoveAll("config"); err != nil && !errors.Is(err, os.ErrNotExist) {
|
os.RemoveAll("config")
|
||||||
|
|
||||||
// won't be called if the error is ErrNotExist
|
|
||||||
common.ErrorCheck(err, "\nError removing old config")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the config folder
|
// Make the config folder
|
||||||
if err := os.Mkdir("config", os.ModePerm); err != nil && !errors.Is(err, os.ErrExist) {
|
os.Mkdir("config", os.ModePerm)
|
||||||
// won't be called if the error is ErrExist
|
|
||||||
common.ErrorCheck(err, "\nError making config folder")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a regex to get the system path instead of the config path
|
// Make a regex to get the system path instead of the config path
|
||||||
syspath_re := regexp.MustCompile(`^config`)
|
syspath_re := regexp.MustCompile(`^config`)
|
||||||
|
@ -101,16 +90,8 @@ func InitConfigs() {
|
||||||
// Get the system path
|
// Get the system path
|
||||||
syspath := syspath_re.ReplaceAllString(confpath, "")
|
syspath := syspath_re.ReplaceAllString(confpath, "")
|
||||||
|
|
||||||
exists, err := fileio.FileExist(syspath)
|
|
||||||
|
|
||||||
// If we received an error that is not ErrNotExist
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorCheck(err, "\nError checking for directory: "+syspath)
|
|
||||||
continue // note: unreachable due to ErrorCheck calling fatal
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the path exists
|
// If the path exists
|
||||||
if exists {
|
if fileio.FileExist(syspath) {
|
||||||
// Write to log
|
// Write to log
|
||||||
logger.Printf(
|
logger.Printf(
|
||||||
"%s found on the system\n"+
|
"%s found on the system\n"+
|
||||||
|
@ -123,10 +104,8 @@ func InitConfigs() {
|
||||||
makeBackupDir(syspath)
|
makeBackupDir(syspath)
|
||||||
|
|
||||||
// Create the directories for our configs
|
// Create the directories for our configs
|
||||||
if err = os.MkdirAll(confpath, os.ModePerm); err != nil && !errors.Is(err, os.ErrExist) {
|
err := os.MkdirAll(confpath, os.ModePerm)
|
||||||
common.ErrorCheck(err, "\nError making directory: "+confpath)
|
errorcheck.ErrorCheck(err)
|
||||||
return // note: unreachable due to ErrorCheck calling fatal
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,15 +128,7 @@ func InitConfigs() {
|
||||||
sysfile := syspath_re.ReplaceAllString(conffile, "")
|
sysfile := syspath_re.ReplaceAllString(conffile, "")
|
||||||
|
|
||||||
// If the file exists
|
// If the file exists
|
||||||
exists, err := fileio.FileExist(sysfile)
|
if fileio.FileExist(sysfile) {
|
||||||
|
|
||||||
// If we received an error that is not ErrNotExist
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorCheck(err, "\nError checking for file: "+sysfile)
|
|
||||||
continue // note: unreachable due to ErrorCheck calling fatal
|
|
||||||
}
|
|
||||||
|
|
||||||
if exists {
|
|
||||||
// Write to log
|
// Write to log
|
||||||
logger.Printf(
|
logger.Printf(
|
||||||
"%s found on the system\n"+
|
"%s found on the system\n"+
|
||||||
|
@ -168,22 +139,16 @@ func InitConfigs() {
|
||||||
|
|
||||||
// Create the directories for our configs
|
// Create the directories for our configs
|
||||||
file, err := os.Create(conffile)
|
file, err := os.Create(conffile)
|
||||||
common.ErrorCheck(err)
|
errorcheck.ErrorCheck(err)
|
||||||
// Close the file so we can edit it
|
// Close the file so we can edit it
|
||||||
_ = file.Close()
|
file.Close()
|
||||||
|
|
||||||
// Backup the sysfile if we do not have a backup
|
// Backup the sysfile if we do not have a backup
|
||||||
backupFile(sysfile)
|
backupFile(sysfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
exists, err = fileio.FileExist(conffile)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorCheck(err, "\nError checking for file: "+conffile)
|
|
||||||
continue // note: unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we now have a config that exists
|
// If we now have a config that exists
|
||||||
if exists {
|
if fileio.FileExist(conffile) {
|
||||||
switch conffile {
|
switch conffile {
|
||||||
case config.Path.ETCMODULES:
|
case config.Path.ETCMODULES:
|
||||||
// Write to logger
|
// Write to logger
|
||||||
|
@ -221,7 +186,7 @@ func vfio_modules() []string {
|
||||||
|
|
||||||
// If we are on a kernel older than 6.2
|
// If we are on a kernel older than 6.2
|
||||||
sysinfo := uname.New()
|
sysinfo := uname.New()
|
||||||
kernel_re := regexp.MustCompile(`^(6\.1|6\.0|[1-5]\.\d{1,2})\.`)
|
kernel_re := regexp.MustCompile(`^(6\.1|6\.0|[1-5]\.)`)
|
||||||
if kernel_re.MatchString(sysinfo.Kernel) {
|
if kernel_re.MatchString(sysinfo.Kernel) {
|
||||||
// Write to the debug log
|
// Write to the debug log
|
||||||
logger.Printf("Linux kernel version %s detected!\nIncluding vfio_virqfd module\n", sysinfo.Kernel)
|
logger.Printf("Linux kernel version %s detected!\nIncluding vfio_virqfd module\n", sysinfo.Kernel)
|
||||||
|
@ -239,28 +204,14 @@ func backupFile(source string) {
|
||||||
// Make a destination path
|
// Make a destination path
|
||||||
dest := fmt.Sprintf("backup%s", source)
|
dest := fmt.Sprintf("backup%s", source)
|
||||||
|
|
||||||
configExists, configFileError := fileio.FileExist(fmt.Sprintf("config%s", source))
|
|
||||||
sysExists, sysFileError := fileio.FileExist(source)
|
|
||||||
destExists, destFileError := fileio.FileExist(dest)
|
|
||||||
|
|
||||||
// If we received an error that is not ErrNotExist on any of the files
|
|
||||||
for _, err := range []error{configFileError, sysFileError, destFileError} {
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorCheck(configFileError, "\nError checking for file: "+source)
|
|
||||||
return // note: unreachable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
// If the file exists in the config but not on the system it is a file we make
|
// If the file exists in the config but not on the system it is a file we make
|
||||||
case configExists && !sysExists:
|
if fileio.FileExist(fmt.Sprintf("config%s", source)) && !fileio.FileExist(source) {
|
||||||
// Create the blank file so that a copy of the backup folder to /etc
|
// Create the blank file so that a copy of the backup folder to /etc
|
||||||
file, err := os.Create(dest)
|
file, err := os.Create(dest)
|
||||||
common.ErrorCheck(err, "Error creating file %s\n", dest)
|
errorcheck.ErrorCheck(err, "Error creating file %s\n", dest)
|
||||||
_ = file.Close()
|
file.Close()
|
||||||
|
} else if !fileio.FileExist(dest) {
|
||||||
// If a backup of the file does not exist
|
// If a backup of the file does not exist
|
||||||
case sysExists && !destExists:
|
|
||||||
// Write to the logger
|
// Write to the logger
|
||||||
logger.Printf("No first time backup of %s detected.\nCreating a backup at %s\n", source, dest)
|
logger.Printf("No first time backup of %s detected.\nCreating a backup at %s\n", source, dest)
|
||||||
|
|
||||||
|
@ -272,64 +223,29 @@ func backupFile(source string) {
|
||||||
|
|
||||||
func makeBackupDir(dest string) {
|
func makeBackupDir(dest string) {
|
||||||
// If a backup directory does not exist
|
// If a backup directory does not exist
|
||||||
exists, err := fileio.FileExist("backup/")
|
if !fileio.FileExist("backup/") {
|
||||||
if err != nil {
|
|
||||||
// If we received an error that is not ErrNotExist
|
|
||||||
common.ErrorCheck(err, "Error checking for backup/ folder")
|
|
||||||
return // note: unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
// Write to the logger
|
// Write to the logger
|
||||||
logger.Printf("Backup directory does not exist!\nCreating backup directory for first run backup")
|
logger.Printf("Backup directory does not exist!\nCreating backup directory for first run backup")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the empty directories
|
// Make the empty directories
|
||||||
if err = os.MkdirAll(fmt.Sprintf("backup/%s", dest), os.ModePerm); errors.Is(err, os.ErrExist) {
|
err := os.MkdirAll(fmt.Sprintf("backup/%s", dest), os.ModePerm)
|
||||||
// ignore if the directory already exists
|
errorcheck.ErrorCheck(err, "Error making backup/ folder")
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
// will return without incident if there's no error
|
|
||||||
common.ErrorCheck(err, "Error making backup/ folder")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyToSystem copies a file to the system.
|
// Copy a file to the system, make sure you have run command.Elevate() recently
|
||||||
func CopyToSystem(isRoot bool, conffile, sysfile string) {
|
func CopyToSystem(conffile, sysfile string) string {
|
||||||
// Since we should be elevated with our sudo token we will copy with cp
|
// 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)
|
// (using built in functions will not work as we are running as the normal user)
|
||||||
|
output, _ := command.Run("sudo", "cp", "-v", conffile, sysfile)
|
||||||
|
|
||||||
// ExecAndLogSudo will write to the logger, so just print here
|
// Clean the output
|
||||||
fmt.Printf("Copying: %s to %s\n", conffile, sysfile)
|
clean_re := regexp.MustCompile(`\n`)
|
||||||
|
clean_output := clean_re.ReplaceAllString(output[0], "")
|
||||||
|
|
||||||
if isRoot {
|
// Write output to logger
|
||||||
logger.Printf("Copying %s to %s\n", conffile, sysfile)
|
logger.Printf("%s\n", clean_output)
|
||||||
fmt.Printf("Copying %s to %s\n", conffile, sysfile)
|
|
||||||
fDat, err := os.ReadFile(conffile)
|
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Failed to read %s", conffile))
|
|
||||||
err = os.WriteFile(sysfile, fDat, 0644)
|
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Failed to write %s", sysfile))
|
|
||||||
logger.Printf("Copied %s to %s\n", conffile, sysfile)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !filepath.IsAbs(conffile) {
|
// Return the output
|
||||||
conffile, _ = filepath.Abs(conffile)
|
return fmt.Sprintf("Copying: %s", clean_output)
|
||||||
}
|
|
||||||
|
|
||||||
err := command.ExecAndLogSudo(isRoot, false, "cp", "-v", conffile, sysfile)
|
|
||||||
|
|
||||||
errMsg := ""
|
|
||||||
if err != nil {
|
|
||||||
errMsg = err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// [command.ExecAndLogSudo] will log the command's output
|
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Failed to copy %s to %s:\n%s", conffile, sysfile, errMsg))
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------
|
|
||||||
// note that if we failed the error check, the following will not appear in the log!
|
|
||||||
// this is because the [common.ErrorCheck] function will call [log.Fatalf] and exit
|
|
||||||
// ---------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
logger.Printf("Copied %s to %s\n", conffile, sysfile)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
package configs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCopyToSystem(t *testing.T) {
|
|
||||||
if err := os.Mkdir("testdir", 0755); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
tFilePath := filepath.Join("testdir", "testfile")
|
|
||||||
if err := os.WriteFile(tFilePath, []byte("test"), 0644); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Cleanup(func() {
|
|
||||||
if err := os.RemoveAll("testdir"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
isRoot := os.Getuid() == 0
|
|
||||||
switch isRoot {
|
|
||||||
case true:
|
|
||||||
t.Run("TestCopyToSystem_AsRoot", func(t *testing.T) {
|
|
||||||
CopyToSystem(true, tFilePath, "/etc/testfile")
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
t.Run("TestCopyToSystem_AsUser", func(t *testing.T) {
|
|
||||||
CopyToSystem(false, tFilePath, "/etc/testfile")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,11 +12,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cavaliergopher/grab/v3"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/untar"
|
"github.com/HikariKnight/quickpassthrough/pkg/untar"
|
||||||
|
"github.com/cavaliergopher/grab/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generated from github API response using https://mholt.github.io/json-to-go/
|
// Generated from github API response using https://mholt.github.io/json-to-go/
|
||||||
|
@ -96,14 +95,14 @@ type Response struct {
|
||||||
func CheckLsIOMMU() {
|
func CheckLsIOMMU() {
|
||||||
// Check the API for releases
|
// Check the API for releases
|
||||||
resp, err := http.Get("https://api.github.com/repos/hikariknight/ls-iommu/releases/latest")
|
resp, err := http.Get("https://api.github.com/repos/hikariknight/ls-iommu/releases/latest")
|
||||||
common.ErrorCheck(err)
|
errorcheck.ErrorCheck(err)
|
||||||
|
|
||||||
// Close the response when function ends
|
// Close the response when function ends
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// Get the response body
|
// Get the response body
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
common.ErrorCheck(err)
|
errorcheck.ErrorCheck(err)
|
||||||
|
|
||||||
var result Response
|
var result Response
|
||||||
if err := json.Unmarshal(body, &result); err != nil {
|
if err := json.Unmarshal(body, &result); err != nil {
|
||||||
|
@ -112,9 +111,9 @@ func CheckLsIOMMU() {
|
||||||
|
|
||||||
// Make the directory for ls-iommu if it does not exist
|
// Make the directory for ls-iommu if it does not exist
|
||||||
path := "utils"
|
path := "utils"
|
||||||
if exists, _ := fileio.FileExist(path); !exists {
|
if !fileio.FileExist(path) {
|
||||||
err := os.Mkdir(path, os.ModePerm)
|
err := os.Mkdir(path, os.ModePerm)
|
||||||
common.ErrorCheck(err)
|
errorcheck.ErrorCheck(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the download url
|
// Generate the download url
|
||||||
|
@ -134,30 +133,30 @@ func CheckLsIOMMU() {
|
||||||
|
|
||||||
// Get the checksum data
|
// Get the checksum data
|
||||||
checksums, err := http.Get(checkSumsUrl)
|
checksums, err := http.Get(checkSumsUrl)
|
||||||
common.ErrorCheck(err)
|
errorcheck.ErrorCheck(err)
|
||||||
defer checksums.Body.Close()
|
defer checksums.Body.Close()
|
||||||
checksums_txt, err := io.ReadAll(checksums.Body)
|
checksums_txt, err := io.ReadAll(checksums.Body)
|
||||||
common.ErrorCheck(err)
|
errorcheck.ErrorCheck(err)
|
||||||
|
|
||||||
// Check if the tar.gz exists
|
// Check if the tar.gz exists
|
||||||
if exists, _ := fileio.FileExist(fileName); !exists {
|
if !fileio.FileExist(fileName) {
|
||||||
downloadNewVersion(path, fileName, downloadUrl)
|
downloadNewVersion(path, fileName, downloadUrl)
|
||||||
if checkSum(string(checksums_txt), fileName) {
|
if checkSum(string(checksums_txt), fileName) {
|
||||||
err = untar.Untar(fmt.Sprintf("%s/", path), fileName)
|
err = untar.Untar(fmt.Sprintf("%s/", path), fileName)
|
||||||
common.ErrorCheck(err)
|
errorcheck.ErrorCheck(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !checkSum(string(checksums_txt), fileName) {
|
if !checkSum(string(checksums_txt), fileName) {
|
||||||
downloadNewVersion(path, fileName, downloadUrl)
|
downloadNewVersion(path, fileName, downloadUrl)
|
||||||
err = untar.Untar(fmt.Sprintf("%s/", path), fileName)
|
err = untar.Untar(fmt.Sprintf("%s/", path), fileName)
|
||||||
common.ErrorCheck(err)
|
errorcheck.ErrorCheck(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkSum(checksums string, fileName string) bool {
|
func checkSum(checksums string, fileName string) bool {
|
||||||
r, err := os.Open(fileName)
|
r, err := os.Open(fileName)
|
||||||
common.ErrorCheck(err)
|
errorcheck.ErrorCheck(err)
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
|
@ -183,7 +182,7 @@ func downloadNewVersion(path, fileName, downloadUrl string) {
|
||||||
// check for errors
|
// check for errors
|
||||||
if err := download.Err(); err != nil {
|
if err := download.Err(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Download failed: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Download failed: %v\n", err)
|
||||||
if exists, _ := fileio.FileExist("utils/ls-iommu"); !exists {
|
if !fileio.FileExist("utils/ls-iommu") {
|
||||||
log.Fatal("If the above error is 404, then we could not communicate with the GitHub API\n Please manually download and extract ls-iommu to: utils/\nYou can download it from: https://github.com/HikariKnight/ls-iommu/releases")
|
log.Fatal("If the above error is 404, then we could not communicate with the GitHub API\n Please manually download and extract ls-iommu to: utils/\nYou can download it from: https://github.com/HikariKnight/ls-iommu/releases")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Existing ls-iommu binary detected in \"utils/\", will use that instead as the GitHub API did not respond.")
|
fmt.Println("Existing ls-iommu binary detected in \"utils/\", will use that instead as the GitHub API did not respond.")
|
||||||
|
|
|
@ -4,14 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gookit/color"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/configs"
|
"github.com/HikariKnight/quickpassthrough/internal/configs"
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/lsiommu"
|
lsiommu "github.com/HikariKnight/quickpassthrough/internal/lsiommu"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/menu"
|
"github.com/HikariKnight/quickpassthrough/pkg/menu"
|
||||||
|
"github.com/gookit/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SelectGPU(config *configs.Config) {
|
func SelectGPU(config *configs.Config) {
|
||||||
|
@ -95,10 +94,10 @@ func viewGPU(config *configs.Config, ext ...int) {
|
||||||
config.Gpu_IDs = lsiommu.GetIOMMU("-g", mode, "-i", config.Gpu_Group, "--id")
|
config.Gpu_IDs = lsiommu.GetIOMMU("-g", mode, "-i", config.Gpu_Group, "--id")
|
||||||
|
|
||||||
// If the kernel_args file already exists
|
// If the kernel_args file already exists
|
||||||
if exists, _ := fileio.FileExist(config.Path.CMDLINE); exists {
|
if fileio.FileExist(config.Path.CMDLINE) {
|
||||||
// Delete it as we will have to make a new one anyway
|
// Delete it as we will have to make a new one anyway
|
||||||
err := os.Remove(config.Path.CMDLINE)
|
err := os.Remove(config.Path.CMDLINE)
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Could not remove %s", config.Path.CMDLINE))
|
errorcheck.ErrorCheck(err, fmt.Sprintf("Could not remove %s", config.Path.CMDLINE))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write initial kernel_arg file
|
// Write initial kernel_arg file
|
||||||
|
@ -115,10 +114,10 @@ func viewGPU(config *configs.Config, ext ...int) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// If the kernel_args file already exists
|
// If the kernel_args file already exists
|
||||||
if exists, _ := fileio.FileExist(config.Path.CMDLINE); exists {
|
if fileio.FileExist(config.Path.CMDLINE) {
|
||||||
// Delete it as we will have to make a new one anyway
|
// Delete it as we will have to make a new one anyway
|
||||||
err := os.Remove(config.Path.CMDLINE)
|
err := os.Remove(config.Path.CMDLINE)
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Could not remove %s", config.Path.CMDLINE))
|
errorcheck.ErrorCheck(err, fmt.Sprintf("Could not remove %s", config.Path.CMDLINE))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write initial kernel_arg file
|
// Write initial kernel_arg file
|
||||||
|
|
|
@ -26,34 +26,21 @@ func genVBIOS_dumper(config *configs.Config) {
|
||||||
scriptdir, _ = os.Getwd()
|
scriptdir, _ = os.Getwd()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for a vbios path and generate the vbios dumping script if found
|
// Get the vbios path and generate the vbios dumping script
|
||||||
vbios_paths := lsiommu.GetIOMMU("-g", "-i", config.Gpu_Group, "--rom")
|
vbios_path := lsiommu.GetIOMMU("-g", "-i", config.Gpu_Group, "--rom")[0]
|
||||||
if len(vbios_paths) != 0 {
|
configs.GenerateVBIOSDumper(vbios_path)
|
||||||
configs.GenerateVBIOSDumper(vbios_paths[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the qemu config folder
|
|
||||||
os.Mkdir(fmt.Sprintf("%s/%s", scriptdir, config.Path.QEMU), os.ModePerm)
|
|
||||||
|
|
||||||
// Generate a dummy rom (1MB rom of zeroes) for use with AMD RX 7000 series cards by recommendation from Gnif
|
|
||||||
// Source: https://forum.level1techs.com/t/the-state-of-amd-rx-7000-series-vfio-passthrough-april-2024/210242
|
|
||||||
command.Run("dd", "if=/dev/zero", fmt.Sprintf("of=%s/%s/dummy.rom", scriptdir, config.Path.QEMU), "bs=1M", "count=1")
|
|
||||||
|
|
||||||
// Write a title
|
// Write a title
|
||||||
title := color.New(color.BgHiBlue, color.White, color.Bold)
|
title := color.New(color.BgHiBlue, color.White, color.Bold)
|
||||||
title.Println("VBIOS roms for Passthrough")
|
title.Println("Generated \"dump VBIOS\" script")
|
||||||
|
|
||||||
// Tell users about the VBIOS dumper script and dummy rom for RX 7000 series cards
|
// Tell users about the VBIOS dumper script
|
||||||
fmt.Print(
|
fmt.Print(
|
||||||
"If you have an RX 7000 series (and possibly newer AMD cards) GPUs, please use the dummy.rom file\n",
|
"For some GPUs, you will need to dump the VBIOS and pass the\n",
|
||||||
fmt.Sprintf("%s/%s/dummy.rom\n", scriptdir, config.Path.QEMU),
|
|
||||||
"Or disable ROM BAR for the card in qemu/libvirt\n",
|
|
||||||
"\n",
|
|
||||||
"For some other GPUs, you will need to instead dump the VBIOS (and possibly patch it) and pass the\n",
|
|
||||||
"rom to the VM along with the card in order to get a functional passthrough.\n",
|
"rom to the VM along with the card in order to get a functional passthrough.\n",
|
||||||
"In many cases you can find your vbios at https://www.techpowerup.com/vgabios/\n",
|
"In many cases you can find your vbios at https://www.techpowerup.com/vgabios/\n",
|
||||||
"\n",
|
"\n",
|
||||||
"If we found a romfile for your GPU you can also attempt to dump your own vbios from TTY using the script in\n",
|
"You can also attempt to dump your own vbios using the script in\n",
|
||||||
fmt.Sprintf("%s/utils/dump_vbios.sh\n", scriptdir),
|
fmt.Sprintf("%s/utils/dump_vbios.sh\n", scriptdir),
|
||||||
"\n",
|
"\n",
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,12 +4,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gookit/color"
|
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/configs"
|
"github.com/HikariKnight/quickpassthrough/internal/configs"
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/lsiommu"
|
lsiommu "github.com/HikariKnight/quickpassthrough/internal/lsiommu"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/menu"
|
"github.com/HikariKnight/quickpassthrough/pkg/menu"
|
||||||
|
"github.com/gookit/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
func selectUSB(config *configs.Config) {
|
func selectUSB(config *configs.Config) {
|
||||||
|
|
|
@ -6,34 +6,34 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/gookit/color"
|
|
||||||
"golang.org/x/term"
|
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/configs"
|
"github.com/HikariKnight/quickpassthrough/internal/configs"
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
"github.com/HikariKnight/quickpassthrough/pkg/command"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/menu"
|
"github.com/HikariKnight/quickpassthrough/pkg/menu"
|
||||||
"github.com/HikariKnight/quickpassthrough/pkg/uname"
|
"github.com/HikariKnight/quickpassthrough/pkg/uname"
|
||||||
|
"github.com/gookit/color"
|
||||||
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
func prepModules(config *configs.Config) {
|
func prepModules(config *configs.Config) {
|
||||||
// If we have files for modprobe
|
// If we have files for modprobe
|
||||||
if exists, _ := fileio.FileExist(config.Path.MODPROBE); exists {
|
if fileio.FileExist(config.Path.MODPROBE) {
|
||||||
// Configure modprobe
|
// Configure modprobe
|
||||||
configs.Set_Modprobe(config.Gpu_IDs)
|
configs.Set_Modprobe(config.Gpu_IDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a folder for dracut
|
// If we have a folder for dracut
|
||||||
if exists, _ := fileio.FileExist(config.Path.DRACUT); exists {
|
if fileio.FileExist(config.Path.DRACUT) {
|
||||||
// Configure dracut
|
// Configure dracut
|
||||||
configs.Set_Dracut()
|
configs.Set_Dracut()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a mkinitcpio.conf file
|
// If we have a mkinitcpio.conf file
|
||||||
if exists, _ := fileio.FileExist(config.Path.MKINITCPIO); exists {
|
if fileio.FileExist(config.Path.MKINITCPIO) {
|
||||||
configs.Set_Mkinitcpio()
|
configs.Set_Mkinitcpio()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,43 +48,6 @@ func prepModules(config *configs.Config) {
|
||||||
finalize(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) {
|
func finalize(config *configs.Config) {
|
||||||
// Clear the screen
|
// Clear the screen
|
||||||
command.Clear()
|
command.Clear()
|
||||||
|
@ -93,43 +56,60 @@ func finalize(config *configs.Config) {
|
||||||
title := color.New(color.BgHiBlue, color.White, color.Bold)
|
title := color.New(color.BgHiBlue, color.White, color.Bold)
|
||||||
title.Println("Finalizing configuration")
|
title.Println("Finalizing configuration")
|
||||||
|
|
||||||
config.IsRoot = os.Getuid() == 0
|
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 \"quickemu\" folder contains files that might be\n useable for quickemu in the future\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",
|
||||||
|
)
|
||||||
|
|
||||||
finalizeNotice(config.IsRoot)
|
// 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.")
|
||||||
|
|
||||||
// Make a choice of going next or back and parse the choice
|
// Parse the choice
|
||||||
switch menu.Next("Press Next to continue with sudo using STDIN, ESC to exit or Back to go back.") {
|
switch choice {
|
||||||
case "next":
|
case "next":
|
||||||
installPassthrough(config)
|
installPassthrough(config)
|
||||||
|
|
||||||
case "back":
|
case "back":
|
||||||
// Go back
|
// Go back
|
||||||
disableVideo(config)
|
disableVideo(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func installPassthrough(config *configs.Config) {
|
func installPassthrough(config *configs.Config) {
|
||||||
// Get the user data
|
// Get the user data
|
||||||
currentUser, err := user.Current()
|
user, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if !config.IsRoot {
|
// Provide a password prompt
|
||||||
// Provide a password prompt
|
fmt.Printf("[sudo] password for %s: ", user.Username)
|
||||||
fmt.Printf("[sudo] password for %s: ", currentUser.Username)
|
bytep, err := term.ReadPassword(int(syscall.Stdin))
|
||||||
bytep, err := term.ReadPassword(syscall.Stdin)
|
if err != nil {
|
||||||
if err != nil {
|
os.Exit(1)
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
fmt.Print("\n")
|
|
||||||
|
|
||||||
// Elevate with sudo
|
|
||||||
command.Elevate(
|
|
||||||
base64.StdEncoding.EncodeToString(
|
|
||||||
bytep,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
fmt.Print("\n")
|
||||||
|
|
||||||
|
// Elevate with sudo
|
||||||
|
command.Elevate(
|
||||||
|
base64.StdEncoding.EncodeToString(
|
||||||
|
bytep,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// Make an output string
|
// Make an output string
|
||||||
var output string
|
var output string
|
||||||
|
@ -140,24 +120,22 @@ func installPassthrough(config *configs.Config) {
|
||||||
logger.Printf("Configuring systemd-boot using kernelstub\n")
|
logger.Printf("Configuring systemd-boot using kernelstub\n")
|
||||||
|
|
||||||
// Configure kernelstub
|
// Configure kernelstub
|
||||||
// callee logs the output and checks for errors
|
output = configs.Set_KernelStub()
|
||||||
configs.Set_KernelStub(config.IsRoot)
|
fmt.Printf("%s\n", output)
|
||||||
|
|
||||||
} else if config.Bootloader == "grubby" {
|
} else if config.Bootloader == "grubby" {
|
||||||
// Write to logger
|
// Write to logger
|
||||||
logger.Printf("Configuring bootloader using grubby\n")
|
logger.Printf("Configuring bootloader using grubby\n")
|
||||||
|
|
||||||
// Configure kernelstub
|
// Configure kernelstub
|
||||||
output = configs.Set_Grubby(config.IsRoot)
|
output = configs.Set_Grubby()
|
||||||
fmt.Printf("%s\n", output)
|
fmt.Printf("%s\n", output)
|
||||||
|
|
||||||
} else if config.Bootloader == "grub2" {
|
} else if config.Bootloader == "grub2" {
|
||||||
// Write to logger
|
// Write to logger
|
||||||
logger.Printf("Applying grub2 changes\n")
|
logger.Printf("Applying grub2 changes\n")
|
||||||
_ = configs.Set_Grub2(config.IsRoot) // note: we set config.IsRoot earlier
|
grub_output, _ := configs.Set_Grub2()
|
||||||
|
fmt.Printf("%s\n", strings.Join(grub_output, "\n"))
|
||||||
// we'll print the output in the [configs.Set_Grub2] method
|
|
||||||
// fmt.Printf("%s\n", strings.Join(grub_output, "\n"))
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
|
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
|
||||||
|
@ -167,62 +145,68 @@ func installPassthrough(config *configs.Config) {
|
||||||
// A lot of linux systems support modprobe along with their own module system
|
// A lot of linux systems support modprobe along with their own module system
|
||||||
// So copy the modprobe files if we have them
|
// So copy the modprobe files if we have them
|
||||||
modprobeFile := fmt.Sprintf("%s/vfio.conf", config.Path.MODPROBE)
|
modprobeFile := fmt.Sprintf("%s/vfio.conf", config.Path.MODPROBE)
|
||||||
|
if fileio.FileExist(modprobeFile) {
|
||||||
// lets hope by now we've already handled any permissions issues...
|
// Copy initramfs-tools module to system
|
||||||
// TODO: verify that we actually can drop the errors on [fileio.FileExist] call below
|
output = configs.CopyToSystem(modprobeFile, "/etc/modprobe.d/vfio.conf")
|
||||||
|
fmt.Printf("%s\n", output)
|
||||||
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
|
// Copy the config files for the system we have
|
||||||
initramfsFile := fmt.Sprintf("%s/modules", config.Path.INITRAMFS)
|
initramfsFile := fmt.Sprintf("%s/modules", config.Path.INITRAMFS)
|
||||||
dracutFile := fmt.Sprintf("%s/vfio.conf", config.Path.DRACUT)
|
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
|
// Copy initramfs-tools module to system
|
||||||
configs.CopyToSystem(config.IsRoot, initramfsFile, "/etc/initramfs-tools/modules")
|
output = configs.CopyToSystem(initramfsFile, "/etc/initramfs-tools/modules")
|
||||||
|
fmt.Printf("%s\n", output)
|
||||||
|
|
||||||
// Copy the modules file to /etc/modules
|
// Copy the modules file to /etc/modules
|
||||||
configs.CopyToSystem(config.IsRoot, config.Path.ETCMODULES, "/etc/modules")
|
output = configs.CopyToSystem(config.Path.ETCMODULES, "/etc/modules")
|
||||||
|
fmt.Printf("%s\n", output)
|
||||||
|
|
||||||
if err = command.ExecAndLogSudo(config.IsRoot, true, "update-initramfs", "-u"); err != nil {
|
// Write to logger
|
||||||
log.Fatalf("Failed to update initramfs: %s", err)
|
logger.Printf("Executing: sudo update-initramfs -u\n")
|
||||||
}
|
|
||||||
|
|
||||||
case dracutExists:
|
// 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) {
|
||||||
// Copy dracut config to /etc/dracut.conf.d/vfio
|
// Copy dracut config to /etc/dracut.conf.d/vfio
|
||||||
configs.CopyToSystem(config.IsRoot, dracutFile, "/etc/dracut.conf.d/vfio")
|
output = configs.CopyToSystem(dracutFile, "/etc/dracut.conf.d/vfio")
|
||||||
|
fmt.Printf("%s\n", output)
|
||||||
|
|
||||||
// Get systeminfo
|
// Get systeminfo
|
||||||
sysinfo := uname.New()
|
sysinfo := uname.New()
|
||||||
|
|
||||||
if err = command.ExecAndLogSudo(config.IsRoot, true, "dracut", "-f", "-v", "--kver", sysinfo.Release); err != nil {
|
// Write to logger
|
||||||
log.Fatalf("Failed to update initramfs: %s", err)
|
logger.Printf("Executing: sudo dracut -f -v --kver %s\n", sysinfo.Release)
|
||||||
}
|
|
||||||
|
|
||||||
case mkinitcpioExists:
|
// 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) {
|
||||||
// Copy dracut config to /etc/dracut.conf.d/vfio
|
// Copy dracut config to /etc/dracut.conf.d/vfio
|
||||||
configs.CopyToSystem(config.IsRoot, config.Path.MKINITCPIO, "/etc/mkinitcpio.conf")
|
output = configs.CopyToSystem(config.Path.MKINITCPIO, "/etc/mkinitcpio.conf")
|
||||||
|
fmt.Printf("%s\n", output)
|
||||||
|
|
||||||
if err = command.ExecAndLogSudo(config.IsRoot, true, "mkinitcpio", "-P"); err != nil {
|
// Write to logger
|
||||||
log.Fatalf("Failed to update initramfs: %s", err)
|
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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure prompt end up on next line
|
// Make sure prompt end up on next line
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
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")
|
|
||||||
}
|
|
|
@ -6,18 +6,17 @@ package internal
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/pages"
|
"github.com/HikariKnight/quickpassthrough/internal/pages"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is where we build everything
|
// This is where we build everything
|
||||||
func Tui() {
|
func Tui() {
|
||||||
// Log all errors to a new logfile (super useful feature of BubbleTea!)
|
// Log all errors to a new logfile (super useful feature of BubbleTea!)
|
||||||
_ = os.Rename("quickpassthrough_debug.log", "quickpassthrough_debug_old.log")
|
os.Remove("debug.log")
|
||||||
logfile, err := tea.LogToFile("quickpassthrough_debug.log", "")
|
logfile, err := tea.LogToFile("debug.log", "")
|
||||||
common.ErrorCheck(err, "Error creating log file")
|
errorcheck.ErrorCheck(err, "Error creating log file")
|
||||||
defer logfile.Close()
|
defer logfile.Close()
|
||||||
|
|
||||||
// New WIP Tui
|
// New WIP Tui
|
||||||
|
|
|
@ -3,18 +3,14 @@ package command
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Run a command and return STDOUT
|
|
||||||
func Run(binary string, args ...string) ([]string, error) {
|
func Run(binary string, args ...string) ([]string, error) {
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
|
|
||||||
|
@ -30,14 +26,14 @@ func Run(binary string, args ...string) ([]string, error) {
|
||||||
output, _ := io.ReadAll(&stdout)
|
output, _ := io.ReadAll(&stdout)
|
||||||
|
|
||||||
// Get the output
|
// Get the output
|
||||||
outputs := make([]string, 0, 1)
|
outputs := []string{}
|
||||||
outputs = append(outputs, string(output))
|
outputs = append(outputs, string(output))
|
||||||
|
|
||||||
// Return our list of items
|
// Return our list of items
|
||||||
return outputs, err
|
return outputs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunErr is just like command.Run() but also returns STDERR
|
// This function is just like command.Run() but also returns STDERR
|
||||||
func RunErr(binary string, args ...string) ([]string, []string, error) {
|
func RunErr(binary string, args ...string) ([]string, []string, error) {
|
||||||
var stdout, stderr bytes.Buffer
|
var stdout, stderr bytes.Buffer
|
||||||
|
|
||||||
|
@ -62,18 +58,8 @@ func RunErr(binary string, args ...string) ([]string, []string, error) {
|
||||||
return outputs, outerrs, err
|
return outputs, outerrs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunErrSudo(isRoot bool, binary string, args ...string) ([]string, []string, error) {
|
// This functions runs the command "sudo -Sk -- echo", this forces sudo
|
||||||
if !isRoot && binary != "sudo" {
|
// to re-authenticate and lets us enter the password to STDIN
|
||||||
args = append([]string{binary}, args...)
|
|
||||||
binary = "sudo"
|
|
||||||
}
|
|
||||||
logger.Printf("Executing (elevated): %s %s\n", binary, strings.Join(args, " "))
|
|
||||||
fmt.Printf("Executing (elevated): %s %s\n", binary, strings.Join(args, " "))
|
|
||||||
return RunErr(binary, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elevate elevates this functions runs the command "sudo -Sk -- echo",
|
|
||||||
// this forces sudo to re-authenticate and lets us enter the password to STDIN
|
|
||||||
// giving us the ability to run sudo commands
|
// giving us the ability to run sudo commands
|
||||||
func Elevate(password string) {
|
func Elevate(password string) {
|
||||||
// Do a simple sudo command to just authenticate with sudo
|
// Do a simple sudo command to just authenticate with sudo
|
||||||
|
@ -84,83 +70,29 @@ func Elevate(password string) {
|
||||||
|
|
||||||
// Open STDIN
|
// Open STDIN
|
||||||
stdin, err := cmd.StdinPipe()
|
stdin, err := cmd.StdinPipe()
|
||||||
common.ErrorCheck(err, "\nFailed to get sudo STDIN")
|
errorcheck.ErrorCheck(err, "\nFailed to get sudo STDIN")
|
||||||
|
|
||||||
// Start the authentication
|
// Start the authentication
|
||||||
err = cmd.Start()
|
cmd.Start()
|
||||||
common.ErrorCheck(err, "\nFailed to start sudo command")
|
|
||||||
|
|
||||||
// Get the passed password
|
// Get the passed password
|
||||||
pw, _ := base64.StdEncoding.DecodeString(password)
|
pw, _ := base64.StdEncoding.DecodeString(password)
|
||||||
_, err = stdin.Write([]byte(string(pw) + "\n"))
|
_, err = stdin.Write([]byte(string(pw) + "\n"))
|
||||||
common.ErrorCheck(err, "\nFailed at typing to STDIN")
|
errorcheck.ErrorCheck(err, "\nFailed at typing to STDIN")
|
||||||
// Clear the password
|
// Clear the password
|
||||||
pw = nil
|
pw = nil
|
||||||
password = ""
|
password = ""
|
||||||
|
|
||||||
_ = stdin.Close()
|
stdin.Close()
|
||||||
|
|
||||||
// Wait for the sudo prompt (If the correct password was given, it will not stay behind)
|
// Wait for the sudo prompt (If the correct password was given, it will not stay behind)
|
||||||
err = cmd.Wait()
|
err = cmd.Wait()
|
||||||
common.ErrorCheck(err, "\nError, password given was wrong")
|
errorcheck.ErrorCheck(err, "\nError, password given was wrong")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear clears the terminal.
|
// Function to just clear the terminal
|
||||||
func Clear() {
|
func Clear() {
|
||||||
c := exec.Command("clear")
|
c := exec.Command("clear")
|
||||||
c.Stdout = os.Stdout
|
c.Stdout = os.Stdout
|
||||||
_ = c.Run()
|
c.Run()
|
||||||
}
|
|
||||||
|
|
||||||
// ExecAndLogSudo executes an elevated command and logs the output.
|
|
||||||
//
|
|
||||||
// * if we're root, the command is executed directly
|
|
||||||
// * if we're not root, the command is prefixed with "sudo"
|
|
||||||
//
|
|
||||||
// - noisy determines if we should print the command to the user
|
|
||||||
// noisy isn't set to true by our copy caller, as it logs differently,
|
|
||||||
// but other callers set it.
|
|
||||||
func ExecAndLogSudo(isRoot, noisy bool, exe string, args ...string) error {
|
|
||||||
if !isRoot && exe != "sudo" {
|
|
||||||
og := exe
|
|
||||||
exe = "sudo"
|
|
||||||
newArgs := make([]string, 0)
|
|
||||||
newArgs = append(newArgs, og)
|
|
||||||
newArgs = append(newArgs, args...)
|
|
||||||
args = newArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write to logger
|
|
||||||
logger.Printf("Executing (elevated): %s %s\n", exe, strings.Join(args, " "))
|
|
||||||
|
|
||||||
if noisy {
|
|
||||||
// Print to the user
|
|
||||||
fmt.Printf("Executing (elevated): %s %s\nSee debug.log for detailed output\n", exe, strings.Join(args, " "))
|
|
||||||
}
|
|
||||||
|
|
||||||
wd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
r := exec.Command(exe, args...)
|
|
||||||
r.Dir = wd
|
|
||||||
|
|
||||||
cmdCombinedOut, err := r.CombinedOutput()
|
|
||||||
outStr := string(cmdCombinedOut)
|
|
||||||
|
|
||||||
// Write to logger, tabulate output
|
|
||||||
// tabulation denotes it's hierarchy as a child of the command
|
|
||||||
outStr = strings.ReplaceAll(outStr, "\n", "\n\t")
|
|
||||||
logger.Printf("\t" + string(cmdCombinedOut) + "\n")
|
|
||||||
if noisy {
|
|
||||||
// Print to the user
|
|
||||||
fmt.Printf("%s\n", outStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("failed to execute %s: %w\n%s", exe, err, outStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
package command
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
const fakeSudo = `#!/bin/sh
|
|
||||||
"$@" -qptest`
|
|
||||||
|
|
||||||
const fakeUtil = `#!/bin/sh
|
|
||||||
echo "$@"
|
|
||||||
if [ "$4" = "-qptest" ]; then exit 0; else exit 1; fi`
|
|
||||||
|
|
||||||
func setupExecTestEnv(t *testing.T) (string, string) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
tmpDir := t.TempDir()
|
|
||||||
fakeSudoPath := filepath.Join(tmpDir, "sudo")
|
|
||||||
fakeUtilPath := filepath.Join(tmpDir, "util")
|
|
||||||
|
|
||||||
if err := os.WriteFile(fakeSudoPath, []byte(fakeSudo), 0755); err != nil {
|
|
||||||
t.Fatalf("failed to write fake sudo stub: %s", err.Error())
|
|
||||||
}
|
|
||||||
if err := os.WriteFile(fakeUtilPath, []byte(fakeUtil), 0755); err != nil {
|
|
||||||
t.Fatalf("failed to write fake util stub: %s", err.Error())
|
|
||||||
}
|
|
||||||
t.Setenv("PATH", tmpDir+":"+os.Getenv("PATH"))
|
|
||||||
|
|
||||||
return fakeSudoPath, fakeUtilPath
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExecAndLogSudo(t *testing.T) {
|
|
||||||
_, fakeUtilPath := setupExecTestEnv(t)
|
|
||||||
|
|
||||||
args := []string{"i am a string with spaces", "i came to ruin parsers and chew bubble gum", "and I'm all out of bubblegum."}
|
|
||||||
|
|
||||||
t.Run("is_not_root", func(t *testing.T) {
|
|
||||||
if err := ExecAndLogSudo(false, false, "util", args...); err != nil {
|
|
||||||
t.Errorf("unexpected error: %s", err.Error())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("is_root", func(t *testing.T) {
|
|
||||||
newFakeUtil := strings.Replace(fakeUtil, "exit 1", "exit 0", 1)
|
|
||||||
newFakeUtil = strings.Replace(newFakeUtil, "exit 0", "exit 1", 1)
|
|
||||||
if err := os.WriteFile(fakeUtilPath, []byte(newFakeUtil), 0755); err != nil {
|
|
||||||
t.Fatalf("failed to overwrite fake util with modified stub: %s", err.Error())
|
|
||||||
}
|
|
||||||
if err := ExecAndLogSudo(false, false, "util", args...); err == nil {
|
|
||||||
t.Errorf("expected error when using modified util with sudo, got nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ExecAndLogSudo(true, true, "util", args...); err != nil {
|
|
||||||
t.Errorf("unexpected error: %s", err.Error())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
|
@ -7,31 +7,29 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This just implements repetetive tasks I have to do with files
|
* This just implements repetetive tasks I have to do with files
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// AppendContent creates a file and appends the content to the file.
|
// Creates a file and appends the content to the file (ending newline must be supplied with content string)
|
||||||
// (ending newline must be supplied with content string)
|
|
||||||
func AppendContent(content string, fileName string) {
|
func AppendContent(content string, fileName string) {
|
||||||
// Open the file
|
// Open the file
|
||||||
f, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
|
f, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
|
||||||
|
errorcheck.ErrorCheck(err, fmt.Sprintf("Error opening \"%s\" for writing", fileName))
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Error opening \"%s\" for writing", fileName))
|
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
// Write the content
|
// Write the content
|
||||||
_, err = f.WriteString(content)
|
_, err = f.WriteString(content)
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Error writing to %s", fileName))
|
errorcheck.ErrorCheck(err, fmt.Sprintf("Error writing to %s", fileName))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadLines reads the file and returns a stringlist with each line.
|
// Reads the file and returns a stringlist with each line
|
||||||
func ReadLines(fileName string) []string {
|
func ReadLines(fileName string) []string {
|
||||||
content, err := os.Open(fileName)
|
content, err := os.Open(fileName)
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Error reading file %s", fileName))
|
errorcheck.ErrorCheck(err, fmt.Sprintf("Error reading file %s", fileName))
|
||||||
defer content.Close()
|
defer content.Close()
|
||||||
|
|
||||||
// Make a list of lines
|
// Make a list of lines
|
||||||
|
@ -48,55 +46,54 @@ func ReadLines(fileName string) []string {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFile reads a file and returns all the content as a string.
|
// Reads a file and returns all the content as a string
|
||||||
func ReadFile(fileName string) string {
|
func ReadFile(fileName string) string {
|
||||||
// Read the whole file
|
// Read the whole file
|
||||||
content, err := os.ReadFile(fileName)
|
content, err := os.ReadFile(fileName)
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Failed to ReadFile on %s", fileName))
|
errorcheck.ErrorCheck(err, fmt.Sprintf("Failed to ReadFile on %s", fileName))
|
||||||
|
|
||||||
// Return all the lines as one string
|
// Return all the lines as one string
|
||||||
return string(content)
|
return string(content)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileExist checks if a file exists and returns a bool and any error that isn't os.ErrNotExist.
|
// Checks if a file exists and returns a bool
|
||||||
func FileExist(fileName string) (bool, error) {
|
func FileExist(fileName string) bool {
|
||||||
var exist bool
|
var exist bool
|
||||||
|
|
||||||
// Check if the file exists
|
// Check if the file exists
|
||||||
_, err := os.Stat(fileName)
|
if _, err := os.Stat(fileName); !errors.Is(err, os.ErrNotExist) {
|
||||||
switch {
|
|
||||||
case err == nil:
|
|
||||||
exist = true
|
|
||||||
case errors.Is(err, os.ErrNotExist):
|
|
||||||
// Set the value to true
|
// Set the value to true
|
||||||
|
exist = true
|
||||||
|
} else {
|
||||||
|
// Set the value to false
|
||||||
exist = false
|
exist = false
|
||||||
err = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return if the file exists
|
// Return if the file exists
|
||||||
return exist, err
|
return exist
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileCopy copies a FILE from source to dest.
|
// Copies a FILE from source to dest
|
||||||
func FileCopy(sourceFile, destFile string) {
|
func FileCopy(sourceFile, destFile string) {
|
||||||
// Get the file info
|
// Get the file info
|
||||||
filestat, err := os.Stat(sourceFile)
|
filestat, err := os.Stat(sourceFile)
|
||||||
common.ErrorCheck(err, "Error getting fileinfo of: %s", sourceFile)
|
errorcheck.ErrorCheck(err, "Error getting fileinfo of: %s", sourceFile)
|
||||||
|
|
||||||
// If the file is a regular file
|
// If the file is a regular file
|
||||||
if filestat.Mode().IsRegular() {
|
if filestat.Mode().IsRegular() {
|
||||||
// Open the source file for reading
|
// Open the source file for reading
|
||||||
source, err := os.Open(sourceFile)
|
source, err := os.Open(sourceFile)
|
||||||
common.ErrorCheck(err, "Error opening %s for copying", sourceFile)
|
errorcheck.ErrorCheck(err, "Error opening %s for copying", sourceFile)
|
||||||
defer source.Close()
|
defer source.Close()
|
||||||
|
|
||||||
// Create the destination file
|
// Create the destination file
|
||||||
dest, err := os.Create(destFile)
|
dest, err := os.Create(destFile)
|
||||||
common.ErrorCheck(err, "Error creating %s", destFile)
|
errorcheck.ErrorCheck(err, "Error creating %s", destFile)
|
||||||
defer dest.Close()
|
defer dest.Close()
|
||||||
|
|
||||||
// Copy the contents of source to dest using io
|
// Copy the contents of source to dest using io
|
||||||
_, err = io.Copy(dest, source)
|
_, err = io.Copy(dest, source)
|
||||||
common.ErrorCheck(err, "Failed to copy \"%s\" to \"%s\"", sourceFile, destFile)
|
errorcheck.ErrorCheck(err, "Failed to copy \"%s\" to \"%s\"", sourceFile, destFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
"github.com/gookit/color"
|
"github.com/gookit/color"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ManualInput(msg string, format string) []string {
|
func ManualInput(msg string, format string) []string {
|
||||||
|
@ -19,7 +18,7 @@ func ManualInput(msg string, format string) []string {
|
||||||
// Get the user input
|
// Get the user input
|
||||||
var input string
|
var input string
|
||||||
_, err := fmt.Scan(&input)
|
_, err := fmt.Scan(&input)
|
||||||
common.ErrorCheck(err)
|
errorcheck.ErrorCheck(err)
|
||||||
|
|
||||||
input_list := strings.Split(input, ",")
|
input_list := strings.Split(input, ",")
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/HikariKnight/quickpassthrough/internal/common"
|
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Slightly modified from source: https://medium.com/@skdomino/taring-untaring-files-in-go-6b07cf56bc07
|
// Slightly modified from source: https://medium.com/@skdomino/taring-untaring-files-in-go-6b07cf56bc07
|
||||||
|
@ -17,7 +17,7 @@ import (
|
||||||
// creating the file structure at 'dst' along the way, and writing any files
|
// creating the file structure at 'dst' along the way, and writing any files
|
||||||
func Untar(dst string, fileName string) error {
|
func Untar(dst string, fileName string) error {
|
||||||
r, err := os.Open(fileName)
|
r, err := os.Open(fileName)
|
||||||
common.ErrorCheck(err, fmt.Sprintf("Failed to open: %s", fileName))
|
errorcheck.ErrorCheck(err, fmt.Sprintf("Failed to open: %s", fileName))
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
||||||
gzr, err := gzip.NewReader(r)
|
gzr, err := gzip.NewReader(r)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue