| // Copyright 2018 The Prometheus Authors |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package procfs |
| |
| import ( |
| "bufio" |
| "errors" |
| "fmt" |
| "io" |
| "os" |
| "strconv" |
| "strings" |
| ) |
| |
| // For the proc file format details, |
| // see https://elixir.bootlin.com/linux/v4.17/source/net/unix/af_unix.c#L2815 |
| // and https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/net.h#L48. |
| |
| const ( |
| netUnixKernelPtrIdx = iota |
| netUnixRefCountIdx |
| _ |
| netUnixFlagsIdx |
| netUnixTypeIdx |
| netUnixStateIdx |
| netUnixInodeIdx |
| |
| // Inode and Path are optional. |
| netUnixStaticFieldsCnt = 6 |
| ) |
| |
| const ( |
| netUnixTypeStream = 1 |
| netUnixTypeDgram = 2 |
| netUnixTypeSeqpacket = 5 |
| |
| netUnixFlagListen = 1 << 16 |
| |
| netUnixStateUnconnected = 1 |
| netUnixStateConnecting = 2 |
| netUnixStateConnected = 3 |
| netUnixStateDisconnected = 4 |
| ) |
| |
| var errInvalidKernelPtrFmt = errors.New("Invalid Num(the kernel table slot number) format") |
| |
| // NetUnixType is the type of the type field. |
| type NetUnixType uint64 |
| |
| // NetUnixFlags is the type of the flags field. |
| type NetUnixFlags uint64 |
| |
| // NetUnixState is the type of the state field. |
| type NetUnixState uint64 |
| |
| // NetUnixLine represents a line of /proc/net/unix. |
| type NetUnixLine struct { |
| KernelPtr string |
| RefCount uint64 |
| Protocol uint64 |
| Flags NetUnixFlags |
| Type NetUnixType |
| State NetUnixState |
| Inode uint64 |
| Path string |
| } |
| |
| // NetUnix holds the data read from /proc/net/unix. |
| type NetUnix struct { |
| Rows []*NetUnixLine |
| } |
| |
| // NewNetUnix returns data read from /proc/net/unix. |
| func NewNetUnix() (*NetUnix, error) { |
| fs, err := NewFS(DefaultMountPoint) |
| if err != nil { |
| return nil, err |
| } |
| |
| return fs.NewNetUnix() |
| } |
| |
| // NewNetUnix returns data read from /proc/net/unix. |
| func (fs FS) NewNetUnix() (*NetUnix, error) { |
| return NewNetUnixByPath(fs.proc.Path("net/unix")) |
| } |
| |
| // NewNetUnixByPath returns data read from /proc/net/unix by file path. |
| // It might returns an error with partial parsed data, if an error occur after some data parsed. |
| func NewNetUnixByPath(path string) (*NetUnix, error) { |
| f, err := os.Open(path) |
| if err != nil { |
| return nil, err |
| } |
| defer f.Close() |
| return NewNetUnixByReader(f) |
| } |
| |
| // NewNetUnixByReader returns data read from /proc/net/unix by a reader. |
| // It might returns an error with partial parsed data, if an error occur after some data parsed. |
| func NewNetUnixByReader(reader io.Reader) (*NetUnix, error) { |
| nu := &NetUnix{ |
| Rows: make([]*NetUnixLine, 0, 32), |
| } |
| scanner := bufio.NewScanner(reader) |
| // Omit the header line. |
| scanner.Scan() |
| header := scanner.Text() |
| // From the man page of proc(5), it does not contain an Inode field, |
| // but in actually it exists. |
| // This code works for both cases. |
| hasInode := strings.Contains(header, "Inode") |
| |
| minFieldsCnt := netUnixStaticFieldsCnt |
| if hasInode { |
| minFieldsCnt++ |
| } |
| for scanner.Scan() { |
| line := scanner.Text() |
| item, err := nu.parseLine(line, hasInode, minFieldsCnt) |
| if err != nil { |
| return nu, err |
| } |
| nu.Rows = append(nu.Rows, item) |
| } |
| |
| return nu, scanner.Err() |
| } |
| |
| func (u *NetUnix) parseLine(line string, hasInode bool, minFieldsCnt int) (*NetUnixLine, error) { |
| fields := strings.Fields(line) |
| fieldsLen := len(fields) |
| if fieldsLen < minFieldsCnt { |
| return nil, fmt.Errorf( |
| "Parse Unix domain failed: expect at least %d fields but got %d", |
| minFieldsCnt, fieldsLen) |
| } |
| kernelPtr, err := u.parseKernelPtr(fields[netUnixKernelPtrIdx]) |
| if err != nil { |
| return nil, fmt.Errorf("Parse Unix domain num(%s) failed: %s", fields[netUnixKernelPtrIdx], err) |
| } |
| users, err := u.parseUsers(fields[netUnixRefCountIdx]) |
| if err != nil { |
| return nil, fmt.Errorf("Parse Unix domain ref count(%s) failed: %s", fields[netUnixRefCountIdx], err) |
| } |
| flags, err := u.parseFlags(fields[netUnixFlagsIdx]) |
| if err != nil { |
| return nil, fmt.Errorf("Parse Unix domain flags(%s) failed: %s", fields[netUnixFlagsIdx], err) |
| } |
| typ, err := u.parseType(fields[netUnixTypeIdx]) |
| if err != nil { |
| return nil, fmt.Errorf("Parse Unix domain type(%s) failed: %s", fields[netUnixTypeIdx], err) |
| } |
| state, err := u.parseState(fields[netUnixStateIdx]) |
| if err != nil { |
| return nil, fmt.Errorf("Parse Unix domain state(%s) failed: %s", fields[netUnixStateIdx], err) |
| } |
| var inode uint64 |
| if hasInode { |
| inodeStr := fields[netUnixInodeIdx] |
| inode, err = u.parseInode(inodeStr) |
| if err != nil { |
| return nil, fmt.Errorf("Parse Unix domain inode(%s) failed: %s", inodeStr, err) |
| } |
| } |
| |
| nuLine := &NetUnixLine{ |
| KernelPtr: kernelPtr, |
| RefCount: users, |
| Type: typ, |
| Flags: flags, |
| State: state, |
| Inode: inode, |
| } |
| |
| // Path field is optional. |
| if fieldsLen > minFieldsCnt { |
| pathIdx := netUnixInodeIdx + 1 |
| if !hasInode { |
| pathIdx-- |
| } |
| nuLine.Path = fields[pathIdx] |
| } |
| |
| return nuLine, nil |
| } |
| |
| func (u NetUnix) parseKernelPtr(str string) (string, error) { |
| if !strings.HasSuffix(str, ":") { |
| return "", errInvalidKernelPtrFmt |
| } |
| return str[:len(str)-1], nil |
| } |
| |
| func (u NetUnix) parseUsers(hexStr string) (uint64, error) { |
| return strconv.ParseUint(hexStr, 16, 32) |
| } |
| |
| func (u NetUnix) parseProtocol(hexStr string) (uint64, error) { |
| return strconv.ParseUint(hexStr, 16, 32) |
| } |
| |
| func (u NetUnix) parseType(hexStr string) (NetUnixType, error) { |
| typ, err := strconv.ParseUint(hexStr, 16, 16) |
| if err != nil { |
| return 0, err |
| } |
| return NetUnixType(typ), nil |
| } |
| |
| func (u NetUnix) parseFlags(hexStr string) (NetUnixFlags, error) { |
| flags, err := strconv.ParseUint(hexStr, 16, 32) |
| if err != nil { |
| return 0, err |
| } |
| return NetUnixFlags(flags), nil |
| } |
| |
| func (u NetUnix) parseState(hexStr string) (NetUnixState, error) { |
| st, err := strconv.ParseInt(hexStr, 16, 8) |
| if err != nil { |
| return 0, err |
| } |
| return NetUnixState(st), nil |
| } |
| |
| func (u NetUnix) parseInode(inodeStr string) (uint64, error) { |
| return strconv.ParseUint(inodeStr, 10, 64) |
| } |
| |
| func (t NetUnixType) String() string { |
| switch t { |
| case netUnixTypeStream: |
| return "stream" |
| case netUnixTypeDgram: |
| return "dgram" |
| case netUnixTypeSeqpacket: |
| return "seqpacket" |
| } |
| return "unknown" |
| } |
| |
| func (f NetUnixFlags) String() string { |
| switch f { |
| case netUnixFlagListen: |
| return "listen" |
| default: |
| return "default" |
| } |
| } |
| |
| func (s NetUnixState) String() string { |
| switch s { |
| case netUnixStateUnconnected: |
| return "unconnected" |
| case netUnixStateConnecting: |
| return "connecting" |
| case netUnixStateConnected: |
| return "connected" |
| case netUnixStateDisconnected: |
| return "disconnected" |
| } |
| return "unknown" |
| } |