khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 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 | |
| 14 | package procfs |
| 15 | |
| 16 | import ( |
| 17 | "bufio" |
| 18 | "errors" |
| 19 | "fmt" |
| 20 | "io" |
| 21 | "os" |
| 22 | "strconv" |
| 23 | "strings" |
| 24 | ) |
| 25 | |
| 26 | // For the proc file format details, |
| 27 | // see https://elixir.bootlin.com/linux/v4.17/source/net/unix/af_unix.c#L2815 |
| 28 | // and https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/net.h#L48. |
| 29 | |
| 30 | const ( |
| 31 | netUnixKernelPtrIdx = iota |
| 32 | netUnixRefCountIdx |
| 33 | _ |
| 34 | netUnixFlagsIdx |
| 35 | netUnixTypeIdx |
| 36 | netUnixStateIdx |
| 37 | netUnixInodeIdx |
| 38 | |
| 39 | // Inode and Path are optional. |
| 40 | netUnixStaticFieldsCnt = 6 |
| 41 | ) |
| 42 | |
| 43 | const ( |
| 44 | netUnixTypeStream = 1 |
| 45 | netUnixTypeDgram = 2 |
| 46 | netUnixTypeSeqpacket = 5 |
| 47 | |
| 48 | netUnixFlagListen = 1 << 16 |
| 49 | |
| 50 | netUnixStateUnconnected = 1 |
| 51 | netUnixStateConnecting = 2 |
| 52 | netUnixStateConnected = 3 |
| 53 | netUnixStateDisconnected = 4 |
| 54 | ) |
| 55 | |
| 56 | var errInvalidKernelPtrFmt = errors.New("Invalid Num(the kernel table slot number) format") |
| 57 | |
| 58 | // NetUnixType is the type of the type field. |
| 59 | type NetUnixType uint64 |
| 60 | |
| 61 | // NetUnixFlags is the type of the flags field. |
| 62 | type NetUnixFlags uint64 |
| 63 | |
| 64 | // NetUnixState is the type of the state field. |
| 65 | type NetUnixState uint64 |
| 66 | |
| 67 | // NetUnixLine represents a line of /proc/net/unix. |
| 68 | type NetUnixLine struct { |
| 69 | KernelPtr string |
| 70 | RefCount uint64 |
| 71 | Protocol uint64 |
| 72 | Flags NetUnixFlags |
| 73 | Type NetUnixType |
| 74 | State NetUnixState |
| 75 | Inode uint64 |
| 76 | Path string |
| 77 | } |
| 78 | |
| 79 | // NetUnix holds the data read from /proc/net/unix. |
| 80 | type NetUnix struct { |
| 81 | Rows []*NetUnixLine |
| 82 | } |
| 83 | |
| 84 | // NewNetUnix returns data read from /proc/net/unix. |
| 85 | func NewNetUnix() (*NetUnix, error) { |
| 86 | fs, err := NewFS(DefaultMountPoint) |
| 87 | if err != nil { |
| 88 | return nil, err |
| 89 | } |
| 90 | |
| 91 | return fs.NewNetUnix() |
| 92 | } |
| 93 | |
| 94 | // NewNetUnix returns data read from /proc/net/unix. |
| 95 | func (fs FS) NewNetUnix() (*NetUnix, error) { |
| 96 | return NewNetUnixByPath(fs.proc.Path("net/unix")) |
| 97 | } |
| 98 | |
| 99 | // NewNetUnixByPath returns data read from /proc/net/unix by file path. |
| 100 | // It might returns an error with partial parsed data, if an error occur after some data parsed. |
| 101 | func NewNetUnixByPath(path string) (*NetUnix, error) { |
| 102 | f, err := os.Open(path) |
| 103 | if err != nil { |
| 104 | return nil, err |
| 105 | } |
| 106 | defer f.Close() |
| 107 | return NewNetUnixByReader(f) |
| 108 | } |
| 109 | |
| 110 | // NewNetUnixByReader returns data read from /proc/net/unix by a reader. |
| 111 | // It might returns an error with partial parsed data, if an error occur after some data parsed. |
| 112 | func NewNetUnixByReader(reader io.Reader) (*NetUnix, error) { |
| 113 | nu := &NetUnix{ |
| 114 | Rows: make([]*NetUnixLine, 0, 32), |
| 115 | } |
| 116 | scanner := bufio.NewScanner(reader) |
| 117 | // Omit the header line. |
| 118 | scanner.Scan() |
| 119 | header := scanner.Text() |
| 120 | // From the man page of proc(5), it does not contain an Inode field, |
| 121 | // but in actually it exists. |
| 122 | // This code works for both cases. |
| 123 | hasInode := strings.Contains(header, "Inode") |
| 124 | |
| 125 | minFieldsCnt := netUnixStaticFieldsCnt |
| 126 | if hasInode { |
| 127 | minFieldsCnt++ |
| 128 | } |
| 129 | for scanner.Scan() { |
| 130 | line := scanner.Text() |
| 131 | item, err := nu.parseLine(line, hasInode, minFieldsCnt) |
| 132 | if err != nil { |
| 133 | return nu, err |
| 134 | } |
| 135 | nu.Rows = append(nu.Rows, item) |
| 136 | } |
| 137 | |
| 138 | return nu, scanner.Err() |
| 139 | } |
| 140 | |
| 141 | func (u *NetUnix) parseLine(line string, hasInode bool, minFieldsCnt int) (*NetUnixLine, error) { |
| 142 | fields := strings.Fields(line) |
| 143 | fieldsLen := len(fields) |
| 144 | if fieldsLen < minFieldsCnt { |
| 145 | return nil, fmt.Errorf( |
| 146 | "Parse Unix domain failed: expect at least %d fields but got %d", |
| 147 | minFieldsCnt, fieldsLen) |
| 148 | } |
| 149 | kernelPtr, err := u.parseKernelPtr(fields[netUnixKernelPtrIdx]) |
| 150 | if err != nil { |
| 151 | return nil, fmt.Errorf("Parse Unix domain num(%s) failed: %s", fields[netUnixKernelPtrIdx], err) |
| 152 | } |
| 153 | users, err := u.parseUsers(fields[netUnixRefCountIdx]) |
| 154 | if err != nil { |
| 155 | return nil, fmt.Errorf("Parse Unix domain ref count(%s) failed: %s", fields[netUnixRefCountIdx], err) |
| 156 | } |
| 157 | flags, err := u.parseFlags(fields[netUnixFlagsIdx]) |
| 158 | if err != nil { |
| 159 | return nil, fmt.Errorf("Parse Unix domain flags(%s) failed: %s", fields[netUnixFlagsIdx], err) |
| 160 | } |
| 161 | typ, err := u.parseType(fields[netUnixTypeIdx]) |
| 162 | if err != nil { |
| 163 | return nil, fmt.Errorf("Parse Unix domain type(%s) failed: %s", fields[netUnixTypeIdx], err) |
| 164 | } |
| 165 | state, err := u.parseState(fields[netUnixStateIdx]) |
| 166 | if err != nil { |
| 167 | return nil, fmt.Errorf("Parse Unix domain state(%s) failed: %s", fields[netUnixStateIdx], err) |
| 168 | } |
| 169 | var inode uint64 |
| 170 | if hasInode { |
| 171 | inodeStr := fields[netUnixInodeIdx] |
| 172 | inode, err = u.parseInode(inodeStr) |
| 173 | if err != nil { |
| 174 | return nil, fmt.Errorf("Parse Unix domain inode(%s) failed: %s", inodeStr, err) |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | nuLine := &NetUnixLine{ |
| 179 | KernelPtr: kernelPtr, |
| 180 | RefCount: users, |
| 181 | Type: typ, |
| 182 | Flags: flags, |
| 183 | State: state, |
| 184 | Inode: inode, |
| 185 | } |
| 186 | |
| 187 | // Path field is optional. |
| 188 | if fieldsLen > minFieldsCnt { |
| 189 | pathIdx := netUnixInodeIdx + 1 |
| 190 | if !hasInode { |
| 191 | pathIdx-- |
| 192 | } |
| 193 | nuLine.Path = fields[pathIdx] |
| 194 | } |
| 195 | |
| 196 | return nuLine, nil |
| 197 | } |
| 198 | |
| 199 | func (u NetUnix) parseKernelPtr(str string) (string, error) { |
| 200 | if !strings.HasSuffix(str, ":") { |
| 201 | return "", errInvalidKernelPtrFmt |
| 202 | } |
| 203 | return str[:len(str)-1], nil |
| 204 | } |
| 205 | |
| 206 | func (u NetUnix) parseUsers(hexStr string) (uint64, error) { |
| 207 | return strconv.ParseUint(hexStr, 16, 32) |
| 208 | } |
| 209 | |
| 210 | func (u NetUnix) parseProtocol(hexStr string) (uint64, error) { |
| 211 | return strconv.ParseUint(hexStr, 16, 32) |
| 212 | } |
| 213 | |
| 214 | func (u NetUnix) parseType(hexStr string) (NetUnixType, error) { |
| 215 | typ, err := strconv.ParseUint(hexStr, 16, 16) |
| 216 | if err != nil { |
| 217 | return 0, err |
| 218 | } |
| 219 | return NetUnixType(typ), nil |
| 220 | } |
| 221 | |
| 222 | func (u NetUnix) parseFlags(hexStr string) (NetUnixFlags, error) { |
| 223 | flags, err := strconv.ParseUint(hexStr, 16, 32) |
| 224 | if err != nil { |
| 225 | return 0, err |
| 226 | } |
| 227 | return NetUnixFlags(flags), nil |
| 228 | } |
| 229 | |
| 230 | func (u NetUnix) parseState(hexStr string) (NetUnixState, error) { |
| 231 | st, err := strconv.ParseInt(hexStr, 16, 8) |
| 232 | if err != nil { |
| 233 | return 0, err |
| 234 | } |
| 235 | return NetUnixState(st), nil |
| 236 | } |
| 237 | |
| 238 | func (u NetUnix) parseInode(inodeStr string) (uint64, error) { |
| 239 | return strconv.ParseUint(inodeStr, 10, 64) |
| 240 | } |
| 241 | |
| 242 | func (t NetUnixType) String() string { |
| 243 | switch t { |
| 244 | case netUnixTypeStream: |
| 245 | return "stream" |
| 246 | case netUnixTypeDgram: |
| 247 | return "dgram" |
| 248 | case netUnixTypeSeqpacket: |
| 249 | return "seqpacket" |
| 250 | } |
| 251 | return "unknown" |
| 252 | } |
| 253 | |
| 254 | func (f NetUnixFlags) String() string { |
| 255 | switch f { |
| 256 | case netUnixFlagListen: |
| 257 | return "listen" |
| 258 | default: |
| 259 | return "default" |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | func (s NetUnixState) String() string { |
| 264 | switch s { |
| 265 | case netUnixStateUnconnected: |
| 266 | return "unconnected" |
| 267 | case netUnixStateConnecting: |
| 268 | return "connecting" |
| 269 | case netUnixStateConnected: |
| 270 | return "connected" |
| 271 | case netUnixStateDisconnected: |
| 272 | return "disconnected" |
| 273 | } |
| 274 | return "unknown" |
| 275 | } |