|
- // 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)
- }
|