...

Source file src/github.com/prometheus/procfs/net_sockstat.go

Documentation: github.com/prometheus/procfs

     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  
    14  package procfs
    15  
    16  import (
    17  	"bufio"
    18  	"bytes"
    19  	"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.
    28  type 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.
    37  type 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.
    48  func (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.
    56  func (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.
    61  func 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 {
    72  		return nil, fmt.Errorf("%s: sockstats from %q: %w", ErrFileRead, name, err)
    73  	}
    74  
    75  	return stat, nil
    76  }
    77  
    78  // parseSockstat reads the contents of a sockstat file and parses a NetSockstat.
    79  func 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 {
    86  			return nil, fmt.Errorf("%w: Malformed sockstat line: %q", ErrFileParse, s.Text())
    87  		}
    88  
    89  		// The remaining fields are key/value pairs.
    90  		kvs, err := parseSockstatKVs(fields[1:])
    91  		if err != nil {
    92  			return nil, fmt.Errorf("%s: sockstat key/value pairs from %q: %w", ErrFileParse, s.Text(), err)
    93  		}
    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.
   119  func parseSockstatKVs(kvs []string) (map[string]int, error) {
   120  	if len(kvs)%2 != 0 {
   121  		return nil, fmt.Errorf("%w:: Odd number of fields in key/value pairs %q", ErrFileParse, kvs)
   122  	}
   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.
   139  func 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  }
   163  

View as plain text