...

Source file src/github.com/prometheus/procfs/mountinfo.go

Documentation: github.com/prometheus/procfs

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

View as plain text