<commit_before>package procspy

// /proc based implementation

import (
	"bufio"
	"encoding/hex"
	"errors"
	"io"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
	"syscall"
)

const procRoot = "/proc"

// SpyProc uses /proc directly to make the connection list.
func SpyProc() ([]ConnProc, error) {
	// A map of inode -> pid
	inodes, err := walkProcPid()
	if err != nil {
		return nil, err
	}

	res := []ConnProc{}
	for _, procFile := range []string{
		procRoot + "/net/tcp",
		procRoot + "/net/tcp6",
	} {
		fh, err := os.Open(procFile)
		if err != nil {
			// File might not be there if IPv{4,6} is not supported.
			continue
		}
		defer fh.Close()
		for _, tp := range parseTransport(fh) {
			if pid, ok := inodes[tp.inode]; ok {
				name, err := procName(pid)
				if err != nil {
					// Process might be gone by now
					continue
				}
				if tp.remoteAddress.IsUnspecified() {
					// Remote address is zero. This is a listen entry.
					continue
				}
				res = append(res, ConnProc{
					Transport:  "tcp",
					LocalAddr:  tp.localAddress.String(),
					LocalPort:  tp.localPort,
					RemoteAddr: tp.remoteAddress.String(),
					RemotePort: tp.remotePort,
					PID:        pid,
					Name:       name,
				})
			}
		}
	}
	return res, nil
}

func walkProcPid() (map[uint64]uint, error) {
	// Walk over all /proc entries (numerical ones, those are PIDs), and see if
	// their ./fd/* files are symlink to sockets.
	// Returns a map from socket id ('inode`) to PID.
	// Will return an error if /proc/ isn't there.
	fh, err := os.Open(procRoot)
	if err != nil {
		return nil, err
	}
	defer fh.Close()
	dirNames, err := fh.Readdirnames(-1)
	if err != nil {
		return nil, err
	}
	procmap := map[uint64]uint{}
	for _, dirName := range dirNames {
		pid, err := strconv.ParseUint(dirName, 10, 0)
		if err != nil {
			// Not a number, so not a PID subdir.
			continue
		}

		fdBase := procRoot + "/" + dirName + "/fd/"
		dfh, err := os.Open(fdBase)
		if err != nil {
			// Process is be gone by now, or we don't have access.
			continue
		}
		fdNames, err := dfh.Readdirnames(-1)
		dfh.Close()
		if err != nil {
			continue
		}
		for _, fdName := range fdNames {
			// We want sockets only
			stat, err := os.Stat(fdBase + fdName)
			if err != nil {
				continue
			}
			if stat.Mode()&os.ModeSocket == 0 {
				continue
			}
			sys, ok := stat.Sys().(*syscall.Stat_t)
			if !ok {
				panic("Weird result from stat.Sys()")
			}
			procmap[sys.Ino] = uint(pid)
		}
	}
	return procmap, nil
}

// transport are found in /proc/net/{tcp,udp}{,6} files
type transport struct {
	localAddress  net.IP
	localPort     uint16
	remoteAddress net.IP
	remotePort    uint16
	uid           int
	inode         uint64
}

// parseTransport parses /proc/net/{tcp,udp}{,6} files
var fieldRe = regexp.MustCompile(`\s+`)

