Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish ss example mapping connections to pids #151

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 59 additions & 5 deletions examples/ss/ss.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io/ioutil"
"log"
"os"
"strconv"
"strings"
"syscall"
"text/tabwriter"
Expand Down Expand Up @@ -86,17 +87,70 @@ func sockets() error {
}, "\t"))
defer w.Flush()

for _, diag := range msgs {
// XXX: A real implementation of ss would find the process holding
// inode of the socket. It would read /proc/<pid>/fd and find all sockets.
pidProgram := "not implemented"
inodeToPid := mapInodesToPid()

for _, diag := range msgs {
fmt.Fprintf(w, "%v\t%v\t%v\t%v:%v\t%v:%v\t%v\t%v\t%v\n",
linux.TCPState(diag.State), diag.RQueue, diag.WQueue,
diag.SrcIP().String(), diag.SrcPort(),
diag.DstIP().String(), diag.DstPort(),
diag.UID, diag.Inode, pidProgram)
diag.UID, diag.Inode, inodeToPid[diag.Inode])
}

return nil
}

func mapInodesToPid() (ret map[uint32]string) {
ret = map[uint32]string{}

fd, err := os.Open("/proc")
if err != nil {
fmt.Printf("Error opening /proc: %v", err)
}
defer fd.Close()

dirContents, err := fd.Readdirnames(0)
if err != nil {
fmt.Printf("Error reading files in /proc: %v", err)
}

for _, pid := range dirContents {
_, err := strconv.ParseUint(pid, 10, 32)
if err != nil {
// exclude files with a not numeric name. We only want to access pid directories
continue
}

pidDir, err := os.Open("/proc/" + string(pid) + "/fd/")
if err != nil {
// ignore errors:
// - missing directory, pid has already finished
// - permission denied
continue
}

fds, err := pidDir.Readdirnames(0)
if err != nil {
continue

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For errors like this, where there's no expectation that they might fail, we might want to actually fail, or at least comment if we expect them to fail for some reason.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could fail if the pid has finished and the dir does not exists any more.

Failing there will generate a lot of useless "warnings" IMHO

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which case, can we at least add a comment to clarify?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gomacro> import "os"
gomacro> a, err := os.Open("/proc/955374/fd")
gomacro> err
<nil>   // error
<--- here I kill process 955374
gomacro> a.Readdirnames(0)
[]      // []string
readdirent: no such file or directory   // error
gomacro>

It is commented in line 126

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. Considering that we can hit errors relating to non-existent PIDs in a few places, can we move that comment block in 126 out of that if block and move it somewhere, perhaps before the Open call just to make the comment a little more explicit?

}

for _, fd := range fds {
link, err := os.Readlink("/proc/" + string(pid) + "/fd/" + fd)
if err != nil {
continue

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto here.

}

var inode uint32

_, err = fmt.Sscanf(link, "socket:[%d]", &inode)
if err != nil {
// this inode is not a socket
continue
}

ret[inode] = pid
}
}

return ret
}