github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/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 "fmt" 15 "io" 16 "io/ioutil" 17 "log" 18 "os" 19 "strings" 20 "sync" 21 "unicode" 22 23 "github.com/u-root/u-root/pkg/shlex" 24 ) 25 26 // CmdLine lets people view the raw & parsed /proc/cmdline in one place 27 type CmdLine struct { 28 Raw string 29 AsMap map[string]string 30 Err error 31 } 32 33 var ( 34 // procCmdLine package level static variable initialized once 35 once sync.Once 36 procCmdLine CmdLine 37 ) 38 39 func cmdLineOpener() { 40 cmdlineReader, err := os.Open("/proc/cmdline") 41 if err != nil { 42 errorMsg := fmt.Sprintf("Can't open /proc/cmdline: %v", err) 43 log.Print(errorMsg) 44 procCmdLine = CmdLine{Err: fmt.Errorf(errorMsg)} 45 return 46 } 47 48 procCmdLine = parse(cmdlineReader) 49 cmdlineReader.Close() 50 } 51 52 // NewCmdLine returns a populated CmdLine struct 53 func NewCmdLine() CmdLine { 54 // We use cmdLineReader so tests can inject here 55 once.Do(cmdLineOpener) 56 return procCmdLine 57 } 58 59 // FullCmdLine returns the full, raw cmdline string 60 func FullCmdLine() string { 61 once.Do(cmdLineOpener) 62 return procCmdLine.Raw 63 } 64 65 // parse returns the current command line, trimmed 66 func parse(cmdlineReader io.Reader) CmdLine { 67 raw, err := ioutil.ReadAll(cmdlineReader) 68 line := CmdLine{} 69 if err != nil { 70 log.Printf("Can't read command line: %v", err) 71 line.Err = err 72 line.Raw = "" 73 } else { 74 line.Raw = strings.TrimRight(string(raw), "\n") 75 line.AsMap = parseToMap(line.Raw) 76 } 77 return line 78 } 79 80 // 81 func doParse(input string, handler func(flag, key, canonicalKey, value, trimmedValue string)) { 82 83 lastQuote := rune(0) 84 quotedFieldsCheck := func(c rune) bool { 85 switch { 86 case c == lastQuote: 87 lastQuote = rune(0) 88 return false 89 case lastQuote != rune(0): 90 return false 91 case unicode.In(c, unicode.Quotation_Mark): 92 lastQuote = c 93 return false 94 default: 95 return unicode.IsSpace(c) 96 } 97 } 98 99 for _, flag := range strings.FieldsFunc(string(input), quotedFieldsCheck) { 100 // kernel variables must allow '-' and '_' to be equivalent in variable 101 // names. We will replace dashes with underscores for processing. 102 103 // Split the flag into a key and value, setting value="1" if none 104 split := strings.Index(flag, "=") 105 106 if len(flag) == 0 { 107 continue 108 } 109 var key, value string 110 if split == -1 { 111 key = flag 112 value = "1" 113 } else { 114 key = flag[:split] 115 value = flag[split+1:] 116 } 117 canonicalKey := strings.Replace(key, "-", "_", -1) 118 trimmedValue := strings.Trim(value, "\"'") 119 120 // Call the user handler 121 handler(flag, key, canonicalKey, value, trimmedValue) 122 } 123 124 } 125 126 // parseToMap turns a space-separated kernel commandline into a map 127 func parseToMap(input string) map[string]string { 128 129 flagMap := make(map[string]string) 130 doParse(input, func(flag, key, canonicalKey, value, trimmedValue string) { 131 // We store the value twice, once with dash, once with underscores 132 // Just in case people check with the wrong method 133 flagMap[canonicalKey] = trimmedValue 134 flagMap[key] = trimmedValue 135 }) 136 137 return flagMap 138 } 139 140 // ContainsFlag verifies that the kernel cmdline has a flag set 141 func ContainsFlag(flag string) bool { 142 once.Do(cmdLineOpener) 143 _, present := Flag(flag) 144 return present 145 } 146 147 // Flag returns the a flag, and whether it was set 148 func Flag(flag string) (string, bool) { 149 once.Do(cmdLineOpener) 150 canonicalFlag := strings.Replace(flag, "-", "_", -1) 151 value, present := procCmdLine.AsMap[canonicalFlag] 152 return value, present 153 } 154 155 // getFlagMap gets specified flags as a map 156 func getFlagMap(flagName string) map[string]string { 157 return parseToMap(flagName) 158 } 159 160 // GetInitFlagMap gets the init flags as a map 161 func GetInitFlagMap() map[string]string { 162 initflags, _ := Flag("uroot.initflags") 163 return getFlagMap(initflags) 164 } 165 166 // GetUinitArgs gets the uinit argvs. 167 func GetUinitArgs() []string { 168 uinitargs, _ := Flag("uroot.uinitargs") 169 return shlex.Argv(uinitargs) 170 } 171 172 // FlagsForModule gets all flags for a designated module 173 // and returns them as a space-seperated string designed to be passed to insmod 174 // Note that similarly to flags, module names with - and _ are treated the same. 175 func FlagsForModule(name string) string { 176 once.Do(cmdLineOpener) 177 var ret string 178 flagsAdded := make(map[string]bool) // Ensures duplicate flags aren't both added 179 // Module flags come as moduleName.flag in /proc/cmdline 180 prefix := strings.Replace(name, "-", "_", -1) + "." 181 for flag, val := range procCmdLine.AsMap { 182 canonicalFlag := strings.Replace(flag, "-", "_", -1) 183 if !flagsAdded[canonicalFlag] && strings.HasPrefix(canonicalFlag, prefix) { 184 flagsAdded[canonicalFlag] = true 185 // They are passed to insmod space seperated as flag=val 186 ret += strings.TrimPrefix(canonicalFlag, prefix) + "=" + val + " " 187 } 188 } 189 return ret 190 }