blob: 240340a83ab91baed60fdf2a01a063eded7efa2a [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001// Copyright 2018 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 "errors"
19 "fmt"
20 "io"
21 "os"
22 "strconv"
23 "strings"
24)
25
26// For the proc file format details,
27// see https://elixir.bootlin.com/linux/v4.17/source/net/unix/af_unix.c#L2815
28// and https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/net.h#L48.
29
30const (
31 netUnixKernelPtrIdx = iota
32 netUnixRefCountIdx
33 _
34 netUnixFlagsIdx
35 netUnixTypeIdx
36 netUnixStateIdx
37 netUnixInodeIdx
38
39 // Inode and Path are optional.
40 netUnixStaticFieldsCnt = 6
41)
42
43const (
44 netUnixTypeStream = 1
45 netUnixTypeDgram = 2
46 netUnixTypeSeqpacket = 5
47
48 netUnixFlagListen = 1 << 16
49
50 netUnixStateUnconnected = 1
51 netUnixStateConnecting = 2
52 netUnixStateConnected = 3
53 netUnixStateDisconnected = 4
54)
55
56var errInvalidKernelPtrFmt = errors.New("Invalid Num(the kernel table slot number) format")
57
58// NetUnixType is the type of the type field.
59type NetUnixType uint64
60
61// NetUnixFlags is the type of the flags field.
62type NetUnixFlags uint64
63
64// NetUnixState is the type of the state field.
65type NetUnixState uint64
66
67// NetUnixLine represents a line of /proc/net/unix.
68type NetUnixLine struct {
69 KernelPtr string
70 RefCount uint64
71 Protocol uint64
72 Flags NetUnixFlags
73 Type NetUnixType
74 State NetUnixState
75 Inode uint64
76 Path string
77}
78
79// NetUnix holds the data read from /proc/net/unix.
80type NetUnix struct {
81 Rows []*NetUnixLine
82}
83
84// NewNetUnix returns data read from /proc/net/unix.
85func NewNetUnix() (*NetUnix, error) {
86 fs, err := NewFS(DefaultMountPoint)
87 if err != nil {
88 return nil, err
89 }
90
91 return fs.NewNetUnix()
92}
93
94// NewNetUnix returns data read from /proc/net/unix.
95func (fs FS) NewNetUnix() (*NetUnix, error) {
96 return NewNetUnixByPath(fs.proc.Path("net/unix"))
97}
98
99// NewNetUnixByPath returns data read from /proc/net/unix by file path.
100// It might returns an error with partial parsed data, if an error occur after some data parsed.
101func NewNetUnixByPath(path string) (*NetUnix, error) {
102 f, err := os.Open(path)
103 if err != nil {
104 return nil, err
105 }
106 defer f.Close()
107 return NewNetUnixByReader(f)
108}
109
110// NewNetUnixByReader returns data read from /proc/net/unix by a reader.
111// It might returns an error with partial parsed data, if an error occur after some data parsed.
112func NewNetUnixByReader(reader io.Reader) (*NetUnix, error) {
113 nu := &NetUnix{
114 Rows: make([]*NetUnixLine, 0, 32),
115 }
116 scanner := bufio.NewScanner(reader)
117 // Omit the header line.
118 scanner.Scan()
119 header := scanner.Text()
120 // From the man page of proc(5), it does not contain an Inode field,
121 // but in actually it exists.
122 // This code works for both cases.
123 hasInode := strings.Contains(header, "Inode")
124
125 minFieldsCnt := netUnixStaticFieldsCnt
126 if hasInode {
127 minFieldsCnt++
128 }
129 for scanner.Scan() {
130 line := scanner.Text()
131 item, err := nu.parseLine(line, hasInode, minFieldsCnt)
132 if err != nil {
133 return nu, err
134 }
135 nu.Rows = append(nu.Rows, item)
136 }
137
138 return nu, scanner.Err()
139}
140
141func (u *NetUnix) parseLine(line string, hasInode bool, minFieldsCnt int) (*NetUnixLine, error) {
142 fields := strings.Fields(line)
143 fieldsLen := len(fields)
144 if fieldsLen < minFieldsCnt {
145 return nil, fmt.Errorf(
146 "Parse Unix domain failed: expect at least %d fields but got %d",
147 minFieldsCnt, fieldsLen)
148 }
149 kernelPtr, err := u.parseKernelPtr(fields[netUnixKernelPtrIdx])
150 if err != nil {
151 return nil, fmt.Errorf("Parse Unix domain num(%s) failed: %s", fields[netUnixKernelPtrIdx], err)
152 }
153 users, err := u.parseUsers(fields[netUnixRefCountIdx])
154 if err != nil {
155 return nil, fmt.Errorf("Parse Unix domain ref count(%s) failed: %s", fields[netUnixRefCountIdx], err)
156 }
157 flags, err := u.parseFlags(fields[netUnixFlagsIdx])
158 if err != nil {
159 return nil, fmt.Errorf("Parse Unix domain flags(%s) failed: %s", fields[netUnixFlagsIdx], err)
160 }
161 typ, err := u.parseType(fields[netUnixTypeIdx])
162 if err != nil {
163 return nil, fmt.Errorf("Parse Unix domain type(%s) failed: %s", fields[netUnixTypeIdx], err)
164 }
165 state, err := u.parseState(fields[netUnixStateIdx])
166 if err != nil {
167 return nil, fmt.Errorf("Parse Unix domain state(%s) failed: %s", fields[netUnixStateIdx], err)
168 }
169 var inode uint64
170 if hasInode {
171 inodeStr := fields[netUnixInodeIdx]
172 inode, err = u.parseInode(inodeStr)
173 if err != nil {
174 return nil, fmt.Errorf("Parse Unix domain inode(%s) failed: %s", inodeStr, err)
175 }
176 }
177
178 nuLine := &NetUnixLine{
179 KernelPtr: kernelPtr,
180 RefCount: users,
181 Type: typ,
182 Flags: flags,
183 State: state,
184 Inode: inode,
185 }
186
187 // Path field is optional.
188 if fieldsLen > minFieldsCnt {
189 pathIdx := netUnixInodeIdx + 1
190 if !hasInode {
191 pathIdx--
192 }
193 nuLine.Path = fields[pathIdx]
194 }
195
196 return nuLine, nil
197}
198
199func (u NetUnix) parseKernelPtr(str string) (string, error) {
200 if !strings.HasSuffix(str, ":") {
201 return "", errInvalidKernelPtrFmt
202 }
203 return str[:len(str)-1], nil
204}
205
206func (u NetUnix) parseUsers(hexStr string) (uint64, error) {
207 return strconv.ParseUint(hexStr, 16, 32)
208}
209
210func (u NetUnix) parseProtocol(hexStr string) (uint64, error) {
211 return strconv.ParseUint(hexStr, 16, 32)
212}
213
214func (u NetUnix) parseType(hexStr string) (NetUnixType, error) {
215 typ, err := strconv.ParseUint(hexStr, 16, 16)
216 if err != nil {
217 return 0, err
218 }
219 return NetUnixType(typ), nil
220}
221
222func (u NetUnix) parseFlags(hexStr string) (NetUnixFlags, error) {
223 flags, err := strconv.ParseUint(hexStr, 16, 32)
224 if err != nil {
225 return 0, err
226 }
227 return NetUnixFlags(flags), nil
228}
229
230func (u NetUnix) parseState(hexStr string) (NetUnixState, error) {
231 st, err := strconv.ParseInt(hexStr, 16, 8)
232 if err != nil {
233 return 0, err
234 }
235 return NetUnixState(st), nil
236}
237
238func (u NetUnix) parseInode(inodeStr string) (uint64, error) {
239 return strconv.ParseUint(inodeStr, 10, 64)
240}
241
242func (t NetUnixType) String() string {
243 switch t {
244 case netUnixTypeStream:
245 return "stream"
246 case netUnixTypeDgram:
247 return "dgram"
248 case netUnixTypeSeqpacket:
249 return "seqpacket"
250 }
251 return "unknown"
252}
253
254func (f NetUnixFlags) String() string {
255 switch f {
256 case netUnixFlagListen:
257 return "listen"
258 default:
259 return "default"
260 }
261}
262
263func (s NetUnixState) String() string {
264 switch s {
265 case netUnixStateUnconnected:
266 return "unconnected"
267 case netUnixStateConnecting:
268 return "connecting"
269 case netUnixStateConnected:
270 return "connected"
271 case netUnixStateDisconnected:
272 return "disconnected"
273 }
274 return "unknown"
275}