github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/cmdline/cmdline.go (about) 1 // Copyright 2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package cmdline is parser for kernel command-line args from /proc/cmdline. 6 // 7 // It's conformant with 8 // https://www.kernel.org/doc/html/v4.14/admin-guide/kernel-parameters.html, 9 // though making 'var_name' and 'var-name' equivalent may need to be done 10 // separately. 11 package cmdline 12 13 import ( 14 "io" 15 "strings" 16 "unicode" 17 18 "github.com/mvdan/u-root-coreutils/pkg/shlex" 19 ) 20 21 // CmdLine lets people view the raw & parsed /proc/cmdline in one place 22 type CmdLine struct { 23 Raw string 24 AsMap map[string]string 25 Err error 26 } 27 28 // NewCmdLine returns a populated CmdLine struct 29 func NewCmdLine() *CmdLine { 30 return getCmdLine() 31 } 32 33 // FullCmdLine returns the full, raw cmdline string 34 func FullCmdLine() string { 35 return getCmdLine().Raw 36 } 37 38 // parse returns the current command line, trimmed 39 func parse(cmdlineReader io.Reader) *CmdLine { 40 var line = &CmdLine{} 41 raw, err := io.ReadAll(cmdlineReader) 42 line.Err = err 43 // This works because string(nil) is "" 44 line.Raw = strings.TrimRight(string(raw), "\n") 45 line.AsMap = parseToMap(line.Raw) 46 return line 47 } 48 49 func doParse(input string, handler func(flag, key, canonicalKey, value, trimmedValue string)) { 50 lastQuote := rune(0) 51 quotedFieldsCheck := func(c rune) bool { 52 switch { 53 case c == lastQuote: 54 lastQuote = rune(0) 55 return false 56 case lastQuote != rune(0): 57 return false 58 case unicode.In(c, unicode.Quotation_Mark): 59 lastQuote = c 60 return false 61 default: 62 return unicode.IsSpace(c) 63 } 64 } 65 66 for _, flag := range strings.FieldsFunc(string(input), quotedFieldsCheck) { 67 // kernel variables must allow '-' and '_' to be equivalent in variable 68 // names. We will replace dashes with underscores for processing. 69 70 // Split the flag into a key and value, setting value="1" if none 71 split := strings.Index(flag, "=") 72 73 if len(flag) == 0 { 74 continue 75 } 76 var key, value string 77 if split == -1 { 78 key = flag 79 value = "1" 80 } else { 81 key = flag[:split] 82 value = flag[split+1:] 83 } 84 canonicalKey := strings.Replace(key, "-", "_", -1) 85 trimmedValue := strings.Trim(value, "\"'") 86 87 // Call the user handler 88 handler(flag, key, canonicalKey, value, trimmedValue) 89 } 90 } 91 92 // parseToMap turns a space-separated kernel commandline into a map 93 func parseToMap(input string) map[string]string { 94 flagMap := make(map[string]string) 95 doParse(input, func(flag, key, canonicalKey, value, trimmedValue string) { 96 // We store the value twice, once with dash, once with underscores 97 // Just in case people check with the wrong method 98 flagMap[canonicalKey] = trimmedValue 99 flagMap[key] = trimmedValue 100 }) 101 102 return flagMap 103 } 104 105 // ContainsFlag verifies that the kernel cmdline has a flag set 106 func (c *CmdLine) ContainsFlag(flag string) bool { 107 _, present := c.Flag(flag) 108 return present 109 } 110 111 // ContainsFlag verifies that the kernel cmdline has a flag set 112 func ContainsFlag(flag string) bool { 113 return getCmdLine().ContainsFlag(flag) 114 } 115 116 // Flag returns the value of a flag, and whether it was set 117 func (c *CmdLine) Flag(flag string) (string, bool) { 118 canonicalFlag := strings.Replace(flag, "-", "_", -1) 119 value, present := c.AsMap[canonicalFlag] 120 return value, present 121 } 122 123 // Flag returns the value of a flag, and whether it was set 124 func Flag(flag string) (string, bool) { 125 return getCmdLine().Flag(flag) 126 } 127 128 // getFlagMap gets specified flags as a map 129 func getFlagMap(flagName string) map[string]string { 130 return parseToMap(flagName) 131 } 132 133 // GetInitFlagMap gets the uroot init flags as a map 134 func (c *CmdLine) GetInitFlagMap() map[string]string { 135 initflags, _ := c.Flag("uroot.initflags") 136 return getFlagMap(initflags) 137 } 138 139 // GetInitFlagMap gets the uroot init flags as a map 140 func GetInitFlagMap() map[string]string { 141 return getCmdLine().GetInitFlagMap() 142 } 143 144 // GetUinitArgs gets the uinit argvs. 145 func (c *CmdLine) GetUinitArgs() []string { 146 uinitargs, _ := getCmdLine().Flag("uroot.uinitargs") 147 return shlex.Argv(uinitargs) 148 } 149 150 // GetUinitArgs gets the uinit argvs. 151 func GetUinitArgs() []string { 152 return getCmdLine().GetUinitArgs() 153 } 154 155 // FlagsForModule gets all flags for a designated module 156 // and returns them as a space-seperated string designed to be passed to insmod 157 // Note that similarly to flags, module names with - and _ are treated the same. 158 func (c *CmdLine) FlagsForModule(name string) string { 159 var ret string 160 flagsAdded := make(map[string]bool) // Ensures duplicate flags aren't both added 161 // Module flags come as moduleName.flag in /proc/cmdline 162 prefix := strings.Replace(name, "-", "_", -1) + "." 163 for flag, val := range c.AsMap { 164 canonicalFlag := strings.Replace(flag, "-", "_", -1) 165 if !flagsAdded[canonicalFlag] && strings.HasPrefix(canonicalFlag, prefix) { 166 flagsAdded[canonicalFlag] = true 167 // They are passed to insmod space seperated as flag=val 168 ret += strings.TrimPrefix(canonicalFlag, prefix) + "=" + val + " " 169 } 170 } 171 return ret 172 } 173 174 // FlagsForModule gets all flags for a designated module 175 // and returns them as a space-seperated string designed to be passed to insmod 176 // Note that similarly to flags, module names with - and _ are treated the same. 177 func FlagsForModule(name string) string { 178 return getCmdLine().FlagsForModule(name) 179 }