net_sockstat.go 4.4 KB

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