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