1 package grub
2
3 import (
4 "errors"
5 "fmt"
6 "path/filepath"
7 "regexp"
8 "strings"
9
10 file "edge-infra.dev/pkg/sds/lib/os/file"
11 )
12
13 type CfgFile interface {
14 WriteGrubCfg(fileHandler file.File, cfgPath string) error
15 SetProperty(property, value string)
16 GetMenuEntries() []MenuEntry
17 GetMenuEntry(name string) (*MenuEntry, error)
18 AddMenuEntry(menuEntry MenuEntry)
19 DeleteMenuEntry(entryName string)
20 WriteGrubCfgReadWrite(fileHandler file.File, grubVolumeMountPath, cfgPath string) error
21 UpdateRequired() bool
22 }
23
24 type grubFile struct {
25 Contents string
26 OriginalContents string
27 }
28
29 type MenuEntry struct {
30 Name string
31 Options string
32 Contents []string
33 }
34
35 func GetGrubConfigPath(fileHandler file.File, grubVolumeMountPath string, grubConfigFileName string) (string, error) {
36 efi := filepath.Join(grubVolumeMountPath, "efi", "EFI", "grub", grubConfigFileName)
37 legacy := filepath.Join(grubVolumeMountPath, "grub", grubConfigFileName)
38
39 if fileHandler.Exists(efi) {
40 return efi, nil
41 } else if fileHandler.Exists(legacy) {
42 return legacy, nil
43 }
44 return "", fmt.Errorf("grub config path not found. Efi: %s Legacy: %s", efi, legacy)
45 }
46
47 func ReadGrubCfg(fileHandler file.File, cfgPath string) (CfgFile, error) {
48 grubContentBytes, err := fileHandler.Read(cfgPath)
49 if err != nil {
50 return nil, err
51 }
52 grubContent := &grubFile{
53 Contents: string(grubContentBytes),
54 OriginalContents: string(grubContentBytes),
55 }
56
57 return grubContent, nil
58 }
59
60 func (grubFile *grubFile) UpdateRequired() bool {
61 return grubFile.OriginalContents != grubFile.Contents
62 }
63
64 func (grubFile *grubFile) WriteGrubCfg(fileHandler file.File, cfgPath string) error {
65 err := fileHandler.Write(cfgPath+".swp", []byte(grubFile.Contents), 0400)
66 if err != nil {
67 return err
68 }
69 return fileHandler.Rename(cfgPath+".swp", cfgPath)
70 }
71
72 func (grubFile *grubFile) WriteGrubCfgReadWrite(fileHandler file.File, grubVolumeMountPath, cfgPath string) error {
73 return fileHandler.WriteWithFsReadWrite(grubVolumeMountPath, cfgPath, []byte(grubFile.Contents), 0400)
74 }
75
76 func (grubFile *grubFile) SetProperty(property, value string) {
77 setStr := "set " + property
78 newLine := fmt.Sprintf("%s=%s", setStr, value)
79 if strings.Contains(grubFile.Contents, setStr) {
80 re := regexp.MustCompile(setStr + "=.*")
81 grubFile.Contents = re.ReplaceAllLiteralString(grubFile.Contents, newLine)
82 } else {
83 grubFile.Contents = grubFile.Contents + "\n" + newLine + "\n"
84 }
85 }
86
87 func (grubFile *grubFile) GetMenuEntries() []MenuEntry {
88 menuEntries := []MenuEntry{}
89 Contents := grubFile.Contents
90 index := strings.Index(Contents, "\nmenuentry")
91 for index >= 0 {
92 Contents = Contents[index+len("\nmenuentry"):]
93 startIdx := strings.Index(Contents, "{\n")
94 endIdx := strings.Index(Contents, "\n}")
95 split := strings.Fields(Contents[:startIdx])
96 newEntry := MenuEntry{
97 Name: split[0],
98 Options: strings.TrimSpace(Contents[strings.Index(Contents, split[0])+len(split[0]) : startIdx]),
99 Contents: strings.Split(Contents[startIdx+2:endIdx], "\n"),
100 }
101 Contents = Contents[endIdx:]
102
103 menuEntries = append(menuEntries, newEntry)
104 index = strings.Index(Contents, "\nmenuentry")
105 }
106 return menuEntries
107 }
108
109 func (grubFile *grubFile) GetMenuEntry(name string) (*MenuEntry, error) {
110 for _, entry := range grubFile.GetMenuEntries() {
111 if strings.HasPrefix(entry.Name, `"`+name) {
112 return &entry, nil
113 }
114 }
115 return nil, errors.New("Menu entry not found")
116 }
117
118 func (grubFile *grubFile) AddMenuEntry(menuEntry MenuEntry) {
119 grubFile.Contents += "\n\n" + menuEntry.String()
120 }
121
122 func (grubFile *grubFile) DeleteMenuEntry(entryName string) {
123 re := regexp.MustCompile(`\n*menuentry\s+.?` + entryName + `.?\s+(.|\n)*\n}`)
124 grubFile.Contents = re.ReplaceAllString(grubFile.Contents, "")
125 }
126
127 func (menuEntry *MenuEntry) String() string {
128 str := fmt.Sprintf("menuentry %s %s {", menuEntry.Name, menuEntry.Options)
129 for _, x := range menuEntry.Contents {
130 str += "\n" + x
131 }
132 str += "\n}"
133 return str
134 }
135
View as plain text