github.com/jlowellwofford/u-root@v1.0.0/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 // The cmdline package 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 // parseToMap turns a space-separated kernel commandline into a map 78 func parseToMap(input string) map[string]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 flagMap := make(map[string]string) 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 // We store the value twice, once with dash, once with underscores 116 // Just in case people check with the wrong method 117 canonicalKey := strings.Replace(key, "-", "_", -1) 118 trimmedValue := strings.Trim(value, "\"'") 119 flagMap[canonicalKey] = trimmedValue 120 flagMap[key] = trimmedValue 121 } 122 123 return flagMap 124 } 125 126 // ContainsFlag verifies that the kernel cmdline has a flag set 127 func ContainsFlag(flag string) bool { 128 once.Do(cmdLineOpener) 129 _, present := Flag(flag) 130 return present 131 } 132 133 // Flag returns the a flag, and whether it was set 134 func Flag(flag string) (string, bool) { 135 once.Do(cmdLineOpener) 136 canonicalFlag := strings.Replace(flag, "-", "_", -1) 137 value, present := procCmdLine.AsMap[canonicalFlag] 138 return value, present 139 } 140 141 // getFlagMap gets specified flags as a map 142 func getFlagMap(flagName string) map[string]string { 143 return parseToMap(flagName) 144 } 145 146 // GetUinitFlagMap gets the uinit flags as a map 147 func GetUinitFlagMap() map[string]string { 148 uinitflags, _ := Flag("uroot.uinitflags") 149 return getFlagMap(uinitflags) 150 } 151 152 // GetInitFlagMap gets the init flags as a map 153 func GetInitFlagMap() map[string]string { 154 initflags, _ := Flag("uroot.initflags") 155 return getFlagMap(initflags) 156 }