func parseTransport(r io.Reader) []transport {
	res := []transport{}
	scanner := bufio.NewScanner(r)
	for i := 0; scanner.Scan(); i++ {
		if i == 0 {
			// Skip header
			continue
		}
		// Fields are:
		//  'sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode <more>'
		// The first field might or might not have leading whitespace.
		line := strings.TrimSpace(scanner.Text())
		fields := fieldRe.Split(line, -1)
		if len(fields) < 10 {
			continue
		}

		localAddress, localPort, err := scanAddress(fields[1])
		if err != nil {<commit_msg>Use strings.Fields() to split fields.<commit_after>package procspy

// /proc based implementation

import (
	"bufio"
	"encoding/hex"
	"errors"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"syscall"
)

const procRoot = "/proc"

// SpyProc uses /proc directly to make the connection list.
func SpyProc() ([]ConnProc, error) {
	// A map of inode -> pid
	inodes, err := walkProcPid()
	if err != nil {
		return nil, err
	}

	res := []ConnProc{}
	for _, procFile := range []string{
		procRoot + "/net/tcp",
		procRoot + "/net/tcp6",
	} {
		fh, err := os.Open(procFile)
		if err != nil {
			// File might not be there if IPv{4,6} is not supported.
			continue
		}
		defer fh.Close()
		for _, tp := range parseTransport(fh) {
			if pid, ok := inodes[tp.inode]; ok {
				name, err := procName(pid)
				if err != nil {
					// Process might be gone by now
					continue
				}
				if tp.remoteAddress.IsUnspecified() {
					// Remote address is zero. This is a listen entry.
					continue
				}
				res = append(res, ConnProc{
					Transport:  "tcp",
					LocalAddr:  tp.localAddress.String(),
					LocalPort:  tp.localPort,
					RemoteAddr: tp.remoteAddress.String(),
					RemotePort: tp.remotePort,
					PID:        pid,
					Name:       name,
				})
			}
		}
	}
	return res, nil
}

func walkProcPid() (map[uint64]uint, error) {
	// Walk over all /proc entries (numerical ones, those are PIDs), and see if
	// their ./fd/* files are symlink to sockets.
	// Returns a map from socket id ('inode`) to PID.
	// Will return an error if /proc/ isn't there.
	fh, err := os.Open(procRoot)
	if err != nil {
		return nil, err
	}
	defer fh.Close()
	dirNames, err := fh.Readdirnames(-1)
	if err != nil {
		return nil, err
	}
	procmap := map[uint64]uint{}
	for _, dirName := range dirNames {
		pid, err := strconv.ParseUint(dirName, 10, 0)
		if err != nil {
			// Not a number, so not a PID subdir.
			continue
		}

		fdBase := procRoot + "/" + dirName + "/fd/"
		dfh, err := os.Open(fdBase)
		if err != nil {
			// Process is be gone by now, or we don't have access.
			continue
		}
		fdNames, err := dfh.Readdirnames(-1)
		dfh.Close()
		if err != nil {
			continue
		}
		for _, fdName := range fdNames {
			// We want sockets only
			stat, err := os.Stat(fdBase + fdName)
			if err != nil {
				continue
			}
			if stat.Mode()&os.ModeSocket == 0 {
				continue
			}
			sys, ok := stat.Sys().(*syscall.Stat_t)
			if !ok {
				panic("Weird result from stat.Sys()")
			}
			procmap[sys.Ino] = uint(pid)
		}
	}
	return procmap, nil
}

// transport are found in /proc/net/{tcp,udp}{,6} files
type transport struct {
	localAddress  net.IP
	localPort     uint16
	remoteAddress net.IP
	remotePort    uint16
	uid           int
	inode         uint64
}

// parseTransport parses /proc/net/{tcp,udp}{,6} files
func parseTransport(r io.Reader) []transport {
	res := []transport{}
	scanner := bufio.NewScanner(r)
	for i := 0; scanner.Scan(); i++ {
		if i == 0 {
			// Skip header
			continue
		}
		// Fields are:
		//  'sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode <more>'
		fields := strings.Fields(scanner.Text())
		if len(fields) < 10 {
			continue
		}

		localAddress, localPort, err := scanAddress(fields[1])
		if err != nil {
{'fraction_non_alphanumeric': 0.14075980021189646, 'fraction_numerical': 0.008021795065839261, 'mean_word_length': 2.614879649890591, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2151924', 'n_tokens_mistral': 2728, 'n_tokens_neox': 2369, 'n_words': 717}