forked from zmap/zgrab2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmonitor.go
More file actions
134 lines (123 loc) · 3.56 KB
/
monitor.go
File metadata and controls
134 lines (123 loc) · 3.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package zgrab2
import (
"fmt"
"sync"
"time"
log "github.com/sirupsen/logrus"
)
// Monitor is a collection of states per scans and a channel to communicate
// those scans to the monitor
type Monitor struct {
startTime time.Time // time when the monitor started, for giving user elapsed time
states map[string]*ModuleMetadata
statusesChan chan moduleStatus
// Callback is invoked after each scan.
Callback func(string)
}
// ModuleMetadata contains information of the status of a particular module's scan
type ModuleMetadata struct {
Successes uint `json:"successes"` // number of successful scans for this module
Failures uint `json:"failures"`
CustomMetadata any `json:"custom_metadata,omitempty"` // module-specific metadata, each module can implement this as they see fit
}
type moduleStatus struct {
name string
st status
}
type status uint
const (
statusSuccess status = iota
statusFailure status = iota
)
// GetStatuses returns a mapping from scanner names to the current number
// of successes and failures for that scanner
func (m *Monitor) GetStatuses() map[string]*ModuleMetadata {
return m.states
}
// Stop indicates the monitor is done and the internal channel should be closed.
// This function does not block, but will allow a call to Wait() on the
// WaitGroup passed to MakeMonitor to return.
func (m *Monitor) Stop() {
close(m.statusesChan)
}
func (m *Monitor) printStatus(isFinalPrint bool) {
if config.statusUpdatesFile == nil {
return // no file to write to
}
scanStatusMsg := ""
if isFinalPrint {
scanStatusMsg = "Scan Complete; "
}
timeSinceStart := time.Since(m.startTime)
scanRate := float64(0)
var totalSuccesses, totalFailures uint
for _, state := range m.states {
totalSuccesses += state.Successes
totalFailures += state.Failures
}
if timeSinceStart.Seconds() > 0 {
scanRate = float64(totalSuccesses+totalFailures) / timeSinceStart.Seconds() // avoid division by zero
}
scanSuccessRate := float64(0)
totalTargetsScanned := totalSuccesses + totalFailures
if totalTargetsScanned > 0 {
scanSuccessRate = float64(totalSuccesses) / float64(totalTargetsScanned) * 100
}
updateLine := fmt.Sprintf("%02dh:%02dm:%02ds; %s%d targets scanned; %.02f targets/sec; %.01f%% success rate",
int(timeSinceStart.Hours()),
int(timeSinceStart.Minutes())%60,
int(timeSinceStart.Seconds())%60,
scanStatusMsg,
totalSuccesses+totalFailures,
scanRate,
scanSuccessRate,
)
_, err := fmt.Fprintln(config.statusUpdatesFile, updateLine)
if err != nil {
log.Errorf("unable to write periodic status update: %v", err)
}
}
// MakeMonitor returns a Monitor object that can be used to collect and send
// the status of a running scan
func MakeMonitor(statusChanSize int, wg *sync.WaitGroup) *Monitor {
m := &Monitor{
statusesChan: make(chan moduleStatus, statusChanSize),
states: make(map[string]*ModuleMetadata),
startTime: time.Now(),
}
wg.Add(1)
go func() {
ticker := time.NewTicker(time.Second)
defer wg.Done()
for {
select {
case s, ok := <-m.statusesChan:
if !ok {
// channel closed, exiting
ticker.Stop()
m.printStatus(true) // print final status
return
}
// process new status
if m.states[s.name] == nil {
m.states[s.name] = new(ModuleMetadata)
}
if m.Callback != nil {
m.Callback(s.name)
}
switch s.st {
case statusSuccess:
m.states[s.name].Successes++
case statusFailure:
m.states[s.name].Failures++
default:
continue
}
case <-ticker.C:
// print per-second summary
m.printStatus(false)
}
}
}()
return m
}