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  }