blob: 98aa8e1c31c1e5257788ebe5cb7c9b63cd1fe3c1 [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"
khenaidooab1f7bd2019-11-14 14:00:27 -050018 "fmt"
19 "io"
20 "os"
21 "strconv"
22 "strings"
23)
24
25// For the proc file format details,
26// see https://elixir.bootlin.com/linux/v4.17/source/net/unix/af_unix.c#L2815
27// and https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/net.h#L48.
28
khenaidood948f772021-08-11 17:49:24 -040029// Constants for the various /proc/net/unix enumerations.
30// TODO: match against x/sys/unix or similar?
khenaidooab1f7bd2019-11-14 14:00:27 -050031const (
32 netUnixTypeStream = 1
33 netUnixTypeDgram = 2
34 netUnixTypeSeqpacket = 5
35
khenaidood948f772021-08-11 17:49:24 -040036 netUnixFlagDefault = 0
37 netUnixFlagListen = 1 << 16
khenaidooab1f7bd2019-11-14 14:00:27 -050038
39 netUnixStateUnconnected = 1
40 netUnixStateConnecting = 2
41 netUnixStateConnected = 3
42 netUnixStateDisconnected = 4
43)
44
khenaidood948f772021-08-11 17:49:24 -040045// NetUNIXType is the type of the type field.
46type NetUNIXType uint64
khenaidooab1f7bd2019-11-14 14:00:27 -050047
khenaidood948f772021-08-11 17:49:24 -040048// NetUNIXFlags is the type of the flags field.
49type NetUNIXFlags uint64
khenaidooab1f7bd2019-11-14 14:00:27 -050050
khenaidood948f772021-08-11 17:49:24 -040051// NetUNIXState is the type of the state field.
52type NetUNIXState uint64
khenaidooab1f7bd2019-11-14 14:00:27 -050053
khenaidood948f772021-08-11 17:49:24 -040054// NetUNIXLine represents a line of /proc/net/unix.
55type NetUNIXLine struct {
khenaidooab1f7bd2019-11-14 14:00:27 -050056 KernelPtr string
57 RefCount uint64
58 Protocol uint64
khenaidood948f772021-08-11 17:49:24 -040059 Flags NetUNIXFlags
60 Type NetUNIXType
61 State NetUNIXState
khenaidooab1f7bd2019-11-14 14:00:27 -050062 Inode uint64
63 Path string
64}
65
khenaidood948f772021-08-11 17:49:24 -040066// NetUNIX holds the data read from /proc/net/unix.
67type NetUNIX struct {
68 Rows []*NetUNIXLine
khenaidooab1f7bd2019-11-14 14:00:27 -050069}
70
khenaidood948f772021-08-11 17:49:24 -040071// NetUNIX returns data read from /proc/net/unix.
72func (fs FS) NetUNIX() (*NetUNIX, error) {
73 return readNetUNIX(fs.proc.Path("net/unix"))
khenaidooab1f7bd2019-11-14 14:00:27 -050074}
75
khenaidood948f772021-08-11 17:49:24 -040076// readNetUNIX reads data in /proc/net/unix format from the specified file.
77func readNetUNIX(file string) (*NetUNIX, error) {
78 // This file could be quite large and a streaming read is desirable versus
79 // reading the entire contents at once.
80 f, err := os.Open(file)
khenaidooab1f7bd2019-11-14 14:00:27 -050081 if err != nil {
82 return nil, err
83 }
84 defer f.Close()
khenaidood948f772021-08-11 17:49:24 -040085
86 return parseNetUNIX(f)
khenaidooab1f7bd2019-11-14 14:00:27 -050087}
88
khenaidood948f772021-08-11 17:49:24 -040089// parseNetUNIX creates a NetUnix structure from the incoming stream.
90func parseNetUNIX(r io.Reader) (*NetUNIX, error) {
91 // Begin scanning by checking for the existence of Inode.
92 s := bufio.NewScanner(r)
93 s.Scan()
khenaidooab1f7bd2019-11-14 14:00:27 -050094
khenaidood948f772021-08-11 17:49:24 -040095 // From the man page of proc(5), it does not contain an Inode field,
96 // but in actually it exists. This code works for both cases.
97 hasInode := strings.Contains(s.Text(), "Inode")
98
99 // Expect a minimum number of fields, but Inode and Path are optional:
100 // Num RefCount Protocol Flags Type St Inode Path
101 minFields := 6
khenaidooab1f7bd2019-11-14 14:00:27 -0500102 if hasInode {
khenaidood948f772021-08-11 17:49:24 -0400103 minFields++
khenaidooab1f7bd2019-11-14 14:00:27 -0500104 }
khenaidood948f772021-08-11 17:49:24 -0400105
106 var nu NetUNIX
107 for s.Scan() {
108 line := s.Text()
109 item, err := nu.parseLine(line, hasInode, minFields)
khenaidooab1f7bd2019-11-14 14:00:27 -0500110 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400111 return nil, fmt.Errorf("failed to parse /proc/net/unix data %q: %w", line, err)
khenaidooab1f7bd2019-11-14 14:00:27 -0500112 }
khenaidood948f772021-08-11 17:49:24 -0400113
khenaidooab1f7bd2019-11-14 14:00:27 -0500114 nu.Rows = append(nu.Rows, item)
115 }
116
khenaidood948f772021-08-11 17:49:24 -0400117 if err := s.Err(); err != nil {
118 return nil, fmt.Errorf("failed to scan /proc/net/unix data: %w", err)
119 }
120
121 return &nu, nil
khenaidooab1f7bd2019-11-14 14:00:27 -0500122}
123
khenaidood948f772021-08-11 17:49:24 -0400124func (u *NetUNIX) parseLine(line string, hasInode bool, min int) (*NetUNIXLine, error) {
khenaidooab1f7bd2019-11-14 14:00:27 -0500125 fields := strings.Fields(line)
khenaidood948f772021-08-11 17:49:24 -0400126
127 l := len(fields)
128 if l < min {
129 return nil, fmt.Errorf("expected at least %d fields but got %d", min, l)
khenaidooab1f7bd2019-11-14 14:00:27 -0500130 }
khenaidood948f772021-08-11 17:49:24 -0400131
132 // Field offsets are as follows:
133 // Num RefCount Protocol Flags Type St Inode Path
134
135 kernelPtr := strings.TrimSuffix(fields[0], ":")
136
137 users, err := u.parseUsers(fields[1])
khenaidooab1f7bd2019-11-14 14:00:27 -0500138 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400139 return nil, fmt.Errorf("failed to parse ref count %q: %w", fields[1], err)
khenaidooab1f7bd2019-11-14 14:00:27 -0500140 }
khenaidood948f772021-08-11 17:49:24 -0400141
142 flags, err := u.parseFlags(fields[3])
khenaidooab1f7bd2019-11-14 14:00:27 -0500143 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400144 return nil, fmt.Errorf("failed to parse flags %q: %w", fields[3], err)
khenaidooab1f7bd2019-11-14 14:00:27 -0500145 }
khenaidood948f772021-08-11 17:49:24 -0400146
147 typ, err := u.parseType(fields[4])
khenaidooab1f7bd2019-11-14 14:00:27 -0500148 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400149 return nil, fmt.Errorf("failed to parse type %q: %w", fields[4], err)
khenaidooab1f7bd2019-11-14 14:00:27 -0500150 }
khenaidood948f772021-08-11 17:49:24 -0400151
152 state, err := u.parseState(fields[5])
khenaidooab1f7bd2019-11-14 14:00:27 -0500153 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400154 return nil, fmt.Errorf("failed to parse state %q: %w", fields[5], err)
khenaidooab1f7bd2019-11-14 14:00:27 -0500155 }
khenaidood948f772021-08-11 17:49:24 -0400156
khenaidooab1f7bd2019-11-14 14:00:27 -0500157 var inode uint64
158 if hasInode {
khenaidood948f772021-08-11 17:49:24 -0400159 inode, err = u.parseInode(fields[6])
khenaidooab1f7bd2019-11-14 14:00:27 -0500160 if err != nil {
khenaidood948f772021-08-11 17:49:24 -0400161 return nil, fmt.Errorf("failed to parse inode %q: %w", fields[6], err)
khenaidooab1f7bd2019-11-14 14:00:27 -0500162 }
163 }
164
khenaidood948f772021-08-11 17:49:24 -0400165 n := &NetUNIXLine{
khenaidooab1f7bd2019-11-14 14:00:27 -0500166 KernelPtr: kernelPtr,
167 RefCount: users,
168 Type: typ,
169 Flags: flags,
170 State: state,
171 Inode: inode,
172 }
173
174 // Path field is optional.
khenaidood948f772021-08-11 17:49:24 -0400175 if l > min {
176 // Path occurs at either index 6 or 7 depending on whether inode is
177 // already present.
178 pathIdx := 7
khenaidooab1f7bd2019-11-14 14:00:27 -0500179 if !hasInode {
180 pathIdx--
181 }
khenaidood948f772021-08-11 17:49:24 -0400182
183 n.Path = fields[pathIdx]
khenaidooab1f7bd2019-11-14 14:00:27 -0500184 }
185
khenaidood948f772021-08-11 17:49:24 -0400186 return n, nil
khenaidooab1f7bd2019-11-14 14:00:27 -0500187}
188
khenaidood948f772021-08-11 17:49:24 -0400189func (u NetUNIX) parseUsers(s string) (uint64, error) {
190 return strconv.ParseUint(s, 16, 32)
khenaidooab1f7bd2019-11-14 14:00:27 -0500191}
192
khenaidood948f772021-08-11 17:49:24 -0400193func (u NetUNIX) parseType(s string) (NetUNIXType, error) {
194 typ, err := strconv.ParseUint(s, 16, 16)
khenaidooab1f7bd2019-11-14 14:00:27 -0500195 if err != nil {
196 return 0, err
197 }
khenaidood948f772021-08-11 17:49:24 -0400198
199 return NetUNIXType(typ), nil
khenaidooab1f7bd2019-11-14 14:00:27 -0500200}
201
khenaidood948f772021-08-11 17:49:24 -0400202func (u NetUNIX) parseFlags(s string) (NetUNIXFlags, error) {
203 flags, err := strconv.ParseUint(s, 16, 32)
khenaidooab1f7bd2019-11-14 14:00:27 -0500204 if err != nil {
205 return 0, err
206 }
khenaidood948f772021-08-11 17:49:24 -0400207
208 return NetUNIXFlags(flags), nil
khenaidooab1f7bd2019-11-14 14:00:27 -0500209}
210
khenaidood948f772021-08-11 17:49:24 -0400211func (u NetUNIX) parseState(s string) (NetUNIXState, error) {
212 st, err := strconv.ParseInt(s, 16, 8)
khenaidooab1f7bd2019-11-14 14:00:27 -0500213 if err != nil {
214 return 0, err
215 }
khenaidood948f772021-08-11 17:49:24 -0400216
217 return NetUNIXState(st), nil
khenaidooab1f7bd2019-11-14 14:00:27 -0500218}
219
khenaidood948f772021-08-11 17:49:24 -0400220func (u NetUNIX) parseInode(s string) (uint64, error) {
221 return strconv.ParseUint(s, 10, 64)
khenaidooab1f7bd2019-11-14 14:00:27 -0500222}
223
khenaidood948f772021-08-11 17:49:24 -0400224func (t NetUNIXType) String() string {
khenaidooab1f7bd2019-11-14 14:00:27 -0500225 switch t {
226 case netUnixTypeStream:
227 return "stream"
228 case netUnixTypeDgram:
229 return "dgram"
230 case netUnixTypeSeqpacket:
231 return "seqpacket"
232 }
233 return "unknown"
234}
235
khenaidood948f772021-08-11 17:49:24 -0400236func (f NetUNIXFlags) String() string {
khenaidooab1f7bd2019-11-14 14:00:27 -0500237 switch f {
238 case netUnixFlagListen:
239 return "listen"
240 default:
241 return "default"
242 }
243}
244
khenaidood948f772021-08-11 17:49:24 -0400245func (s NetUNIXState) String() string {
khenaidooab1f7bd2019-11-14 14:00:27 -0500246 switch s {
247 case netUnixStateUnconnected:
248 return "unconnected"
249 case netUnixStateConnecting:
250 return "connecting"
251 case netUnixStateConnected:
252 return "connected"
253 case netUnixStateDisconnected:
254 return "disconnected"
255 }
256 return "unknown"
257}