|
|
|
package pcoroner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
"strings"
|
|
|
|
"strconv"
|
|
|
|
"sync"
|
|
|
|
"os"
|
|
|
|
"syscall"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|