gatus/vendor/github.com/prometheus/procfs/mountinfo.go

181 lines
5.2 KiB
Go
Raw Normal View History

2019-11-16 21:46:52 +01:00
// 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"
2020-09-17 01:32:13 +02:00
"bytes"
2019-11-16 21:46:52 +01:00
"fmt"
"strconv"
"strings"
2020-09-17 01:32:13 +02:00
"github.com/prometheus/procfs/internal/util"
)
2019-11-16 21:46:52 +01:00
// 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 {
2020-09-17 01:32:13 +02:00
// Unique ID for the mount
MountID int
// The ID of the parent mount
ParentID int
2019-11-16 21:46:52 +01:00
// 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
}
// Reads each line of the mountinfo file, and returns a list of formatted MountInfo structs.
2020-09-17 01:32:13 +02:00
func parseMountInfo(info []byte) ([]*MountInfo, error) {
2019-11-16 21:46:52 +01:00
mounts := []*MountInfo{}
2020-09-17 01:32:13 +02:00
scanner := bufio.NewScanner(bytes.NewReader(info))
2019-11-16 21:46:52 +01:00
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
2020-09-17 01:32:13 +02:00
mountInfo := strings.Split(mountString, " ")
mountInfoLength := len(mountInfo)
if mountInfoLength < 10 {
return nil, fmt.Errorf("couldn't find enough fields in mount string: %s", mountString)
2019-11-16 21:46:52 +01:00
}
2020-09-17 01:32:13 +02:00
if mountInfo[mountInfoLength-4] != "-" {
return nil, fmt.Errorf("couldn't find separator in expected field: %s", mountInfo[mountInfoLength-4])
2019-11-16 21:46:52 +01:00
}
mount := &MountInfo{
2020-09-17 01:32:13 +02:00
MajorMinorVer: mountInfo[2],
Root: mountInfo[3],
MountPoint: mountInfo[4],
Options: mountOptionsParser(mountInfo[5]),
2019-11-16 21:46:52 +01:00
OptionalFields: nil,
2020-09-17 01:32:13 +02:00
FSType: mountInfo[mountInfoLength-3],
Source: mountInfo[mountInfoLength-2],
SuperOptions: mountOptionsParser(mountInfo[mountInfoLength-1]),
2019-11-16 21:46:52 +01:00
}
2020-09-17 01:32:13 +02:00
mount.MountID, err = strconv.Atoi(mountInfo[0])
2019-11-16 21:46:52 +01:00
if err != nil {
return nil, fmt.Errorf("failed to parse mount ID")
}
2020-09-17 01:32:13 +02:00
mount.ParentID, err = strconv.Atoi(mountInfo[1])
2019-11-16 21:46:52 +01:00
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
2020-09-17 01:32:13 +02:00
if mountInfo[6] != "" {
mount.OptionalFields, err = mountOptionsParseOptionalFields(mountInfo[6 : mountInfoLength-4])
if err != nil {
return nil, err
2019-11-16 21:46:52 +01:00
}
}
return mount, nil
}
2020-09-17 01:32:13 +02:00
// mountOptionsIsValidField checks a string against a valid list of optional fields keys.
func mountOptionsIsValidField(s string) bool {
switch s {
case
"shared",
"master",
"propagate_from",
"unbindable":
return true
}
return false
}
// mountOptionsParseOptionalFields parses a list of optional fields strings into a double map of strings.
func mountOptionsParseOptionalFields(o []string) (map[string]string, error) {
optionalFields := make(map[string]string)
for _, field := range o {
optionSplit := strings.SplitN(field, ":", 2)
value := ""
if len(optionSplit) == 2 {
value = optionSplit[1]
}
if mountOptionsIsValidField(optionSplit[0]) {
optionalFields[optionSplit[0]] = value
}
}
return optionalFields, nil
}
// mountOptionsParser parses the mount options, superblock options.
2019-11-16 21:46:52 +01:00
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
}
2020-09-17 01:32:13 +02:00
// GetMounts retrieves mountinfo information from `/proc/self/mountinfo`.
2019-11-16 21:46:52 +01:00
func GetMounts() ([]*MountInfo, error) {
2020-09-17 01:32:13 +02:00
data, err := util.ReadFileNoStat("/proc/self/mountinfo")
2019-11-16 21:46:52 +01:00
if err != nil {
return nil, err
}
2020-09-17 01:32:13 +02:00
return parseMountInfo(data)
2019-11-16 21:46:52 +01:00
}
2020-09-17 01:32:13 +02:00
// GetProcMounts retrieves mountinfo information from a processes' `/proc/<pid>/mountinfo`.
2019-11-16 21:46:52 +01:00
func GetProcMounts(pid int) ([]*MountInfo, error) {
2020-09-17 01:32:13 +02:00
data, err := util.ReadFileNoStat(fmt.Sprintf("/proc/%d/mountinfo", pid))
2019-11-16 21:46:52 +01:00
if err != nil {
return nil, err
}
2020-09-17 01:32:13 +02:00
return parseMountInfo(data)
2019-11-16 21:46:52 +01:00
}