github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+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 provides a parser and convenience functions for reading 6 // configuration data from /proc/cmdline it's conformant with 7 // https://www.kernel.org/doc/html/v4.14/admin-guide/kernel-parameters.html, 8 // though making 'var_name' and 'var-name' equivalent may need to be done 9 // separately. 10 package cmdline 11 12 import ( 13 "fmt" 14 "io" 15 "io/ioutil" 16 "log" 17 "os" 18 "strings" 19 "sync" 20 "unicode" 21 ) 22 23 // CmdLine lets people view the raw & parsed /proc/cmdline in one place 24 type CmdLine struct { 25 Raw string 26 AsMap map[string]string 27 Err error 28 } 29 30 var ( 31 // procCmdLine package level static variable initialized once 32 once sync.Once 33 procCmdLine CmdLine 34 ) 35 36 func cmdLineOpener() { 37 cmdlineReader, err := os.Open("/proc/cmdline") 38 if err != nil { 39 errorMsg := fmt.Sprintf("Can't open /proc/cmdline: %v", err) 40 log.Print(errorMsg) 41 procCmdLine = CmdLine{Err: fmt.Errorf(errorMsg)} 42 return 43 } 44 45 procCmdLine = parse(cmdlineReader) 46 cmdlineReader.Close() 47 } 48 49 // NewCmdLine returns a populated CmdLine struct 50 func NewCmdLine() CmdLine { 51 // We use cmdLineReader so tests can inject here 52 once.Do(cmdLineOpener) 53 return procCmdLine 54 } 55 56 // FullCmdLine returns the full, raw cmdline string 57 func FullCmdLine() string { 58 once.Do(cmdLineOpener) 59 return procCmdLine.Raw 60 } 61 62 // parse returns the current command line, trimmed 63 func parse(cmdlineReader io.Reader) CmdLine { 64 raw, err := ioutil.ReadAll(cmdlineReader) 65 line := CmdLine{} 66 if err != nil { 67 log.Printf("Can't read command line: %v", err) 68 line.Err = err 69 line.Raw = "" 70 } else { 71 line.Raw = strings.TrimRight(string(raw), "\n") 72 line.AsMap = parseToMap(line.Raw) 73 } 74 return line 75 } 76 77 // 78 func doParse(input string, handler func(flag, key, canonicalKey, value, trimmedValue string)) { 79 80 lastQuote := rune(0) 81 quotedFieldsCheck := func(c rune) bool { 82 switch { 83 case c == lastQuote: 84 lastQuote = rune(0) 85 return false 86 case lastQuote != rune(0): 87 return false 88 case unicode.In(c, unicode.Quotation_Mark): 89 lastQuote = c 90 return false 91 default: 92 return unicode.IsSpace(c) 93 } 94 } 95 96 for _, flag := range strings.FieldsFunc(string(input), quotedFieldsCheck) { 97 // kernel variables must allow '-' and '_' to be equivalent in variable 98 // names. We will replace dashes with underscores for processing. 99 100 // Split the flag into a key and value, setting value="1" if none 101 split := strings.Index(flag, "=") 102 103 if len(flag) == 0 { 104 continue 105 } 106 var key, value string 107 if split == -1 { 108 key = flag 109 value = "1" 110 } else { 111 key = flag[:split] 112 value = flag[split+1:] 113 } 114 canonicalKey := strings.Replace(key, "-", "_", -1) 115 trimmedValue := strings.Trim(value, "\"'") 116 117 // Call the user handler 118 handler(flag, key, canonicalKey, value, trimmedValue) 119 } 120 121 } 122 123 // parseToMap turns a space-separated kernel commandline into a map 124 func parseToMap(input string) map[string]string { 125 126 flagMap := make(map[string]string) 127 doParse(input, func(flag, key, canonicalKey, value, trimmedValue string) { 128 // We store the value twice, once with dash, once with underscores 129 // Just in case people check with the wrong method 130 flagMap[canonicalKey] = trimmedValue 131 flagMap[key] = trimmedValue 132 }) 133 134 return flagMap 135 } 136 137 // ContainsFlag verifies that the kernel cmdline has a flag set 138 func ContainsFlag(flag string) bool { 139 once.Do(cmdLineOpener) 140 _, present := Flag(flag) 141 return present 142 } 143 144 // Flag returns the a flag, and whether it was set 145 func Flag(flag string) (string, bool) { 146 once.Do(cmdLineOpener) 147 canonicalFlag := strings.Replace(flag, "-", "_", -1) 148 value, present := procCmdLine.AsMap[canonicalFlag] 149 return value, present 150 } 151 152 // getFlagMap gets specified flags as a map 153 func getFlagMap(flagName string) map[string]string { 154 return parseToMap(flagName) 155 } 156 157 // GetUinitFlagMap gets the uinit flags as a map 158 func GetUinitFlagMap() map[string]string { 159 uinitflags, _ := Flag("uroot.uinitflags") 160 return getFlagMap(uinitflags) 161 } 162 163 // GetInitFlagMap gets the init flags as a map 164 func GetInitFlagMap() map[string]string { 165 initflags, _ := Flag("uroot.initflags") 166 return getFlagMap(initflags) 167 }