// 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, walker process.Walker) (map[uint64]*Proc, error) { var ( res = map[uint64]*Proc{} namespaces = map[uint64]bool{} // map namespace id -> has connections statT syscall.Stat_t ) walker.Walk(func(p, _ process.Process) { dirName := strconv.Itoa(p.PID) fdBase := filepath.Join(procRoot, dirName, "fd") // Read network namespace, and if we haven't seen it before, // read /proc/<pid>/net/tcp if err := fs.Lstat(filepath.Join(procRoot, dirName, "/ns/net"), &statT); err != nil { return } hasConns, ok := namespaces[statT.Ino] if !ok { read, _ := readFile(filepath.Join(procRoot, dirName, "/net/tcp"), buf) read6, _ := readFile(filepath.Join(procRoot, dirName, "/net/tcp6"), buf) hasConns = read+read6 > 0 namespaces[statT.Ino] = hasConns } if !hasConns { return } fds, err := fs.ReadDirNames(fdBase) if err != nil { // Process is be gone by now, or we don't have access. return } var proc *Proc for _, fd := range fds { // Direct use of syscall.Stat() to save garbage. err = fs.Stat(filepath.Join(fdBase, fd), &statT) if err != nil { continue } // We want sockets only. if statT.Mode&syscall.S_IFMT != syscall.S_IFSOCK { continue } if proc == nil { proc = &Proc{ PID: uint(p.PID), Name: p.Name, } } res[statT.Ino] = proc } }) return res, nil }
func (p dir) Stat(path string, stat *syscall.Stat_t) error { if path == "/" { return nil } head, tail := split(path) fs, ok := p.entries[head] if !ok { return fmt.Errorf("Not found: %s", path) } return fs.Stat(tail, stat) }