mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-28 01:31:17 +00:00
Move procspy out of vendor into probe/endpoint.
This commit is contained in:
21
vendor/github.com/weaveworks/procspy/LICENSE
generated
vendored
21
vendor/github.com/weaveworks/procspy/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Harmen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
20
vendor/github.com/weaveworks/procspy/Makefile
generated
vendored
20
vendor/github.com/weaveworks/procspy/Makefile
generated
vendored
@@ -1,20 +0,0 @@
|
||||
.PHONY: all build buildall test install bench
|
||||
all: test build buildall install
|
||||
|
||||
build:
|
||||
go build
|
||||
go vet
|
||||
golint .
|
||||
|
||||
buildall:
|
||||
GOOS=darwin go build
|
||||
GOOS=linux go build
|
||||
|
||||
test:
|
||||
go test
|
||||
|
||||
install:
|
||||
go install
|
||||
|
||||
bench:
|
||||
go test -bench .
|
||||
64
vendor/github.com/weaveworks/procspy/README.md
generated
vendored
64
vendor/github.com/weaveworks/procspy/README.md
generated
vendored
@@ -1,64 +0,0 @@
|
||||
Go module to list all TCP connections, with an option to try to find the owning PID and processname.
|
||||
|
||||
Works by reading /proc directly on Linux, and by executing `netstat` and `lsof -i` on Darwin.
|
||||
|
||||
Works for IPv4 and IPv6 TCP connections. Only established connections are listed; ports where something is only listening or TIME_WAITs are skipped.
|
||||
|
||||
If you want to find all processes you'll need to run this as root.
|
||||
|
||||
Status:
|
||||
-------
|
||||
|
||||
Tested on Linux and Darwin (10.9).
|
||||
|
||||
Install:
|
||||
--------
|
||||
|
||||
`go install`
|
||||
|
||||
Usage:
|
||||
------
|
||||
|
||||
Only list the connections:
|
||||
|
||||
```
|
||||
cs, err := procspy.Connections(false)
|
||||
for c := cs.Next(); c != nil; c = cs.Next() {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
List the connections and try to find the owning process:
|
||||
|
||||
```
|
||||
cs, err := procspy.Connections(true)
|
||||
for c := cs.Next(); c != nil; c = cs.Next() {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
(See ./example\_test.go)
|
||||
|
||||
``` go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/weaveworks/procspy"
|
||||
)
|
||||
|
||||
func main() {
|
||||
lookupProcesses := true
|
||||
cs, err := procspy.Connections(lookupProcesses)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("TCP Connections:\n")
|
||||
for c := cs.Next(); c != nil; c = cs.Next() {
|
||||
fmt.Printf(" - %v\n", c)
|
||||
}
|
||||
}
|
||||
```
|
||||
33
vendor/github.com/weaveworks/procspy/benchmark_test.go
generated
vendored
33
vendor/github.com/weaveworks/procspy/benchmark_test.go
generated
vendored
@@ -1,33 +0,0 @@
|
||||
package procspy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkParseConnectionsBaseline(b *testing.B) {
|
||||
readFile = func(string, *bytes.Buffer) error { return nil }
|
||||
benchmarkConnections(b)
|
||||
// 333 ns/op, 0 allocs/op
|
||||
}
|
||||
|
||||
func BenchmarkParseConnectionsFixture(b *testing.B) {
|
||||
readFile = func(_ string, buf *bytes.Buffer) error { _, err := buf.Write(fixture); return err }
|
||||
benchmarkConnections(b)
|
||||
// 15553 ns/op, 12 allocs/op
|
||||
}
|
||||
|
||||
func benchmarkConnections(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cbConnections(false)
|
||||
}
|
||||
}
|
||||
|
||||
var fixture = []byte(` sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
0: 00000000:A6C0 00000000:0000 01 00000000:00000000 00:00000000 00000000 105 0 5107 1 ffff8800a6aaf040 100 0 0 10 0
|
||||
1: 00000000:006F 00000000:0000 01 00000000:00000000 00:00000000 00000000 0 0 5084 1 ffff8800a6aaf740 100 0 0 10 0
|
||||
2: 0100007F:0019 00000000:0000 01 00000000:00000000 00:00000000 00000000 0 0 10550 1 ffff8800a729b780 100 0 0 10 0
|
||||
3: A12CF62E:E4D7 57FC1EC0:01BB 01 00000000:00000000 02:000006FA 00000000 1000 0 639474 2 ffff88007e75a740 48 4 26 10 -1
|
||||
`)
|
||||
20
vendor/github.com/weaveworks/procspy/example_test.go
generated
vendored
20
vendor/github.com/weaveworks/procspy/example_test.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
package procspy_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/weaveworks/procspy"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
lookupProcesses := true
|
||||
cs, err := procspy.Connections(lookupProcesses)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("TCP Connections:\n")
|
||||
for c := cs.Next(); c != nil; c = cs.Next() {
|
||||
fmt.Printf(" - %v\n", c)
|
||||
}
|
||||
}
|
||||
26
vendor/github.com/weaveworks/procspy/fixture.go
generated
vendored
26
vendor/github.com/weaveworks/procspy/fixture.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
package procspy
|
||||
|
||||
// SetFixtures declares constant Connection and ConnectionProcs which will
|
||||
// always be returned by the package-level Connections and Processes
|
||||
// functions. It's designed to be used in tests.
|
||||
|
||||
type fixedConnIter []Connection
|
||||
|
||||
func (f *fixedConnIter) Next() *Connection {
|
||||
if len(*f) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
car := (*f)[0]
|
||||
*f = (*f)[1:]
|
||||
|
||||
return &car
|
||||
}
|
||||
|
||||
// SetFixtures is used in test scenarios to have known output.
|
||||
func SetFixtures(c []Connection) {
|
||||
cbConnections = func(bool) (ConnIter, error) {
|
||||
f := fixedConnIter(c)
|
||||
return &f, nil
|
||||
}
|
||||
}
|
||||
77
vendor/github.com/weaveworks/procspy/lsof.go
generated
vendored
77
vendor/github.com/weaveworks/procspy/lsof.go
generated
vendored
@@ -1,77 +0,0 @@
|
||||
package procspy
|
||||
|
||||
// lsof-executing implementation.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
lsofFields = "cn" // parseLSOF() depends on the order
|
||||
)
|
||||
|
||||
// parseLsof parses lsof out with `-F cn` argument.
|
||||
//
|
||||
// Format description: the first letter is the type of record, records are
|
||||
// newline seperated, the record starting with 'p' (pid) is a new processid.
|
||||
// There can be multiple connections for the same 'p' record in which case the
|
||||
// 'p' is not repeated.
|
||||
//
|
||||
// For example, this is one process with two listens and one connection:
|
||||
//
|
||||
// p13100
|
||||
// cmpd
|
||||
// n[::1]:6600
|
||||
// n127.0.0.1:6600
|
||||
// n[::1]:6600->[::1]:50992
|
||||
//
|
||||
func parseLSOF(out string) (map[string]Proc, error) {
|
||||
var (
|
||||
res = map[string]Proc{} // Local addr -> Proc
|
||||
cp = Proc{}
|
||||
)
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
if len(line) <= 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
field = line[0]
|
||||
value = line[1:]
|
||||
)
|
||||
switch field {
|
||||
case 'p':
|
||||
pid, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid 'p' field in lsof output: %#v", value)
|
||||
}
|
||||
cp.PID = uint(pid)
|
||||
|
||||
case 'n':
|
||||
// 'n' is the last field, with '-F cn'
|
||||
// format examples:
|
||||
// "192.168.2.111:44013->54.229.241.196:80"
|
||||
// "[2003:45:2b57:8900:1869:2947:f942:aba7]:55711->[2a00:1450:4008:c01::11]:443"
|
||||
// "*:111" <- a listen
|
||||
addresses := strings.SplitN(value, "->", 2)
|
||||
if len(addresses) != 2 {
|
||||
// That's a listen entry.
|
||||
continue
|
||||
}
|
||||
res[addresses[0]] = Proc{
|
||||
Name: cp.Name,
|
||||
PID: cp.PID,
|
||||
}
|
||||
|
||||
case 'c':
|
||||
cp.Name = value
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected lsof field: %c in %#v", field, value)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
77
vendor/github.com/weaveworks/procspy/lsof_test.go
generated
vendored
77
vendor/github.com/weaveworks/procspy/lsof_test.go
generated
vendored
@@ -1,77 +0,0 @@
|
||||
package procspy
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLSOFParsing(t *testing.T) {
|
||||
// List of lsof -> expected entries
|
||||
for in, expected := range map[string]map[string]Proc{
|
||||
// Single connection
|
||||
"p25196\n" +
|
||||
"ccello-app\n" +
|
||||
"n127.0.0.1:48094->127.0.0.1:4039\n" +
|
||||
"n*:4040\n": map[string]Proc{
|
||||
"127.0.0.1:48094": Proc{
|
||||
PID: 25196,
|
||||
Name: "cello-app",
|
||||
},
|
||||
},
|
||||
|
||||
// Only listen()s.
|
||||
"cdhclient\n" +
|
||||
"n*:68\n" +
|
||||
"n*:38282\n" +
|
||||
"n*:40625\n": map[string]Proc{},
|
||||
|
||||
// A bunch
|
||||
"p13100\n" +
|
||||
"cmpd\n" +
|
||||
"n[::1]:6600\n" +
|
||||
"n127.0.0.1:6600\n" +
|
||||
"n[::1]:6600->[::1]:50992\n" +
|
||||
"p14612\n" +
|
||||
"cchromium\n" +
|
||||
"n[2003:45:2b57:8900:1869:2947:f942:aba7]:55711->[2a00:1450:4008:c01::11]:443\n" +
|
||||
"n192.168.2.111:37158->192.0.72.2:80\n" +
|
||||
"n192.168.2.111:44013->54.229.241.196:80\n" +
|
||||
"n192.168.2.111:56385->74.201.105.31:443\n" +
|
||||
"p21356\n" +
|
||||
"cssh\n" +
|
||||
"n192.168.2.111:33963->192.168.2.71:22\n": map[string]Proc{
|
||||
"[::1]:6600": Proc{
|
||||
PID: 13100,
|
||||
Name: "mpd",
|
||||
},
|
||||
"[2003:45:2b57:8900:1869:2947:f942:aba7]:55711": Proc{
|
||||
PID: 14612,
|
||||
Name: "chromium",
|
||||
},
|
||||
"192.168.2.111:37158": Proc{
|
||||
PID: 14612,
|
||||
Name: "chromium",
|
||||
},
|
||||
"192.168.2.111:44013": Proc{
|
||||
PID: 14612,
|
||||
Name: "chromium",
|
||||
},
|
||||
"192.168.2.111:56385": Proc{
|
||||
PID: 14612,
|
||||
Name: "chromium",
|
||||
},
|
||||
"192.168.2.111:33963": Proc{
|
||||
PID: 21356,
|
||||
Name: "ssh",
|
||||
},
|
||||
},
|
||||
} {
|
||||
got, err := parseLSOF(in)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(expected, got) {
|
||||
t.Errorf("Expected:\n %#v\nGot:\n %#v\n", expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
18
vendor/github.com/weaveworks/procspy/lsproc/lsproc.go
generated
vendored
18
vendor/github.com/weaveworks/procspy/lsproc/lsproc.go
generated
vendored
@@ -1,18 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/weaveworks/procspy"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cs, err := procspy.Connections(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("TCP Connections:\n")
|
||||
for c := cs.Next(); c != nil; c = cs.Next() {
|
||||
fmt.Printf(" - %+v\n", c)
|
||||
}
|
||||
}
|
||||
83
vendor/github.com/weaveworks/procspy/netstat.go
generated
vendored
83
vendor/github.com/weaveworks/procspy/netstat.go
generated
vendored
@@ -1,83 +0,0 @@
|
||||
package procspy
|
||||
|
||||
// netstat reading.
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parseDarwinNetstat parses netstat output. (Linux has ip:port, darwin
|
||||
// ip.port. The 'Proto' column value also differs.)
|
||||
func parseDarwinNetstat(out string) []Connection {
|
||||
//
|
||||
// Active Internet connections
|
||||
// Proto Recv-Q Send-Q Local Address Foreign Address (state)
|
||||
// tcp4 0 0 10.0.1.6.58287 1.2.3.4.443 ESTABLISHED
|
||||
//
|
||||
res := []Connection{}
|
||||
for i, line := range strings.Split(out, "\n") {
|
||||
if i == 0 || i == 1 {
|
||||
// Skip header
|
||||
continue
|
||||
}
|
||||
|
||||
// Fields are:
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != 6 {
|
||||
continue
|
||||
}
|
||||
|
||||
if fields[5] != "ESTABLISHED" {
|
||||
continue
|
||||
}
|
||||
|
||||
t := Connection{
|
||||
Transport: "tcp",
|
||||
}
|
||||
|
||||
// Format is <ip>.<port>
|
||||
locals := strings.Split(fields[3], ".")
|
||||
if len(locals) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
localAddress = strings.Join(locals[:len(locals)-1], ".")
|
||||
localPort = locals[len(locals)-1]
|
||||
)
|
||||
|
||||
t.LocalAddress = net.ParseIP(localAddress)
|
||||
|
||||
p, err := strconv.Atoi(localPort)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
t.LocalPort = uint16(p)
|
||||
|
||||
remotes := strings.Split(fields[4], ".")
|
||||
if len(remotes) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
remoteAddress = strings.Join(remotes[:len(remotes)-1], ".")
|
||||
remotePort = remotes[len(remotes)-1]
|
||||
)
|
||||
|
||||
t.RemoteAddress = net.ParseIP(remoteAddress)
|
||||
|
||||
p, err = strconv.Atoi(remotePort)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
t.RemotePort = uint16(p)
|
||||
|
||||
res = append(res, t)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
58
vendor/github.com/weaveworks/procspy/netstat_test.go
generated
vendored
58
vendor/github.com/weaveworks/procspy/netstat_test.go
generated
vendored
@@ -1,58 +0,0 @@
|
||||
package procspy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNetstatDarwin(t *testing.T) {
|
||||
testString := `Active Internet connections
|
||||
Proto Recv-Q Send-Q Local Address Foreign Address (state)
|
||||
tcp4 0 0 10.0.1.6.58287 1.2.3.4.443 ESTABLISHED
|
||||
tcp4 0 0 10.0.1.6.58279 2.3.4.5.80 ESTABLISHED
|
||||
tcp4 0 0 10.0.1.6.58276 44.55.66.77.443 ESTABLISHED
|
||||
tcp4 0 0 10.0.1.6.1 4.0.4.0.443 GONE
|
||||
`
|
||||
res := parseDarwinNetstat(testString)
|
||||
expected := []Connection{
|
||||
{
|
||||
Transport: "tcp",
|
||||
LocalAddress: net.ParseIP("10.0.1.6"),
|
||||
LocalPort: 58287,
|
||||
RemoteAddress: net.ParseIP("1.2.3.4"),
|
||||
RemotePort: 443,
|
||||
},
|
||||
{
|
||||
Transport: "tcp",
|
||||
LocalAddress: net.ParseIP("10.0.1.6"),
|
||||
LocalPort: 58279,
|
||||
RemoteAddress: net.ParseIP("2.3.4.5"),
|
||||
RemotePort: 80,
|
||||
},
|
||||
{
|
||||
Transport: "tcp",
|
||||
LocalAddress: net.ParseIP("10.0.1.6"),
|
||||
LocalPort: 58276,
|
||||
RemoteAddress: net.ParseIP("44.55.66.77"),
|
||||
RemotePort: 443,
|
||||
},
|
||||
/*
|
||||
{
|
||||
Transport: "tcp",
|
||||
LocalAddress: "::1",
|
||||
LocalPort: "6600",
|
||||
RemoteAddress: "::1",
|
||||
RemotePort: "41993",
|
||||
},
|
||||
*/
|
||||
}
|
||||
|
||||
if len(res) != 3 {
|
||||
t.Errorf("Wanted 3")
|
||||
}
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
t.Errorf("OS x netstat 4 error. Got\n%+v\nExpected\n%+v\n", res, expected)
|
||||
}
|
||||
|
||||
}
|
||||
138
vendor/github.com/weaveworks/procspy/proc.go
generated
vendored
138
vendor/github.com/weaveworks/procspy/proc.go
generated
vendored
@@ -1,138 +0,0 @@
|
||||
package procspy
|
||||
|
||||
// /proc-based implementation.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
procRoot = "/proc"
|
||||
)
|
||||
|
||||
// SetProcRoot sets the location of the proc filesystem.
|
||||
func SetProcRoot(root string) {
|
||||
procRoot = root
|
||||
}
|
||||
|
||||
// walkProcPid walks over all numerical (PID) /proc entries, and sees 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.
|
||||
func walkProcPid(buf *bytes.Buffer) (map[uint64]Proc, error) {
|
||||
fh, err := os.Open(procRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dirNames, err := fh.Readdirnames(-1)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
res = map[uint64]Proc{}
|
||||
namespaces = map[uint64]struct{}{}
|
||||
stat syscall.Stat_t
|
||||
)
|
||||
for _, dirName := range dirNames {
|
||||
pid, err := strconv.ParseUint(dirName, 10, 0)
|
||||
if err != nil {
|
||||
// Not a number, so not a PID subdir.
|
||||
continue
|
||||
}
|
||||
|
||||
fdBase := filepath.Join(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
|
||||
}
|
||||
|
||||
// Read network namespace, and if we haven't seen it before,
|
||||
// read /proc/<pid>/net/tcp
|
||||
err = syscall.Lstat(filepath.Join(procRoot, dirName, "/ns/net"), &stat)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := namespaces[stat.Ino]; !ok {
|
||||
namespaces[stat.Ino] = struct{}{}
|
||||
readFile(filepath.Join(procRoot, dirName, "/net/tcp"), buf)
|
||||
readFile(filepath.Join(procRoot, dirName, "/net/tcp6"), buf)
|
||||
}
|
||||
|
||||
var name string
|
||||
for _, fdName := range fdNames {
|
||||
// Direct use of syscall.Stat() to save garbage.
|
||||
err = syscall.Stat(filepath.Join(fdBase, fdName), &stat)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// We want sockets only.
|
||||
if stat.Mode&syscall.S_IFMT != syscall.S_IFSOCK {
|
||||
continue
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
if name = procName(filepath.Join(procRoot, dirName)); name == "" {
|
||||
// Process might be gone by now
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
res[stat.Ino] = Proc{
|
||||
PID: uint(pid),
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// procName does a pid->name lookup.
|
||||
func procName(base string) string {
|
||||
fh, err := os.Open(filepath.Join(base, "/comm"))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
name := make([]byte, 64)
|
||||
l, err := fh.Read(name)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if l < 2 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// drop trailing "\n"
|
||||
return string(name[:l-1])
|
||||
}
|
||||
|
||||
// readFile reads an arbitrary file into a buffer. It's a variable so it can
|
||||
// be overwritten for benchmarks. That's bad practice and we should change it
|
||||
// to be a dependency.
|
||||
var readFile = func(filename string, buf *bytes.Buffer) error {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = buf.ReadFrom(f)
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
170
vendor/github.com/weaveworks/procspy/procnet.go
generated
vendored
170
vendor/github.com/weaveworks/procspy/procnet.go
generated
vendored
@@ -1,170 +0,0 @@
|
||||
package procspy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ProcNet is an iterator to parse /proc/net/tcp{,6} files.
|
||||
type ProcNet struct {
|
||||
b []byte
|
||||
c Connection
|
||||
wantedState uint
|
||||
bytesLocal, bytesRemote [16]byte
|
||||
seen map[uint64]struct{}
|
||||
}
|
||||
|
||||
// NewProcNet gives a new ProcNet parser.
|
||||
func NewProcNet(b []byte, wantedState uint) *ProcNet {
|
||||
return &ProcNet{
|
||||
b: b,
|
||||
c: Connection{},
|
||||
wantedState: wantedState,
|
||||
seen: map[uint64]struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns the next connection. All buffers are re-used, so if you want
|
||||
// to keep the IPs you have to copy them.
|
||||
func (p *ProcNet) Next() *Connection {
|
||||
again:
|
||||
if len(p.b) == 0 {
|
||||
return nil
|
||||
}
|
||||
b := p.b
|
||||
|
||||
if p.b[2] == 's' {
|
||||
// Skip header
|
||||
p.b = nextLine(b)
|
||||
goto again
|
||||
}
|
||||
|
||||
var (
|
||||
local, remote, state, inode []byte
|
||||
)
|
||||
_, b = nextField(b) // 'sl' column
|
||||
local, b = nextField(b)
|
||||
remote, b = nextField(b)
|
||||
state, b = nextField(b)
|
||||
if parseHex(state) != p.wantedState {
|
||||
p.b = nextLine(b)
|
||||
goto again
|
||||
}
|
||||
_, b = nextField(b) // 'tx_queue' column
|
||||
_, b = nextField(b) // 'rx_queue' column
|
||||
_, b = nextField(b) // 'tr' column
|
||||
_, b = nextField(b) // 'uid' column
|
||||
_, b = nextField(b) // 'timeout' column
|
||||
inode, b = nextField(b)
|
||||
|
||||
p.c.LocalAddress, p.c.LocalPort = scanAddressNA(local, &p.bytesLocal)
|
||||
p.c.RemoteAddress, p.c.RemotePort = scanAddressNA(remote, &p.bytesRemote)
|
||||
p.c.inode = parseDec(inode)
|
||||
p.b = nextLine(b)
|
||||
if _, alreadySeen := p.seen[p.c.inode]; alreadySeen {
|
||||
goto again
|
||||
}
|
||||
p.seen[p.c.inode] = struct{}{}
|
||||
return &p.c
|
||||
}
|
||||
|
||||
// scanAddressNA parses 'A12CF62E:00AA' to the address/port. Handles IPv4 and
|
||||
// IPv6 addresses. The address is a big endian 32 bit ints, hex encoded. We
|
||||
// just decode the hex and flip the bytes in every group of 4.
|
||||
func scanAddressNA(in []byte, buf *[16]byte) (net.IP, uint16) {
|
||||
col := bytes.IndexByte(in, ':')
|
||||
if col == -1 {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
// Network address is big endian. Can be either ipv4 or ipv6.
|
||||
address := hexDecode32bigNA(in[:col], buf)
|
||||
return net.IP(address), uint16(parseHex(in[col+1:]))
|
||||
}
|
||||
|
||||
// hexDecode32big decodes sequences of 32bit big endian bytes.
|
||||
func hexDecode32bigNA(src []byte, buf *[16]byte) []byte {
|
||||
blocks := len(src) / 8
|
||||
for block := 0; block < blocks; block++ {
|
||||
for i := 0; i < 4; i++ {
|
||||
a := fromHexChar(src[block*8+i*2])
|
||||
b := fromHexChar(src[block*8+i*2+1])
|
||||
buf[block*4+3-i] = (a << 4) | b
|
||||
}
|
||||
}
|
||||
return buf[:blocks*4]
|
||||
}
|
||||
|
||||
func nextField(s []byte) ([]byte, []byte) {
|
||||
// Skip whitespace.
|
||||
for i, b := range s {
|
||||
if b != ' ' {
|
||||
s = s[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Up until the next whitespace field.
|
||||
for i, b := range s {
|
||||
if b == ' ' {
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func nextLine(s []byte) []byte {
|
||||
i := bytes.IndexByte(s, '\n')
|
||||
if i == -1 {
|
||||
return nil
|
||||
}
|
||||
return s[i+1:]
|
||||
}
|
||||
|
||||
// Simplified copy of strconv.ParseUint(16).
|
||||
func parseHex(s []byte) uint {
|
||||
n := uint(0)
|
||||
for i := 0; i < len(s); i++ {
|
||||
n *= 16
|
||||
n += uint(fromHexChar(s[i]))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Simplified copy of strconv.ParseUint(10).
|
||||
func parseDec(s []byte) uint64 {
|
||||
n := uint64(0)
|
||||
for _, c := range s {
|
||||
n *= 10
|
||||
n += uint64(c - '0')
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// hexDecode32big decodes sequences of 32bit big endian bytes.
|
||||
func hexDecode32big(src []byte) []byte {
|
||||
dst := make([]byte, len(src)/2)
|
||||
blocks := len(src) / 8
|
||||
for block := 0; block < blocks; block++ {
|
||||
for i := 0; i < 4; i++ {
|
||||
a := fromHexChar(src[block*8+i*2])
|
||||
b := fromHexChar(src[block*8+i*2+1])
|
||||
dst[block*4+3-i] = (a << 4) | b
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// fromHexChar converts a hex character into its value.
|
||||
func fromHexChar(c byte) uint8 {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return c - '0'
|
||||
case 'a' <= c && c <= 'f':
|
||||
return c - 'a' + 10
|
||||
case 'A' <= c && c <= 'F':
|
||||
return c - 'A' + 10
|
||||
}
|
||||
return 0
|
||||
}
|
||||
162
vendor/github.com/weaveworks/procspy/procnet_test.go
generated
vendored
162
vendor/github.com/weaveworks/procspy/procnet_test.go
generated
vendored
@@ -1,162 +0,0 @@
|
||||
package procspy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProcNet(t *testing.T) {
|
||||
testString := ` sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout Inode
|
||||
0: 00000000:A6C0 00000000:0000 01 00000000:00000000 00:00000000 00000000 105 0 5107 1 ffff8800a6aaf040 100 0 0 10 0
|
||||
1: 00000000:006F 00000000:0000 01 00000000:00000000 00:00000000 00000000 0 0 5084 1 ffff8800a6aaf740 100 0 0 10 0
|
||||
2: 0100007F:0019 00000000:0000 01 00000000:00000000 00:00000000 00000000 0 0 10550 1 ffff8800a729b780 100 0 0 10 0
|
||||
3: A12CF62E:E4D7 57FC1EC0:01BB 01 00000000:00000000 02:000006FA 00000000 1000 0 639474 2 ffff88007e75a740 48 4 26 10 -1
|
||||
`
|
||||
p := NewProcNet([]byte(testString), tcpEstablished)
|
||||
expected := []Connection{
|
||||
{
|
||||
LocalAddress: net.IP([]byte{0, 0, 0, 0}),
|
||||
LocalPort: 0xa6c0,
|
||||
RemoteAddress: net.IP([]byte{0, 0, 0, 0}),
|
||||
RemotePort: 0x0,
|
||||
inode: 5107,
|
||||
},
|
||||
{
|
||||
LocalAddress: net.IP([]byte{0, 0, 0, 0}),
|
||||
LocalPort: 0x006f,
|
||||
RemoteAddress: net.IP([]byte{0, 0, 0, 0}),
|
||||
RemotePort: 0x0,
|
||||
inode: 5084,
|
||||
},
|
||||
{
|
||||
LocalAddress: net.IP([]byte{0x7f, 0x0, 0x0, 0x01}),
|
||||
LocalPort: 0x0019,
|
||||
RemoteAddress: net.IP([]byte{0, 0, 0, 0}),
|
||||
RemotePort: 0x0,
|
||||
inode: 10550,
|
||||
},
|
||||
{
|
||||
LocalAddress: net.IP([]byte{0x2e, 0xf6, 0x2c, 0xa1}),
|
||||
LocalPort: 0xe4d7,
|
||||
RemoteAddress: net.IP([]byte{0xc0, 0x1e, 0xfc, 0x57}),
|
||||
RemotePort: 0x01bb,
|
||||
inode: 639474,
|
||||
},
|
||||
}
|
||||
for i := 0; i < 4; i++ {
|
||||
have := p.Next()
|
||||
want := expected[i]
|
||||
if !reflect.DeepEqual(*have, want) {
|
||||
t.Errorf("transport 4 error. Got\n%+v\nExpected\n%+v\n", *have, want)
|
||||
}
|
||||
}
|
||||
if got := p.Next(); got != nil {
|
||||
t.Errorf("p.Next() wasn't empty")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTransport6(t *testing.T) {
|
||||
// Abridged copy of my /proc/net/tcp6
|
||||
testString := ` sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout Inode
|
||||
0: 00000000000000000000000000000000:19C8 00000000000000000000000000000000:0000 01 00000000:00000000 00:00000000 00000000 0 0 23661201 1 ffff880103fb4800 100 0 0 10 -1
|
||||
8: 4500032000BE692B8AE31EBD919D9D10:D61C 5014002A080805400000000015100000:01BB 01 00000000:00000000 02:00000045 00000000 1000 0 36856710 2 ffff88010b796080 22 4 30 8 7
|
||||
`
|
||||
|
||||
p := NewProcNet([]byte(testString), tcpEstablished)
|
||||
expected := []Connection{
|
||||
{
|
||||
// state: 10,
|
||||
LocalAddress: net.IP(make([]byte, 16)),
|
||||
LocalPort: 0x19c8,
|
||||
RemoteAddress: net.IP(make([]byte, 16)),
|
||||
RemotePort: 0x0,
|
||||
// uid: 0,
|
||||
inode: 23661201,
|
||||
},
|
||||
{
|
||||
// state: 1,
|
||||
LocalAddress: net.IP([]byte{
|
||||
0x20, 0x03, 0, 0x45,
|
||||
0x2b, 0x69, 0xbe, 0x00,
|
||||
0xbd, 0x1e, 0xe3, 0x8a,
|
||||
0x10, 0x9d, 0x9d, 0x91,
|
||||
}),
|
||||
LocalPort: 0xd61c,
|
||||
RemoteAddress: net.IP([]byte{
|
||||
0x2a, 0x00, 0x14, 0x50,
|
||||
0x40, 0x05, 0x08, 0x08,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0x10, 0x15,
|
||||
}),
|
||||
RemotePort: 0x01bb,
|
||||
// uid: 1000,
|
||||
inode: 36856710,
|
||||
},
|
||||
}
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
have := p.Next()
|
||||
want := expected[i]
|
||||
if !reflect.DeepEqual(*have, want) {
|
||||
t.Errorf("got\n%+v\nExpected\n%+v\n", *have, want)
|
||||
}
|
||||
}
|
||||
if got := p.Next(); got != nil {
|
||||
t.Errorf("p.Next() wasn't empty")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTransportNonsense(t *testing.T) {
|
||||
testString := ` sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout Inode
|
||||
0: 00000000:A6C0 00000000:0000 01 000000
|
||||
broken line
|
||||
`
|
||||
p := NewProcNet([]byte(testString), tcpEstablished)
|
||||
expected := []Connection{
|
||||
{
|
||||
LocalAddress: net.IP([]byte{0, 0, 0, 0}),
|
||||
LocalPort: 0xa6c0,
|
||||
RemoteAddress: net.IP([]byte{0, 0, 0, 0}),
|
||||
RemotePort: 0x0,
|
||||
},
|
||||
}
|
||||
|
||||
for i := 0; i < 1; i++ {
|
||||
have := p.Next()
|
||||
want := expected[i]
|
||||
if !reflect.DeepEqual(*have, want) {
|
||||
t.Errorf("Got\n%+v\nExpected\n%+v\n", *have, want)
|
||||
}
|
||||
}
|
||||
if got := p.Next(); got != nil {
|
||||
t.Errorf("p.Next() wasn't empty")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestProcNetFiltersDuplicates(t *testing.T) {
|
||||
testString := ` sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout Inode
|
||||
0: 00000000:A6C0 00000000:0000 01 00000000:00000000 00:00000000 00000000 105 0 5107 1 ffff8800a6aaf040 100 0 0 10 0
|
||||
1: 00000000:A6C0 00000000:0000 01 00000000:00000000 00:00000000 00000000 105 0 5107 1 ffff8800a6aaf040 100 0 0 10 0
|
||||
`
|
||||
p := NewProcNet([]byte(testString), tcpEstablished)
|
||||
expected := Connection{
|
||||
LocalAddress: net.IP([]byte{0, 0, 0, 0}),
|
||||
LocalPort: 0xa6c0,
|
||||
RemoteAddress: net.IP([]byte{0, 0, 0, 0}),
|
||||
RemotePort: 0x0,
|
||||
inode: 5107,
|
||||
}
|
||||
have := p.Next()
|
||||
want := expected
|
||||
if !reflect.DeepEqual(*have, want) {
|
||||
t.Errorf("transport 4 error. Got\n%+v\nExpected\n%+v\n", *have, want)
|
||||
}
|
||||
if got := p.Next(); got != nil {
|
||||
t.Errorf("p.Next() wasn't empty")
|
||||
}
|
||||
|
||||
}
|
||||
43
vendor/github.com/weaveworks/procspy/spy.go
generated
vendored
43
vendor/github.com/weaveworks/procspy/spy.go
generated
vendored
@@ -1,43 +0,0 @@
|
||||
// Package procspy lists TCP connections, and optionally tries to find the
|
||||
// owning processes. Works on Linux (via /proc) and Darwin (via `lsof -i` and
|
||||
// `netstat`). You'll need root to use Processes().
|
||||
package procspy
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
tcpEstablished = 1 // according to /include/net/tcp_states.h
|
||||
)
|
||||
|
||||
// Connection is a (TCP) connection. The Proc struct might not be filled in.
|
||||
type Connection struct {
|
||||
Transport string
|
||||
LocalAddress net.IP
|
||||
LocalPort uint16
|
||||
RemoteAddress net.IP
|
||||
RemotePort uint16
|
||||
inode uint64
|
||||
Proc
|
||||
}
|
||||
|
||||
// Proc is a single process with PID and process name.
|
||||
type Proc struct {
|
||||
PID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// ConnIter is returned by Connections().
|
||||
type ConnIter interface {
|
||||
Next() *Connection
|
||||
}
|
||||
|
||||
// Connections returns all established (TCP) connections. If processes is
|
||||
// false we'll just list all TCP connections, and there is no need to be root.
|
||||
// If processes is true it'll additionally try to lookup the process owning the
|
||||
// connection, filling in the Proc field. You will need to run this as root to
|
||||
// find all processes.
|
||||
func Connections(processes bool) (ConnIter, error) {
|
||||
return cbConnections(processes)
|
||||
}
|
||||
62
vendor/github.com/weaveworks/procspy/spy_darwin.go
generated
vendored
62
vendor/github.com/weaveworks/procspy/spy_darwin.go
generated
vendored
@@ -1,62 +0,0 @@
|
||||
package procspy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
netstatBinary = "netstat"
|
||||
lsofBinary = "lsof"
|
||||
)
|
||||
|
||||
// Connections returns all established (TCP) connections. No need to be root
|
||||
// to run this. If processes is true it also tries to fill in the process
|
||||
// fields of the connection. You need to be root to find all processes.
|
||||
var cbConnections = func(processes bool) (ConnIter, error) {
|
||||
out, err := exec.Command(
|
||||
netstatBinary,
|
||||
"-n", // no number resolving
|
||||
"-W", // Wide output
|
||||
// "-l", // full IPv6 addresses // What does this do?
|
||||
"-p", "tcp", // only TCP
|
||||
).CombinedOutput()
|
||||
if err != nil {
|
||||
// log.Printf("lsof error: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
connections := parseDarwinNetstat(string(out))
|
||||
|
||||
if processes {
|
||||
out, err := exec.Command(
|
||||
lsofBinary,
|
||||
"-i", // only Internet files
|
||||
"-n", "-P", // no number resolving
|
||||
"-w", // no warnings
|
||||
"-F", lsofFields, // \n based output of only the fields we want.
|
||||
).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
procs, err := parseLSOF(string(out))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for local, proc := range procs {
|
||||
for i, c := range connections {
|
||||
localAddr := net.JoinHostPort(
|
||||
c.LocalAddress.String(),
|
||||
strconv.Itoa(int(c.LocalPort)),
|
||||
)
|
||||
if localAddr == local {
|
||||
connections[i].Proc = proc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f := fixedConnIter(connections)
|
||||
return &f, nil
|
||||
}
|
||||
57
vendor/github.com/weaveworks/procspy/spy_linux.go
generated
vendored
57
vendor/github.com/weaveworks/procspy/spy_linux.go
generated
vendored
@@ -1,57 +0,0 @@
|
||||
package procspy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return bytes.NewBuffer(make([]byte, 0, 5000))
|
||||
},
|
||||
}
|
||||
|
||||
type pnConnIter struct {
|
||||
pn *ProcNet
|
||||
buf *bytes.Buffer
|
||||
procs map[uint64]Proc
|
||||
}
|
||||
|
||||
func (c *pnConnIter) Next() *Connection {
|
||||
n := c.pn.Next()
|
||||
if n == nil {
|
||||
// Done!
|
||||
bufPool.Put(c.buf)
|
||||
return nil
|
||||
}
|
||||
if proc, ok := c.procs[n.inode]; ok {
|
||||
n.Proc = proc
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// cbConnections sets Connections()
|
||||
var cbConnections = func(processes bool) (ConnIter, error) {
|
||||
// buffer for contents of /proc/<pid>/net/tcp
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
|
||||
var procs map[uint64]Proc
|
||||
if processes {
|
||||
var err error
|
||||
if procs, err = walkProcPid(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if buf.Len() == 0 {
|
||||
readFile(procRoot+"/net/tcp", buf)
|
||||
readFile(procRoot+"/net/tcp6", buf)
|
||||
}
|
||||
|
||||
return &pnConnIter{
|
||||
pn: NewProcNet(buf.Bytes(), tcpEstablished),
|
||||
buf: buf,
|
||||
procs: procs,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user