1// +build linux 2 3package net 4 5import ( 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 22const ( // 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. 47func IOCounters(pernic bool) ([]IOCountersStat, error) { 48 return IOCountersWithContext(context.Background(), pernic) 49} 50 51func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) { 52 filename := common.HostProc("net/dev") 53 return IOCountersByFileWithContext(ctx, pernic, filename) 54} 55 56func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { 57 return IOCountersByFileWithContext(context.Background(), pernic, filename) 58} 59 60func 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 150var 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 164func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { 165 return ProtoCountersWithContext(context.Background(), protocols) 166} 167 168func 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. 227func FilterCounters() ([]FilterStat, error) { 228 return FilterCountersWithContext(context.Background()) 229} 230 231func 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 257func ConntrackStats(percpu bool) ([]ConntrackStat, error) { 258 return ConntrackStatsWithContext(context.Background(), percpu) 259} 260 261// ConntrackStatsWithContext returns more detailed info about the conntrack table 262func 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 269func 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 309var 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 323type netConnectionKindType struct { 324 family uint32 325 sockType uint32 326 filename string 327} 328 329var kindTCP4 = netConnectionKindType{ 330 family: syscall.AF_INET, 331 sockType: syscall.SOCK_STREAM, 332 filename: "tcp", 333} 334var kindTCP6 = netConnectionKindType{ 335 family: syscall.AF_INET6, 336 sockType: syscall.SOCK_STREAM, 337 filename: "tcp6", 338} 339var kindUDP4 = netConnectionKindType{ 340 family: syscall.AF_INET, 341 sockType: syscall.SOCK_DGRAM, 342 filename: "udp", 343} 344var kindUDP6 = netConnectionKindType{ 345 family: syscall.AF_INET6, 346 sockType: syscall.SOCK_DGRAM, 347 filename: "udp6", 348} 349var kindUNIX = netConnectionKindType{ 350 family: syscall.AF_UNIX, 351 filename: "unix", 352} 353 354var 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 368type inodeMap struct { 369 pid int32 370 fd uint32 371} 372 373type 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. 386func Connections(kind string) ([]ConnectionStat, error) { 387 return ConnectionsWithContext(context.Background(), kind) 388} 389 390func 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. 396func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { 397 return ConnectionsMaxWithContext(context.Background(), kind, max) 398} 399 400func 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. 407func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) { 408 return ConnectionsWithoutUidsWithContext(context.Background(), kind) 409} 410 411func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) { 412 return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0) 413} 414 415func 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. 420func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { 421 return ConnectionsPidWithContext(context.Background(), kind, pid) 422} 423 424func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) { 425 return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid) 426} 427 428func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) { 429 return ConnectionsPidMaxWithContext(ctx, kind, pid, 0) 430} 431 432func 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. 437func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) { 438 return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max) 439} 440 441func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) { 442 return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max) 443} 444 445func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { 446 return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, false) 447} 448 449func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) { 450 return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, true) 451} 452 453func 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 476func 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. 539func 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. 587func Pids() ([]int32, error) { 588 return PidsWithContext(context.Background()) 589} 590 591func 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() 620type 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 626func (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 635func (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 664func 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 693func 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. 724func Reverse(s []byte) []byte { 725 return ReverseWithContext(context.Background(), s) 726} 727 728func 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 736func 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 749func 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 816func 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 874func 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