...

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

Documentation: github.com/shirou/gopsutil/net

     1  // +build openbsd
     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  var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
    18  
    19  func ParseNetstat(output string, mode string,
    20  	iocs map[string]IOCountersStat) error {
    21  	lines := strings.Split(output, "\n")
    22  
    23  	exists := make([]string, 0, len(lines)-1)
    24  
    25  	columns := 6
    26  	if mode == "ind" {
    27  		columns = 10
    28  	}
    29  	for _, line := range lines {
    30  		values := strings.Fields(line)
    31  		if len(values) < 1 || values[0] == "Name" {
    32  			continue
    33  		}
    34  		if common.StringsHas(exists, values[0]) {
    35  			// skip if already get
    36  			continue
    37  		}
    38  
    39  		if len(values) < columns {
    40  			continue
    41  		}
    42  		base := 1
    43  		// sometimes Address is omitted
    44  		if len(values) < columns {
    45  			base = 0
    46  		}
    47  
    48  		parsed := make([]uint64, 0, 8)
    49  		var vv []string
    50  		if mode == "inb" {
    51  			vv = []string{
    52  				values[base+3], // BytesRecv
    53  				values[base+4], // BytesSent
    54  			}
    55  		} else {
    56  			vv = []string{
    57  				values[base+3], // Ipkts
    58  				values[base+4], // Ierrs
    59  				values[base+5], // Opkts
    60  				values[base+6], // Oerrs
    61  				values[base+8], // Drops
    62  			}
    63  		}
    64  		for _, target := range vv {
    65  			if target == "-" {
    66  				parsed = append(parsed, 0)
    67  				continue
    68  			}
    69  
    70  			t, err := strconv.ParseUint(target, 10, 64)
    71  			if err != nil {
    72  				return err
    73  			}
    74  			parsed = append(parsed, t)
    75  		}
    76  		exists = append(exists, values[0])
    77  
    78  		n, present := iocs[values[0]]
    79  		if !present {
    80  			n = IOCountersStat{Name: values[0]}
    81  		}
    82  		if mode == "inb" {
    83  			n.BytesRecv = parsed[0]
    84  			n.BytesSent = parsed[1]
    85  		} else {
    86  			n.PacketsRecv = parsed[0]
    87  			n.Errin = parsed[1]
    88  			n.PacketsSent = parsed[2]
    89  			n.Errout = parsed[3]
    90  			n.Dropin = parsed[4]
    91  			n.Dropout = parsed[4]
    92  		}
    93  
    94  		iocs[n.Name] = n
    95  	}
    96  	return nil
    97  }
    98  
    99  func IOCounters(pernic bool) ([]IOCountersStat, error) {
   100  	return IOCountersWithContext(context.Background(), pernic)
   101  }
   102  
   103  func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
   104  	netstat, err := exec.LookPath("netstat")
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	out, err := invoke.CommandWithContext(ctx, netstat, "-inb")
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	out2, err := invoke.CommandWithContext(ctx, netstat, "-ind")
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	iocs := make(map[string]IOCountersStat)
   117  
   118  	lines := strings.Split(string(out), "\n")
   119  	ret := make([]IOCountersStat, 0, len(lines)-1)
   120  
   121  	err = ParseNetstat(string(out), "inb", iocs)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	err = ParseNetstat(string(out2), "ind", iocs)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	for _, ioc := range iocs {
   131  		ret = append(ret, ioc)
   132  	}
   133  
   134  	if pernic == false {
   135  		return getIOCountersAll(ret)
   136  	}
   137  
   138  	return ret, nil
   139  }
   140  
   141  // NetIOCountersByFile is an method which is added just a compatibility for linux.
   142  func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
   143  	return IOCountersByFileWithContext(context.Background(), pernic, filename)
   144  }
   145  
   146  func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
   147  	return IOCounters(pernic)
   148  }
   149  
   150  func FilterCounters() ([]FilterStat, error) {
   151  	return FilterCountersWithContext(context.Background())
   152  }
   153  
   154  func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
   155  	return nil, common.ErrNotImplementedError
   156  }
   157  
   158  func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
   159  	return ConntrackStatsWithContext(context.Background(), percpu)
   160  }
   161  
   162  func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
   163  	return nil, common.ErrNotImplementedError
   164  }
   165  
   166  // NetProtoCounters returns network statistics for the entire system
   167  // If protocols is empty then all protocols are returned, otherwise
   168  // just the protocols in the list are returned.
   169  // Not Implemented for OpenBSD
   170  func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
   171  	return ProtoCountersWithContext(context.Background(), protocols)
   172  }
   173  
   174  func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
   175  	return nil, common.ErrNotImplementedError
   176  }
   177  
   178  func parseNetstatLine(line string) (ConnectionStat, error) {
   179  	f := strings.Fields(line)
   180  	if len(f) < 5 {
   181  		return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
   182  	}
   183  
   184  	var netType, netFamily uint32
   185  	switch f[0] {
   186  	case "tcp":
   187  		netType = syscall.SOCK_STREAM
   188  		netFamily = syscall.AF_INET
   189  	case "udp":
   190  		netType = syscall.SOCK_DGRAM
   191  		netFamily = syscall.AF_INET
   192  	case "tcp6":
   193  		netType = syscall.SOCK_STREAM
   194  		netFamily = syscall.AF_INET6
   195  	case "udp6":
   196  		netType = syscall.SOCK_DGRAM
   197  		netFamily = syscall.AF_INET6
   198  	default:
   199  		return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0])
   200  	}
   201  
   202  	laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily)
   203  	if err != nil {
   204  		return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4])
   205  	}
   206  
   207  	n := ConnectionStat{
   208  		Fd:     uint32(0), // not supported
   209  		Family: uint32(netFamily),
   210  		Type:   uint32(netType),
   211  		Laddr:  laddr,
   212  		Raddr:  raddr,
   213  		Pid:    int32(0), // not supported
   214  	}
   215  	if len(f) == 6 {
   216  		n.Status = f[5]
   217  	}
   218  
   219  	return n, nil
   220  }
   221  
   222  func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) {
   223  	parse := func(l string) (Addr, error) {
   224  		matches := portMatch.FindStringSubmatch(l)
   225  		if matches == nil {
   226  			return Addr{}, fmt.Errorf("wrong addr, %s", l)
   227  		}
   228  		host := matches[1]
   229  		port := matches[2]
   230  		if host == "*" {
   231  			switch family {
   232  			case syscall.AF_INET:
   233  				host = "0.0.0.0"
   234  			case syscall.AF_INET6:
   235  				host = "::"
   236  			default:
   237  				return Addr{}, fmt.Errorf("unknown family, %d", family)
   238  			}
   239  		}
   240  		lport, err := strconv.Atoi(port)
   241  		if err != nil {
   242  			return Addr{}, err
   243  		}
   244  		return Addr{IP: host, Port: uint32(lport)}, nil
   245  	}
   246  
   247  	laddr, err = parse(local)
   248  	if remote != "*.*" { // remote addr exists
   249  		raddr, err = parse(remote)
   250  		if err != nil {
   251  			return laddr, raddr, err
   252  		}
   253  	}
   254  
   255  	return laddr, raddr, err
   256  }
   257  
   258  // Return a list of network connections opened.
   259  func Connections(kind string) ([]ConnectionStat, error) {
   260  	return ConnectionsWithContext(context.Background(), kind)
   261  }
   262  
   263  func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
   264  	var ret []ConnectionStat
   265  
   266  	args := []string{"-na"}
   267  	switch strings.ToLower(kind) {
   268  	default:
   269  		fallthrough
   270  	case "":
   271  		fallthrough
   272  	case "all":
   273  		fallthrough
   274  	case "inet":
   275  		// nothing to add
   276  	case "inet4":
   277  		args = append(args, "-finet")
   278  	case "inet6":
   279  		args = append(args, "-finet6")
   280  	case "tcp":
   281  		args = append(args, "-ptcp")
   282  	case "tcp4":
   283  		args = append(args, "-ptcp", "-finet")
   284  	case "tcp6":
   285  		args = append(args, "-ptcp", "-finet6")
   286  	case "udp":
   287  		args = append(args, "-pudp")
   288  	case "udp4":
   289  		args = append(args, "-pudp", "-finet")
   290  	case "udp6":
   291  		args = append(args, "-pudp", "-finet6")
   292  	case "unix":
   293  		return ret, common.ErrNotImplementedError
   294  	}
   295  
   296  	netstat, err := exec.LookPath("netstat")
   297  	if err != nil {
   298  		return nil, err
   299  	}
   300  	out, err := invoke.CommandWithContext(ctx, netstat, args...)
   301  
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  	lines := strings.Split(string(out), "\n")
   306  	for _, line := range lines {
   307  		if !(strings.HasPrefix(line, "tcp") || strings.HasPrefix(line, "udp")) {
   308  			continue
   309  		}
   310  		n, err := parseNetstatLine(line)
   311  		if err != nil {
   312  			continue
   313  		}
   314  
   315  		ret = append(ret, n)
   316  	}
   317  
   318  	return ret, nil
   319  }
   320  

View as plain text