From 995bc4fad18e10002ac9044ac44b42f53672e97f Mon Sep 17 00:00:00 2001 From: Dipack P Panjabi Date: Wed, 5 Jun 2019 13:37:54 -0400 Subject: [PATCH 1/6] Add mountinfo parsing to procfs Signed-off-by: Dipack P Panjabi --- mountinfo.go | 184 ++++++++++++++++++++++++++++++++++++++++++++++ mountinfo_test.go | 118 +++++++++++++++++++++++++++++ proc.go | 10 +++ 3 files changed, 312 insertions(+) create mode 100644 mountinfo.go create mode 100644 mountinfo_test.go diff --git a/mountinfo.go b/mountinfo.go new file mode 100644 index 000000000..aa88bc808 --- /dev/null +++ b/mountinfo.go @@ -0,0 +1,184 @@ +// Copyright 2018 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" +) + +// Fields description for mountinfo is here: +// 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 getMountInfoPartString(parts []string, idx int) string { + if idx >= len(parts) { + return "" + } + return parts[idx] +} + +// Checks if the 'keys' in the optional fields in the mountinfo line are acceptable +// Allowed 'keys' are shared, master, propagate_from, unbindable +func isValidOptionalField(field string) bool { + acceptableFields := []string{"shared", "master", "propagate_from", "unbindable"} + for _, acceptable := range acceptableFields { + if field == acceptable { + return true + } + } + return false +} + +// 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, fmt.Errorf("failed to parse mount info string") + } + mounts = append(mounts, parsedMounts) + } + + if err := scanner.Err(); err != nil { + return mounts, err + } + + return mounts, nil +} + +// 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:]) + splits := strings.Fields(mountString) + if len(splits) < 7 { + return nil, fmt.Errorf("too few fields") + } + mount := &MountInfo{ + MajorMinorVer: getMountInfoPartString(beforeFields, 2), + Root: getMountInfoPartString(beforeFields, 3), + MountPoint: getMountInfoPartString(beforeFields, 4), + Options: mountOptionsParser(getMountInfoPartString(beforeFields, 5)), + OptionalFields: nil, + FSType: getMountInfoPartString(afterFields, 0), + Source: getMountInfoPartString(afterFields, 1), + SuperOptions: mountOptionsParser(getMountInfoPartString(afterFields, 2)), + } + + mount.MountId, err = strconv.Atoi(getMountInfoPartString(beforeFields, 0)) + if err != nil { + return nil, fmt.Errorf("failed to parse mount ID") + } + mount.ParentId, err = strconv.Atoi(getMountInfoPartString(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 := "", "" + if len(optionSplit) == 2 { + target, value = optionSplit[0], optionSplit[1] + } else if len(optionSplit) == 1 { + target = optionSplit[0] + } + if isValidOptionalField(target) { + 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//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) +} diff --git a/mountinfo_test.go b/mountinfo_test.go new file mode 100644 index 000000000..a894226c1 --- /dev/null +++ b/mountinfo_test.go @@ -0,0 +1,118 @@ +// Copyright 2018 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 ( + "reflect" + "testing" +) + +func TestMountInfo(t *testing.T) { + tests := []struct { + name string + s string + mount *MountInfo + invalid bool + }{ + { + name: "Regular sysfs mounted at /sys", + s: "16 21 0:16 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw", + invalid: false, + mount: &MountInfo{ + MountId: 16, + ParentId: 21, + MajorMinorVer: "0:16", + Root: "/", + MountPoint: "/sys", + Options: map[string]string{"rw": "", "nosuid": "", "nodev": "", "noexec": "", "relatime": ""}, + OptionalFields: map[string]string{"shared": "7"}, + FSType: "sysfs", + Source: "sysfs", + SuperOptions: map[string]string{"rw": ""}, + }, + }, + { + name: "Not enough information", + s: "hello", + invalid: true, + }, + { + name: "Tmpfs mounted at /run", + s: "225 20 0:39 / /run/user/112 rw,nosuid,nodev,relatime shared:177 - tmpfs tmpfs rw,size=405096k,mode=700,uid=112,gid=116", + mount: &MountInfo{ + MountId: 225, + ParentId: 20, + MajorMinorVer: "0:39", + Root: "/", + MountPoint: "/run/user/112", + Options: map[string]string{"rw": "", "nosuid": "", "nodev": "", "relatime": ""}, + OptionalFields: map[string]string{"shared": "177"}, + FSType: "tmpfs", + Source: "tmpfs", + SuperOptions: map[string]string{"rw": "", "size": "405096k", "mode": "700", "uid": "112", "gid": "116"}, + }, + invalid: false, + }, + { + name: "Tmpfs mounted at /run, but no optional values", + s: "225 20 0:39 / /run/user/112 rw,nosuid,nodev,relatime - tmpfs tmpfs rw,size=405096k,mode=700,uid=112,gid=116", + mount: &MountInfo{ + MountId: 225, + ParentId: 20, + MajorMinorVer: "0:39", + Root: "/", + MountPoint: "/run/user/112", + Options: map[string]string{"rw": "", "nosuid": "", "nodev": "", "relatime": ""}, + OptionalFields: nil, + FSType: "tmpfs", + Source: "tmpfs", + SuperOptions: map[string]string{"rw": "", "size": "405096k", "mode": "700", "uid": "112", "gid": "116"}, + }, + invalid: false, + }, + { + name: "Tmpfs mounted at /run, with multiple optional values", + s: "225 20 0:39 / /run/user/112 rw,nosuid,nodev,relatime shared:177 master:8 - tmpfs tmpfs rw,size=405096k,mode=700,uid=112,gid=116", + mount: &MountInfo{ + MountId: 225, + ParentId: 20, + MajorMinorVer: "0:39", + Root: "/", + MountPoint: "/run/user/112", + Options: map[string]string{"rw": "", "nosuid": "", "nodev": "", "relatime": ""}, + OptionalFields: map[string]string{"shared": "177", "master": "8"}, + FSType: "tmpfs", + Source: "tmpfs", + SuperOptions: map[string]string{"rw": "", "size": "405096k", "mode": "700", "uid": "112", "gid": "116"}, + }, + invalid: false, + }, + } + + for i, test := range tests { + t.Logf("[%02d] test %q", i, test.name) + + mount, err := parseMountInfoString(test.s) + + if test.invalid && err == nil { + t.Error("expected an error, but none occurred") + } + if !test.invalid && err != nil { + t.Errorf("unexpected error: %v", err) + } + + if want, have := test.mount, mount; !reflect.DeepEqual(want, have) { + t.Errorf("mounts:\nwant:\n%+v\nhave:\n%+v", want, have) + } + } +} diff --git a/proc.go b/proc.go index 8a8430147..12bb3dadd 100644 --- a/proc.go +++ b/proc.go @@ -247,6 +247,16 @@ func (p Proc) MountStats() ([]*Mount, error) { return parseMountStats(f) } +func (p Proc) MountInfo() ([]*MountInfo, error) { + f, err := os.Open(p.path("mountinfo")) + if err != nil { + return nil, err + } + defer f.Close() + + return parseMountInfo(f) +} + func (p Proc) fileDescriptors() ([]string, error) { d, err := os.Open(p.path("fd")) if err != nil { From d52e8426bb92f7c96a81bdc95ea54cb9f6073f59 Mon Sep 17 00:00:00 2001 From: Dipack P Panjabi Date: Thu, 6 Jun 2019 17:29:35 -0400 Subject: [PATCH 2/6] Return expressive error message Signed-off-by: Dipack P Panjabi --- mountinfo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mountinfo.go b/mountinfo.go index aa88bc808..7dadeeaf0 100644 --- a/mountinfo.go +++ b/mountinfo.go @@ -77,7 +77,7 @@ func parseMountInfo(r io.Reader) ([]*MountInfo, error) { mountString := scanner.Text() parsedMounts, err := parseMountInfoString(mountString) if err != nil { - return nil, fmt.Errorf("failed to parse mount info string") + return nil, err } mounts = append(mounts, parsedMounts) } From 00e851a0db15025f2517f2d35ecca3c3dd11bb5a Mon Sep 17 00:00:00 2001 From: Dipack P Panjabi Date: Fri, 7 Jun 2019 17:04:39 -0400 Subject: [PATCH 3/6] Minor changes to comply with repo standards Signed-off-by: Dipack P Panjabi --- mountinfo.go | 57 ++++++++++++++++++----------------------------- mountinfo_test.go | 17 ++++++++++++++ 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/mountinfo.go b/mountinfo.go index 7dadeeaf0..fe6bf6f9a 100644 --- a/mountinfo.go +++ b/mountinfo.go @@ -22,6 +22,8 @@ import ( "strings" ) +var validOptionalFields = map[string]bool{"shared": true, "master": true, "propagate_from": true, "unbindable": true} + // Fields description for mountinfo is here: // http://man7.org/linux/man-pages/man5/proc.5.html @@ -50,25 +52,13 @@ type MountInfo struct { } // Returns part of the mountinfo line, if it exists, else an empty string -func getMountInfoPartString(parts []string, idx int) string { +func getStringSliceElement(parts []string, idx int, defaultValue string) string { if idx >= len(parts) { - return "" + return defaultValue } return parts[idx] } -// Checks if the 'keys' in the optional fields in the mountinfo line are acceptable -// Allowed 'keys' are shared, master, propagate_from, unbindable -func isValidOptionalField(field string) bool { - acceptableFields := []string{"shared", "master", "propagate_from", "unbindable"} - for _, acceptable := range acceptableFields { - if field == acceptable { - return true - } - } - return false -} - // Reads each line of the mountinfo file, and returns a list of formatted MountInfo structs func parseMountInfo(r io.Reader) ([]*MountInfo, error) { mounts := []*MountInfo{} @@ -82,11 +72,8 @@ func parseMountInfo(r io.Reader) ([]*MountInfo, error) { mounts = append(mounts, parsedMounts) } - if err := scanner.Err(); err != nil { - return mounts, err - } - - return mounts, nil + err := scanner.Err() + return mounts, err } // Parses a mountinfo file line, and converts it to a MountInfo struct @@ -102,26 +89,26 @@ func parseMountInfoString(mountString string) (*MountInfo, error) { } beforeFields := strings.Fields(mountString[:separatorIndex]) afterFields := strings.Fields(mountString[separatorIndex+1:]) - splits := strings.Fields(mountString) - if len(splits) < 7 { + if (len(beforeFields) + len(afterFields)) < 7 { return nil, fmt.Errorf("too few fields") } + mount := &MountInfo{ - MajorMinorVer: getMountInfoPartString(beforeFields, 2), - Root: getMountInfoPartString(beforeFields, 3), - MountPoint: getMountInfoPartString(beforeFields, 4), - Options: mountOptionsParser(getMountInfoPartString(beforeFields, 5)), + MajorMinorVer: getStringSliceElement(beforeFields, 2, ""), + Root: getStringSliceElement(beforeFields, 3, ""), + MountPoint: getStringSliceElement(beforeFields, 4, ""), + Options: mountOptionsParser(getStringSliceElement(beforeFields, 5, "")), OptionalFields: nil, - FSType: getMountInfoPartString(afterFields, 0), - Source: getMountInfoPartString(afterFields, 1), - SuperOptions: mountOptionsParser(getMountInfoPartString(afterFields, 2)), + FSType: getStringSliceElement(afterFields, 0, ""), + Source: getStringSliceElement(afterFields, 1, ""), + SuperOptions: mountOptionsParser(getStringSliceElement(afterFields, 2, "")), } - mount.MountId, err = strconv.Atoi(getMountInfoPartString(beforeFields, 0)) + 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(getMountInfoPartString(beforeFields, 1)) + mount.ParentId, err = strconv.Atoi(getStringSliceElement(beforeFields, 1, "")) if err != nil { return nil, fmt.Errorf("failed to parse parent ID") } @@ -132,13 +119,13 @@ func parseMountInfoString(mountString string) (*MountInfo, error) { optionalFields := beforeFields[6:] for _, field := range optionalFields { optionSplit := strings.Split(field, ":") - target, value := "", "" + target, value := optionSplit[0], "" if len(optionSplit) == 2 { - target, value = optionSplit[0], optionSplit[1] - } else if len(optionSplit) == 1 { - target = optionSplit[0] + value = optionSplit[1] } - if isValidOptionalField(target) { + // 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 } } diff --git a/mountinfo_test.go b/mountinfo_test.go index a894226c1..5d42a2b69 100644 --- a/mountinfo_test.go +++ b/mountinfo_test.go @@ -97,6 +97,23 @@ func TestMountInfo(t *testing.T) { }, invalid: false, }, + { + name: "Tmpfs mounted at /run, with a mixture of valid and invalid optional values", + s: "225 20 0:39 / /run/user/112 rw,nosuid,nodev,relatime shared:177 master:8 foo:bar - tmpfs tmpfs rw,size=405096k,mode=700,uid=112,gid=116", + mount: &MountInfo{ + MountId: 225, + ParentId: 20, + MajorMinorVer: "0:39", + Root: "/", + MountPoint: "/run/user/112", + Options: map[string]string{"rw": "", "nosuid": "", "nodev": "", "relatime": ""}, + OptionalFields: map[string]string{"shared": "177", "master": "8"}, + FSType: "tmpfs", + Source: "tmpfs", + SuperOptions: map[string]string{"rw": "", "size": "405096k", "mode": "700", "uid": "112", "gid": "116"}, + }, + invalid: false, + }, } for i, test := range tests { From 36e5957e6878ade556e5254172d8ebbc139ffbd8 Mon Sep 17 00:00:00 2001 From: Dipack P Panjabi Date: Mon, 10 Jun 2019 09:24:37 -0400 Subject: [PATCH 4/6] Slight changes to comply with requests from SuperQ Signed-off-by: Dipack P Panjabi --- mountinfo.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mountinfo.go b/mountinfo.go index fe6bf6f9a..a2fb4d5be 100644 --- a/mountinfo.go +++ b/mountinfo.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// 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 @@ -22,11 +22,18 @@ import ( "strings" ) -var validOptionalFields = map[string]bool{"shared": true, "master": true, "propagate_from": true, "unbindable": true} +var validOptionalFields = map[string]bool{ + "shared": true, + "master": true, + "propagate_from": true, + "unbindable": true, +} -// Fields description for mountinfo is here: +// 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 From af9defb7cb467a53131f3af29ec17d046bcb85e3 Mon Sep 17 00:00:00 2001 From: Dipack P Panjabi Date: Mon, 10 Jun 2019 10:25:48 -0400 Subject: [PATCH 5/6] Few more changes suggested by SuperQ Signed-off-by: Dipack P Panjabi --- mountinfo.go | 24 ++++++++++++------------ mountinfo_test.go | 2 +- proc.go | 6 +++++- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/mountinfo.go b/mountinfo.go index a2fb4d5be..61fa61887 100644 --- a/mountinfo.go +++ b/mountinfo.go @@ -30,9 +30,9 @@ var validOptionalFields = map[string]bool{ } // A MountInfo is a type that describes the details, options -// for each mount, parsed from /proc/self/mountinfo +// 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 +// 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 @@ -58,7 +58,7 @@ type MountInfo struct { SuperOptions map[string]string } -// Returns part of the mountinfo line, if it exists, else an empty 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 @@ -66,7 +66,7 @@ func getStringSliceElement(parts []string, idx int, defaultValue string) string return parts[idx] } -// Reads each line of the mountinfo file, and returns a list of formatted MountInfo structs +// 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) @@ -83,9 +83,9 @@ func parseMountInfo(r io.Reader) ([]*MountInfo, error) { return mounts, err } -// Parses a mountinfo file line, and converts it to a MountInfo struct +// 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 +// it means that the line is malformed. func parseMountInfoString(mountString string) (*MountInfo, error) { var err error @@ -119,7 +119,7 @@ func parseMountInfoString(mountString string) (*MountInfo, error) { if err != nil { return nil, fmt.Errorf("failed to parse parent ID") } - // Has optional fields, which is a space separated list of values + // 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) @@ -130,8 +130,8 @@ func parseMountInfoString(mountString string) (*MountInfo, error) { 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 + // 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 } @@ -140,7 +140,7 @@ func parseMountInfoString(mountString string) (*MountInfo, error) { return mount, nil } -// Parses the mount options, superblock options +// Parses the mount options, superblock options. func mountOptionsParser(mountOptions string) map[string]string { opts := make(map[string]string) options := strings.Split(mountOptions, ",") @@ -157,7 +157,7 @@ func mountOptionsParser(mountOptions string) map[string]string { return opts } -// Retrieves mountinfo information from `/proc/self/mountinfo` +// Retrieves mountinfo information from `/proc/self/mountinfo`. func GetMounts() ([]*MountInfo, error) { f, err := os.Open("/proc/self/mountinfo") if err != nil { @@ -167,7 +167,7 @@ func GetMounts() ([]*MountInfo, error) { return parseMountInfo(f) } -// Retrieves mountinfo information from a processes' `/proc//mountinfo` +// Retrieves mountinfo information from a processes' `/proc//mountinfo`. func GetProcMounts(pid int) ([]*MountInfo, error) { f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) if err != nil { diff --git a/mountinfo_test.go b/mountinfo_test.go index 5d42a2b69..35659796d 100644 --- a/mountinfo_test.go +++ b/mountinfo_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// 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 diff --git a/proc.go b/proc.go index 12bb3dadd..804435b9b 100644 --- a/proc.go +++ b/proc.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// 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 @@ -247,6 +247,10 @@ func (p Proc) MountStats() ([]*Mount, error) { return parseMountStats(f) } +// MountInfo retrieves mount information for mount points in a +// process's namespace. +// It supplies information missing in `/proc/self/mounts` and +// fixes various other problems with that file too. func (p Proc) MountInfo() ([]*MountInfo, error) { f, err := os.Open(p.path("mountinfo")) if err != nil { From 770427a2ade6451f3b77db7b25be6c23300e2984 Mon Sep 17 00:00:00 2001 From: Dipack P Panjabi Date: Wed, 19 Jun 2019 12:57:58 -0400 Subject: [PATCH 6/6] Reset copyright date for proc.go to 2018 Signed-off-by: Dipack P Panjabi --- proc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proc.go b/proc.go index 804435b9b..41c148d06 100644 --- a/proc.go +++ b/proc.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright 2018 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