...

Source file src/github.com/shirou/gopsutil/net/net_linux.go

Documentation: github.com/shirou/gopsutil/net

     1  // +build linux
     2  
     3  package net
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"encoding/hex"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"net"
    14  	"os"
    15  	"strconv"
    16  	"strings"
    17  	"syscall"
    18  
    19  	"github.com/shirou/gopsutil/internal/common"
    20  )
    21  
    22  const ( // Conntrack Column numbers
    23  	CT_ENTRIES = iota
    24  	CT_SEARCHED
    25  	CT_FOUND
    26  	CT_NEW
    27  	CT_INVALID
    28  	CT_IGNORE
    29  	CT_DELETE
    30  	CT_DELETE_LIST
    31  	CT_INSERT
    32  	CT_INSERT_FAILED
    33  	CT_DROP
    34  	CT_EARLY_DROP
    35  	CT_ICMP_ERROR
    36  	CT_EXPECT_NEW
    37  	CT_EXPECT_CREATE
    38  	CT_EXPECT_DELETE
    39  	CT_SEARCH_RESTART
    40  )
    41  
    42  // NetIOCounters returnes network I/O statistics for every network
    43  // interface installed on the system.  If pernic argument is false,
    44  // return only sum of all information (which name is 'all'). If true,
    45  // every network interface installed on the system is returned
    46  // separately.
    47  func IOCounters(pernic bool) ([]IOCountersStat, error) {
    48  	return IOCountersWithContext(context.Background(), pernic)
    49  }
    50  
    51  func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
    52  	filename := common.HostProc("net/dev")
    53  	return IOCountersByFileWithContext(ctx, pernic, filename)
    54  }
    55  
    56  func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
    57  	return IOCountersByFileWithContext(context.Background(), pernic, filename)
    58  }
    59  
    60  func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
    61  	lines, err := common.ReadLines(filename)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	parts := make([]string, 2)
    67  
    68  	statlen := len(lines) - 1
    69  
    70  	ret := make([]IOCountersStat, 0, statlen)
    71  
    72  	for _, line := range lines[2:] {
    73  		separatorPos := strings.LastIndex(line, ":")
    74  		if separatorPos == -1 {
    75  			continue
    76  		}
    77  		parts[0] = line[0:separatorPos]
    78  		parts[1] = line[separatorPos+1:]
    79  
    80  		interfaceName := strings.TrimSpace(parts[0])
    81  		if interfaceName == "" {
    82  			continue
    83  		}
    84  
    85  		fields := strings.Fields(strings.TrimSpace(parts[1]))
    86  		bytesRecv, err := strconv.ParseUint(fields[0], 10, 64)
    87  		if err != nil {
    88  			return ret, err
    89  		}
    90  		packetsRecv, err := strconv.ParseUint(fields[1], 10, 64)
    91  		if err != nil {
    92  			return ret, err
    93  		}
    94  		errIn, err := strconv.ParseUint(fields[2], 10, 64)
    95  		if err != nil {
    96  			return ret, err
    97  		}
    98  		dropIn, err := strconv.ParseUint(fields[3], 10, 64)
    99  		if err != nil {
   100  			return ret, err
   101  		}
   102  		fifoIn, err := strconv.ParseUint(fields[4], 10, 64)
   103  		if err != nil {
   104  			return ret, err
   105  		}
   106  		bytesSent, err := strconv.ParseUint(fields[8], 10, 64)
   107  		if err != nil {
   108  			return ret, err
   109  		}
   110  		packetsSent, err := strconv.ParseUint(fields[9], 10, 64)
   111  		if err != nil {
   112  			return ret, err
   113  		}
   114  		errOut, err := strconv.ParseUint(fields[10], 10, 64)
   115  		if err != nil {
   116  			return ret, err
   117  		}
   118  		dropOut, err := strconv.ParseUint(fields[11], 10, 64)
   119  		if err != nil {
   120  			return ret, err
   121  		}
   122  		fifoOut, err := strconv.ParseUint(fields[12], 10, 64)
   123  		if err != nil {
   124  			return ret, err
   125  		}
   126  
   127  		nic := IOCountersStat{
   128  			Name:        interfaceName,
   129  			BytesRecv:   bytesRecv,
   130  			PacketsRecv: packetsRecv,
   131  			Errin:       errIn,
   132  			Dropin:      dropIn,
   133  			Fifoin:      fifoIn,
   134  			BytesSent:   bytesSent,
   135  			PacketsSent: packetsSent,
   136  			Errout:      errOut,
   137  			Dropout:     dropOut,
   138  			Fifoout:     fifoOut,
   139  		}
   140  		ret = append(ret, nic)
   141  	}
   142  
   143  	if pernic == false {
   144  		return getIOCountersAll(ret)
   145  	}
   146  
   147  	return ret, nil
   148  }
   149  
   150  var netProtocols = []string{
   151  	"ip",
   152  	"icmp",
   153  	"icmpmsg",
   154  	"tcp",
   155  	"udp",
   156  	"udplite",
   157  }
   158  
   159  // NetProtoCounters returns network statistics for the entire system
   160  // If protocols is empty then all protocols are returned, otherwise
   161  // just the protocols in the list are returned.
   162  // Available protocols:
   163  //   ip,icmp,icmpmsg,tcp,udp,udplite
   164  func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
   165  	return ProtoCountersWithContext(context.Background(), protocols)
   166  }
   167  
   168  func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
   169  	if len(protocols) == 0 {
   170  		protocols = netProtocols
   171  	}
   172  
   173  	stats := make([]ProtoCountersStat, 0, len(protocols))
   174  	protos := make(map[string]bool, len(protocols))
   175  	for _, p := range protocols {
   176  		protos[p] = true
   177  	}
   178  
   179  	filename := common.HostProc("net/snmp")
   180  	lines, err := common.ReadLines(filename)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	linecount := len(lines)
   186  	for i := 0; i < linecount; i++ {
   187  		line := lines[i]
   188  		r := strings.IndexRune(line, ':')
   189  		if r == -1 {
   190  			return nil, errors.New(filename + " is not fomatted correctly, expected ':'.")
   191  		}
   192  		proto := strings.ToLower(line[:r])
   193  		if !protos[proto] {
   194  			// skip protocol and data line
   195  			i++
   196  			continue
   197  		}
   198  
   199  		// Read header line
   200  		statNames := strings.Split(line[r+2:], " ")
   201  
   202  		// Read data line
   203  		i++
   204  		statValues := strings.Split(lines[i][r+2:], " ")
   205  		if len(statNames) != len(statValues) {
   206  			return nil, errors.New(filename + " is not fomatted correctly, expected same number of columns.")
   207  		}
   208  		stat := ProtoCountersStat{
   209  			Protocol: proto,
   210  			Stats:    make(map[string]int64, len(statNames)),
   211  		}
   212  		for j := range statNames {
   213  			value, err := strconv.ParseInt(statValues[j], 10, 64)
   214  			if err != nil {
   215  				return nil, err
   216  			}
   217  			stat.Stats[statNames[j]] = value
   218  		}
   219  		stats = append(stats, stat)
   220  	}
   221  	return stats, nil
   222  }
   223  
   224  // NetFilterCounters returns iptables conntrack statistics
   225  // the currently in use conntrack count and the max.
   226  // If the file does not exist or is invalid it will return nil.
   227  func FilterCounters() ([]FilterStat, error) {
   228  	return FilterCountersWithContext(context.Background())
   229  }
   230  
   231  func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
   232  	countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count")
   233  	maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max")
   234  
   235  	count, err := common.ReadInts(countfile)
   236  
   237  	if err != nil {
   238  		return nil, err
   239  	}
   240  	stats := make([]FilterStat, 0, 1)
   241  
   242  	max, err := common.ReadInts(maxfile)
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  
   247  	payload := FilterStat{
   248  		ConnTrackCount: count[0],
   249  		ConnTrackMax:   max[0],
   250  	}
   251  
   252  	stats = append(stats, payload)
   253  	return stats, nil
   254  }
   255  
   256  // ConntrackStats returns more detailed info about the conntrack table
   257  func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
   258  	return ConntrackStatsWithContext(context.Background(), percpu)
   259  }
   260  
   261  // ConntrackStatsWithContext returns more detailed info about the conntrack table
   262  func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
   263  	return conntrackStatsFromFile(common.HostProc("net/stat/nf_conntrack"), percpu)
   264  }
   265  
   266  // conntrackStatsFromFile returns more detailed info about the conntrack table
   267  // from `filename`
   268  // If 'percpu' is false, the result will contain exactly one item with totals/summary
   269  func conntrackStatsFromFile(filename string, percpu bool) ([]ConntrackStat, error) {
   270  	lines, err := common.ReadLines(filename)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  
   275  	statlist := NewConntrackStatList()
   276  
   277  	for _, line := range lines {
   278  		fields := strings.Fields(line)
   279  		if len(fields) == 17 && fields[0] != "entries" {
   280  			statlist.Append(NewConntrackStat(
   281  				common.HexToUint32(fields[CT_ENTRIES]),
   282  				common.HexToUint32(fields[CT_SEARCHED]),
   283  				common.HexToUint32(fields[CT_FOUND]),
   284  				common.HexToUint32(fields[CT_NEW]),
   285  				common.HexToUint32(fields[CT_INVALID]),
   286  				common.HexToUint32(fields[CT_IGNORE]),
   287  				common.HexToUint32(fields[CT_DELETE]),
   288  				common.HexToUint32(fields[CT_DELETE_LIST]),
   289  				common.HexToUint32(fields[CT_INSERT]),
   290  				common.HexToUint32(fields[CT_INSERT_FAILED]),
   291  				common.HexToUint32(fields[CT_DROP]),
   292  				common.HexToUint32(fields[CT_EARLY_DROP]),
   293  				common.HexToUint32(fields[CT_ICMP_ERROR]),
   294  				common.HexToUint32(fields[CT_EXPECT_NEW]),
   295  				common.HexToUint32(fields[CT_EXPECT_CREATE]),
   296  				common.HexToUint32(fields[CT_EXPECT_DELETE]),
   297  				common.HexToUint32(fields[CT_SEARCH_RESTART]),
   298  			))
   299  		}
   300  	}
   301  
   302  	if percpu {
   303  		return statlist.Items(), nil
   304  	}
   305  	return statlist.Summary(), nil
   306  }
   307  
   308  // http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
   309  var TCPStatuses = map[string]string{
   310  	"01": "ESTABLISHED",
   311  	"02": "SYN_SENT",
   312  	"03": "SYN_RECV",
   313  	"04": "FIN_WAIT1",
   314  	"05": "FIN_WAIT2",
   315  	"06": "TIME_WAIT",
   316  	"07": "CLOSE",
   317  	"08": "CLOSE_WAIT",
   318  	"09": "LAST_ACK",
   319  	"0A": "LISTEN",
   320  	"0B": "CLOSING",
   321  }
   322  
   323  type netConnectionKindType struct {
   324  	family   uint32
   325  	sockType uint32
   326  	filename string
   327  }
   328  
   329  var kindTCP4 = netConnectionKindType{
   330  	family:   syscall.AF_INET,
   331  	sockType: syscall.SOCK_STREAM,
   332  	filename: "tcp",
   333  }
   334  var kindTCP6 = netConnectionKindType{
   335  	family:   syscall.AF_INET6,
   336  	sockType: syscall.SOCK_STREAM,
   337  	filename: "tcp6",
   338  }
   339  var kindUDP4 = netConnectionKindType{
   340  	family:   syscall.AF_INET,
   341  	sockType: syscall.SOCK_DGRAM,
   342  	filename: "udp",
   343  }
   344  var kindUDP6 = netConnectionKindType{
   345  	family:   syscall.AF_INET6,
   346  	sockType: syscall.SOCK_DGRAM,
   347  	filename: "udp6",
   348  }
   349  var kindUNIX = netConnectionKindType{
   350  	family:   syscall.AF_UNIX,
   351  	filename: "unix",
   352  }
   353  
   354  var netConnectionKindMap = map[string][]netConnectionKindType{
   355  	"all":   {kindTCP4, kindTCP6, kindUDP4, kindUDP6, kindUNIX},
   356  	"tcp":   {kindTCP4, kindTCP6},
   357  	"tcp4":  {kindTCP4},
   358  	"tcp6":  {kindTCP6},
   359  	"udp":   {kindUDP4, kindUDP6},
   360  	"udp4":  {kindUDP4},
   361  	"udp6":  {kindUDP6},
   362  	"unix":  {kindUNIX},
   363  	"inet":  {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
   364  	"inet4": {kindTCP4, kindUDP4},
   365  	"inet6": {kindTCP6, kindUDP6},
   366  }
   367  
   368  type inodeMap struct {
   369  	pid int32
   370  	fd  uint32
   371  }
   372  
   373  type connTmp struct {
   374  	fd       uint32
   375  	family   uint32
   376  	sockType uint32
   377  	laddr    Addr
   378  	raddr    Addr
   379  	status   string
   380  	pid      int32
   381  	boundPid int32
   382  	path     string
   383  }
   384  
   385  // Return a list of network connections opened.
   386  func Connections(kind string) ([]ConnectionStat, error) {
   387  	return ConnectionsWithContext(context.Background(), kind)
   388  }
   389  
   390  func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   391  	return ConnectionsPid(kind, 0)
   392  }
   393  
   394  // Return a list of network connections opened returning at most `max`
   395  // connections for each running process.
   396  func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
   397  	return ConnectionsMaxWithContext(context.Background(), kind, max)
   398  }
   399  
   400  func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   401  	return ConnectionsPidMax(kind, 0, max)
   402  }
   403  
   404  // Return a list of network connections opened, omitting `Uids`.
   405  // WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
   406  // removed from the API in the future.
   407  func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
   408  	return ConnectionsWithoutUidsWithContext(context.Background(), kind)
   409  }
   410  
   411  func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   412  	return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
   413  }
   414  
   415  func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   416  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
   417  }
   418  
   419  // Return a list of network connections opened by a process.
   420  func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
   421  	return ConnectionsPidWithContext(context.Background(), kind, pid)
   422  }
   423  
   424  func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
   425  	return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
   426  }
   427  
   428  func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
   429  	return ConnectionsPidMaxWithContext(ctx, kind, pid, 0)
   430  }
   431  
   432  func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
   433  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
   434  }
   435  
   436  // Return up to `max` network connections opened by a process.
   437  func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) {
   438  	return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max)
   439  }
   440  
   441  func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
   442  	return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
   443  }
   444  
   445  func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   446  	return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, false)
   447  }
   448  
   449  func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   450  	return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, true)
   451  }
   452  
   453  func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int, skipUids bool) ([]ConnectionStat, error) {
   454  	tmap, ok := netConnectionKindMap[kind]
   455  	if !ok {
   456  		return nil, fmt.Errorf("invalid kind, %s", kind)
   457  	}
   458  	root := common.HostProc()
   459  	var err error
   460  	var inodes map[string][]inodeMap
   461  	if pid == 0 {
   462  		inodes, err = getProcInodesAll(root, max)
   463  	} else {
   464  		inodes, err = getProcInodes(root, pid, max)
   465  		if len(inodes) == 0 {
   466  			// no connection for the pid
   467  			return []ConnectionStat{}, nil
   468  		}
   469  	}
   470  	if err != nil {
   471  		return nil, fmt.Errorf("cound not get pid(s), %d: %s", pid, err)
   472  	}
   473  	return statsFromInodes(root, pid, tmap, inodes, skipUids)
   474  }
   475  
   476  func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) {
   477  	dupCheckMap := make(map[string]struct{})
   478  	var ret []ConnectionStat
   479  
   480  	var err error
   481  	for _, t := range tmap {
   482  		var path string
   483  		var connKey string
   484  		var ls []connTmp
   485  		if pid == 0 {
   486  			path = fmt.Sprintf("%s/net/%s", root, t.filename)
   487  		} else {
   488  			path = fmt.Sprintf("%s/%d/net/%s", root, pid, t.filename)
   489  		}
   490  		switch t.family {
   491  		case syscall.AF_INET, syscall.AF_INET6:
   492  			ls, err = processInet(path, t, inodes, pid)
   493  		case syscall.AF_UNIX:
   494  			ls, err = processUnix(path, t, inodes, pid)
   495  		}
   496  		if err != nil {
   497  			return nil, err
   498  		}
   499  		for _, c := range ls {
   500  			// Build TCP key to id the connection uniquely
   501  			// socket type, src ip, src port, dst ip, dst port and state should be enough
   502  			// to prevent duplications.
   503  			connKey = fmt.Sprintf("%d-%s:%d-%s:%d-%s", c.sockType, c.laddr.IP, c.laddr.Port, c.raddr.IP, c.raddr.Port, c.status)
   504  			if _, ok := dupCheckMap[connKey]; ok {
   505  				continue
   506  			}
   507  
   508  			conn := ConnectionStat{
   509  				Fd:     c.fd,
   510  				Family: c.family,
   511  				Type:   c.sockType,
   512  				Laddr:  c.laddr,
   513  				Raddr:  c.raddr,
   514  				Status: c.status,
   515  				Pid:    c.pid,
   516  			}
   517  			if c.pid == 0 {
   518  				conn.Pid = c.boundPid
   519  			} else {
   520  				conn.Pid = c.pid
   521  			}
   522  
   523  			if !skipUids {
   524  				// fetch process owner Real, effective, saved set, and filesystem UIDs
   525  				proc := process{Pid: conn.Pid}
   526  				conn.Uids, _ = proc.getUids()
   527  			}
   528  
   529  			ret = append(ret, conn)
   530  			dupCheckMap[connKey] = struct{}{}
   531  		}
   532  
   533  	}
   534  
   535  	return ret, nil
   536  }
   537  
   538  // getProcInodes returnes fd of the pid.
   539  func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, error) {
   540  	ret := make(map[string][]inodeMap)
   541  
   542  	dir := fmt.Sprintf("%s/%d/fd", root, pid)
   543  	f, err := os.Open(dir)
   544  	if err != nil {
   545  		return ret, err
   546  	}
   547  	defer f.Close()
   548  	files, err := f.Readdir(max)
   549  	if err != nil {
   550  		return ret, err
   551  	}
   552  	for _, fd := range files {
   553  		inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, fd.Name())
   554  
   555  		inode, err := os.Readlink(inodePath)
   556  		if err != nil {
   557  			continue
   558  		}
   559  		if !strings.HasPrefix(inode, "socket:[") {
   560  			continue
   561  		}
   562  		// the process is using a socket
   563  		l := len(inode)
   564  		inode = inode[8 : l-1]
   565  		_, ok := ret[inode]
   566  		if !ok {
   567  			ret[inode] = make([]inodeMap, 0)
   568  		}
   569  		fd, err := strconv.Atoi(fd.Name())
   570  		if err != nil {
   571  			continue
   572  		}
   573  
   574  		i := inodeMap{
   575  			pid: pid,
   576  			fd:  uint32(fd),
   577  		}
   578  		ret[inode] = append(ret[inode], i)
   579  	}
   580  	return ret, nil
   581  }
   582  
   583  // Pids retunres all pids.
   584  // Note: this is a copy of process_linux.Pids()
   585  // FIXME: Import process occures import cycle.
   586  // move to common made other platform breaking. Need consider.
   587  func Pids() ([]int32, error) {
   588  	return PidsWithContext(context.Background())
   589  }
   590  
   591  func PidsWithContext(ctx context.Context) ([]int32, error) {
   592  	var ret []int32
   593  
   594  	d, err := os.Open(common.HostProc())
   595  	if err != nil {
   596  		return nil, err
   597  	}
   598  	defer d.Close()
   599  
   600  	fnames, err := d.Readdirnames(-1)
   601  	if err != nil {
   602  		return nil, err
   603  	}
   604  	for _, fname := range fnames {
   605  		pid, err := strconv.ParseInt(fname, 10, 32)
   606  		if err != nil {
   607  			// if not numeric name, just skip
   608  			continue
   609  		}
   610  		ret = append(ret, int32(pid))
   611  	}
   612  
   613  	return ret, nil
   614  }
   615  
   616  // Note: the following is based off process_linux structs and methods
   617  // we need these to fetch the owner of a process ID
   618  // FIXME: Import process occures import cycle.
   619  // see remarks on pids()
   620  type process struct {
   621  	Pid  int32 `json:"pid"`
   622  	uids []int32
   623  }
   624  
   625  // Uids returns user ids of the process as a slice of the int
   626  func (p *process) getUids() ([]int32, error) {
   627  	err := p.fillFromStatus()
   628  	if err != nil {
   629  		return []int32{}, err
   630  	}
   631  	return p.uids, nil
   632  }
   633  
   634  // Get status from /proc/(pid)/status
   635  func (p *process) fillFromStatus() error {
   636  	pid := p.Pid
   637  	statPath := common.HostProc(strconv.Itoa(int(pid)), "status")
   638  	contents, err := ioutil.ReadFile(statPath)
   639  	if err != nil {
   640  		return err
   641  	}
   642  	lines := strings.Split(string(contents), "\n")
   643  	for _, line := range lines {
   644  		tabParts := strings.SplitN(line, "\t", 2)
   645  		if len(tabParts) < 2 {
   646  			continue
   647  		}
   648  		value := tabParts[1]
   649  		switch strings.TrimRight(tabParts[0], ":") {
   650  		case "Uid":
   651  			p.uids = make([]int32, 0, 4)
   652  			for _, i := range strings.Split(value, "\t") {
   653  				v, err := strconv.ParseInt(i, 10, 32)
   654  				if err != nil {
   655  					return err
   656  				}
   657  				p.uids = append(p.uids, int32(v))
   658  			}
   659  		}
   660  	}
   661  	return nil
   662  }
   663  
   664  func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) {
   665  	pids, err := Pids()
   666  	if err != nil {
   667  		return nil, err
   668  	}
   669  	ret := make(map[string][]inodeMap)
   670  
   671  	for _, pid := range pids {
   672  		t, err := getProcInodes(root, pid, max)
   673  		if err != nil {
   674  			// skip if permission error or no longer exists
   675  			if os.IsPermission(err) || os.IsNotExist(err) || err == io.EOF {
   676  				continue
   677  			}
   678  			return ret, err
   679  		}
   680  		if len(t) == 0 {
   681  			continue
   682  		}
   683  		// TODO: update ret.
   684  		ret = updateMap(ret, t)
   685  	}
   686  	return ret, nil
   687  }
   688  
   689  // decodeAddress decode addresse represents addr in proc/net/*
   690  // ex:
   691  // "0500000A:0016" -> "10.0.0.5", 22
   692  // "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53
   693  func decodeAddress(family uint32, src string) (Addr, error) {
   694  	t := strings.Split(src, ":")
   695  	if len(t) != 2 {
   696  		return Addr{}, fmt.Errorf("does not contain port, %s", src)
   697  	}
   698  	addr := t[0]
   699  	port, err := strconv.ParseUint(t[1], 16, 16)
   700  	if err != nil {
   701  		return Addr{}, fmt.Errorf("invalid port, %s", src)
   702  	}
   703  	decoded, err := hex.DecodeString(addr)
   704  	if err != nil {
   705  		return Addr{}, fmt.Errorf("decode error, %s", err)
   706  	}
   707  	var ip net.IP
   708  	// Assumes this is little_endian
   709  	if family == syscall.AF_INET {
   710  		ip = net.IP(Reverse(decoded))
   711  	} else { // IPv6
   712  		ip, err = parseIPv6HexString(decoded)
   713  		if err != nil {
   714  			return Addr{}, err
   715  		}
   716  	}
   717  	return Addr{
   718  		IP:   ip.String(),
   719  		Port: uint32(port),
   720  	}, nil
   721  }
   722  
   723  // Reverse reverses array of bytes.
   724  func Reverse(s []byte) []byte {
   725  	return ReverseWithContext(context.Background(), s)
   726  }
   727  
   728  func ReverseWithContext(ctx context.Context, s []byte) []byte {
   729  	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
   730  		s[i], s[j] = s[j], s[i]
   731  	}
   732  	return s
   733  }
   734  
   735  // parseIPv6HexString parse array of bytes to IPv6 string
   736  func parseIPv6HexString(src []byte) (net.IP, error) {
   737  	if len(src) != 16 {
   738  		return nil, fmt.Errorf("invalid IPv6 string")
   739  	}
   740  
   741  	buf := make([]byte, 0, 16)
   742  	for i := 0; i < len(src); i += 4 {
   743  		r := Reverse(src[i : i+4])
   744  		buf = append(buf, r...)
   745  	}
   746  	return net.IP(buf), nil
   747  }
   748  
   749  func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
   750  
   751  	if strings.HasSuffix(file, "6") && !common.PathExists(file) {
   752  		// IPv6 not supported, return empty.
   753  		return []connTmp{}, nil
   754  	}
   755  
   756  	// Read the contents of the /proc file with a single read sys call.
   757  	// This minimizes duplicates in the returned connections
   758  	// For more info:
   759  	// https://github.com/shirou/gopsutil/pull/361
   760  	contents, err := ioutil.ReadFile(file)
   761  	if err != nil {
   762  		return nil, err
   763  	}
   764  
   765  	lines := bytes.Split(contents, []byte("\n"))
   766  
   767  	var ret []connTmp
   768  	// skip first line
   769  	for _, line := range lines[1:] {
   770  		l := strings.Fields(string(line))
   771  		if len(l) < 10 {
   772  			continue
   773  		}
   774  		laddr := l[1]
   775  		raddr := l[2]
   776  		status := l[3]
   777  		inode := l[9]
   778  		pid := int32(0)
   779  		fd := uint32(0)
   780  		i, exists := inodes[inode]
   781  		if exists {
   782  			pid = i[0].pid
   783  			fd = i[0].fd
   784  		}
   785  		if filterPid > 0 && filterPid != pid {
   786  			continue
   787  		}
   788  		if kind.sockType == syscall.SOCK_STREAM {
   789  			status = TCPStatuses[status]
   790  		} else {
   791  			status = "NONE"
   792  		}
   793  		la, err := decodeAddress(kind.family, laddr)
   794  		if err != nil {
   795  			continue
   796  		}
   797  		ra, err := decodeAddress(kind.family, raddr)
   798  		if err != nil {
   799  			continue
   800  		}
   801  
   802  		ret = append(ret, connTmp{
   803  			fd:       fd,
   804  			family:   kind.family,
   805  			sockType: kind.sockType,
   806  			laddr:    la,
   807  			raddr:    ra,
   808  			status:   status,
   809  			pid:      pid,
   810  		})
   811  	}
   812  
   813  	return ret, nil
   814  }
   815  
   816  func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
   817  	// Read the contents of the /proc file with a single read sys call.
   818  	// This minimizes duplicates in the returned connections
   819  	// For more info:
   820  	// https://github.com/shirou/gopsutil/pull/361
   821  	contents, err := ioutil.ReadFile(file)
   822  	if err != nil {
   823  		return nil, err
   824  	}
   825  
   826  	lines := bytes.Split(contents, []byte("\n"))
   827  
   828  	var ret []connTmp
   829  	// skip first line
   830  	for _, line := range lines[1:] {
   831  		tokens := strings.Fields(string(line))
   832  		if len(tokens) < 6 {
   833  			continue
   834  		}
   835  		st, err := strconv.Atoi(tokens[4])
   836  		if err != nil {
   837  			return nil, err
   838  		}
   839  
   840  		inode := tokens[6]
   841  
   842  		var pairs []inodeMap
   843  		pairs, exists := inodes[inode]
   844  		if !exists {
   845  			pairs = []inodeMap{
   846  				{},
   847  			}
   848  		}
   849  		for _, pair := range pairs {
   850  			if filterPid > 0 && filterPid != pair.pid {
   851  				continue
   852  			}
   853  			var path string
   854  			if len(tokens) == 8 {
   855  				path = tokens[len(tokens)-1]
   856  			}
   857  			ret = append(ret, connTmp{
   858  				fd:       pair.fd,
   859  				family:   kind.family,
   860  				sockType: uint32(st),
   861  				laddr: Addr{
   862  					IP: path,
   863  				},
   864  				pid:    pair.pid,
   865  				status: "NONE",
   866  				path:   path,
   867  			})
   868  		}
   869  	}
   870  
   871  	return ret, nil
   872  }
   873  
   874  func updateMap(src map[string][]inodeMap, add map[string][]inodeMap) map[string][]inodeMap {
   875  	for key, value := range add {
   876  		a, exists := src[key]
   877  		if !exists {
   878  			src[key] = value
   879  			continue
   880  		}
   881  		src[key] = append(a, value...)
   882  	}
   883  	return src
   884  }
   885  

View as plain text