added dialog to inspect details of a process in realtime (procfs)

New dialog added to display details of a process in realtime, gathered
from ProcFS.
Process tab -> double click on an app -> click on the button with the
search icon.

We have also improved the discovery of apps icons and names. It should
work better on systems where the DE is not properly configured.

Tested, but not bulletproof, still in beta.
This commit is contained in:
Gustavo Iñiguez Goia 2020-11-16 17:09:52 +01:00
parent a3a7becbc8
commit c8d1161061
14 changed files with 815 additions and 220 deletions

164
daemon/procmon/details.go Normal file
View file

@ -0,0 +1,164 @@
package procmon
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"github.com/gustavo-iniguez-goya/opensnitch/daemon/core"
)
// GetInfo collects information of a process.
func (p *Process) GetInfo() error {
if err := p.readPath(); err != nil {
return err
}
p.readCwd()
p.readCmdline()
p.readEnv()
p.readDescriptors()
p.readIOStats()
p.readStatus()
p.cleanPath()
return nil
}
func (p *Process) setCwd(cwd string) {
p.CWD = cwd
}
func (p *Process) readCwd() {
if link, err := os.Readlink(fmt.Sprintf("/proc/%d/cwd", p.ID)); err == nil {
p.CWD = link
}
}
// read and parse environment variables of a process.
func (p *Process) readEnv() {
if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/environ", p.ID)); err == nil {
for _, s := range strings.Split(string(data), "\x00") {
parts := strings.SplitN(core.Trim(s), "=", 2)
if parts != nil && len(parts) == 2 {
key := core.Trim(parts[0])
val := core.Trim(parts[1])
p.Env[key] = val
}
}
}
}
func (p *Process) readPath() error {
linkName := fmt.Sprint("/proc/", p.ID, "/exe")
if _, err := os.Lstat(linkName); err != nil {
return err
}
if link, err := os.Readlink(linkName); err == nil {
p.Path = link
}
return nil
}
func (p *Process) readCmdline() {
if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", p.ID)); err == nil {
for i, b := range data {
if b == 0x00 {
data[i] = byte(' ')
}
}
p.Args = make([]string, 0)
args := strings.Split(string(data), " ")
for _, arg := range args {
arg = core.Trim(arg)
if arg != "" {
p.Args = append(p.Args, arg)
}
}
}
}
func (p *Process) readDescriptors() {
f, err := os.Open(fmt.Sprint("/proc/", p.ID, "/fd/"))
if err != nil {
return
}
fDesc, err := f.Readdir(-1)
f.Close()
p.Descriptors = nil
for _, fd := range fDesc {
tempFd := &procDescriptors{
Name: fd.Name(),
}
if link, err := os.Readlink(fmt.Sprint("/proc/", p.ID, "/fd/", fd.Name())); err == nil {
tempFd.SymLink = link
if linkInfo, err := os.Lstat(link); err == nil {
tempFd.Size = linkInfo.Size()
tempFd.ModTime = linkInfo.ModTime()
}
}
p.Descriptors = append(p.Descriptors, tempFd)
}
}
func (p *Process) readIOStats() {
f, err := os.Open(fmt.Sprint("/proc/", p.ID, "/io"))
if err != nil {
return
}
defer f.Close()
p.IOStats = &procIOstats{}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
s := strings.Split(scanner.Text(), " ")
switch s[0] {
case "rchar:":
p.IOStats.RChar, _ = strconv.ParseInt(s[1], 10, 64)
case "wchar:":
p.IOStats.WChar, _ = strconv.ParseInt(s[1], 10, 64)
case "syscr:":
p.IOStats.SyscallRead, _ = strconv.ParseInt(s[1], 10, 64)
case "syscw:":
p.IOStats.SyscallWrite, _ = strconv.ParseInt(s[1], 10, 64)
case "read_bytes:":
p.IOStats.ReadBytes, _ = strconv.ParseInt(s[1], 10, 64)
case "write_bytes:":
p.IOStats.WriteBytes, _ = strconv.ParseInt(s[1], 10, 64)
}
}
}
func (p *Process) readStatus() {
if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/status")); err == nil {
p.Status = string(data)
}
if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/stat")); err == nil {
p.Stat = string(data)
}
if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/stack")); err == nil {
p.Stack = string(data)
}
if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/maps")); err == nil {
p.Maps = string(data)
}
if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/statm")); err == nil {
p.Statm = &procStatm{}
fmt.Sscanf(string(data), "%d %d %d %d %d %d %d", &p.Statm.Size, &p.Statm.Resident, &p.Statm.Shared, &p.Statm.Text, &p.Statm.Lib, &p.Statm.Data, &p.Statm.Dt)
}
}
func (p *Process) cleanPath() {
pathLen := len(p.Path)
if pathLen >= 10 && p.Path[pathLen-10:] == " (deleted)" {
p.Path = p.Path[:len(p.Path)-10]
}
}

View file

