...

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

Documentation: github.com/shirou/gopsutil/net

     1  // +build aix
     2  
     3  package net
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"os/exec"
     9  	"regexp"
    10  	"strconv"
    11  	"strings"
    12  	"syscall"
    13  
    14  	"github.com/shirou/gopsutil/internal/common"
    15  )
    16  
    17  func parseNetstatI(output string) ([]IOCountersStat, error) {
    18  	lines := strings.Split(string(output), "\n")
    19  	ret := make([]IOCountersStat, 0, len(lines)-1)
    20  	exists := make([]string, 0, len(ret))
    21  
    22  	// Check first line is header
    23  	if len(lines) > 0 && strings.Fields(lines[0])[0] != "Name" {
    24  		return nil, fmt.Errorf("not a 'netstat -i' output")
    25  	}
    26  
    27  	for _, line := range lines[1:] {
    28  		values := strings.Fields(line)
    29  		if len(values) < 1 || values[0] == "Name" {
    30  			continue
    31  		}
    32  		if common.StringsHas(exists, values[0]) {
    33  			// skip if already get
    34  			continue
    35  		}
    36  		exists = append(exists, values[0])
    37  
    38  		if len(values) < 9 {
    39  			continue
    40  		}
    41  
    42  		base := 1
    43  		// sometimes Address is omitted
    44  		if len(values) < 10 {
    45  			base = 0
    46  		}
    47  
    48  		parsed := make([]uint64, 0, 5)
    49  		vv := []string{
    50  			values[base+3], // Ipkts == PacketsRecv
    51  			values[base+4], // Ierrs == Errin
    52  			values[base+5], // Opkts == PacketsSent
    53  			values[base+6], // Oerrs == Errout
    54  			values[base+8], // Drops == Dropout
    55  		}
    56  
    57  		for _, target := range vv {
    58  			if target == "-" {
    59  				parsed = append(parsed, 0)
    60  				continue
    61  			}
    62  
    63  			t, err := strconv.ParseUint(target, 10, 64)
    64  			if err != nil {
    65  				return nil, err
    66  			}
    67  			parsed = append(parsed, t)
    68  		}
    69  
    70  		n := IOCountersStat{
    71  			Name:        values[0],
    72  			PacketsRecv: parsed[0],
    73  			Errin:       parsed[1],
    74  			PacketsSent: parsed[2],
    75  			Errout:      parsed[3],
    76  			Dropout:     parsed[4],
    77  		}
    78  		ret = append(ret, n)
    79  	}
    80  	return ret, nil
    81  }
    82  
    83  func IOCounters(pernic bool) ([]IOCountersStat, error) {
    84  	return IOCountersWithContext(context.Background(), pernic)
    85  }
    86  
    87  func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
    88  	netstat, err := exec.LookPath("netstat")
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	out, err := invoke.CommandWithContext(ctx, netstat, "-idn")
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	iocounters, err := parseNetstatI(string(out))
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	if pernic == false {
   102  		return getIOCountersAll(iocounters)
   103  	}
   104  	return iocounters, nil
   105  
   106  }
   107  
   108  // NetIOCountersByFile is an method which is added just a compatibility for linux.
   109  func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
   110  	return IOCountersByFileWithContext(context.Background(), pernic, filename)
   111  }
   112  
   113  func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
   114  	return IOCounters(pernic)
   115  }
   116  
   117  func FilterCounters() ([]FilterStat, error) {
   118  	return FilterCountersWithContext(context.Background())
   119  }
   120  
   121  func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
   122  	return nil, common.ErrNotImplementedError
   123  }
   124  
   125  func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
   126  	return ConntrackStatsWithContext(context.Background(), percpu)
   127  }
   128  
   129  func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
   130  	return nil, common.ErrNotImplementedError
   131  }
   132  
   133  func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
   134  	return ProtoCountersWithContext(context.Background(), protocols)
   135  }
   136  
   137  func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
   138  	return nil, common.ErrNotImplementedError
   139  }
   140  
   141  func parseNetstatNetLine(line string) (ConnectionStat, error) {
   142  	f := strings.Fields(line)
   143  	if len(f) < 5 {
   144  		return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
   145  	}
   146  
   147  	var netType, netFamily uint32
   148  	switch f[0] {
   149  	case "tcp", "tcp4":
   150  		netType = syscall.SOCK_STREAM
   151  		netFamily = syscall.AF_INET
   152  	case "udp", "udp4":
   153  		netType = syscall.SOCK_DGRAM
   154  		netFamily = syscall.AF_INET
   155  	case "tcp6":
   156  		netType = syscall.SOCK_STREAM
   157  		netFamily = syscall.AF_INET6
   158  	case "udp6":
   159  		netType = syscall.SOCK_DGRAM
   160  		netFamily = syscall.AF_INET6
   161  	default:
   162  		return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0])
   163  	}
   164  
   165  	laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily)
   166  	if err != nil {
   167  		return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4])
   168  	}
   169  
   170  	n := ConnectionStat{
   171  		Fd:     uint32(0), // not supported
   172  		Family: uint32(netFamily),
   173  		Type:   uint32(netType),
   174  		Laddr:  laddr,
   175  		Raddr:  raddr,
   176  		Pid:    int32(0), // not supported
   177  	}
   178  	if len(f) == 6 {
   179  		n.Status = f[5]
   180  	}
   181  
   182  	return n, nil
   183  }
   184  
   185  var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
   186  
   187  // This function only works for netstat returning addresses with a "."
   188  // before the port (0.0.0.0.22 instead of 0.0.0.0:22).
   189  func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) {
   190  	parse := func(l string) (Addr, error) {
   191  		matches := portMatch.FindStringSubmatch(l)
   192  		if matches == nil {
   193  			return Addr{}, fmt.Errorf("wrong addr, %s", l)
   194  		}
   195  		host := matches[1]
   196  		port := matches[2]
   197  		if host == "*" {
   198  			switch family {
   199  			case syscall.AF_INET:
   200  				host = "0.0.0.0"
   201  			case syscall.AF_INET6:
   202  				host = "::"
   203  			default:
   204  				return Addr{}, fmt.Errorf("unknown family, %d", family)
   205  			}
   206  		}
   207  		lport, err := strconv.Atoi(port)
   208  		if err != nil {
   209  			return Addr{}, err
   210  		}
   211  		return Addr{IP: host, Port: uint32(lport)}, nil
   212  	}
   213  
   214  	laddr, err = parse(local)
   215  	if remote != "*.*" { // remote addr exists
   216  		raddr, err = parse(remote)
   217  		if err != nil {
   218  			return laddr, raddr, err
   219  		}
   220  	}
   221  
   222  	return laddr, raddr, err
   223  }
   224  
   225  func parseNetstatUnixLine(f []string) (ConnectionStat, error) {
   226  	if len(f) < 8 {
   227  		return ConnectionStat{}, fmt.Errorf("wrong number of fields: expected >=8 got %d", len(f))
   228  	}
   229  
   230  	var netType uint32
   231  
   232  	switch f[1] {
   233  	case "dgram":
   234  		netType = syscall.SOCK_DGRAM
   235  	case "stream":
   236  		netType = syscall.SOCK_STREAM
   237  	default:
   238  		return ConnectionStat{}, fmt.Errorf("unknown type: %s", f[1])
   239  	}
   240  
   241  	// Some Unix Socket don't have any address associated
   242  	addr := ""
   243  	if len(f) == 9 {
   244  		addr = f[8]
   245  	}
   246  
   247  	c := ConnectionStat{
   248  		Fd:     uint32(0), // not supported
   249  		Family: uint32(syscall.AF_UNIX),
   250  		Type:   uint32(netType),
   251  		Laddr: Addr{
   252  			IP: addr,
   253  		},
   254  		Status: "NONE",
   255  		Pid:    int32(0), // not supported
   256  	}
   257  
   258  	return c, nil
   259  }
   260  
   261  // Return true if proto is the corresponding to the kind parameter
   262  // Only for Inet lines
   263  func hasCorrectInetProto(kind, proto string) bool {
   264  	switch kind {
   265  	case "all", "inet":
   266  		return true
   267  	case "unix":
   268  		return false
   269  	case "inet4":
   270  		return !strings.HasSuffix(proto, "6")
   271  	case "inet6":
   272  		return strings.HasSuffix(proto, "6")
   273  	case "tcp":
   274  		return proto == "tcp" || proto == "tcp4" || proto == "tcp6"
   275  	case "tcp4":
   276  		return proto == "tcp" || proto == "tcp4"
   277  	case "tcp6":
   278  		return proto == "tcp6"
   279  	case "udp":
   280  		return proto == "udp" || proto == "udp4" || proto == "udp6"
   281  	case "udp4":
   282  		return proto == "udp" || proto == "udp4"
   283  	case "udp6":
   284  		return proto == "udp6"
   285  	}
   286  	return false
   287  }
   288  
   289  func parseNetstatA(output string, kind string) ([]ConnectionStat, error) {
   290  	var ret []ConnectionStat
   291  	lines := strings.Split(string(output), "\n")
   292  
   293  	for _, line := range lines {
   294  		fields := strings.Fields(line)
   295  		if len(fields) < 1 {
   296  			continue
   297  		}
   298  
   299  		if strings.HasPrefix(fields[0], "f1") {
   300  			// Unix lines
   301  			if len(fields) < 2 {
   302  				// every unix connections have two lines
   303  				continue
   304  			}
   305  
   306  			c, err := parseNetstatUnixLine(fields)
   307  			if err != nil {
   308  				return nil, fmt.Errorf("failed to parse Unix Address (%s): %s", line, err)
   309  			}
   310  
   311  			ret = append(ret, c)
   312  
   313  		} else if strings.HasPrefix(fields[0], "tcp") || strings.HasPrefix(fields[0], "udp") {
   314  			// Inet lines
   315  			if !hasCorrectInetProto(kind, fields[0]) {
   316  				continue
   317  			}
   318  
   319  			// On AIX, netstat display some connections with "*.*" as local addresses
   320  			// Skip them as they aren't real connections.
   321  			if fields[3] == "*.*" {
   322  				continue
   323  			}
   324  
   325  			c, err := parseNetstatNetLine(line)
   326  			if err != nil {
   327  				return nil, fmt.Errorf("failed to parse Inet Address (%s): %s", line, err)
   328  			}
   329  
   330  			ret = append(ret, c)
   331  		} else {
   332  			// Header lines
   333  			continue
   334  		}
   335  	}
   336  
   337  	return ret, nil
   338  
   339  }
   340  
   341  func Connections(kind string) ([]ConnectionStat, error) {
   342  	return ConnectionsWithContext(context.Background(), kind)
   343  }
   344  
   345  func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   346  
   347  	args := []string{"-na"}
   348  	switch strings.ToLower(kind) {
   349  	default:
   350  		fallthrough
   351  	case "":
   352  		kind = "all"
   353  	case "all":
   354  		// nothing to add
   355  	case "inet", "inet4", "inet6":
   356  		args = append(args, "-finet")
   357  	case "tcp", "tcp4", "tcp6":
   358  		args = append(args, "-finet")
   359  	case "udp", "udp4", "udp6":
   360  		args = append(args, "-finet")
   361  	case "unix":
   362  		args = append(args, "-funix")
   363  	}
   364  
   365  	netstat, err := exec.LookPath("netstat")
   366  	if err != nil {
   367  		return nil, err
   368  	}
   369  	out, err := invoke.CommandWithContext(ctx, netstat, args...)
   370  
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  
   375  	ret, err := parseNetstatA(string(out), kind)
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  
   380  	return ret, nil
   381  
   382  }
   383  
   384  func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
   385  	return ConnectionsMaxWithContext(context.Background(), kind, max)
   386  }
   387  
   388  func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   389  	return []ConnectionStat{}, common.ErrNotImplementedError
   390  }
   391  
   392  // Return a list of network connections opened, omitting `Uids`.
   393  // WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
   394  // removed from the API in the future.
   395  func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
   396  	return ConnectionsWithoutUidsWithContext(context.Background(), kind)
   397  }
   398  
   399  func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   400  	return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
   401  }
   402  
   403  func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
   404  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
   405  }
   406  
   407  func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
   408  	return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
   409  }
   410  
   411  func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
   412  	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
   413  }
   414  
   415  func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
   416  	return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
   417  }
   418  
   419  func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   420  	return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
   421  }
   422  
   423  func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
   424  	return []ConnectionStat{}, common.ErrNotImplementedError
   425  }
   426  

View as plain text