net_unix.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. // 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. package procfs
  14. import (
  15. "bufio"
  16. "fmt"
  17. "io"
  18. "os"
  19. "strconv"
  20. "strings"
  21. )
  22. // For the proc file format details,
  23. // see https://elixir.bootlin.com/linux/v4.17/source/net/unix/af_unix.c#L2815
  24. // and https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/net.h#L48.
  25. // Constants for the various /proc/net/unix enumerations.
  26. // TODO: match against x/sys/unix or similar?
  27. const (
  28. netUnixTypeStream = 1
  29. netUnixTypeDgram = 2
  30. netUnixTypeSeqpacket = 5
  31. netUnixFlagDefault = 0
  32. netUnixFlagListen = 1 << 16
  33. netUnixStateUnconnected = 1
  34. netUnixStateConnecting = 2
  35. netUnixStateConnected = 3
  36. netUnixStateDisconnected = 4
  37. )
  38. // NetUNIXType is the type of the type field.
  39. type NetUNIXType uint64
  40. // NetUNIXFlags is the type of the flags field.
  41. type NetUNIXFlags uint64
  42. // NetUNIXState is the type of the state field.
  43. type NetUNIXState uint64
  44. // NetUNIXLine represents a line of /proc/net/unix.
  45. type NetUNIXLine struct {
  46. KernelPtr string
  47. RefCount uint64
  48. Protocol uint64
  49. Flags NetUNIXFlags
  50. Type NetUNIXType
  51. State NetUNIXState
  52. Inode uint64
  53. Path string
  54. }
  55. // NetUNIX holds the data read from /proc/net/unix.
  56. type NetUNIX struct {
  57. Rows []*NetUNIXLine
  58. }
  59. // NetUNIX returns data read from /proc/net/unix.
  60. func (fs FS) NetUNIX() (*NetUNIX, error) {
  61. return readNetUNIX(fs.proc.Path("net/unix"))
  62. }
  63. // readNetUNIX reads data in /proc/net/unix format from the specified file.
  64. func readNetUNIX(file string) (*NetUNIX, error) {
  65. // This file could be quite large and a streaming read is desirable versus
  66. // reading the entire contents at once.
  67. f, err := os.Open(file)
  68. if err != nil {
  69. return nil, err
  70. }
  71. defer f.Close()
  72. return parseNetUNIX(f)
  73. }
  74. // parseNetUNIX creates a NetUnix structure from the incoming stream.
  75. func parseNetUNIX(r io.Reader) (*NetUNIX, error) {
  76. // Begin scanning by checking for the existence of Inode.
  77. s := bufio.NewScanner(r)
  78. s.Scan()
  79. // From the man page of proc(5), it does not contain an Inode field,
  80. // but in actually it exists. This code works for both cases.
  81. hasInode := strings.Contains(s.Text(), "Inode")
  82. // Expect a minimum number of fields, but Inode and Path are optional:
  83. // Num RefCount Protocol Flags Type St Inode Path
  84. minFields := 6
  85. if hasInode {
  86. minFields++
  87. }
  88. var nu NetUNIX
  89. for s.Scan() {
  90. line := s.Text()
  91. item, err := nu.parseLine(line, hasInode, minFields)
  92. if err != nil {
  93. return nil, fmt.Errorf("%s: /proc/net/unix encountered data %q: %w", ErrFileParse, line, err)
  94. }
  95. nu.Rows = append(nu.Rows, item)
  96. }
  97. if err := s.Err(); err != nil {
  98. return nil, fmt.Errorf("%s: /proc/net/unix encountered data: %w", ErrFileParse, err)
  99. }
  100. return &nu, nil
  101. }
  102. func (u *NetUNIX) parseLine(line string, hasInode bool, min int) (*NetUNIXLine, error) {
  103. fields := strings.Fields(line)
  104. l := len(fields)
  105. if l < min {
  106. return nil, fmt.Errorf("%w: expected at least %d fields but got %d", ErrFileParse, min, l)
  107. }
  108. // Field offsets are as follows:
  109. // Num RefCount Protocol Flags Type St Inode Path
  110. kernelPtr := strings.TrimSuffix(fields[0], ":")
  111. users, err := u.parseUsers(fields[1])
  112. if err != nil {
  113. return nil, fmt.Errorf("%s: ref count %q: %w", ErrFileParse, fields[1], err)
  114. }
  115. flags, err := u.parseFlags(fields[3])
  116. if err != nil {
  117. return nil, fmt.Errorf("%s: Unable to parse flags %q: %w", ErrFileParse, fields[3], err)
  118. }
  119. typ, err := u.parseType(fields[4])
  120. if err != nil {
  121. return nil, fmt.Errorf("%s: Failed to parse type %q: %w", ErrFileParse, fields[4], err)
  122. }
  123. state, err := u.parseState(fields[5])
  124. if err != nil {
  125. return nil, fmt.Errorf("%s: Failed to parse state %q: %w", ErrFileParse, fields[5], err)
  126. }
  127. var inode uint64
  128. if hasInode {
  129. inode, err = u.parseInode(fields[6])
  130. if err != nil {
  131. return nil, fmt.Errorf("%s failed to parse inode %q: %w", ErrFileParse, fields[6], err)
  132. }
  133. }
  134. n := &NetUNIXLine{
  135. KernelPtr: kernelPtr,
  136. RefCount: users,
  137. Type: typ,
  138. Flags: flags,
  139. State: state,
  140. Inode: inode,
  141. }
  142. // Path field is optional.
  143. if l > min {
  144. // Path occurs at either index 6 or 7 depending on whether inode is
  145. // already present.
  146. pathIdx := 7
  147. if !hasInode {
  148. pathIdx--
  149. }
  150. n.Path = fields[pathIdx]
  151. }
  152. return n, nil
  153. }
  154. func (u NetUNIX) parseUsers(s string) (uint64, error) {
  155. return strconv.ParseUint(s, 16, 32)
  156. }
  157. func (u NetUNIX) parseType(s string) (NetUNIXType, error) {
  158. typ, err := strconv.ParseUint(s, 16, 16)
  159. if err != nil {
  160. return 0, err
  161. }
  162. return NetUNIXType(typ), nil
  163. }
  164. func (u NetUNIX) parseFlags(s string) (NetUNIXFlags, error) {
  165. flags, err := strconv.ParseUint(s, 16, 32)
  166. if err != nil {
  167. return 0, err
  168. }
  169. return NetUNIXFlags(flags), nil
  170. }
  171. func (u NetUNIX) parseState(s string) (NetUNIXState, error) {
  172. st, err := strconv.ParseInt(s, 16, 8)
  173. if err != nil {
  174. return 0, err
  175. }
  176. return NetUNIXState(st), nil
  177. }
  178. func (u NetUNIX) parseInode(s string) (uint64, error) {
  179. return strconv.ParseUint(s, 10, 64)
  180. }
  181. func (t NetUNIXType) String() string {
  182. switch t {
  183. case netUnixTypeStream:
  184. return "stream"
  185. case netUnixTypeDgram:
  186. return "dgram"
  187. case netUnixTypeSeqpacket:
  188. return "seqpacket"
  189. }
  190. return "unknown"
  191. }
  192. func (f NetUNIXFlags) String() string {
  193. switch f {
  194. case netUnixFlagListen:
  195. return "listen"
  196. default:
  197. return "default"
  198. }
  199. }
  200. func (s NetUNIXState) String() string {
  201. switch s {
  202. case netUnixStateUnconnected:
  203. return "unconnected"
  204. case netUnixStateConnecting:
  205. return "connecting"
  206. case netUnixStateConnected:
  207. return "connected"
  208. case netUnixStateDisconnected:
  209. return "disconnected"
  210. }
  211. return "unknown"
  212. }