blob: fae62b13d9615aa901e8bec7d25645d4cfed41c9 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// 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"
khenaidood948f772021-08-11 17:49:24 -040019 "fmt"
20 "io"
21 "strings"
22
23 "github.com/prometheus/procfs/internal/util"
24)
25
26// A NetSockstat contains the output of /proc/net/sockstat{,6} for IPv4 or IPv6,
27// respectively.
28type NetSockstat struct {
29 // Used is non-nil for IPv4 sockstat results, but nil for IPv6.
30 Used *int
31 Protocols []NetSockstatProtocol
32}
33
34// A NetSockstatProtocol contains statistics about a given socket protocol.
35// Pointer fields indicate that the value may or may not be present on any
36// given protocol.
37type NetSockstatProtocol struct {
38 Protocol string
39 InUse int
40 Orphan *int
41 TW *int
42 Alloc *int
43 Mem *int
44 Memory *int
45}
46
47// NetSockstat retrieves IPv4 socket statistics.
48func (fs FS) NetSockstat() (*NetSockstat, error) {
49 return readSockstat(fs.proc.Path("net", "sockstat"))
50}
51
52// NetSockstat6 retrieves IPv6 socket statistics.
53//
54// If IPv6 is disabled on this kernel, the returned error can be checked with
55// os.IsNotExist.
56func (fs FS) NetSockstat6() (*NetSockstat, error) {
57 return readSockstat(fs.proc.Path("net", "sockstat6"))
58}
59
60// readSockstat opens and parses a NetSockstat from the input file.
61func readSockstat(name string) (*NetSockstat, error) {
62 // This file is small and can be read with one syscall.
63 b, err := util.ReadFileNoStat(name)
64 if err != nil {
65 // Do not wrap this error so the caller can detect os.IsNotExist and
66 // similar conditions.
67 return nil, err
68 }
69
70 stat, err := parseSockstat(bytes.NewReader(b))
71 if err != nil {
Abhay Kumara2ae5992025-11-10 14:02:24 +000072 return nil, fmt.Errorf("%w: sockstats from %q: %w", ErrFileRead, name, err)
khenaidood948f772021-08-11 17:49:24 -040073 }
74
75 return stat, nil
76}
77
78// parseSockstat reads the contents of a sockstat file and parses a NetSockstat.
79func parseSockstat(r io.Reader) (*NetSockstat, error) {
80 var stat NetSockstat
81 s := bufio.NewScanner(r)
82 for s.Scan() {
83 // Expect a minimum of a protocol and one key/value pair.
84 fields := strings.Split(s.Text(), " ")
85 if len(fields) < 3 {
Abhay Kumara2ae5992025-11-10 14:02:24 +000086 return nil, fmt.Errorf("%w: Malformed sockstat line: %q", ErrFileParse, s.Text())
khenaidood948f772021-08-11 17:49:24 -040087 }
88
89 // The remaining fields are key/value pairs.
90 kvs, err := parseSockstatKVs(fields[1:])
91 if err != nil {
Abhay Kumara2ae5992025-11-10 14:02:24 +000092 return nil, fmt.Errorf("%w: sockstat key/value pairs from %q: %w", ErrFileParse, s.Text(), err)
khenaidood948f772021-08-11 17:49:24 -040093 }
94
95 // The first field is the protocol. We must trim its colon suffix.
96 proto := strings.TrimSuffix(fields[0], ":")
97 switch proto {
98 case "sockets":
99 // Special case: IPv4 has a sockets "used" key/value pair that we
100 // embed at the top level of the structure.
101 used := kvs["used"]
102 stat.Used = &used
103 default:
104 // Parse all other lines as individual protocols.
105 nsp := parseSockstatProtocol(kvs)
106 nsp.Protocol = proto
107 stat.Protocols = append(stat.Protocols, nsp)
108 }
109 }
110
111 if err := s.Err(); err != nil {
112 return nil, err
113 }
114
115 return &stat, nil
116}
117
118// parseSockstatKVs parses a string slice into a map of key/value pairs.
119func parseSockstatKVs(kvs []string) (map[string]int, error) {
120 if len(kvs)%2 != 0 {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000121 return nil, fmt.Errorf("%w:: Odd number of fields in key/value pairs %q", ErrFileParse, kvs)
khenaidood948f772021-08-11 17:49:24 -0400122 }
123
124 // Iterate two values at a time to gather key/value pairs.
125 out := make(map[string]int, len(kvs)/2)
126 for i := 0; i < len(kvs); i += 2 {
127 vp := util.NewValueParser(kvs[i+1])
128 out[kvs[i]] = vp.Int()
129
130 if err := vp.Err(); err != nil {
131 return nil, err
132 }
133 }
134
135 return out, nil
136}
137
138// parseSockstatProtocol parses a NetSockstatProtocol from the input kvs map.
139func parseSockstatProtocol(kvs map[string]int) NetSockstatProtocol {
140 var nsp NetSockstatProtocol
141 for k, v := range kvs {
142 // Capture the range variable to ensure we get unique pointers for
143 // each of the optional fields.
144 v := v
145 switch k {
146 case "inuse":
147 nsp.InUse = v
148 case "orphan":
149 nsp.Orphan = &v
150 case "tw":
151 nsp.TW = &v
152 case "alloc":
153 nsp.Alloc = &v
154 case "mem":
155 nsp.Mem = &v
156 case "memory":
157 nsp.Memory = &v
158 }
159 }
160
161 return nsp
162}