You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fw-daemon/proc-coroner/pcoroner.go

167 lines
3.1 KiB

package pcoroner
import (
"fmt"
"os"
"strconv"
"strings"
"sync"
"syscall"
"time"
)
type WatchProcess struct {
Pid int
Inode uint64
Ppid int
Stime int
}
type CallbackEntry struct {
fn procCB
param interface{}
}
type procCB func(int, interface{})
var Callbacks []CallbackEntry
var pmutex = &sync.Mutex{}
var pidMap map[int]WatchProcess = make(map[int]WatchProcess)
func MonitorProcess(pid int) bool {
pmutex.Lock()
defer pmutex.Unlock()
_, ok := pidMap[pid]
if ok {
return false
}
watcher := WatchProcess{Pid: pid}
watcher.Inode = 0
res := checkProcess(&watcher, true)
if res {
pidMap[pid] = watcher
}
return res
}
func UnmonitorProcess(pid int) {
pmutex.Lock()
defer pmutex.Unlock()
delete(pidMap, pid)
return
}
func AddCallback(cbfunc procCB, param interface{}) {
cbe := CallbackEntry{cbfunc, param}
Callbacks = append(Callbacks, cbe)
}
func MonitorThread(cbfunc procCB, param interface{}) {
for {
/* if len(pidMap) == 0 {
fmt.Println("TICK")
} else { fmt.Println("len = ", len(pidMap)) } */
pmutex.Lock()
pmutex.Unlock()
for pkey, pval := range pidMap {
// fmt.Printf("PID %v -> %v\n", pkey, pval)
res := checkProcess(&pval, false)
if !res {
delete(pidMap, pkey)
if cbfunc != nil {
cbfunc(pkey, param)
}
for i := 0; i < len(Callbacks); i++ {
Callbacks[i].fn(pkey, Callbacks[i].param)
}
continue
}
}
time.Sleep(1 * time.Second)
}
}
func checkProcess(proc *WatchProcess, init bool) bool {
ppath := fmt.Sprintf("/proc/%d/stat", proc.Pid)
f, err := os.Open(ppath)
if err != nil {
// fmt.Printf("Error opening path %s: %s\n", ppath, err)
return false
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
fmt.Printf("Error calling stat on file %s: %s\n", ppath, err)
return false
}
sb, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
fmt.Println("Unexpected error reading stat information from proc file")
} else if init {
proc.Inode = sb.Ino
} else {
if sb.Ino != proc.Inode {
fmt.Printf("/proc inode mismatch for process %d: %v vs %v\n", proc.Pid, sb.Ino, proc.Inode)
return false
}
}
var buf [512]byte
nread, err := f.Read(buf[:])
if err != nil {
fmt.Printf("Error reading stat for process %d: %v", proc.Pid, err)
return true
} else if nread <= 0 {
fmt.Printf("Unexpected error reading stat for process %d", proc.Pid)
return true
}
bstr := string(buf[:])
// fmt.Println("sstr = ", bstr)
fields := strings.Split(bstr, " ")
if len(fields) < 22 {
fmt.Printf("Unexpected error reading data from /proc stat for process %d", proc.Pid)
return true
}
ppid, err := strconv.Atoi(fields[3])
if err != nil {
ppid = -1
}
if init {
proc.Ppid = ppid
} else if proc.Ppid != ppid {
fmt.Printf("Cached process ppid did not match value in /proc: %v vs %v\n", proc.Ppid, ppid)
return false
}
stime, err := strconv.Atoi(fields[21])
if err != nil {
stime = -1
}
if init {
proc.Stime = stime
} else if proc.Stime != stime {
fmt.Printf("Cached process start time did not match value in /proc: %v vs %v\n", proc.Stime, stime)
return false
}
return true
}