You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

181 lines
5.2 KiB

  1. // Copyright 2019 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package procfs
  14. import (
  15. "bufio"
  16. "bytes"
  17. "fmt"
  18. "strconv"
  19. "strings"
  20. "github.com/prometheus/procfs/internal/util"
  21. )
  22. // A MountInfo is a type that describes the details, options
  23. // for each mount, parsed from /proc/self/mountinfo.
  24. // The fields described in each entry of /proc/self/mountinfo
  25. // is described in the following man page.
  26. // http://man7.org/linux/man-pages/man5/proc.5.html
  27. type MountInfo struct {
  28. // Unique ID for the mount
  29. MountID int
  30. // The ID of the parent mount
  31. ParentID int
  32. // The value of `st_dev` for the files on this FS
  33. MajorMinorVer string
  34. // The pathname of the directory in the FS that forms
  35. // the root for this mount
  36. Root string
  37. // The pathname of the mount point relative to the root
  38. MountPoint string
  39. // Mount options
  40. Options map[string]string
  41. // Zero or more optional fields
  42. OptionalFields map[string]string
  43. // The Filesystem type
  44. FSType string
  45. // FS specific information or "none"
  46. Source string
  47. // Superblock options
  48. SuperOptions map[string]string
  49. }
  50. // Reads each line of the mountinfo file, and returns a list of formatted MountInfo structs.
  51. func parseMountInfo(info []byte) ([]*MountInfo, error) {
  52. mounts := []*MountInfo{}
  53. scanner := bufio.NewScanner(bytes.NewReader(info))
  54. for scanner.Scan() {
  55. mountString := scanner.Text()
  56. parsedMounts, err := parseMountInfoString(mountString)
  57. if err != nil {
  58. return nil, err
  59. }
  60. mounts = append(mounts, parsedMounts)
  61. }
  62. err := scanner.Err()
  63. return mounts, err
  64. }
  65. // Parses a mountinfo file line, and converts it to a MountInfo struct.
  66. // An important check here is to see if the hyphen separator, as if it does not exist,
  67. // it means that the line is malformed.
  68. func parseMountInfoString(mountString string) (*MountInfo, error) {
  69. var err error
  70. mountInfo := strings.Split(mountString, " ")
  71. mountInfoLength := len(mountInfo)
  72. if mountInfoLength < 10 {
  73. return nil, fmt.Errorf("couldn't find enough fields in mount string: %s", mountString)
  74. }
  75. if mountInfo[mountInfoLength-4] != "-" {
  76. return nil, fmt.Errorf("couldn't find separator in expected field: %s", mountInfo[mountInfoLength-4])
  77. }
  78. mount := &MountInfo{
  79. MajorMinorVer: mountInfo[2],
  80. Root: mountInfo[3],
  81. MountPoint: mountInfo[4],
  82. Options: mountOptionsParser(mountInfo[5]),
  83. OptionalFields: nil,
  84. FSType: mountInfo[mountInfoLength-3],
  85. Source: mountInfo[mountInfoLength-2],
  86. SuperOptions: mountOptionsParser(mountInfo[mountInfoLength-1]),
  87. }
  88. mount.MountID, err = strconv.Atoi(mountInfo[0])
  89. if err != nil {
  90. return nil, fmt.Errorf("failed to parse mount ID")
  91. }
  92. mount.ParentID, err = strconv.Atoi(mountInfo[1])
  93. if err != nil {
  94. return nil, fmt.Errorf("failed to parse parent ID")
  95. }
  96. // Has optional fields, which is a space separated list of values.
  97. // Example: shared:2 master:7
  98. if mountInfo[6] != "" {
  99. mount.OptionalFields, err = mountOptionsParseOptionalFields(mountInfo[6 : mountInfoLength-4])
  100. if err != nil {
  101. return nil, err
  102. }
  103. }
  104. return mount, nil
  105. }
  106. // mountOptionsIsValidField checks a string against a valid list of optional fields keys.
  107. func mountOptionsIsValidField(s string) bool {
  108. switch s {
  109. case
  110. "shared",
  111. "master",
  112. "propagate_from",
  113. "unbindable":
  114. return true
  115. }
  116. return false
  117. }
  118. // mountOptionsParseOptionalFields parses a list of optional fields strings into a double map of strings.
  119. func mountOptionsParseOptionalFields(o []string) (map[string]string, error) {
  120. optionalFields := make(map[string]string)
  121. for _, field := range o {
  122. optionSplit := strings.SplitN(field, ":", 2)
  123. value := ""
  124. if len(optionSplit) == 2 {
  125. value = optionSplit[1]
  126. }
  127. if mountOptionsIsValidField(optionSplit[0]) {
  128. optionalFields[optionSplit[0]] = value
  129. }
  130. }
  131. return optionalFields, nil
  132. }
  133. // mountOptionsParser parses the mount options, superblock options.
  134. func mountOptionsParser(mountOptions string) map[string]string {
  135. opts := make(map[string]string)
  136. options := strings.Split(mountOptions, ",")
  137. for _, opt := range options {
  138. splitOption := strings.Split(opt, "=")
  139. if len(splitOption) < 2 {
  140. key := splitOption[0]
  141. opts[key] = ""
  142. } else {
  143. key, value := splitOption[0], splitOption[1]
  144. opts[key] = value
  145. }
  146. }
  147. return opts
  148. }
  149. // GetMounts retrieves mountinfo information from `/proc/self/mountinfo`.
  150. func GetMounts() ([]*MountInfo, error) {
  151. data, err := util.ReadFileNoStat("/proc/self/mountinfo")
  152. if err != nil {
  153. return nil, err
  154. }
  155. return parseMountInfo(data)
  156. }
  157. // GetProcMounts retrieves mountinfo information from a processes' `/proc/<pid>/mountinfo`.
  158. func GetProcMounts(pid int) ([]*MountInfo, error) {
  159. data, err := util.ReadFileNoStat(fmt.Sprintf("/proc/%d/mountinfo", pid))
  160. if err != nil {
  161. return nil, err
  162. }
  163. return parseMountInfo(data)
  164. }