2020-02-20 09:58:19 +01:00
|
|
|
package procmon
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"sort"
|
2020-03-03 23:51:25 +01:00
|
|
|
"time"
|
2020-02-20 09:58:19 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type Inode struct {
|
2020-03-03 23:51:25 +01:00
|
|
|
Pid int
|
|
|
|
FdPath string
|
2020-02-20 09:58:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type ProcEntry struct {
|
2020-03-03 23:51:25 +01:00
|
|
|
Pid int
|
|
|
|
FdPath string
|
2020-02-20 09:58:19 +01:00
|
|
|
Descriptors []string
|
2020-03-03 23:51:25 +01:00
|
|
|
Time time.Time
|
2020-02-20 09:58:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
// cache of inodes, which help to not iterate over all the pidsCache and
|
|
|
|
// descriptors of /proc/<pid>/fd/
|
|
|
|
// 20-50us vs 50-80ms
|
2020-03-03 23:51:25 +01:00
|
|
|
inodesCache = make(map[string]*Inode)
|
2020-02-20 09:58:19 +01:00
|
|
|
maxCachedInodes = 24
|
|
|
|
// 2nd cache of already known running pids, which also saves time by
|
|
|
|
// iterating only over a few pids' descriptors, (30us-2ms vs. 50-80ms)
|
|
|
|
// since it's more likely that most of the connections will be made by the
|
|
|
|
// same (running) processes.
|
2020-03-03 23:51:25 +01:00
|
|
|
// The cache is ordered by time, placing in the first places those PIDs with
|
|
|
|
// active connections.
|
|
|
|
pidsCache []*ProcEntry
|
2020-02-20 09:58:19 +01:00
|
|
|
pidsDescriptorsCache = make(map[int][]string)
|
2020-03-03 23:51:25 +01:00
|
|
|
maxCachedPids = 24
|
2020-02-20 09:58:19 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func addProcEntry(fdPath string, fd_list []string, pid int) {
|
|
|
|
for n, _ := range pidsCache {
|
|
|
|
if pidsCache[n].Pid == pid {
|
|
|
|
pidsCache[n].Time = time.Now()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2020-03-03 23:51:25 +01:00
|
|
|
pidsCache = append(pidsCache, &ProcEntry{Pid: pid, FdPath: fdPath, Descriptors: fd_list, Time: time.Now()})
|
2020-02-20 09:58:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func sortProcEntries() {
|
|
|
|
sort.Slice(pidsCache, func(i, j int) bool {
|
|
|
|
t := pidsCache[i].Time.UnixNano()
|
|
|
|
u := pidsCache[j].Time.UnixNano()
|
2020-03-03 23:51:25 +01:00
|
|
|
return t > u || t == u
|
2020-02-20 09:58:19 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteProcEntry(pid int) {
|
|
|
|
for n, procEntry := range pidsCache {
|
|
|
|
if procEntry.Pid == pid {
|
|
|
|
pidsCache = append(pidsCache[:n], pidsCache[n+1:]...)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func cleanUpCaches() {
|
|
|
|
if len(inodesCache) > maxCachedInodes {
|
|
|
|
for k, _ := range inodesCache {
|
|
|
|
delete(inodesCache, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(pidsCache) > maxCachedPids {
|
|
|
|
pidsCache = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetPidByInodeFromCache(inodeKey string) int {
|
|
|
|
if _, found := inodesCache[inodeKey]; found == true {
|
|
|
|
// sometimes the process may have dissapeared at this point
|
|
|
|
if _, err := os.Lstat(fmt.Sprint("/proc/", inodesCache[inodeKey].Pid, "/exe")); err == nil {
|
|
|
|
return inodesCache[inodeKey].Pid
|
|
|
|
}
|
|
|
|
deleteProcEntry(inodesCache[inodeKey].Pid)
|
|
|
|
delete(inodesCache, inodeKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
func getPidDescriptorsFromCache(pid int, fdPath string, expect string, descriptors []string) int {
|
2020-03-03 23:51:25 +01:00
|
|
|
for fdIdx := 0; fdIdx < len(descriptors); fdIdx++ {
|
2020-02-20 09:58:19 +01:00
|
|
|
descLink := fmt.Sprint(fdPath, descriptors[fdIdx])
|
|
|
|
if link, err := os.Readlink(descLink); err == nil && link == expect {
|
|
|
|
return fdIdx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
2020-03-03 23:51:25 +01:00
|
|
|
func getPidFromCache(inode int, inodeKey string, expect string) (int, int) {
|
2020-02-20 09:58:19 +01:00
|
|
|
// loop over the processes that have generated connections
|
|
|
|
for n, procEntry := range pidsCache {
|
2020-03-04 01:56:48 +01:00
|
|
|
if n >= len(pidsCache) {
|
|
|
|
break
|
|
|
|
}
|
2020-02-20 09:58:19 +01:00
|
|
|
if idxDesc := getPidDescriptorsFromCache(procEntry.Pid, procEntry.FdPath, expect, procEntry.Descriptors); idxDesc != -1 {
|
|
|
|
pidsCache[n].Time = time.Now()
|
2020-03-03 23:51:25 +01:00
|
|
|
return procEntry.Pid, n
|
2020-02-20 09:58:19 +01:00
|
|
|
}
|
|
|
|
|
2020-03-04 01:56:48 +01:00
|
|
|
descriptors := lookupPidDescriptors(procEntry.FdPath)
|
|
|
|
if descriptors == nil {
|
|
|
|
deleteProcEntry(procEntry.Pid)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
pidsCache[n].Descriptors = descriptors
|
2020-02-20 09:58:19 +01:00
|
|
|
|
2020-03-04 01:56:48 +01:00
|
|
|
if idxDesc := getPidDescriptorsFromCache(procEntry.Pid, procEntry.FdPath, expect, descriptors); idxDesc != -1 {
|
|
|
|
pidsCache[n].Time = time.Now()
|
|
|
|
return procEntry.Pid, n
|
2020-02-20 09:58:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 23:51:25 +01:00
|
|
|
return -1, -1
|
2020-02-20 09:58:19 +01:00
|
|
|
}
|