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 "regexp" 21 22 "github.com/prometheus/procfs/internal/util" 23) 24 25// Regexp variables 26var ( 27 rPos = regexp.MustCompile(`^pos:\s+(\d+)$`) 28 rFlags = regexp.MustCompile(`^flags:\s+(\d+)$`) 29 rMntID = regexp.MustCompile(`^mnt_id:\s+(\d+)$`) 30 rInotify = regexp.MustCompile(`^inotify`) 31 rInotifyParts = regexp.MustCompile(`^inotify\s+wd:([0-9a-f]+)\s+ino:([0-9a-f]+)\s+sdev:([0-9a-f]+)(?:\s+mask:([0-9a-f]+))?`) 32) 33 34// ProcFDInfo contains represents file descriptor information. 35type ProcFDInfo struct { 36 // File descriptor 37 FD string 38 // File offset 39 Pos string 40 // File access mode and status flags 41 Flags string 42 // Mount point ID 43 MntID string 44 // List of inotify lines (structured) in the fdinfo file (kernel 3.8+ only) 45 InotifyInfos []InotifyInfo 46} 47 48// FDInfo constructor. On kernels older than 3.8, InotifyInfos will always be empty. 49func (p Proc) FDInfo(fd string) (*ProcFDInfo, error) { 50 data, err := util.ReadFileNoStat(p.path("fdinfo", fd)) 51 if err != nil { 52 return nil, err 53 } 54 55 var text, pos, flags, mntid string 56 var inotify []InotifyInfo 57 58 scanner := bufio.NewScanner(bytes.NewReader(data)) 59 for scanner.Scan() { 60 text = scanner.Text() 61 if rPos.MatchString(text) { 62 pos = rPos.FindStringSubmatch(text)[1] 63 } else if rFlags.MatchString(text) { 64 flags = rFlags.FindStringSubmatch(text)[1] 65 } else if rMntID.MatchString(text) { 66 mntid = rMntID.FindStringSubmatch(text)[1] 67 } else if rInotify.MatchString(text) { 68 newInotify, err := parseInotifyInfo(text) 69 if err != nil { 70 return nil, err 71 } 72 inotify = append(inotify, *newInotify) 73 } 74 } 75 76 i := &ProcFDInfo{ 77 FD: fd, 78 Pos: pos, 79 Flags: flags, 80 MntID: mntid, 81 InotifyInfos: inotify, 82 } 83 84 return i, nil 85} 86 87// InotifyInfo represents a single inotify line in the fdinfo file. 88type InotifyInfo struct { 89 // Watch descriptor number 90 WD string 91 // Inode number 92 Ino string 93 // Device ID 94 Sdev string 95 // Mask of events being monitored 96 Mask string 97} 98 99// InotifyInfo constructor. Only available on kernel 3.8+. 100func parseInotifyInfo(line string) (*InotifyInfo, error) { 101 m := rInotifyParts.FindStringSubmatch(line) 102 if len(m) >= 4 { 103 var mask string 104 if len(m) == 5 { 105 mask = m[4] 106 } 107 i := &InotifyInfo{ 108 WD: m[1], 109 Ino: m[2], 110 Sdev: m[3], 111 Mask: mask, 112 } 113 return i, nil 114 } 115 return nil, fmt.Errorf("invalid inode entry: %q", line) 116} 117 118// ProcFDInfos represents a list of ProcFDInfo structs. 119type ProcFDInfos []ProcFDInfo 120 121func (p ProcFDInfos) Len() int { return len(p) } 122func (p ProcFDInfos) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 123func (p ProcFDInfos) Less(i, j int) bool { return p[i].FD < p[j].FD } 124 125// InotifyWatchLen returns the total number of inotify watches 126func (p ProcFDInfos) InotifyWatchLen() (int, error) { 127 length := 0 128 for _, f := range p { 129 length += len(f.InotifyInfos) 130 } 131 132 return length, nil 133} 134