Files
weave-scope/probe/endpoint/procspy/spy_linux.go
Matthias Radestock 30e0444914 ensure connections from /proc/net/tcp{,6} get the right pid
ProcNet.Next does not allocate Connection structs, for efficiency.
Instead it always returns a *Connection pointing to the same instance.
As a result, any mutations by the caller to struct elements that
aren't actually set by ProcNet.Next, in particular Connection.Proc,
are carried across to subsequent calls.

This had hilarious consequences: connections referencing an inode
which we hadn't come across during proc walking would be associated
with the process corresponding to the last successfully looked up
inode.

The fix is to clear out the garbage left over from previous calls.

Fixes #2638.
2017-06-25 10:59:58 +01:00

94 lines
1.8 KiB
Go

package procspy
import (
"bytes"
"sync"
"github.com/weaveworks/scope/probe/process"
)
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
} else {
// ProcNet.Next() always returns a pointer to the same
// struct. We therefore must clear any garbage left over from
// the previous call.
n.Proc = Proc{}
}
return n
}
// NewConnectionScanner creates a new Linux ConnectionScanner
func NewConnectionScanner(walker process.Walker, processes bool) ConnectionScanner {
scanner := &linuxScanner{}
if processes {
scanner.r = newBackgroundReader(walker)
}
return scanner
}
// NewSyncConnectionScanner creates a new synchronous Linux ConnectionScanner
func NewSyncConnectionScanner(walker process.Walker, processes bool) ConnectionScanner {
scanner := &linuxScanner{}
if processes {
scanner.r = newForegroundReader(walker)
}
return scanner
}
type linuxScanner struct {
r reader
}
func (s *linuxScanner) Connections() (ConnIter, error) {
// buffer for contents of /proc/<pid>/net/tcp
buf := bufPool.Get().(*bytes.Buffer)
buf.Reset()
var procs map[uint64]*Proc
if s.r != nil {
var err error
if procs, err = s.r.getWalkedProcPid(buf); err != nil {
return nil, err
}
}
if buf.Len() == 0 {
readFile(procRoot+"/net/tcp", buf)
if ipv6IsSupported {
readFile(procRoot+"/net/tcp6", buf)
}
}
return &pnConnIter{
pn: NewProcNet(buf.Bytes()),
buf: buf,
procs: procs,
}, nil
}
func (s *linuxScanner) Stop() {
if s.r != nil {
s.r.stop()
}
}