mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-30 18:20:19 +00:00
179 lines
5.5 KiB
Go
179 lines
5.5 KiB
Go
|
// 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)
|
||
|
}
|