@ -2,12 +2,10 @@ package procmon
import (
"fmt"
"io/ioutil"
"os"
"strings"
"time"
"github.com/gustavo-iniguez-goya/opensnitch/daemon/core"
"github.com/gustavo-iniguez-goya/opensnitch/daemon/log"
"github.com/gustavo-iniguez-goya/opensnitch/daemon/procmon/audit"
)
@ -85,50 +83,6 @@ func GetPIDFromINode(inode int, inodeKey string) int {
return found
}
func cleanPath(proc *Process) {
pathLen := len(proc.Path)
if pathLen >= 10 && proc.Path[pathLen-10:] == " (deleted)" {
proc.Path = proc.Path[:len(proc.Path)-10]
}
}
func parseCmdLine(proc *Process) {
if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", proc.ID)); err == nil {
for i, b := range data {
if b == 0x00 {
data[i] = byte(' ')
}
}
args := strings.Split(string(data), " ")
for _, arg := range args {
arg = core.Trim(arg)
if arg != "" {
proc.Args = append(proc.Args, arg)
}
}
}
}
func parseCWD(proc *Process) {
if link, err := os.Readlink(fmt.Sprintf("/proc/%d/cwd", proc.ID)); err == nil {
proc.CWD = link
}
}
func parseEnv(proc *Process) {
if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/environ", proc.ID)); err == nil {
for _, s := range strings.Split(string(data), "\x00") {
parts := strings.SplitN(core.Trim(s), "=", 2)
if parts != nil && len(parts) == 2 {
key := core.Trim(parts[0])
val := core.Trim(parts[1])
proc.Env[key] = val
}
}
}
}
// FindProcess checks if a process exists given a PID.
// If it exists in /proc, a new Process{} object is returned with the details
// to identify a process (cmdline, name, environment variables, etc).
@ -141,14 +95,14 @@ func FindProcess(pid int, interceptUnknown bool) *Process {
audit.Lock.RLock()
proc := NewProcess(pid, aevent.ProcPath)
proc.Args = strings.Split(strings.Replace(aevent.ProcCmdLine, "\x00", " ", -1), " ")
proc.CWD = aevent.ProcDir
proc.setCwd(aevent.ProcDir)
audit.Lock.RUnlock()
// if the proc dir contains non alhpa-numeric chars the field is empty
if proc.CWD == "" {
parseCWD(proc)
proc.readCwd()
}
parseEnv(proc)
cleanPath(proc)
proc.readEnv()
proc.cleanPath()
return proc
}
@ -162,10 +116,10 @@ func FindProcess(pid int, interceptUnknown bool) *Process {
if link, err := os.Readlink(linkName); err == nil {
proc := NewProcess(pid, link)
parseCmdLine(proc)
parseCWD(proc)
parseEnv(proc)
cleanPath(proc)
proc.readCmdline()
proc.readCwd()
proc.readEnv()
proc.cleanPath()
return proc
}

View file

@ -1,17 +1,53 @@
package procmon
import (
"time"
"github.com/gustavo-iniguez-goya/opensnitch/daemon/log"
"github.com/gustavo-iniguez-goya/opensnitch/daemon/procmon/audit"
)
// Process holds the information of a process.
// man 5 proc; man procfs
type procIOstats struct {
RChar int64
WChar int64
SyscallRead int64
SyscallWrite int64
ReadBytes int64
WriteBytes int64
}
type procDescriptors struct {
Name string
SymLink string
Size int64
ModTime time.Time
}
type procStatm struct {
Size int64
Resident int64
Shared int64
Text int64
Lib int64
Data int64 // data + stack
Dt int
}
// Process holds the details of a process.
type Process struct {
ID int
Path string
Args []string
Env map[string]string
CWD string
ID int
Path string
Args []string
Env map[string]string
CWD string
Descriptors []*procDescriptors
IOStats *procIOstats
Status string
Stat string
Statm *procStatm
Stack string
Maps string
}
// NewProcess returns a new Process structure.

View file

@ -1,9 +1,11 @@
package ui
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"strconv"
"strings"
"time"
@ -16,6 +18,8 @@ import (
"golang.org/x/net/context"
)
var stopMonitoringProcess = make(chan int)
// NewReply constructs a new protocol notification reply
func NewReply(rID uint64, replyCode protocol.NotificationReplyCode, data string) *protocol.NotificationReply {
return &protocol.NotificationReply{
@ -48,13 +52,42 @@ func (c *Client) getClientConfig() *protocol.ClientConfig {
}
}
func (c *Client) monitorProcessDetails(pid int, stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
p := procmon.NewProcess(pid, "")
ticker := time.NewTicker(2 * time.Second)
for {
select {
case _pid := <-stopMonitoringProcess:
if _pid != pid {
continue
}
goto Exit
case <-ticker.C:
if err := p.GetInfo(); err != nil {
c.sendNotificationReply(stream, notification.Id, notification.Data, err)
goto Exit
}
pJSON, err := json.Marshal(p)
notification.Data = string(pJSON)
if errs := c.sendNotificationReply(stream, notification.Id, notification.Data, err); errs != nil {
goto Exit
}
}
}
Exit:
ticker.Stop()
}
func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
log.Info("[notification] Reloading configuration")
// Parse received configuration first, to get the new proc monitor method.
newConf, err := c.parseConf(notification.Data)
if err != nil {
log.Warning("[notification] error parsing received config: %v", notification.Data)
c.sendNotificationReply(stream, notification.Id, err)
c.sendNotificationReply(stream, notification.Id, "", err)
return
}
@ -73,7 +106,7 @@ func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient
procmon.Init()
}
c.sendNotificationReply(stream, notification.Id, err)
c.sendNotificationReply(stream, notification.Id, "", err)
}
func (c *Client) handleActionEnableRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
@ -86,7 +119,7 @@ func (c *Client) handleActionEnableRule(stream protocol.UI_NotificationsClient,
// save to disk only if the duration is rule.Always
err = c.rules.Replace(r, r.Duration == rule.Always)
}
c.sendNotificationReply(stream, notification.Id, err)
c.sendNotificationReply(stream, notification.Id, "", err)
}
func (c *Client) handleActionDisableRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
@ -97,7 +130,7 @@ func (c *Client) handleActionDisableRule(stream protocol.UI_NotificationsClient,
r.Enabled = false
err = c.rules.Replace(r, r.Duration == rule.Always)
}
c.sendNotificationReply(stream, notification.Id, err)
c.sendNotificationReply(stream, notification.Id, "", err)
}
func (c *Client) handleActionChangeRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
@ -114,7 +147,7 @@ func (c *Client) handleActionChangeRule(stream protocol.UI_NotificationsClient,
rErr = err
}
}
c.sendNotificationReply(stream, notification.Id, rErr)
c.sendNotificationReply(stream, notification.Id, "", rErr)
}
func (c *Client) handleActionDeleteRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
@ -126,23 +159,53 @@ func (c *Client) handleActionDeleteRule(stream protocol.UI_NotificationsClient,
log.Error("[notification] Error deleting rule: ", err, rul)
}
}
c.sendNotificationReply(stream, notification.Id, err)
c.sendNotificationReply(stream, notification.Id, "", err)
}
func (c *Client) handleActionMonitorProcess(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
pid, err := strconv.Atoi(notification.Data)
if err != nil {
log.Error("parsing PID to monitor")
return
}
if !core.Exists(fmt.Sprint("/proc/", pid)) {
c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("The process is no longer running"))
return
}
go c.monitorProcessDetails(pid, stream, notification)
}
func (c *Client) handleActionStopMonitorProcess(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
pid, err := strconv.Atoi(notification.Data)
if err != nil {
log.Error("parsing PID to stop monitor")
c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("Error stopping monitor: ", notification.Data))
return
}
stopMonitoringProcess <- pid
c.sendNotificationReply(stream, notification.Id, "", nil)
}
func (c *Client) handleNotification(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
switch {
case notification.Type == protocol.Action_MONITOR_PROCESS:
c.handleActionMonitorProcess(stream, notification)
case notification.Type == protocol.Action_STOP_MONITOR_PROCESS:
c.handleActionStopMonitorProcess(stream, notification)
case notification.Type == protocol.Action_CHANGE_CONFIG:
c.handleActionChangeConfig(stream, notification)
case notification.Type == protocol.Action_LOAD_FIREWALL:
log.Info("[notification] starting firewall")
firewall.Init(nil)
c.sendNotificationReply(stream, notification.Id, nil)
c.sendNotificationReply(stream, notification.Id, "", nil)
case notification.Type == protocol.Action_UNLOAD_FIREWALL:
log.Info("[notification] stopping firewall")
firewall.Stop(nil)
c.sendNotificationReply(stream, notification.Id, nil)
c.sendNotificationReply(stream, notification.Id, "", nil)
// ENABLE_RULE just replaces the rule on disk
case notification.Type == protocol.Action_ENABLE_RULE:
@ -160,15 +223,18 @@ func (c *Client) handleNotification(stream protocol.UI_NotificationsClient, noti
}
}
func (c *Client) sendNotificationReply(stream protocol.UI_NotificationsClient, nID uint64, err error) {
reply := NewReply(nID, protocol.NotificationReplyCode_OK, "")
func (c *Client) sendNotificationReply(stream protocol.UI_NotificationsClient, nID uint64, data string, err error) error {
reply := NewReply(nID, protocol.NotificationReplyCode_OK, data)
if err != nil {
reply.Code = protocol.NotificationReplyCode_ERROR
reply.Data = fmt.Sprint(err)
}
if err := stream.Send(reply); err != nil {
log.Error("Error replying to notification:", err, reply.Id)
return err
}
return nil
}
// Subscribe opens a connection with the server (UI), to start

View file

@ -27,42 +27,48 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Action int32
const (
Action_NONE Action = 0
Action_LOAD_FIREWALL Action = 1
Action_UNLOAD_FIREWALL Action = 2
Action_CHANGE_CONFIG Action = 3
Action_ENABLE_RULE Action = 4
Action_DISABLE_RULE Action = 5
Action_DELETE_RULE Action = 6
Action_CHANGE_RULE Action = 7
Action_LOG_LEVEL Action = 8
Action_STOP Action = 9
Action_NONE Action = 0
Action_LOAD_FIREWALL Action = 1
Action_UNLOAD_FIREWALL Action = 2
Action_CHANGE_CONFIG Action = 3
Action_ENABLE_RULE Action = 4
Action_DISABLE_RULE Action = 5
Action_DELETE_RULE Action = 6
Action_CHANGE_RULE Action = 7
Action_LOG_LEVEL Action = 8
Action_STOP Action = 9
Action_MONITOR_PROCESS Action = 10
Action_STOP_MONITOR_PROCESS Action = 11
)
var Action_name = map[int32]string{
0: "NONE",
1: "LOAD_FIREWALL",
2: "UNLOAD_FIREWALL",
3: "CHANGE_CONFIG",
4: "ENABLE_RULE",
5: "DISABLE_RULE",
6: "DELETE_RULE",
7: "CHANGE_RULE",
8: "LOG_LEVEL",
9: "STOP",
0: "NONE",
1: "LOAD_FIREWALL",
2: "UNLOAD_FIREWALL",
3: "CHANGE_CONFIG",
4: "ENABLE_RULE",
5: "DISABLE_RULE",
6: "DELETE_RULE",
7: "CHANGE_RULE",
8: "LOG_LEVEL",
9: "STOP",
10: "MONITOR_PROCESS",
11: "STOP_MONITOR_PROCESS",
}
var Action_value = map[string]int32{
"NONE": 0,
"LOAD_FIREWALL": 1,
"UNLOAD_FIREWALL": 2,
"CHANGE_CONFIG": 3,
"ENABLE_RULE": 4,
"DISABLE_RULE": 5,
"DELETE_RULE": 6,
"CHANGE_RULE": 7,
"LOG_LEVEL": 8,
"STOP": 9,
"NONE": 0,
"LOAD_FIREWALL": 1,
"UNLOAD_FIREWALL": 2,
"CHANGE_CONFIG": 3,
"ENABLE_RULE": 4,
"DISABLE_RULE": 5,
"DELETE_RULE": 6,
"CHANGE_RULE": 7,
"LOG_LEVEL": 8,
"STOP": 9,
"MONITOR_PROCESS": 10,
"STOP_MONITOR_PROCESS": 11,
}
func (x Action) String() string {
@ -711,88 +717,90 @@ func init() {
}
var fileDescriptor_63867a62624c1283 = []byte{
// 1292 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xdf, 0x73, 0xd3, 0xc6,
0x13, 0x8f, 0x1d, 0x5b, 0x96, 0xd6, 0x76, 0xe2, 0x1c, 0x84, 0xaf, 0xbe, 0xa6, 0x85, 0x60, 0x68,
0x9b, 0xc9, 0x74, 0x32, 0x6d, 0x60, 0x3a, 0xc0, 0xc0, 0x74, 0x8c, 0x11, 0xe0, 0xe2, 0xda, 0x99,
0x0b, 0xa1, 0x8f, 0x1a, 0x49, 0x77, 0x38, 0x57, 0x84, 0xa4, 0xea, 0x4e, 0xa6, 0xfe, 0x9b, 0xfa,
0x0f, 0xf4, 0xa9, 0xaf, 0x7d, 0xe9, 0x63, 0xff, 0x98, 0x3e, 0x76, 0xee, 0x4e, 0xb2, 0x94, 0x5f,
0x74, 0xf2, 0x64, 0xed, 0x67, 0xf7, 0xb3, 0xb7, 0xbb, 0xb7, 0xb7, 0x6b, 0x30, 0x33, 0xb6, 0x9f,
0xa4, 0xb1, 0x88, 0x91, 0xa9, 0x7e, 0x82, 0x38, 0x1c, 0x64, 0xd0, 0x74, 0x16, 0x34, 0x12, 0x08,
0x41, 0x43, 0xb0, 0x0f, 0xd4, 0xae, 0xed, 0xd4, 0x76, 0x2d, 0xac, 0xbe, 0xd1, 0x03, 0x80, 0x20,
0x8e, 0x22, 0x1a, 0x08, 0x16, 0x47, 0x76, 0x7d, 0xa7, 0xb6, 0xdb, 0x3e, 0xb8, 0xbe, 0x5f, 0x70,
0xf7, 0x47, 0x2b, 0x1d, 0xae, 0xd8, 0xa1, 0x01, 0x34, 0xd2, 0x2c, 0xa4, 0xf6, 0xba, 0xb2, 0xdf,
0x28, 0xed, 0x71, 0x16, 0x52, 0xac, 0x74, 0x83, 0x3f, 0x4d, 0x80, 0x23, 0xe1, 0x09, 0xc6, 0x05,
0x0b, 0x38, 0xfa, 0x02, 0x36, 0x88, 0x47, 0x3f, 0xc4, 0x91, 0xbb, 0xa0, 0x29, 0x97, 0x87, 0xe9,
0x30, 0xba, 0x1a, 0x7d, 0xab, 0x41, 0x74, 0x1d, 0x9a, 0x92, 0xcd, 0x55, 0x28, 0x0d, 0xac, 0x05,
0x74, 0x03, 0x8c, 0x2c, 0x51, 0xb1, 0xaf, 0x2b, 0x38, 0x97, 0xd0, 0x5d, 0xe8, 0x92, 0x88, 0xbb,
0x29, 0xe5, 0x49, 0x1c, 0x71, 0xca, 0xed, 0x86, 0x52, 0x77, 0x48, 0xc4, 0x71, 0x81, 0xa1, 0x1d,
0x68, 0x97, 0xa1, 0x73, 0xbb, 0xa9, 0x4c, 0xaa, 0x10, 0xb2, 0xa1, 0xc5, 0xe6, 0x51, 0x9c, 0x52,
0x62, 0x1b, 0x4a, 0x5b, 0x88, 0xa8, 0x0f, 0xa6, 0x17, 0x04, 0x34, 0x11, 0x94, 0xd8, 0x2d, 0xa5,
0x5a, 0xc9, 0x92, 0x45, 0xd2, 0x38, 0x49, 0x28, 0xb1, 0x4d, 0xcd, 0xca, 0x45, 0x74, 0x13, 0x2c,
0x19, 0xb7, 0x7b, 0xc2, 0x04, 0xb7, 0x2d, 0x4d, 0x93, 0xc0, 0x2b, 0x26, 0x38, 0xba, 0x0d, 0x6d,
0xa5, 0xfc, 0xc0, 0xb8, 0x8c, 0x18, 0x94, 0x1a, 0x24, 0xf4, 0xa3, 0x42, 0xd0, 0x13, 0x30, 0xfd,
0xa5, 0xab, 0x4a, 0x6a, 0xb7, 0x77, 0xd6, 0x77, 0xdb, 0x07, 0x77, 0xca, 0x02, 0x97, 0x15, 0xdd,
0x7f, 0xb6, 0x3c, 0x94, 0xa8, 0x13, 0x89, 0x74, 0x89, 0x5b, 0xbe, 0x96, 0xd0, 0x33, 0x00, 0x7f,
0xe9, 0x7a, 0x84, 0xa4, 0x94, 0x73, 0xbb, 0xa3, 0xf8, 0x77, 0x2f, 0xe1, 0x0f, 0xb5, 0x95, 0xf6,
0x60, 0xf9, 0x85, 0x8c, 0x1e, 0x41, 0xcb, 0x5f, 0xba, 0x27, 0x31, 0x17, 0x76, 0x57, 0x39, 0xd8,
0xb9, 0xc4, 0xc1, 0xab, 0x98, 0x0b, 0xcd, 0x36, 0x7c, 0x25, 0xe4, 0xd4, 0x24, 0x4e, 0x85, 0xbd,
0xf1, 0x49, 0xea, 0x61, 0x9c, 0x96, 0x54, 0x29, 0xa0, 0xef, 0xc0, 0xf0, 0x97, 0x6e, 0xc6, 0x88,
0xbd, 0xa9, 0x98, 0xb7, 0x2f, 0x61, 0x1e, 0x33, 0xa2, 0x89, 0x4d, 0x5f, 0x7e, 0xa3, 0xd7, 0xd0,
0xf5, 0x97, 0x2e, 0xfd, 0x95, 0x06, 0x99, 0xf0, 0xfc, 0x90, 0xda, 0x3d, 0x45, 0xff, 0xf2, 0x12,
0xba, 0xb3, 0x32, 0xd4, 0x5e, 0x3a, 0x7e, 0x05, 0x42, 0x5f, 0x81, 0x41, 0xe5, 0x63, 0xe1, 0xf6,
0x96, 0xf2, 0xb2, 0x59, 0x7a, 0x51, 0x8f, 0x08, 0xe7, 0xea, 0xfe, 0x63, 0xe8, 0x54, 0x2f, 0x00,
0xf5, 0x60, 0xfd, 0x3d, 0x5d, 0xe6, 0x4d, 0x2d, 0x3f, 0x65, 0x2b, 0x2f, 0xbc, 0x30, 0xa3, 0x45,
0x2b, 0x2b, 0xe1, 0x71, 0xfd, 0x61, 0xad, 0xff, 0x04, 0x36, 0x4e, 0x17, 0xff, 0x4a, 0xec, 0x47,
0xd0, 0xae, 0x54, 0xfe, 0xea, 0xd4, 0x55, 0xe5, 0xaf, 0x44, 0x7d, 0x08, 0x50, 0x96, 0xfe, 0x4a,
0xcc, 0xef, 0x61, 0xeb, 0x5c, 0xd5, 0xaf, 0xe2, 0x60, 0x30, 0x86, 0xf6, 0x21, 0x8b, 0xe6, 0x98,
0xfe, 0x92, 0x51, 0x2e, 0xd0, 0x06, 0xd4, 0x19, 0x51, 0xcc, 0x06, 0xae, 0x33, 0x82, 0xf6, 0xa0,
0xc9, 0x85, 0x27, 0xf8, 0xf9, 0xe9, 0x55, 0xde, 0x3b, 0xd6, 0x26, 0x83, 0x9b, 0x60, 0x69, 0x57,
0x49, 0xb8, 0x3c, 0xeb, 0x68, 0xf0, 0xd7, 0x3a, 0x40, 0x39, 0xf0, 0xe4, 0xdb, 0x2f, 0x3c, 0xe5,
0x71, 0xae, 0x64, 0xb4, 0x0d, 0x06, 0x4f, 0x03, 0x97, 0x25, 0xea, 0x50, 0x0b, 0x37, 0x79, 0x1a,
0x8c, 0x13, 0xf4, 0x7f, 0x30, 0x25, 0xac, 0xda, 0x5f, 0x4e, 0xaa, 0x2e, 0x6e, 0xf1, 0x34, 0x50,
0xdd, 0xbd, 0x0d, 0x06, 0xe1, 0x42, 0x32, 0x1a, 0x9a, 0x41, 0xb8, 0xd0, 0x0c, 0x09, 0xab, 0xb7,
0xd6, 0x54, 0x8a, 0x16, 0xe1, 0x42, 0x3d, 0xa5, 0x5c, 0xa5, 0x9c, 0x19, 0xda, 0x19, 0xe1, 0x42,
0x39, 0xfb, 0x1f, 0xb4, 0x32, 0x4e, 0x53, 0x97, 0xe9, 0xa9, 0xd4, 0xc5, 0x86, 0x14, 0xc7, 0x04,
0x7d, 0x0e, 0x90, 0xa4, 0x71, 0x40, 0x39, 0x97, 0x3a, 0x53, 0xe9, 0xac, 0x1c, 0x19, 0x13, 0x74,
0x07, 0x3a, 0x85, 0x3a, 0xf1, 0xc4, 0x89, 0x9a, 0x4d, 0x16, 0x6e, 0xe7, 0xd8, 0xa1, 0x27, 0x4e,
0xe4, 0x78, 0x2a, 0x4c, 0x82, 0x8f, 0x44, 0x8d, 0x27, 0x0b, 0x17, 0x4e, 0x47, 0x1f, 0x4f, 0xf9,
0xf0, 0xd2, 0x39, 0x57, 0x23, 0xaa, 0xf4, 0x31, 0x4c, 0xe7, 0x1c, 0x39, 0xa5, 0x0f, 0x1a, 0x2d,
0xf2, 0x21, 0x74, 0xef, 0xa2, 0xad, 0xb2, 0x7f, 0xa8, 0xed, 0x9c, 0x68, 0xa1, 0x5f, 0x63, 0x71,
0x92, 0x13, 0x2d, 0xfa, 0x4f, 0x61, 0xf3, 0x8c, 0xfa, 0xbf, 0xda, 0xc6, 0xaa, 0xb6, 0xcd, 0xcf,
0x60, 0xce, 0x12, 0x9a, 0x7a, 0x22, 0x4e, 0xd5, 0xea, 0x5b, 0x26, 0xe5, 0xea, 0x5b, 0x26, 0x54,
0xce, 0xef, 0x58, 0xea, 0x23, 0x92, 0x73, 0x0b, 0x51, 0x5a, 0x13, 0x4f, 0x78, 0xea, 0x0a, 0x2d,
0xac, 0xbe, 0xd1, 0x67, 0x60, 0x71, 0x1a, 0x71, 0x26, 0xd8, 0x82, 0xaa, 0x2b, 0x34, 0x71, 0x09,
0x0c, 0x7e, 0xaf, 0x41, 0x43, 0xee, 0x3e, 0x49, 0x8d, 0xbc, 0x72, 0xc7, 0xca, 0x6f, 0x79, 0x10,
0x8d, 0x64, 0xeb, 0xeb, 0x83, 0x4c, 0x5c, 0x88, 0xe8, 0x96, 0xbc, 0x2e, 0x1a, 0x50, 0x42, 0xa3,
0x40, 0xef, 0x36, 0x13, 0x57, 0x10, 0xb9, 0xf7, 0x3c, 0xbd, 0x99, 0x75, 0xd3, 0xe4, 0x92, 0x6c,
0x4d, 0x92, 0xa5, 0x9e, 0xd2, 0xe8, 0xae, 0x59, 0xc9, 0x68, 0x1f, 0xcc, 0x38, 0x4f, 0x5b, 0xb5,
0x4d, 0xfb, 0x00, 0x95, 0x95, 0x2f, 0x0a, 0x82, 0x57, 0x36, 0x83, 0xbf, 0x6b, 0xd0, 0x19, 0x85,
0x8c, 0x46, 0x62, 0x14, 0x47, 0xef, 0xd8, 0xfc, 0xdc, 0xfb, 0x2a, 0x52, 0xaa, 0x9f, 0x4e, 0xa9,
0x58, 0xe3, 0xba, 0x48, 0x85, 0x88, 0xbe, 0x86, 0x2d, 0xc6, 0x5f, 0xb0, 0x94, 0x7e, 0xf4, 0xc2,
0x10, 0x67, 0x51, 0xc4, 0xa2, 0x79, 0x5e, 0xaf, 0xf3, 0x0a, 0x99, 0x60, 0xa0, 0x4e, 0xcd, 0xd3,
0xc8, 0x25, 0x99, 0x60, 0x18, 0xcf, 0x27, 0x74, 0x41, 0xc3, 0xbc, 0xf7, 0x57, 0x32, 0xba, 0x57,
0xfc, 0x45, 0x68, 0xa9, 0xbe, 0x3a, 0xfb, 0xef, 0x43, 0x2b, 0x07, 0x7f, 0xd4, 0xa0, 0x33, 0x8d,
0x05, 0x7b, 0xc7, 0x02, 0x5d, 0x97, 0xb3, 0x69, 0xdd, 0x02, 0x08, 0x54, 0xda, 0xd3, 0x32, 0xb9,
0x0a, 0x22, 0xf5, 0x9c, 0xa6, 0x0b, 0x9a, 0x2a, 0xbd, 0xce, 0xb2, 0x82, 0xa0, 0x7b, 0x79, 0x4b,
0xc9, 0xdc, 0x36, 0x0e, 0x7a, 0x65, 0x14, 0x43, 0xfd, 0x7f, 0x49, 0x37, 0x59, 0xd1, 0x4a, 0xcd,
0x4a, 0x2b, 0xad, 0x12, 0x30, 0x3e, 0x95, 0x40, 0x08, 0x5b, 0xd5, 0xf8, 0x2f, 0x1c, 0x59, 0xe8,
0x3e, 0x34, 0x82, 0x98, 0xe8, 0xf0, 0x37, 0xaa, 0x1b, 0xf3, 0x1c, 0x75, 0x14, 0x13, 0x8a, 0x95,
0xf1, 0x45, 0xed, 0xbd, 0xf7, 0x5b, 0x0d, 0x0c, 0x1d, 0x38, 0x32, 0xa1, 0x31, 0x9d, 0x4d, 0x9d,
0xde, 0x1a, 0xda, 0x82, 0xee, 0x64, 0x36, 0x7c, 0xee, 0xbe, 0x18, 0x63, 0xe7, 0xa7, 0xe1, 0x64,
0xd2, 0xab, 0xa1, 0x6b, 0xb0, 0x79, 0x3c, 0x3d, 0x0d, 0xd6, 0xa5, 0xdd, 0xe8, 0xd5, 0x70, 0xfa,
0xd2, 0x71, 0x47, 0xb3, 0xe9, 0x8b, 0xf1, 0xcb, 0xde, 0x3a, 0xda, 0x84, 0xb6, 0x33, 0x1d, 0x3e,
0x9b, 0x38, 0x2e, 0x3e, 0x9e, 0x38, 0xbd, 0x06, 0xea, 0x41, 0xe7, 0xf9, 0xf8, 0xa8, 0x44, 0x9a,
0xd2, 0xe4, 0xb9, 0x33, 0x71, 0xde, 0xe4, 0x80, 0x21, 0x81, 0xdc, 0x8d, 0x02, 0x5a, 0xa8, 0x0b,
0xd6, 0x64, 0xf6, 0xd2, 0x9d, 0x38, 0x6f, 0x9d, 0x49, 0xcf, 0x94, 0x81, 0x1d, 0xbd, 0x99, 0x1d,
0xf6, 0xac, 0xbd, 0x3d, 0xd8, 0xbe, 0x30, 0x41, 0x64, 0x40, 0x7d, 0xf6, 0xba, 0xb7, 0x86, 0x2c,
0x68, 0x3a, 0x18, 0xcf, 0x70, 0xaf, 0x76, 0xf0, 0x4f, 0x0d, 0xea, 0xc7, 0x63, 0xf4, 0x00, 0x1a,
0x72, 0xf2, 0xa3, 0xed, 0xb2, 0x46, 0x95, 0xa5, 0xd2, 0xbf, 0x76, 0x16, 0x4e, 0xc2, 0xe5, 0x60,
0x0d, 0x7d, 0x0b, 0xad, 0x21, 0x7f, 0xaf, 0x5e, 0xf6, 0x85, 0xff, 0x8a, 0xfb, 0x67, 0x2e, 0x6f,
0xb0, 0x86, 0x9e, 0x82, 0x75, 0x94, 0xf9, 0x3c, 0x48, 0x99, 0x4f, 0xd1, 0x8d, 0x0a, 0xa9, 0xf2,
0xc6, 0xfa, 0x97, 0xe0, 0x83, 0x35, 0xf4, 0x03, 0x74, 0xab, 0xa9, 0x71, 0x74, 0xf3, 0x13, 0x97,
0x5a, 0xf5, 0x53, 0x55, 0x0e, 0xd6, 0x76, 0x6b, 0xdf, 0xd4, 0x7c, 0x43, 0x29, 0xef, 0xff, 0x1b,
0x00, 0x00, 0xff, 0xff, 0x75, 0xf5, 0x8c, 0x39, 0x16, 0x0c, 0x00, 0x00,
// 1315 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xdd, 0x73, 0xd3, 0x46,
0x10, 0x8f, 0x1d, 0x5b, 0x96, 0xd6, 0x1f, 0x71, 0x16, 0x42, 0x55, 0xd3, 0x42, 0x30, 0xb4, 0xcd,
0x64, 0x3a, 0x99, 0x36, 0x30, 0x1d, 0x60, 0x60, 0x3a, 0xc6, 0x08, 0x70, 0x31, 0xb6, 0xe7, 0x42,
0xe8, 0xa3, 0x46, 0x1f, 0x87, 0xa3, 0x62, 0x24, 0x55, 0x77, 0x36, 0xf5, 0x7f, 0xd6, 0xa7, 0xbe,
0xf6, 0xa5, 0x8f, 0x7d, 0xea, 0x5f, 0xd2, 0xc7, 0xce, 0xdd, 0x49, 0x96, 0xe2, 0x7c, 0x74, 0xf2,
0x64, 0xed, 0xef, 0xb7, 0xbf, 0xd5, 0xee, 0x6a, 0xef, 0xd6, 0xa0, 0xcf, 0x83, 0x83, 0x38, 0x89,
0x78, 0x84, 0xba, 0xfc, 0xf1, 0xa2, 0x59, 0x77, 0x0e, 0x55, 0x6b, 0x41, 0x43, 0x8e, 0x08, 0x15,
0x1e, 0x7c, 0xa4, 0x66, 0x69, 0xb7, 0xb4, 0x67, 0x10, 0xf9, 0x8c, 0x0f, 0x00, 0xbc, 0x28, 0x0c,
0xa9, 0xc7, 0x83, 0x28, 0x34, 0xcb, 0xbb, 0xa5, 0xbd, 0xfa, 0xe1, 0xf5, 0x83, 0x4c, 0x7b, 0xd0,
0x5f, 0x71, 0xa4, 0xe0, 0x87, 0x5d, 0xa8, 0x24, 0xf3, 0x19, 0x35, 0x37, 0xa5, 0x7f, 0x2b, 0xf7,
0x27, 0xf3, 0x19, 0x25, 0x92, 0xeb, 0xfe, 0xa9, 0x03, 0x1c, 0x71, 0x87, 0x07, 0x8c, 0x07, 0x1e,
0xc3, 0xaf, 0xa0, 0xe5, 0x3b, 0xf4, 0x63, 0x14, 0xda, 0x0b, 0x9a, 0x30, 0xf1, 0x32, 0x95, 0x46,
0x53, 0xa1, 0xef, 0x14, 0x88, 0xd7, 0xa1, 0x2a, 0xd4, 0x4c, 0xa6, 0x52, 0x21, 0xca, 0xc0, 0x1b,
0xa0, 0xcd, 0x63, 0x99, 0xfb, 0xa6, 0x84, 0x53, 0x0b, 0xef, 0x42, 0xd3, 0x0f, 0x99, 0x9d, 0x50,
0x16, 0x47, 0x21, 0xa3, 0xcc, 0xac, 0x48, 0xba, 0xe1, 0x87, 0x8c, 0x64, 0x18, 0xee, 0x42, 0x3d,
0x4f, 0x9d, 0x99, 0x55, 0xe9, 0x52, 0x84, 0xd0, 0x84, 0x5a, 0x30, 0x0d, 0xa3, 0x84, 0xfa, 0xa6,
0x26, 0xd9, 0xcc, 0xc4, 0x0e, 0xe8, 0x8e, 0xe7, 0xd1, 0x98, 0x53, 0xdf, 0xac, 0x49, 0x6a, 0x65,
0x0b, 0x95, 0x9f, 0x44, 0x71, 0x4c, 0x7d, 0x53, 0x57, 0xaa, 0xd4, 0xc4, 0x9b, 0x60, 0x88, 0xbc,
0xed, 0x93, 0x80, 0x33, 0xd3, 0x50, 0x32, 0x01, 0xbc, 0x0a, 0x38, 0xc3, 0xdb, 0x50, 0x97, 0xe4,
0xc7, 0x80, 0x89, 0x8c, 0x41, 0xd2, 0x20, 0xa0, 0x37, 0x12, 0xc1, 0x27, 0xa0, 0xbb, 0x4b, 0x5b,
0xb6, 0xd4, 0xac, 0xef, 0x6e, 0xee, 0xd5, 0x0f, 0xef, 0xe4, 0x0d, 0xce, 0x3b, 0x7a, 0xf0, 0x6c,
0x39, 0x11, 0xa8, 0x15, 0xf2, 0x64, 0x49, 0x6a, 0xae, 0xb2, 0xf0, 0x19, 0x80, 0xbb, 0xb4, 0x1d,
0xdf, 0x4f, 0x28, 0x63, 0x66, 0x43, 0xea, 0xef, 0x5e, 0xa0, 0xef, 0x29, 0x2f, 0x15, 0xc1, 0x70,
0x33, 0x1b, 0x1f, 0x41, 0xcd, 0x5d, 0xda, 0x27, 0x11, 0xe3, 0x66, 0x53, 0x06, 0xd8, 0xbd, 0x20,
0xc0, 0xab, 0x88, 0x71, 0xa5, 0xd6, 0x5c, 0x69, 0xa4, 0xd2, 0x38, 0x4a, 0xb8, 0xd9, 0xba, 0x54,
0x3a, 0x89, 0x92, 0x5c, 0x2a, 0x0c, 0xfc, 0x01, 0x34, 0x77, 0x69, 0xcf, 0x03, 0xdf, 0xdc, 0x92,
0xca, 0xdb, 0x17, 0x28, 0x8f, 0x03, 0x5f, 0x09, 0xab, 0xae, 0x78, 0xc6, 0xd7, 0xd0, 0x74, 0x97,
0x36, 0xfd, 0x8d, 0x7a, 0x73, 0xee, 0xb8, 0x33, 0x6a, 0xb6, 0xa5, 0xfc, 0xeb, 0x0b, 0xe4, 0xd6,
0xca, 0x51, 0x45, 0x69, 0xb8, 0x05, 0x08, 0xbf, 0x01, 0x8d, 0x8a, 0xc3, 0xc2, 0xcc, 0x6d, 0x19,
0x65, 0x2b, 0x8f, 0x22, 0x0f, 0x11, 0x49, 0xe9, 0xce, 0x63, 0x68, 0x14, 0x3f, 0x00, 0xb6, 0x61,
0xf3, 0x03, 0x5d, 0xa6, 0x43, 0x2d, 0x1e, 0xc5, 0x28, 0x2f, 0x9c, 0xd9, 0x9c, 0x66, 0xa3, 0x2c,
0x8d, 0xc7, 0xe5, 0x87, 0xa5, 0xce, 0x13, 0x68, 0x9d, 0x6e, 0xfe, 0x95, 0xd4, 0x8f, 0xa0, 0x5e,
0xe8, 0xfc, 0xd5, 0xa5, 0xab, 0xce, 0x5f, 0x49, 0xfa, 0x10, 0x20, 0x6f, 0xfd, 0x95, 0x94, 0x3f,
0xc2, 0xf6, 0x99, 0xae, 0x5f, 0x25, 0x40, 0x77, 0x00, 0xf5, 0x49, 0x10, 0x4e, 0x09, 0xfd, 0x75,
0x4e, 0x19, 0xc7, 0x16, 0x94, 0x03, 0x5f, 0x2a, 0x2b, 0xa4, 0x1c, 0xf8, 0xb8, 0x0f, 0x55, 0xc6,
0x1d, 0xce, 0xce, 0xde, 0x5e, 0xf9, 0x77, 0x27, 0xca, 0xa5, 0x7b, 0x13, 0x0c, 0x15, 0x2a, 0x9e,
0x2d, 0xd7, 0x03, 0x75, 0xff, 0xda, 0x04, 0xc8, 0x2f, 0x3c, 0x71, 0xf6, 0xb3, 0x48, 0x69, 0x9e,
0x2b, 0x1b, 0x77, 0x40, 0x63, 0x89, 0x67, 0x07, 0xb1, 0x7c, 0xa9, 0x41, 0xaa, 0x2c, 0xf1, 0x06,
0x31, 0x7e, 0x0e, 0xba, 0x80, 0xe5, 0xf8, 0x8b, 0x9b, 0xaa, 0x49, 0x6a, 0x2c, 0xf1, 0xe4, 0x74,
0xef, 0x80, 0xe6, 0x33, 0x2e, 0x14, 0x15, 0xa5, 0xf0, 0x19, 0x57, 0x0a, 0x01, 0xcb, 0xb3, 0x56,
0x95, 0x44, 0xcd, 0x67, 0x5c, 0x1e, 0xa5, 0x94, 0x92, 0xc1, 0x34, 0x15, 0xcc, 0x67, 0x5c, 0x06,
0xfb, 0x0c, 0x6a, 0x73, 0x46, 0x13, 0x3b, 0x50, 0xb7, 0x52, 0x93, 0x68, 0xc2, 0x1c, 0xf8, 0xf8,
0x25, 0x40, 0x9c, 0x44, 0x1e, 0x65, 0x4c, 0x70, 0xba, 0xe4, 0x8c, 0x14, 0x19, 0xf8, 0x78, 0x07,
0x1a, 0x19, 0x1d, 0x3b, 0xfc, 0x44, 0xde, 0x4d, 0x06, 0xa9, 0xa7, 0xd8, 0xc4, 0xe1, 0x27, 0xe2,
0x7a, 0xca, 0x5c, 0xbc, 0x4f, 0xbe, 0xbc, 0x9e, 0x0c, 0x92, 0x05, 0xed, 0x7f, 0x3a, 0x15, 0xc3,
0x49, 0xa6, 0x4c, 0x5e, 0x51, 0x79, 0x8c, 0x5e, 0x32, 0x65, 0x68, 0xe5, 0x31, 0x68, 0xb8, 0x48,
0x2f, 0xa1, 0x7b, 0xe7, 0x6d, 0x95, 0x83, 0x89, 0xf2, 0xb3, 0xc2, 0x85, 0x3a, 0x8d, 0xd9, 0x9b,
0xac, 0x70, 0xd1, 0x79, 0x0a, 0x5b, 0x6b, 0xf4, 0xff, 0x8d, 0x8d, 0x51, 0x1c, 0x9b, 0x5f, 0x40,
0x1f, 0xc7, 0x34, 0x71, 0x78, 0x94, 0xc8, 0xd5, 0xb7, 0x8c, 0xf3, 0xd5, 0xb7, 0x8c, 0xa9, 0xb8,
0xbf, 0x23, 0xc1, 0x87, 0x7e, 0xaa, 0xcd, 0x4c, 0xe1, 0xed, 0x3b, 0xdc, 0x91, 0x9f, 0xd0, 0x20,
0xf2, 0x19, 0xbf, 0x00, 0x83, 0xd1, 0x90, 0x05, 0x3c, 0x58, 0x50, 0xf9, 0x09, 0x75, 0x92, 0x03,
0xdd, 0xdf, 0x4b, 0x50, 0x11, 0xbb, 0x4f, 0x48, 0x43, 0x27, 0xdf, 0xb1, 0xe2, 0x59, 0xbc, 0x88,
0x86, 0x62, 0xf4, 0xd5, 0x8b, 0x74, 0x92, 0x99, 0x78, 0x4b, 0x7c, 0x2e, 0xea, 0x51, 0x9f, 0x86,
0x9e, 0xda, 0x6d, 0x3a, 0x29, 0x20, 0x62, 0xef, 0x39, 0x6a, 0x33, 0xab, 0xa1, 0x49, 0x2d, 0x31,
0x9a, 0xfe, 0x3c, 0x71, 0x24, 0xa3, 0xa6, 0x66, 0x65, 0xe3, 0x01, 0xe8, 0x51, 0x5a, 0xb6, 0x1c,
0x9b, 0xfa, 0x21, 0xe6, 0x9d, 0xcf, 0x1a, 0x42, 0x56, 0x3e, 0xdd, 0xbf, 0x4b, 0xd0, 0xe8, 0xcf,
0x02, 0x1a, 0xf2, 0x7e, 0x14, 0xbe, 0x0f, 0xa6, 0x67, 0xce, 0x57, 0x56, 0x52, 0xf9, 0x74, 0x49,
0xd9, 0x1a, 0x57, 0x4d, 0xca, 0x4c, 0xfc, 0x16, 0xb6, 0x03, 0xf6, 0x22, 0x48, 0xe8, 0x27, 0x67,
0x36, 0x23, 0xf3, 0x30, 0x0c, 0xc2, 0x69, 0xda, 0xaf, 0xb3, 0x84, 0x28, 0xd0, 0x93, 0x6f, 0x4d,
0xcb, 0x48, 0x2d, 0x51, 0xe0, 0x2c, 0x9a, 0x0e, 0xe9, 0x82, 0xce, 0xd2, 0xd9, 0x5f, 0xd9, 0x78,
0x2f, 0xfb, 0x8b, 0x50, 0x93, 0x73, 0xb5, 0xfe, 0xef, 0x43, 0x91, 0xdd, 0x3f, 0x4a, 0xd0, 0x18,
0x45, 0x3c, 0x78, 0x1f, 0x78, 0xaa, 0x2f, 0xeb, 0x65, 0xdd, 0x02, 0xf0, 0x64, 0xd9, 0xa3, 0xbc,
0xb8, 0x02, 0x22, 0x78, 0x46, 0x93, 0x05, 0x4d, 0x24, 0xaf, 0xaa, 0x2c, 0x20, 0x78, 0x2f, 0x1d,
0x29, 0x51, 0x5b, 0xeb, 0xb0, 0x9d, 0x67, 0xd1, 0x53, 0xff, 0x97, 0xd4, 0x90, 0x65, 0xa3, 0x54,
0x2d, 0x8c, 0xd2, 0xaa, 0x00, 0xed, 0xb2, 0x02, 0x66, 0xb0, 0x5d, 0xcc, 0xff, 0xdc, 0x2b, 0x0b,
0xef, 0x43, 0xc5, 0x8b, 0x7c, 0x95, 0x7e, 0xab, 0xb8, 0x31, 0xcf, 0x48, 0xfb, 0x91, 0x4f, 0x89,
0x74, 0x3e, 0x6f, 0xbc, 0xf7, 0xff, 0x29, 0x81, 0xa6, 0x12, 0x47, 0x1d, 0x2a, 0xa3, 0xf1, 0xc8,
0x6a, 0x6f, 0xe0, 0x36, 0x34, 0x87, 0xe3, 0xde, 0x73, 0xfb, 0xc5, 0x80, 0x58, 0x3f, 0xf7, 0x86,
0xc3, 0x76, 0x09, 0xaf, 0xc1, 0xd6, 0xf1, 0xe8, 0x34, 0x58, 0x16, 0x7e, 0xfd, 0x57, 0xbd, 0xd1,
0x4b, 0xcb, 0xee, 0x8f, 0x47, 0x2f, 0x06, 0x2f, 0xdb, 0x9b, 0xb8, 0x05, 0x75, 0x6b, 0xd4, 0x7b,
0x36, 0xb4, 0x6c, 0x72, 0x3c, 0xb4, 0xda, 0x15, 0x6c, 0x43, 0xe3, 0xf9, 0xe0, 0x28, 0x47, 0xaa,
0xc2, 0xe5, 0xb9, 0x35, 0xb4, 0xde, 0xa6, 0x80, 0x26, 0x80, 0x34, 0x8c, 0x04, 0x6a, 0xd8, 0x04,
0x63, 0x38, 0x7e, 0x69, 0x0f, 0xad, 0x77, 0xd6, 0xb0, 0xad, 0x8b, 0xc4, 0x8e, 0xde, 0x8e, 0x27,
0x6d, 0x43, 0x64, 0xf1, 0x66, 0x3c, 0x1a, 0xbc, 0x1d, 0x13, 0x7b, 0x42, 0xc6, 0x7d, 0xeb, 0xe8,
0xa8, 0x0d, 0x68, 0xc2, 0x75, 0x41, 0xdb, 0xeb, 0x4c, 0x7d, 0x7f, 0x1f, 0x76, 0xce, 0xed, 0x07,
0x6a, 0x50, 0x1e, 0xbf, 0x6e, 0x6f, 0xa0, 0x01, 0x55, 0x8b, 0x90, 0x31, 0x69, 0x97, 0x0e, 0xff,
0x2d, 0x41, 0xf9, 0x78, 0x80, 0x0f, 0xa0, 0x22, 0x16, 0x05, 0xee, 0xe4, 0x2d, 0x2d, 0xec, 0xa0,
0xce, 0xb5, 0x75, 0x38, 0x9e, 0x2d, 0xbb, 0x1b, 0xf8, 0x3d, 0xd4, 0x7a, 0xec, 0x83, 0xbc, 0x08,
0xce, 0xfd, 0x13, 0xdd, 0x59, 0xfb, 0xd6, 0xdd, 0x0d, 0x7c, 0x0a, 0xc6, 0xd1, 0xdc, 0x65, 0x5e,
0x12, 0xb8, 0x14, 0x6f, 0x14, 0x44, 0x85, 0x23, 0xd9, 0xb9, 0x00, 0xef, 0x6e, 0xe0, 0x4f, 0xd0,
0x2c, 0x96, 0xc6, 0xf0, 0xe6, 0x25, 0x33, 0x50, 0x8c, 0x53, 0x24, 0xbb, 0x1b, 0x7b, 0xa5, 0xef,
0x4a, 0xae, 0x26, 0xc9, 0xfb, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xcb, 0xf1, 0x95, 0x55, 0x45,
0x0c, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.

View file

@ -86,6 +86,8 @@ enum Action {
CHANGE_RULE = 7;
LOG_LEVEL = 8;
STOP = 9;
MONITOR_PROCESS = 10;
STOP_MONITOR_PROCESS = 11;
}
// client configuration sent on Subscribe()

View file

@ -19,6 +19,7 @@ class LinuxDesktopParser(threading.Thread):
self.daemon = True
self.running = False
self.apps = {}
self.apps_by_name = {}
# some things are just weird
# (not really, i don't want to keep track of parent pids
# just because of icons tho, this hack is way easier)
@ -55,18 +56,45 @@ class LinuxDesktopParser(threading.Thread):
return cmd
def _discover_app_icon(self, app_name):
# more hacks
# normally qt will find icons if the system if configured properly.
# if it's not, qt won't be able to find the icon by using QIcon().fromTheme(""),
# so we fallback to try to determine if the icon exist in some well known system paths.
icon_dirs = ("/usr/share/icons/gnome/48x48/apps/", "/usr/share/pixmaps/", "/usr/share/icons/hicolor/48x48/apps/")
icon_exts = (".png", ".xpm", ".svg")
for idir in icon_dirs:
for iext in icon_exts:
iconPath = idir + app_name + iext
if os.path.exists(iconPath):
print("found on last chance: ", iconPath)
return iconPath
def _parse_desktop_file(self, desktop_path):
parser = configparser.ConfigParser(strict=False) # Allow duplicate config entries
try:
basename = os.path.basename(desktop_path)[:-8]
parser.read(desktop_path, 'utf8')
cmd = parser.get('Desktop Entry', 'exec', raw=True, fallback=None)
if cmd == None:
cmd = parser.get('Desktop Entry', 'Exec', raw=True, fallback=None)
if cmd is not None:
cmd = self._parse_exec(cmd)
icon = parser.get('Desktop Entry', 'Icon', raw=True, fallback=None)
name = parser.get('Desktop Entry', 'Name', raw=True, fallback=None)
if icon == None:
# Some .desktop files doesn't have the Icon entry
# FIXME: even if we return an icon, if the DE is not properly configured,
# it won't be loaded/displayed.
icon = self._discover_app_icon(basename)
with self.lock:
# The Exec entry may have an absolute path to a binary or just the binary with parameters.
# /path/binary or binary, so save both
self.apps[cmd] = (name, icon, desktop_path)
self.apps[basename] = (name, icon, desktop_path)
# if the command is a symlink, add the real binary too
if os.path.islink(cmd):
link_to = os.path.realpath(cmd)
@ -82,8 +110,16 @@ class LinuxDesktopParser(threading.Thread):
path = to
break
app_name = self.apps.get(path)
if app_name == None:
return self.apps.get(def_name, (def_name, default_icon, None))
return self.apps.get(path, (def_name, default_icon, None))
def get_info_by_binname(self, name, default_icon):
def_name = os.path.basename(name)
return self.apps.get(def_name, (def_name, default_icon, None))
def run(self):
self.running = True
wm = pyinotify.WatchManager()

View file

@ -6,6 +6,7 @@ import sys
import os
import csv
import time
import json
from PyQt5 import Qt, QtCore, QtGui, uic, QtWidgets
from PyQt5.QtSql import QSqlDatabase, QSqlDatabase, QSqlQueryModel, QSqlQuery, QSqlTableModel
@ -17,6 +18,7 @@ from version import version
from nodes import Nodes
from dialogs.preferences import PreferencesDialog
from dialogs.ruleseditor import RulesEditorDialog
from dialogs.processdetails import ProcessDetailsDialog
from customwidgets import ColorizedDelegate
DIALOG_UI_PATH = "%s/../res/stats.ui" % os.path.dirname(sys.modules[__name__].__file__)
@ -228,6 +230,9 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self._cfg = Config.get()
self._nodes = Nodes.instance()
# TODO: allow to display multiples dialogs
self._proc_details_dialog = ProcessDetailsDialog()
self.daemon_connected = False
# skip table updates if a context menu is active
self._context_menu_active = False
@ -265,6 +270,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self.delRuleButton.setVisible(False)
self.editRuleButton.setVisible(False)
self.nodeRuleLabel.setVisible(False)
self.cmdProcDetails.clicked.connect(self._cb_proc_details_clicked)
self.TABLES[self.TAB_MAIN]['view'] = self._setup_table(QtWidgets.QTreeView, self.eventsTable, "connections",
self.TABLES[self.TAB_MAIN]['display_fields'],
@ -463,21 +469,38 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self._db.remove("DELETE FROM rules WHERE name='%s' AND node='%s'" % (rule.name, node_addr))
self._refresh_active_table()
def _cb_proc_details_clicked(self):
table = self._tables[self.tabWidget.currentIndex()]
nrows = table.model().rowCount()
pids = {}
for row in range(0, nrows):
pid = table.model().index(row, 6).data()
node = table.model().index(row, 1).data()
if pid not in pids:
pids[pid] = node
self._proc_details_dialog.monitor(pids)
@QtCore.pyqtSlot(ui_pb2.NotificationReply)
def _cb_notification_callback(self, reply):
#print("[stats dialog] notification reply: ", reply.id, reply.code)
if reply.id in self._notifications_sent:
#print("[stats] not received: ", self._notifications_sent[reply.id].type)
if reply.code == ui_pb2.ERROR:
msgBox = QtWidgets.QMessageBox()
msgBox.setText(reply.data)
msgBox.setIcon(QtWidgets.QMessageBox.Warning)
msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
else:
print("[stats] unknown notification received: ", self._notifications_sent[reply.id].type)
def _cb_tab_changed(self, index):
if index == self.TAB_MAIN:
self._set_events_query()
else:
if index == self.TAB_PROCS:
self.cmdProcDetails.setVisible(False)
self._refresh_active_table()
def _cb_table_context_menu(self, pos):
@ -571,6 +594,9 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self.editRuleButton.setVisible(False)
self.nodeRuleLabel.setText("")
self.rulesFilterLine.setVisible(True)
elif cur_idx == StatsDialog.TAB_PROCS:
self.cmdProcDetails.setVisible(False)
model = self._get_active_table().model()
self.setQuery(model, self._db.get_query(self.TABLES[cur_idx]['name'], self.TABLES[cur_idx]['display_fields']) + self._get_order())
@ -586,7 +612,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
elif idx == StatsDialog.COL_PROCS:
cur_idx = 4
self.tabWidget.setCurrentIndex(cur_idx)
self._set_process_query(data)
self._set_process_tab_active(data)
elif idx == StatsDialog.COL_RULES:
cur_idx = 2
self._set_rules_tab_active(row, cur_idx)
@ -715,6 +741,10 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
if self.TABLES[cur_idx].get('cmdCleanStats') != None:
self.TABLES[cur_idx]['cmdCleanStats'].setVisible(not state)
def _set_proc_tab_active(self, data):
self.cmdProcDetails.setVisible(False)
self._set_process_query(data)
def _set_rules_tab_active(self, row, cur_idx):
data = row.data()
self.delRuleButton.setVisible(True)
@ -837,6 +867,9 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
"FROM procs as p, connections as c " \
"WHERE p.what = '%s' AND p.what = c.process GROUP BY c.dst_ip, c.dst_host, c.dst_port, UserID, Action, Node %s" % (data, self._get_order()))
nrows = self._get_active_table().model().rowCount()
self.cmdProcDetails.setVisible(nrows != 0)
def _set_addrs_query(self, data):
model = self._get_active_table().model()
self.setQuery(model, "SELECT " \

View file

@ -3,6 +3,7 @@ from datetime import datetime
import time
import json
import ui_pb2
from database import Database
class Nodes():
@ -155,7 +156,10 @@ class Nodes():
try:
notification.id = int(str(time.time()).replace(".", ""))
self._nodes[addr]['notifications'].put(notification)
self._notifications_sent[notification.id] = callback_signal
self._notifications_sent[notification.id] = {
'callback': callback_signal,
'type': notification.type
}
except Exception as e:
print(self.LOG_TAG + " exception sending notification: ", e, addr, notification)
@ -170,20 +174,28 @@ class Nodes():
notification.id = int(str(time.time()).replace(".", ""))
for c in self._nodes:
self._nodes[c]['notifications'].put(notification)
self._notifications_sent[notification.id] = callback_signal
self._notifications_sent[notification.id] = {
'callback': callback_signal,
'type': notification.type
}
except Exception as e:
print(self.LOG_TAG + " exception sending notifications: ", e, notification)
return notification.id
def reply_notification(self, reply):
def reply_notification(self, addr, reply):
if reply == None:
print(self.LOG_TAG, " reply notification None")
return
if reply.id in self._notifications_sent:
if self._notifications_sent[reply.id] != None:
self._notifications_sent[reply.id].emit(reply)
del self._notifications_sent[reply.id]
self._notifications_sent[reply.id]['callback'].emit(reply)
# delete only one-time notifications
# we need the ID of streaming notifications from the server
# (monitor_process for example) to keep track of the data sent to us.
if self._notifications_sent[reply.id]['type'] != ui_pb2.MONITOR_PROCESS:
del self._notifications_sent[reply.id]
def update(self, proto, addr, status=ONLINE):
try:

View file

@ -0,0 +1,259 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProcessDetailsDialog</class>
<widget class="QDialog" name="ProcessDetailsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>731</width>
<height>478</height>
</rect>
</property>
<property name="windowTitle">
<string>Process details</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="labelProcIcon">
<property name="minimumSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="labelProcName">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>loading...</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelProcArgs">
<property name="text">
<string>loading...</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="labelCwd">
<property name="text">
<string>CWD: loading...</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="labelStatm">
<property name="text">
<string>mem stats: loading...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="tabPosition">
<enum>QTabWidget::South</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="documentMode">
<bool>true</bool>
</property>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Status</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="textStatus">
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Open files</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="0">
<widget class="QPlainTextEdit" name="textOpenedFiles">
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabWidgetPage1">
<attribute name="title">
<string>I/O Statistics</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QPlainTextEdit" name="textIOStats">
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabWidgetPage2">
<attribute name="title">
<string>Memory mapped files</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QPlainTextEdit" name="textMappedFiles">
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Stack</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="textStack">
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>Environment variables</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="textEnv">
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Application pids</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboPids"/>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cmdAction">
<property name="toolTip">
<string>Start or stop monitoring this process</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="media-playback-start"/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cmdClose">
<property name="text">
<string>Close</string>
</property>
<property name="icon">
<iconset theme="window-close"/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -72,7 +72,7 @@
</property>
<property name="icon">
<iconset theme="emblem-default">
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</item>
<item>
@ -81,7 +81,7 @@
</property>
<property name="icon">
<iconset theme="emblem-important">
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</item>
</widget>
@ -153,7 +153,8 @@
<string/>
</property>
<property name="icon">
<iconset theme="edit-clear-all"/>
<iconset theme="edit-clear-all">
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -191,7 +192,7 @@
</property>
<property name="icon">
<iconset theme="go-previous">
<normaloff>.</normaloff>.</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -210,7 +211,7 @@
</widget>
</item>
<item>
<spacer name="horizontalSpacer_11">
<spacer name="nodesSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -260,7 +261,7 @@
</property>
<property name="icon">
<iconset theme="go-previous">
<normaloff>.</normaloff>.</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -278,7 +279,7 @@
</property>
<property name="icon">
<iconset theme="accessories-text-editor">
<normaloff>.</normaloff>.</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -289,7 +290,7 @@
</property>
<property name="icon">
<iconset theme="edit-delete">
<normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -384,7 +385,7 @@
</property>
<property name="icon">
<iconset theme="go-previous">
<normaloff>.</normaloff>.</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -431,7 +432,8 @@
<string/>
</property>
<property name="icon">
<iconset theme="edit-clear-all"/>
<iconset theme="edit-clear-all">
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -472,7 +474,17 @@
</property>
<property name="icon">
<iconset theme="go-previous">
<normaloff>.</normaloff>.</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cmdProcDetails">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="system-search"/>
</property>
</widget>
</item>
@ -519,7 +531,8 @@
<string/>
</property>
<property name="icon">
<iconset theme="edit-clear-all"/>
<iconset theme="edit-clear-all">
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -557,7 +570,7 @@
</property>
<property name="icon">
<iconset theme="go-previous">
<normaloff>.</normaloff>.</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -601,7 +614,8 @@
<string/>
</property>
<property name="icon">
<iconset theme="edit-clear-all"/>
<iconset theme="edit-clear-all">
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -639,7 +653,7 @@
</property>
<property name="icon">
<iconset theme="go-previous">
<normaloff>.</normaloff>.</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -683,7 +697,8 @@
<string/>
</property>
<property name="icon">
<iconset theme="edit-clear-all"/>
<iconset theme="edit-clear-all">
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -721,7 +736,7 @@
</property>
<property name="icon">
<iconset theme="go-previous">
<normaloff>.</normaloff>.</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -775,7 +790,8 @@
<string/>
</property>
<property name="icon">
<iconset theme="edit-clear-all"/>
<iconset theme="edit-clear-all">
<normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -1105,7 +1121,7 @@
</property>
<property name="icon">
<iconset theme="document-save">
<normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
@ -1131,7 +1147,7 @@
</property>
<property name="icon">
<iconset theme="preferences-desktop">
<normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
@ -1153,8 +1169,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="document-new">
<normaloff>.</normaloff>.</iconset>
<iconset theme="document-new"/>
</property>
</widget>
</item>
@ -1171,7 +1186,7 @@
</property>
<property name="icon">
<iconset theme="media-playback-start">
<normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
<property name="checkable">
<bool>true</bool>

View file

@ -509,7 +509,7 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
in_message = next(node_iter)
if in_message == None:
continue
self._nodes.reply_notification(in_message)
self._nodes.reply_notification(addr, in_message)
except StopIteration as e:
print("[Notifications] Node exited")
except Exception as e:

View file

@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
name='ui.proto',
package='protocol',
syntax='proto3',
serialized_pb=_b('\n\x08ui.proto\x12\x08protocol\"]\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\xc8\x02\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"J\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\"\x81\x01\n\x04Rule\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\x12\x12\n\nprecedence\x18\x03 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x04 \x01(\t\x12\x10\n\x08\x64uration\x18\x05 \x01(\t\x12$\n\x08operator\x18\x06 \x01(\x0b\x32\x12.protocol.Operator\"\x95\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\"\x8f\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\xab\x01\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x11\n\rLOAD_FIREWALL\x10\x01\x12\x13\n\x0fUNLOAD_FIREWALL\x10\x02\x12\x11\n\rCHANGE_CONFIG\x10\x03\x12\x0f\n\x0b\x45NABLE_RULE\x10\x04\x12\x10\n\x0c\x44ISABLE_RULE\x10\x05\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\x06\x12\x0f\n\x0b\x43HANGE_RULE\x10\x07\x12\r\n\tLOG_LEVEL\x10\x08\x12\x08\n\x04STOP\x10\t**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xf8\x01\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x62\x06proto3')
serialized_pb=_b('\n\x08ui.proto\x12\x08protocol\"]\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\xc8\x02\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"J\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\"\x81\x01\n\x04Rule\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\x12\x12\n\nprecedence\x18\x03 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x04 \x01(\t\x12\x10\n\x08\x64uration\x18\x05 \x01(\t\x12$\n\x08operator\x18\x06 \x01(\x0b\x32\x12.protocol.Operator\"\x95\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\"\x8f\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\xda\x01\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x11\n\rLOAD_FIREWALL\x10\x01\x12\x13\n\x0fUNLOAD_FIREWALL\x10\x02\x12\x11\n\rCHANGE_CONFIG\x10\x03\x12\x0f\n\x0b\x45NABLE_RULE\x10\x04\x12\x10\n\x0c\x44ISABLE_RULE\x10\x05\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\x06\x12\x0f\n\x0b\x43HANGE_RULE\x10\x07\x12\r\n\tLOG_LEVEL\x10\x08\x12\x08\n\x04STOP\x10\t\x12\x13\n\x0fMONITOR_PROCESS\x10\n\x12\x18\n\x14STOP_MONITOR_PROCESS\x10\x0b**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xf8\x01\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x62\x06proto3')
)
_ACTION = _descriptor.EnumDescriptor(
@ -69,11 +69,19 @@ _ACTION = _descriptor.EnumDescriptor(
name='STOP', index=9, number=9,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MONITOR_PROCESS', index=10, number=10,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='STOP_MONITOR_PROCESS', index=11, number=11,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=1992,
serialized_end=2163,
serialized_end=2210,
)
_sym_db.RegisterEnumDescriptor(_ACTION)
@ -95,8 +103,8 @@ _NOTIFICATIONREPLYCODE = _descriptor.EnumDescriptor(
],
containing_type=None,
options=None,
serialized_start=2165,
serialized_end=2207,
serialized_start=2212,
serialized_end=2254,
)
_sym_db.RegisterEnumDescriptor(_NOTIFICATIONREPLYCODE)
@ -111,6 +119,8 @@ DELETE_RULE = 6
CHANGE_RULE = 7
LOG_LEVEL = 8
STOP = 9
MONITOR_PROCESS = 10
STOP_MONITOR_PROCESS = 11
OK = 0
ERROR = 1
@ -1226,8 +1236,8 @@ _UI = _descriptor.ServiceDescriptor(
file=DESCRIPTOR,
index=0,
options=None,
serialized_start=2210,
serialized_end=2458,
serialized_start=2257,
serialized_end=2505,
methods=[
_descriptor.MethodDescriptor(
name='Ping',

View file

@ -1 +1 @@
version = '1.2.0'
version = '1.3.0rc1'