cmdline.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package cmdline
  2. import (
  3. "io/ioutil"
  4. "strings"
  5. "github.com/rancher/os/pkg/util"
  6. yaml "github.com/cloudfoundry-incubator/candiedyaml"
  7. )
  8. func Read(parseAll bool) (m map[interface{}]interface{}, err error) {
  9. cmdLine, err := ioutil.ReadFile("/proc/cmdline")
  10. if err != nil {
  11. return nil, err
  12. }
  13. if len(cmdLine) == 0 {
  14. return nil, nil
  15. }
  16. cmdLineObj := Parse(strings.TrimSpace(util.UnescapeKernelParams(string(cmdLine))), parseAll)
  17. return cmdLineObj, nil
  18. }
  19. func GetCmdline(key string) interface{} {
  20. parseAll := true
  21. if strings.HasPrefix(key, "cc.") || strings.HasPrefix(key, "rancher.") {
  22. // the normal case
  23. parseAll = false
  24. }
  25. cmdline, _ := Read(parseAll)
  26. v, _ := GetOrSetVal(key, cmdline, nil)
  27. return v
  28. }
  29. func GetOrSetVal(args string, data map[interface{}]interface{}, value interface{}) (interface{}, map[interface{}]interface{}) {
  30. parts := strings.Split(args, ".")
  31. tData := data
  32. if value != nil {
  33. tData = util.MapCopy(data)
  34. }
  35. t := tData
  36. for i, part := range parts {
  37. val, ok := t[part]
  38. last := i+1 == len(parts)
  39. // Reached end, set the value
  40. if last && value != nil {
  41. if s, ok := value.(string); ok {
  42. value = UnmarshalOrReturnString(s)
  43. }
  44. t[part] = value
  45. return value, tData
  46. }
  47. // Missing intermediate key, create key
  48. if !last && value != nil && !ok {
  49. newData := map[interface{}]interface{}{}
  50. t[part] = newData
  51. t = newData
  52. continue
  53. }
  54. if !ok {
  55. break
  56. }
  57. if last {
  58. return val, tData
  59. }
  60. newData, ok := val.(map[interface{}]interface{})
  61. if !ok {
  62. break
  63. }
  64. t = newData
  65. }
  66. return "", tData
  67. }
  68. // Replace newlines, colons, and question marks with random strings
  69. // This is done to avoid YAML treating these as special characters
  70. var (
  71. newlineMagicString = "9XsJcx6dR5EERYCC"
  72. colonMagicString = "V0Rc21pIVknMm2rr"
  73. questionMarkMagicString = "FoPL6JLMAaJqKMJT"
  74. )
  75. func reverseReplacement(result interface{}) interface{} {
  76. switch val := result.(type) {
  77. case map[interface{}]interface{}:
  78. for k, v := range val {
  79. val[k] = reverseReplacement(v)
  80. }
  81. return val
  82. case []interface{}:
  83. for i, item := range val {
  84. val[i] = reverseReplacement(item)
  85. }
  86. return val
  87. case string:
  88. val = strings.Replace(val, newlineMagicString, "\n", -1)
  89. val = strings.Replace(val, colonMagicString, ":", -1)
  90. val = strings.Replace(val, questionMarkMagicString, "?", -1)
  91. return val
  92. }
  93. return result
  94. }
  95. func UnmarshalOrReturnString(value string) (result interface{}) {
  96. value = strings.Replace(value, "\n", newlineMagicString, -1)
  97. value = strings.Replace(value, ":", colonMagicString, -1)
  98. value = strings.Replace(value, "?", questionMarkMagicString, -1)
  99. if err := yaml.Unmarshal([]byte(value), &result); err != nil {
  100. result = value
  101. }
  102. result = reverseReplacement(result)
  103. return
  104. }
  105. func Parse(cmdLine string, parseAll bool) map[interface{}]interface{} {
  106. result := map[interface{}]interface{}{}
  107. outer:
  108. for _, part := range strings.Split(cmdLine, " ") {
  109. if strings.HasPrefix(part, "cc.") {
  110. part = part[3:]
  111. } else if !strings.HasPrefix(part, "rancher.") {
  112. if !parseAll {
  113. continue
  114. }
  115. }
  116. var value string
  117. kv := strings.SplitN(part, "=", 2)
  118. if len(kv) == 1 {
  119. value = "true"
  120. } else {
  121. value = kv[1]
  122. }
  123. current := result
  124. keys := strings.Split(kv[0], ".")
  125. for i, key := range keys {
  126. if i == len(keys)-1 {
  127. current[key] = UnmarshalOrReturnString(value)
  128. } else {
  129. if obj, ok := current[key]; ok {
  130. if newCurrent, ok := obj.(map[interface{}]interface{}); ok {
  131. current = newCurrent
  132. } else {
  133. continue outer
  134. }
  135. } else {
  136. newCurrent := make(map[interface{}]interface{})
  137. current[key] = newCurrent
  138. current = newCurrent
  139. }
  140. }
  141. }
  142. }
  143. return result
  144. }