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