Skip to content

Commit 6c48a35

Browse files
authored
Feat: conditional permissions behavior (#28)
* Fix: don't need sudo if we're root + other aesthetics * Heavy refactoring, see PR #28 * Fix: avoid silent fatalities demo: https://tcp.ac/i/JMSUc.gif * Fix: Inverse check on `IsRoot` * D.R.Y: check for permissions error in `common.ErrorCheck` Reduce cognitive complexity. * Fix: Issue with copying * Resolve #28 (comment) * Resolve #28 (comment) and #28 (comment) * Revert "Resolve #28 (comment) and #28 (comment)" This reverts commit ce15213. * Resolve #28 (comment)
1 parent 4d0086d commit 6c48a35

21 files changed

+606
-257
lines changed

internal/common/errors.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package common
2+
3+
import (
4+
"errors"
5+
"os"
6+
"time"
7+
8+
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
9+
"github.com/gookit/color"
10+
)
11+
12+
const PermissionNotice = `
13+
<yellowB>Permissions error occured during file operations.</>
14+
15+
<blue_b>Hint</>:
16+
17+
If you initially ran QuickPassthrough as root or using sudo,
18+
but are now running it as a normal user, this is expected behavior.
19+
20+
<us>Try running QuickPassthrough as root or using sudo if so.</>
21+
22+
If this does not work, double check your filesystem's permissions,
23+
and be sure to check the debug log for more information.
24+
`
25+
26+
// ErrorCheck serves as a wrapper for HikariKnight/ls-iommu/pkg/common.ErrorCheck that allows for visibile error messages
27+
func ErrorCheck(err error, msg ...string) {
28+
_, _ = os.Stdout.WriteString("\033[H\033[2J") // clear the screen
29+
if err == nil {
30+
return
31+
}
32+
if errors.Is(err, os.ErrPermission) {
33+
color.Printf(PermissionNotice)
34+
}
35+
oneMsg := ""
36+
if len(msg) < 1 {
37+
oneMsg = ""
38+
} else {
39+
for _, v := range msg {
40+
oneMsg += v + "\n"
41+
}
42+
}
43+
color.Printf("\n<red_b>FATAL</>: %s\n%s\nAborting", err.Error(), oneMsg)
44+
for i := 0; i < 10; i++ {
45+
time.Sleep(1 * time.Second)
46+
print(".")
47+
}
48+
print("\n")
49+
errorcheck.ErrorCheck(err, msg...)
50+
}

internal/configs/config_bootloaders.go

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package configs
22

33
import (
4+
"errors"
45
"fmt"
56
"os"
7+
"os/exec"
68
"regexp"
79
"strings"
810

9-
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
11+
"github.com/klauspost/cpuid/v2"
12+
13+
"github.com/HikariKnight/quickpassthrough/internal/common"
1014
"github.com/HikariKnight/quickpassthrough/internal/logger"
1115
"github.com/HikariKnight/quickpassthrough/pkg/command"
1216
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
13-
"github.com/klauspost/cpuid/v2"
1417
)
1518

1619
// This function just adds what bootloader the system has to our config.bootloader value
@@ -70,39 +73,32 @@ func Set_Cmdline(gpu_IDs []string) {
7073
fileio.AppendContent(fmt.Sprintf(" vfio_pci.ids=%s", strings.Join(gpu_IDs, ",")), config.Path.CMDLINE)
7174
}
7275

73-
// Configures systemd-boot using kernelstub
74-
func Set_KernelStub() string {
76+
// Set_KernelStub configures systemd-boot using kernelstub.
77+
func Set_KernelStub(isRoot bool) {
7578
// Get the config
7679
config := GetConfig()
7780

7881
// Get the kernel args
7982
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
8083

81-
// Write to logger
82-
logger.Printf("Running command:\nsudo kernelstub -a \"%s\"\n", kernel_args)
83-
84-
// Run the command
85-
_, err := command.Run("sudo", "kernelstub", "-a", kernel_args)
86-
errorcheck.ErrorCheck(err, "Error, kernelstub command returned exit code 1")
87-
88-
// Return what we did
89-
return fmt.Sprintf("Executed: sudo kernelstub -a \"%s\"", kernel_args)
84+
// Run and log, check for errors
85+
common.ErrorCheck(
86+
command.ExecAndLogSudo(isRoot, true, "kernelstub", "-a", kernel_args),
87+
"Error, kernelstub command returned exit code 1",
88+
)
9089
}
9190

92-
// Configures grub2 and/or systemd-boot using grubby
93-
func Set_Grubby() string {
91+
// Set_Grubby configures grub2 and/or systemd-boot using grubby
92+
func Set_Grubby(isRoot bool) string {
9493
// Get the config
9594
config := GetConfig()
9695

9796
// Get the kernel args
9897
kernel_args := fileio.ReadFile(config.Path.CMDLINE)
9998

100-
// Write to logger
101-
logger.Printf("Running command:\nsudo grubby --update-kernel=ALL --args=\"%s\"\n", kernel_args)
102-
103-
// Run the command
104-
_, err := command.Run("sudo", "grubby", "--update-kernel=ALL", fmt.Sprintf("--args=%s", kernel_args))
105-
errorcheck.ErrorCheck(err, "Error, grubby command returned exit code 1")
99+
// Run and log, check for errors
100+
err := command.ExecAndLogSudo(isRoot, true, "grubby", "--update-kernel=ALL", fmt.Sprintf("--args=%s", kernel_args))
101+
common.ErrorCheck(err, "Error, grubby command returned exit code 1")
106102

107103
// Return what we did
108104
return fmt.Sprintf("Executed: sudo grubby --update-kernel=ALL --args=\"%s\"", kernel_args)
@@ -116,8 +112,8 @@ func Configure_Grub2() {
116112
conffile := fmt.Sprintf("%s/grub", config.Path.DEFAULT)
117113

118114
// Make sure we start from scratch by deleting any old file
119-
if fileio.FileExist(conffile) {
120-
os.Remove(conffile)
115+
if exists, _ := fileio.FileExist(conffile); exists {
116+
_ = os.Remove(conffile)
121117
}
122118

123119
// Make a regex to get the system path instead of the config path
@@ -201,8 +197,8 @@ func clean_Grub2_Args(old_kernel_args []string) []string {
201197
return clean_kernel_args
202198
}
203199

204-
// This function copies our config to /etc/default/grub and updates grub
205-
func Set_Grub2() ([]string, error) {
200+
// Set_Grub2 copies our config to /etc/default/grub and updates grub
201+
func Set_Grub2(isRoot bool) error {
206202
// Get the config
207203
config := GetConfig()
208204

@@ -213,38 +209,45 @@ func Set_Grub2() ([]string, error) {
213209
sysfile_re := regexp.MustCompile(`^config`)
214210
sysfile := sysfile_re.ReplaceAllString(conffile, "")
215211

216-
// Write to logger
217-
logger.Printf("Executing command:\nsudo cp -v \"%s\" %s\n", conffile, sysfile)
218-
219-
// Make our output slice
220-
var output []string
212+
// [CopyToSystem] will log the operation
213+
// logger.Printf("Executing command:\nsudo cp -v \"%s\" %s\n", conffile, sysfile)
221214

222-
// Copy files to system
223-
output = append(output, CopyToSystem(conffile, sysfile))
215+
// Copy files to system, logging and error checking is done in the function
216+
CopyToSystem(isRoot, conffile, sysfile)
224217

225218
// Set a variable for the mkconfig command
226-
mkconfig := "grub-mkconfig"
219+
var mkconfig string
220+
var grubPath = "/boot/grub/grub.cfg"
221+
var lpErr error
222+
227223
// Check for grub-mkconfig
228-
_, err := command.Run("which", "grub-mkconfig")
229-
if err == nil {
230-
// Set binary as grub-mkconfig
231-
mkconfig = "grub-mkconfig"
232-
} else {
233-
mkconfig = "grub2-mkconfig"
224+
mkconfig, lpErr = exec.LookPath("grub-mkconfig")
225+
switch {
226+
case errors.Is(lpErr, exec.ErrNotFound) || mkconfig == "":
227+
// Check for grub2-mkconfig
228+
mkconfig, lpErr = exec.LookPath("grub2-mkconfig")
229+
if lpErr == nil && mkconfig != "" {
230+
grubPath = "/boot/grub2/grub.cfg"
231+
break // skip below, we found grub2-mkconfig
232+
}
233+
if lpErr == nil {
234+
// we know mkconfig is empty despite no error;
235+
// so set an error for [common.ErrorCheck].
236+
lpErr = errors.New("neither grub-mkconfig or grub2-mkconfig found")
237+
}
238+
common.ErrorCheck(lpErr, lpErr.Error()+"\n")
239+
return lpErr // note: unreachable as [common.ErrorCheck] calls fatal
240+
default:
234241
}
235242

236-
// Update grub.cfg
237-
if fileio.FileExist("/boot/grub/grub.cfg") {
238-
output = append(output, fmt.Sprintf("Executed: sudo %s -o /boot/grub/grub.cfg\nSee debug.log for more detailed output", mkconfig))
239-
_, mklog, err := command.RunErr("sudo", mkconfig, "-o", "/boot/grub/grub.cfg")
240-
logger.Printf(strings.Join(mklog, "\n"))
241-
errorcheck.ErrorCheck(err, "Failed to update /boot/grub/grub.cfg")
242-
} else {
243-
output = append(output, fmt.Sprintf("Executed: sudo %s -o /boot/grub/grub.cfg\nSee debug.log for more detailed output", mkconfig))
244-
_, mklog, err := command.RunErr("sudo", mkconfig, "-o", "/boot/grub2/grub.cfg")
245-
logger.Printf(strings.Join(mklog, "\n"))
246-
errorcheck.ErrorCheck(err, "Failed to update /boot/grub/grub.cfg")
247-
}
243+
_, mklog, err := command.RunErrSudo(isRoot, mkconfig, "-o", grubPath)
244+
245+
// tabulate the output, [command.RunErrSudo] logged the execution.
246+
logger.Printf("\t" + strings.Join(mklog, "\n\t"))
247+
common.ErrorCheck(err, "Failed to update /boot/grub/grub.cfg")
248248

249-
return output, err
249+
// always returns nil as [common.ErrorCheck] calls fatal
250+
// keeping the ret signature, as we should consider passing down errors
251+
// but that's a massive rabbit hole to go down for this codebase as a whole
252+
return err
250253
}

internal/configs/config_dracut.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ import (
99
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
1010
)
1111

12-
// This function writes a dracut configuration file for /etc/dracut.conf.d/
12+
// Set_Dracut writes a dracut configuration file for `/etc/dracut.conf.d/`.
1313
func Set_Dracut() {
1414
config := GetConfig()
1515

1616
// Set the dracut config file
1717
dracutConf := fmt.Sprintf("%s/vfio.conf", config.Path.DRACUT)
1818

1919
// If the file already exists then delete it
20-
if fileio.FileExist(dracutConf) {
21-
os.Remove(dracutConf)
20+
if exists, _ := fileio.FileExist(dracutConf); exists {
21+
_ = os.Remove(dracutConf)
2222
}
2323

2424
// Write to logger

internal/configs/config_initramfstools.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import (
77
"regexp"
88
"strings"
99

10-
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
10+
"github.com/HikariKnight/quickpassthrough/internal/common"
1111
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
1212
)
1313

1414
// Special function to read the header of a file (reads the first N lines)
1515
func initramfs_readHeader(lines int, fileName string) string {
1616
// Open the file
1717
f, err := os.Open(fileName)
18-
errorcheck.ErrorCheck(err, fmt.Sprintf("Error opening %s", fileName))
18+
common.ErrorCheck(err, fmt.Sprintf("Error opening %s", fileName))
1919
defer f.Close()
2020

2121
header_re := regexp.MustCompile(`^#`)
@@ -50,7 +50,7 @@ func initramfs_addModules(conffile string) {
5050

5151
// Open the system file for reading
5252
sysfile, err := os.Open(syspath)
53-
errorcheck.ErrorCheck(err, fmt.Sprintf("Error opening file for reading %s", syspath))
53+
common.ErrorCheck(err, fmt.Sprintf("Error opening file for reading %s", syspath))
5454
defer sysfile.Close()
5555

5656
// Check if user has vendor-reset installed/enabled and make sure that is first

internal/configs/config_mkinitcpio.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ import (
1010
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
1111
)
1212

13-
// This function copies the content of /etc/mkinitcpio.conf to the config folder and does an inline replace/insert on the MODULES=() line
13+
// Set_Mkinitcpio copies the content of /etc/mkinitcpio.conf to the config folder and does an inline replace/insert on the MODULES=() line
1414
func Set_Mkinitcpio() {
1515
// Get the config struct
1616
config := GetConfig()
1717

1818
// Make sure we start from scratch by deleting any old file
19-
if fileio.FileExist(config.Path.MKINITCPIO) {
20-
os.Remove(config.Path.MKINITCPIO)
19+
if exists, _ := fileio.FileExist(config.Path.MKINITCPIO); exists {
20+
_ = os.Remove(config.Path.MKINITCPIO)
2121
}
2222

2323
// Make a regex to get the system path instead of the config path

internal/configs/config_modprobe.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ func Set_Modprobe(gpu_IDs []string) {
3030
conffile := fmt.Sprintf("%s/vfio.conf", config.Path.MODPROBE)
3131

3232
// If the file exists
33-
if fileio.FileExist(conffile) {
33+
if exists, _ := fileio.FileExist(conffile); exists {
3434
// Delete the old file
35-
os.Remove(conffile)
35+
_ = os.Remove(conffile)
3636
}
3737

3838
content := fmt.Sprint(

internal/configs/config_vbios_dumper.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"path/filepath"
77
"strings"
88

9-
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
9+
"github.com/HikariKnight/quickpassthrough/internal/common"
1010
"github.com/HikariKnight/quickpassthrough/internal/logger"
1111
)
1212

@@ -55,12 +55,12 @@ func GenerateVBIOSDumper(vbios_path string) {
5555

5656
// Make the script file
5757
scriptfile, err := os.Create("utils/dump_vbios.sh")
58-
errorcheck.ErrorCheck(err, "Cannot create file \"utils/dump_vbios.sh\"")
58+
common.ErrorCheck(err, "Cannot create file \"utils/dump_vbios.sh\"")
5959
defer scriptfile.Close()
6060

6161
// Make the script executable
6262
scriptfile.Chmod(0775)
63-
errorcheck.ErrorCheck(err, "Could not change permissions of \"utils/dump_vbios.sh\"")
63+
common.ErrorCheck(err, "Could not change permissions of \"utils/dump_vbios.sh\"")
6464

6565
// Write to logger
6666
logger.Printf("Writing utils/dump_vbios.sh\n")

internal/configs/config_vfio_video.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"os"
66
"strings"
77

8-
"github.com/HikariKnight/ls-iommu/pkg/errorcheck"
8+
"github.com/HikariKnight/quickpassthrough/internal/common"
99
"github.com/HikariKnight/quickpassthrough/internal/logger"
1010
"github.com/HikariKnight/quickpassthrough/pkg/fileio"
1111
)
@@ -26,7 +26,7 @@ func DisableVFIOVideo(i int) {
2626
if strings.Contains(kernel_args, "vfio_pci.disable_vga") {
2727
// Remove the old file
2828
err := os.Remove(config.Path.CMDLINE)
29-
errorcheck.ErrorCheck(err, fmt.Sprintf("Could not rewrite %s", config.Path.CMDLINE))
29+
common.ErrorCheck(err, fmt.Sprintf("Could not rewrite %s", config.Path.CMDLINE))
3030

3131
// Enable or disable the VGA based on our given value
3232
if i == 0 {

0 commit comments

Comments
 (0)