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