package configs import ( "fmt" "os" "regexp" "github.com/HikariKnight/ls-iommu/pkg/errorcheck" "github.com/HikariKnight/quickpassthrough/internal/logger" "github.com/HikariKnight/quickpassthrough/pkg/command" "github.com/HikariKnight/quickpassthrough/pkg/fileio" "github.com/HikariKnight/quickpassthrough/pkg/uname" "github.com/klauspost/cpuid/v2" ) type Path struct { CMDLINE string MODPROBE string INITRAMFS string ETCMODULES string DEFAULT string QUICKEMU string DRACUT string MKINITCPIO string } type Config struct { Bootloader string Cpuvendor string Path *Path Gpu_Group string Gpu_IDs []string } // Gets the path to all the config files func GetConfigPaths() *Path { Paths := &Path{ CMDLINE: "config/kernel_args", MODPROBE: "config/etc/modprobe.d", INITRAMFS: "config/etc/initramfs-tools", ETCMODULES: "config/etc/modules", DEFAULT: "config/etc/default", QUICKEMU: "config/quickemu", DRACUT: "config/etc/dracut.conf.d", MKINITCPIO: "config/etc/mkinitcpio.conf", } return Paths } // Gets all the configs and returns the struct func GetConfig() *Config { config := &Config{ Bootloader: "unknown", Cpuvendor: cpuid.CPU.VendorString, Path: GetConfigPaths(), Gpu_Group: "", Gpu_IDs: []string{}, } // Detect the bootloader we are using getBootloader(config) return config } // Constructs the empty config files and folders based on what exists on the system func InitConfigs() { config := GetConfig() // Add all directories we need into a stringlist dirs := []string{ config.Path.MODPROBE, config.Path.INITRAMFS, config.Path.DEFAULT, config.Path.DRACUT, } // Remove old config os.RemoveAll("config") // Make the config folder os.Mkdir("config", os.ModePerm) // Make a regex to get the system path instead of the config path syspath_re := regexp.MustCompile(`^config`) // For each directory for _, confpath := range dirs { // Get the system path syspath := syspath_re.ReplaceAllString(confpath, "") // If the path exists if fileio.FileExist(syspath) { // Write to log logger.Printf( "%s found on the system\n"+ "Creating %s\n", syspath, confpath, ) // Make a backup directory makeBackupDir(syspath) // Create the directories for our configs err := os.MkdirAll(confpath, os.ModePerm) errorcheck.ErrorCheck(err) } } // Add all files we need to a stringlist files := []string{ config.Path.ETCMODULES, config.Path.MKINITCPIO, fmt.Sprintf("%s/modules", config.Path.INITRAMFS), fmt.Sprintf("%s/grub", config.Path.DEFAULT), } // If we are using grubby if config.Bootloader == "grubby" { // Do not create an empty /etc/default/grub file files = files[:len(files)-1] } for _, conffile := range files { // Get the system file path sysfile := syspath_re.ReplaceAllString(conffile, "") // If the file exists if fileio.FileExist(sysfile) { // Write to log logger.Printf( "%s found on the system\n"+ "Creating %s\n", sysfile, conffile, ) // Create the directories for our configs file, err := os.Create(conffile) errorcheck.ErrorCheck(err) // Close the file so we can edit it file.Close() // Backup the sysfile if we do not have a backup backupFile(sysfile) } // If we now have a config that exists if fileio.FileExist(conffile) { switch conffile { case config.Path.ETCMODULES: // Write to logger logger.Printf("Getting the header (if it is there) from %s\n", conffile) // Read the header header := initramfs_readHeader(4, sysfile) fileio.AppendContent(header, conffile) // Add the modules to the config file initramfs_addModules(conffile) case fmt.Sprintf("%s/modules", config.Path.INITRAMFS): // Write to logger logger.Printf("Getting the header (if it is there) from %s\n", conffile) // Read the header header := initramfs_readHeader(11, sysfile) fileio.AppendContent(header, conffile) // Add the modules to the config file initramfs_addModules(conffile) } } } } // Returns a list of modules used for vfio based on the systems kernel version func vfio_modules() []string { // Make the list of modules modules := []string{ "vfio_pci", "vfio", "vfio_iommu_type1", } // If we are on a kernel older than 6.2 sysinfo := uname.New() kernel_re := regexp.MustCompile(`^(6\.1|6\.0|[1-5]\.)`) if kernel_re.MatchString(sysinfo.Kernel) { // Write to the debug log logger.Printf("Linux kernel version %s detected!\nIncluding vfio_virqfd module\n") // Include the vfio_virqfd module // NOTE: this driver was merged into the vfio module in 6.2 modules = append(modules, "vfio_virqfd") } // Return the modules return modules } func backupFile(source string) { // Make a destination path dest := fmt.Sprintf("backup%s", source) // 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) { // 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) { // If a backup of the file does not exist // Write to the logger logger.Printf("No first time backup of %s detected.\nCreating a backup at %s\n", source, dest) // Copy the file fileio.FileCopy(source, dest) } } func makeBackupDir(dest string) { // If a backup directory does not exist if !fileio.FileExist("backup/") { // 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) errorcheck.ErrorCheck(err, "Error making backup/ folder") } // Copy a file to the system, make sure you have run command.Elevate() recently func CopyToSystem(conffile, sysfile string) string { // Since we should be elevated with our sudo token we will copy with cp // (using built in functions will not work as we are running as the normal user) output, _ := command.Run("sudo", "cp", "-v", conffile, sysfile) // Clean the output clean_re := regexp.MustCompile(`\n`) clean_output := clean_re.ReplaceAllString(output[0], "") // Write output to logger logger.Printf(clean_output) // Return the output return fmt.Sprintf("Copying: %s", clean_output) }