Heavy refactoring, see PR #28

This commit is contained in:
kayos@tcp.direct 2024-06-16 23:57:19 -07:00
parent ab104bb7bc
commit 3337efcb8f
No known key found for this signature in database
GPG key ID: 4B841471B4BEE979
11 changed files with 323 additions and 156 deletions

View file

@ -1,16 +1,19 @@
package configs
import (
"errors"
"fmt"
"os"
"os/exec"
"regexp"
"strings"
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
"github.com/klauspost/cpuid/v2"
"github.com/HikariKnight/quickpassthrough/internal/logger"
"github.com/HikariKnight/quickpassthrough/pkg/command"
"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
@ -70,38 +73,32 @@ func Set_Cmdline(gpu_IDs []string) {
fileio.AppendContent(fmt.Sprintf(" vfio_pci.ids=%s", strings.Join(gpu_IDs, ",")), config.Path.CMDLINE)
}
// Configures systemd-boot using kernelstub
func Set_KernelStub() string {
// Set_KernelStub configures systemd-boot using kernelstub.
func Set_KernelStub(isRoot bool) {
// Get the config
config := GetConfig()
// Get the kernel args
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
// Write to logger
logger.Printf("Running command:\nsudo kernelstub -a \"%s\"\n", kernel_args)
// 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)
// Run and log, check for errors
errorcheck.ErrorCheck(command.ExecAndLogSudo(isRoot, true,
"kernelstub -a "+kernel_args,
),
"Error, kernelstub command returned exit code 1",
)
}
// Configures grub2 and/or systemd-boot using grubby
func Set_Grubby() string {
// Set_Grubby configures grub2 and/or systemd-boot using grubby
func Set_Grubby(isRoot bool) string {
// Get the config
config := GetConfig()
// Get the kernel args
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
// Write to logger
logger.Printf("Running command:\nsudo grubby --update-kernel=ALL --args=\"%s\"\n", kernel_args)
// Run the command
_, err := command.Run("sudo", "grubby", "--update-kernel=ALL", fmt.Sprintf("--args=%s", kernel_args))
// Run and log, check for errors
err := command.ExecAndLogSudo(isRoot, true, "grubby --update-kernel=ALL "+fmt.Sprintf("--args=%s", kernel_args))
errorcheck.ErrorCheck(err, "Error, grubby command returned exit code 1")
// Return what we did
@ -116,8 +113,8 @@ func Configure_Grub2() {
conffile := fmt.Sprintf("%s/grub", config.Path.DEFAULT)
// Make sure we start from scratch by deleting any old file
if fileio.FileExist(conffile) {
os.Remove(conffile)
if exists, _ := fileio.FileExist(conffile); exists {
_ = os.Remove(conffile)
}
// Make a regex to get the system path instead of the config path
@ -201,8 +198,8 @@ func clean_Grub2_Args(old_kernel_args []string) []string {
return clean_kernel_args
}
// This function copies our config to /etc/default/grub and updates grub
func Set_Grub2() ([]string, error) {
// Set_Grub2 copies our config to /etc/default/grub and updates grub
func Set_Grub2(isRoot bool) error {
// Get the config
config := GetConfig()
@ -213,38 +210,45 @@ func Set_Grub2() ([]string, error) {
sysfile_re := regexp.MustCompile(`^config`)
sysfile := sysfile_re.ReplaceAllString(conffile, "")
// Write to logger
logger.Printf("Executing command:\nsudo cp -v \"%s\" %s\n", conffile, sysfile)
// [CopyToSystem] will log the operation
// logger.Printf("Executing command:\nsudo cp -v \"%s\" %s\n", conffile, sysfile)
// Make our output slice
var output []string
// Copy files to system
output = append(output, CopyToSystem(conffile, sysfile))
// Copy files to system, logging and error checking is done in the function
CopyToSystem(isRoot, conffile, sysfile)
// Set a variable for the mkconfig command
mkconfig := "grub-mkconfig"
var mkconfig string
var grubPath = "/boot/grub/grub.cfg"
var lpErr error
// Check for grub-mkconfig
_, err := command.Run("which", "grub-mkconfig")
if err == nil {
// Set binary as grub-mkconfig
mkconfig = "grub-mkconfig"
} else {
mkconfig = "grub2-mkconfig"
mkconfig, lpErr = exec.LookPath("grub-mkconfig")
switch {
case errors.Is(lpErr, exec.ErrNotFound) || mkconfig == "":
// Check for grub2-mkconfig
mkconfig, lpErr = exec.LookPath("grub2-mkconfig")
if lpErr == nil && 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 [errorcheck.ErrorCheck].
lpErr = errors.New("neither grub-mkconfig or grub2-mkconfig found")
}
errorcheck.ErrorCheck(lpErr, lpErr.Error()+"\n")
return lpErr // note: unreachable as [errorcheck.ErrorCheck] calls fatal
default:
}
// 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")
}
_, mklog, err := command.RunErrSudo(isRoot, mkconfig, "-o", grubPath)
return output, err
// tabulate the output, [command.RunErrSudo] logged the execution.
logger.Printf("\t" + strings.Join(mklog, "\n\t"))
errorcheck.ErrorCheck(err, "Failed to update /boot/grub/grub.cfg")
// always returns nil as [errorcheck.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
}

View file

@ -9,7 +9,7 @@ import (
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
)
// This function writes a dracut configuration file for /etc/dracut.conf.d/
// Set_Dracut writes a dracut configuration file for `/etc/dracut.conf.d/`.
func Set_Dracut() {
config := GetConfig()
@ -17,8 +17,8 @@ func Set_Dracut() {
dracutConf := fmt.Sprintf("%s/vfio.conf", config.Path.DRACUT)
// If the file already exists then delete it
if fileio.FileExist(dracutConf) {
os.Remove(dracutConf)
if exists, _ := fileio.FileExist(dracutConf); exists {
_ = os.Remove(dracutConf)
}
// Write to logger

View file

@ -10,14 +10,14 @@ import (
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
)
// This function copies the content of /etc/mkinitcpio.conf to the config folder and does an inline replace/insert on the MODULES=() line
// Set_Mkinitcpio copies the content of /etc/mkinitcpio.conf to the config folder and does an inline replace/insert on the MODULES=() line
func Set_Mkinitcpio() {
// Get the config struct
config := GetConfig()
// Make sure we start from scratch by deleting any old file
if fileio.FileExist(config.Path.MKINITCPIO) {
os.Remove(config.Path.MKINITCPIO)
if exists, _ := fileio.FileExist(config.Path.MKINITCPIO); exists {
_ = os.Remove(config.Path.MKINITCPIO)
}
// Make a regex to get the system path instead of the config path

View file

@ -30,9 +30,9 @@ func Set_Modprobe(gpu_IDs []string) {
conffile := fmt.Sprintf("%s/vfio.conf", config.Path.MODPROBE)
// If the file exists
if fileio.FileExist(conffile) {
if exists, _ := fileio.FileExist(conffile); exists {
// Delete the old file
os.Remove(conffile)
_ = os.Remove(conffile)
}
content := fmt.Sprint(

View file

@ -1,6 +1,7 @@
package configs
import (
"errors"
"fmt"
"os"
"regexp"
@ -8,6 +9,7 @@ import (
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
"github.com/klauspost/cpuid/v2"
"github.com/HikariKnight/quickpassthrough/internal/common"
"github.com/HikariKnight/quickpassthrough/internal/logger"
"github.com/HikariKnight/quickpassthrough/pkg/command"
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
@ -34,7 +36,7 @@ type Config struct {
IsRoot bool
}
// Gets the path to all the config files
// GetConfigPaths retrieves the path to all the config files.
func GetConfigPaths() *Path {
Paths := &Path{
CMDLINE: "config/kernel_args",
@ -50,7 +52,7 @@ func GetConfigPaths() *Path {
return Paths
}
// Gets all the configs and returns the struct
// GetConfig retrieves all the configs and returns the struct.
func GetConfig() *Config {
config := &Config{
Bootloader: "unknown",
@ -66,7 +68,7 @@ func GetConfig() *Config {
return config
}
// Constructs the empty config files and folders based on what exists on the system
// InitConfigs constructs the empty config files and folders based on what exists on the system
func InitConfigs() {
config := GetConfig()
@ -79,10 +81,24 @@ func InitConfigs() {
}
// Remove old config
os.RemoveAll("config")
if err := os.RemoveAll("config"); err != nil && !errors.Is(err, os.ErrNotExist) {
if errors.Is(err, os.ErrPermission) {
errorcheck.ErrorCheck(err, common.PermissionNotice)
return // note: unreachable due to ErrorCheck calling fatal
}
// won't be called if the error is ErrNotExist
errorcheck.ErrorCheck(err, "\nError removing old config")
}
// Make the config folder
os.Mkdir("config", os.ModePerm)
if err := os.Mkdir("config", os.ModePerm); err != nil && !errors.Is(err, os.ErrExist) {
if errors.Is(err, os.ErrPermission) {
errorcheck.ErrorCheck(err, common.PermissionNotice)
return // note: unreachable due to ErrorCheck calling fatal
}
// won't be called if the error is ErrExist
errorcheck.ErrorCheck(err, "\nError making config folder")
}
// Make a regex to get the system path instead of the config path
syspath_re := regexp.MustCompile(`^config`)
@ -92,8 +108,20 @@ func InitConfigs() {
// Get the system path
syspath := syspath_re.ReplaceAllString(confpath, "")
exists, err := fileio.FileExist(syspath)
// If we received an error that is not ErrNotExist
if err != nil {
if errors.Is(err, os.ErrPermission) {
errorcheck.ErrorCheck(err, common.PermissionNotice)
return // note: unreachable due to ErrorCheck calling fatal
}
errorcheck.ErrorCheck(err, "\nError checking for directory: "+syspath)
continue // note: also unreachable
}
// If the path exists
if fileio.FileExist(syspath) {
if exists {
// Write to log
logger.Printf(
"%s found on the system\n"+
@ -106,8 +134,13 @@ func InitConfigs() {
makeBackupDir(syspath)
// Create the directories for our configs
err := os.MkdirAll(confpath, os.ModePerm)
errorcheck.ErrorCheck(err)
if err = os.MkdirAll(confpath, os.ModePerm); err != nil && !errors.Is(err, os.ErrExist) {
if errors.Is(err, os.ErrPermission) {
errorcheck.ErrorCheck(err, common.PermissionNotice)
return // note: unreachable due to ErrorCheck calling fatal
}
errorcheck.ErrorCheck(err, "\nError making directory: "+confpath)
}
}
}
@ -130,7 +163,19 @@ func InitConfigs() {
sysfile := syspath_re.ReplaceAllString(conffile, "")
// If the file exists
if fileio.FileExist(sysfile) {
exists, err := fileio.FileExist(sysfile)
// If we received an error that is not ErrNotExist
if err != nil {
if errors.Is(err, os.ErrPermission) {
errorcheck.ErrorCheck(err, common.PermissionNotice)
return // note: unreachable due to ErrorCheck calling fatal
}
errorcheck.ErrorCheck(err, "\nError checking for file: "+sysfile)
continue // note: also unreachable
}
if exists {
// Write to log
logger.Printf(
"%s found on the system\n"+
@ -143,14 +188,24 @@ func InitConfigs() {
file, err := os.Create(conffile)
errorcheck.ErrorCheck(err)
// Close the file so we can edit it
file.Close()
_ = file.Close()
// Backup the sysfile if we do not have a backup
backupFile(sysfile)
}
exists, err = fileio.FileExist(conffile)
if err != nil {
if errors.Is(err, os.ErrPermission) {
errorcheck.ErrorCheck(err, common.PermissionNotice)
return // note: unreachable due to ErrorCheck calling fatal
}
errorcheck.ErrorCheck(err, "\nError checking for file: "+conffile)
continue // note: also unreachable
}
// If we now have a config that exists
if fileio.FileExist(conffile) {
if exists {
switch conffile {
case config.Path.ETCMODULES:
// Write to logger
@ -206,14 +261,32 @@ func backupFile(source string) {
// Make a destination path
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 {
if errors.Is(configFileError, os.ErrPermission) {
errorcheck.ErrorCheck(configFileError, common.PermissionNotice)
return // note: unreachable due to ErrorCheck calling fatal
}
errorcheck.ErrorCheck(configFileError, "\nError checking for file: "+source)
return // note: also unreachable
}
}
switch {
// If the file exists in the config but not on the system it is a file we make
if fileio.FileExist(fmt.Sprintf("config%s", source)) && !fileio.FileExist(source) {
case configExists && !sysExists:
// Create the blank file so that a copy of the backup folder to /etc
file, err := os.Create(dest)
errorcheck.ErrorCheck(err, "Error creating file %s\n", dest)
file.Close()
} else if !fileio.FileExist(dest) {
_ = file.Close()
// If a backup of the file does not exist
case sysExists && !destExists:
// Write to the logger
logger.Printf("No first time backup of %s detected.\nCreating a backup at %s\n", source, dest)
@ -225,29 +298,52 @@ func backupFile(source string) {
func makeBackupDir(dest string) {
// If a backup directory does not exist
if !fileio.FileExist("backup/") {
exists, err := fileio.FileExist("backup/")
if err != nil {
// If we received an error that is not ErrNotExist
if errors.Is(err, os.ErrPermission) {
errorcheck.ErrorCheck(err, common.PermissionNotice)
return // note: unreachable due to ErrorCheck calling fatal
}
errorcheck.ErrorCheck(err, "Error checking for backup/ folder")
return // note: also unreachable
}
if !exists {
// Write to the logger
logger.Printf("Backup directory does not exist!\nCreating backup directory for first run backup")
}
// Make the empty directories
err := os.MkdirAll(fmt.Sprintf("backup/%s", dest), os.ModePerm)
if err = os.MkdirAll(fmt.Sprintf("backup/%s", dest), os.ModePerm); errors.Is(err, os.ErrExist) {
err = nil
}
if errors.Is(err, os.ErrPermission) {
errorcheck.ErrorCheck(err, common.PermissionNotice)
return // note: unreachable due to ErrorCheck calling fatal
}
errorcheck.ErrorCheck(err, "Error making backup/ folder")
}
// Copy a file to the system, make sure you have run command.Elevate() recently
func CopyToSystem(conffile, sysfile string) string {
// CopyToSystem copies a file to the system.
func CopyToSystem(isRoot bool, conffile, sysfile string) {
// Since we should be elevated with our sudo token we will copy with cp
// (using built in functions will not work as we are running as the normal user)
output, _ := command.Run("sudo", "cp", "-v", conffile, sysfile)
// Clean the output
clean_re := regexp.MustCompile(`\n`)
clean_output := clean_re.ReplaceAllString(output[0], "")
// ExecAndLogSudo will write to the logger, so just print here
fmt.Printf("Copying: %s to %s\n", conffile, sysfile)
// Write output to logger
logger.Printf("%s\n", clean_output)
// [command.ExecAndLogSudo] will log the command's output
errorcheck.ErrorCheck(command.ExecAndLogSudo(isRoot, false,
fmt.Sprintf("cp -v \"%s\" %s", conffile, sysfile),
), // if error, log and exit
fmt.Sprintf("Failed to copy %s to %s", conffile, sysfile),
)
// Return the output
return fmt.Sprintf("Copying: %s", clean_output)
// ---------------------------------------------------------------------------------
// note that if we failed the error check, the following will not appear in the log!
// this is because the [errorcheck.ErrorCheck] function will call [log.Fatalf] and exit
// ---------------------------------------------------------------------------------
logger.Printf("Copied %s to %s\n", conffile, sysfile)
}