portlist: fix "readdirent: no such file or directory" errors on Linux.

This could happen when a process disappeared while we were reading its
file descriptor list.

I was able to replicate the problem by running this in another
terminal:

    while :; do for i in $(seq 10); do
      /bin/true & done >&/dev/null; wait >&/dev/null;
    done

And then running the portlist tests thousands of times.

Fixes #339.

Signed-off-by: Avery Pennarun <apenwarr@tailscale.com>
This commit is contained in:
Avery Pennarun 2020-05-19 01:48:55 -04:00
parent c97c45f268
commit 806de4ac94
2 changed files with 20 additions and 0 deletions

View File

@ -110,6 +110,12 @@ func addProcesses(pl []Port) ([]Port, error) {
if err == io.EOF { if err == io.EOF {
return nil return nil
} }
if os.IsNotExist(err) {
// This can happen if the directory we're
// reading disappears during the run. No big
// deal.
return nil
}
if err != nil { if err != nil {
return fmt.Errorf("addProcesses.readDir: %w", err) return fmt.Errorf("addProcesses.readDir: %w", err)
} }
@ -155,6 +161,12 @@ func foreachPID(fn func(pidStr string) error) error {
if err == io.EOF { if err == io.EOF {
return nil return nil
} }
if os.IsNotExist(err) {
// This can happen if the directory we're
// reading disappears during the run. No big
// deal.
return nil
}
if err != nil { if err != nil {
return fmt.Errorf("foreachPID.readdir: %w", err) return fmt.Errorf("foreachPID.readdir: %w", err)
} }

View File

@ -7,9 +7,14 @@ package portlist
import ( import (
"net" "net"
"testing" "testing"
"tailscale.com/tstest"
) )
func TestGetList(t *testing.T) { func TestGetList(t *testing.T) {
rc := tstest.NewResourceCheck()
defer rc.Assert(t)
pl, err := GetList(nil) pl, err := GetList(nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -21,6 +26,9 @@ func TestGetList(t *testing.T) {
} }
func TestIgnoreLocallyBoundPorts(t *testing.T) { func TestIgnoreLocallyBoundPorts(t *testing.T) {
rc := tstest.NewResourceCheck()
defer rc.Assert(t)
ln, err := net.Listen("tcp", "127.0.0.1:0") ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {
t.Skipf("failed to bind: %v", err) t.Skipf("failed to bind: %v", err)