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