diff options
Diffstat (limited to 'vendor/github.com/prometheus/procfs/mountinfo.go')
-rw-r--r-- | vendor/github.com/prometheus/procfs/mountinfo.go | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/vendor/github.com/prometheus/procfs/mountinfo.go b/vendor/github.com/prometheus/procfs/mountinfo.go new file mode 100644 index 000000000..61fa61887 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/mountinfo.go @@ -0,0 +1,178 @@ +// Copyright 2019 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package procfs + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" +) + +var validOptionalFields = map[string]bool{ + "shared": true, + "master": true, + "propagate_from": true, + "unbindable": true, +} + +// A MountInfo is a type that describes the details, options +// for each mount, parsed from /proc/self/mountinfo. +// The fields described in each entry of /proc/self/mountinfo +// is described in the following man page. +// http://man7.org/linux/man-pages/man5/proc.5.html +type MountInfo struct { + // Unique Id for the mount + MountId int + // The Id of the parent mount + ParentId int + // The value of `st_dev` for the files on this FS + MajorMinorVer string + // The pathname of the directory in the FS that forms + // the root for this mount + Root string + // The pathname of the mount point relative to the root + MountPoint string + // Mount options + Options map[string]string + // Zero or more optional fields + OptionalFields map[string]string + // The Filesystem type + FSType string + // FS specific information or "none" + Source string + // Superblock options + SuperOptions map[string]string +} + +// Returns part of the mountinfo line, if it exists, else an empty string. +func getStringSliceElement(parts []string, idx int, defaultValue string) string { + if idx >= len(parts) { + return defaultValue + } + return parts[idx] +} + +// Reads each line of the mountinfo file, and returns a list of formatted MountInfo structs. +func parseMountInfo(r io.Reader) ([]*MountInfo, error) { + mounts := []*MountInfo{} + scanner := bufio.NewScanner(r) + for scanner.Scan() { + mountString := scanner.Text() + parsedMounts, err := parseMountInfoString(mountString) + if err != nil { + return nil, err + } + mounts = append(mounts, parsedMounts) + } + + err := scanner.Err() + return mounts, err +} + +// Parses a mountinfo file line, and converts it to a MountInfo struct. +// An important check here is to see if the hyphen separator, as if it does not exist, +// it means that the line is malformed. +func parseMountInfoString(mountString string) (*MountInfo, error) { + var err error + + // OptionalFields can be zero, hence these checks to ensure we do not populate the wrong values in the wrong spots + separatorIndex := strings.Index(mountString, "-") + if separatorIndex == -1 { + return nil, fmt.Errorf("no separator found in mountinfo string: %s", mountString) + } + beforeFields := strings.Fields(mountString[:separatorIndex]) + afterFields := strings.Fields(mountString[separatorIndex+1:]) + if (len(beforeFields) + len(afterFields)) < 7 { + return nil, fmt.Errorf("too few fields") + } + + mount := &MountInfo{ + MajorMinorVer: getStringSliceElement(beforeFields, 2, ""), + Root: getStringSliceElement(beforeFields, 3, ""), + MountPoint: getStringSliceElement(beforeFields, 4, ""), + Options: mountOptionsParser(getStringSliceElement(beforeFields, 5, "")), + OptionalFields: nil, + FSType: getStringSliceElement(afterFields, 0, ""), + Source: getStringSliceElement(afterFields, 1, ""), + SuperOptions: mountOptionsParser(getStringSliceElement(afterFields, 2, "")), + } + + mount.MountId, err = strconv.Atoi(getStringSliceElement(beforeFields, 0, "")) + if err != nil { + return nil, fmt.Errorf("failed to parse mount ID") + } + mount.ParentId, err = strconv.Atoi(getStringSliceElement(beforeFields, 1, "")) + if err != nil { + return nil, fmt.Errorf("failed to parse parent ID") + } + // Has optional fields, which is a space separated list of values. + // Example: shared:2 master:7 + if len(beforeFields) > 6 { + mount.OptionalFields = make(map[string]string) + optionalFields := beforeFields[6:] + for _, field := range optionalFields { + optionSplit := strings.Split(field, ":") + target, value := optionSplit[0], "" + if len(optionSplit) == 2 { + value = optionSplit[1] + } + // Checks if the 'keys' in the optional fields in the mountinfo line are acceptable. + // Allowed 'keys' are shared, master, propagate_from, unbindable. + if _, ok := validOptionalFields[target]; ok { + mount.OptionalFields[target] = value + } + } + } + return mount, nil +} + +// Parses the mount options, superblock options. +func mountOptionsParser(mountOptions string) map[string]string { + opts := make(map[string]string) + options := strings.Split(mountOptions, ",") + for _, opt := range options { + splitOption := strings.Split(opt, "=") + if len(splitOption) < 2 { + key := splitOption[0] + opts[key] = "" + } else { + key, value := splitOption[0], splitOption[1] + opts[key] = value + } + } + return opts +} + +// Retrieves mountinfo information from `/proc/self/mountinfo`. +func GetMounts() ([]*MountInfo, error) { + f, err := os.Open("/proc/self/mountinfo") + if err != nil { + return nil, err + } + defer f.Close() + return parseMountInfo(f) +} + +// Retrieves mountinfo information from a processes' `/proc/<pid>/mountinfo`. +func GetProcMounts(pid int) ([]*MountInfo, error) { + f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) + if err != nil { + return nil, err + } + defer f.Close() + return parseMountInfo(f) +} |