Skip to content

Commit 0b40c4e

Browse files
committed
add: [sshd] Flushing Statistics Feature
1 parent b874e6c commit 0b40c4e

File tree

3 files changed

+126
-41
lines changed

3 files changed

+126
-41
lines changed

logparser/parser.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ type (
77
// It should provide:
88
// Set to assign a redis connection to it
99
// Parse to parse a line of log
10+
// Flush recomputes statisitcs and recompile output
1011
Parser interface {
1112
Set(*redis.Conn, *redis.Conn)
1213
Parse(string) error
14+
Flush() error
1315
Compile() error
1416
}
1517
)

logparser/sshd.go

Lines changed: 86 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import (
2222
// SshdParser Holds a struct that corresponds to a sshd log line
2323
// and the redis connection
2424
type SshdParser struct {
25+
// Write
2526
r1 *redis.Conn
27+
// Read
2628
r2 *redis.Conn
2729
}
2830

@@ -32,6 +34,66 @@ func (s *SshdParser) Set(rconn1 *redis.Conn, rconn2 *redis.Conn) {
3234
s.r2 = rconn2
3335
}
3436

37+
// Flush recomputes statistics and recompile HTML output
38+
func (s *SshdParser) Flush() error {
39+
log.Println("Flushing")
40+
r1 := *s.r1
41+
r0 := *s.r2
42+
// writing in database 1
43+
if _, err := r1.Do("SELECT", 1); err != nil {
44+
r0.Close()
45+
r1.Close()
46+
return err
47+
}
48+
// flush stats DB
49+
if _, err := r1.Do("FLUSHDB"); err != nil {
50+
r0.Close()
51+
r1.Close()
52+
return err
53+
}
54+
log.Println("Statistics Database Flushed")
55+
56+
// reading from database 0
57+
if _, err := r0.Do("SELECT", 0); err != nil {
58+
r0.Close()
59+
r1.Close()
60+
return err
61+
}
62+
63+
// Compile statistics / html output for each line
64+
keys, err := redis.Strings(r0.Do("KEYS", "*"))
65+
if err != nil {
66+
r0.Close()
67+
r1.Close()
68+
return err
69+
}
70+
for _, v := range keys {
71+
dateHost := strings.Split(v, ":")
72+
kkeys, err := redis.StringMap(r0.Do("HGETALL", v))
73+
if err != nil {
74+
r0.Close()
75+
r1.Close()
76+
return err
77+
}
78+
79+
dateInt, err := strconv.ParseInt(dateHost[0], 10, 64)
80+
if err != nil {
81+
r0.Close()
82+
r1.Close()
83+
return err
84+
}
85+
parsedTime := time.Unix(dateInt, 0)
86+
err = compileStats(s, parsedTime, kkeys["src"], kkeys["username"], dateHost[1])
87+
if err != nil {
88+
r0.Close()
89+
r1.Close()
90+
return err
91+
}
92+
}
93+
94+
return nil
95+
}
96+
3597
// Parse parses a line of sshd log
3698
func (s *SshdParser) Parse(logline string) error {
3799
r := *s.r1
@@ -58,12 +120,26 @@ func (s *SshdParser) Parse(logline string) error {
58120
r.Close()
59121
return err
60122
}
123+
124+
// Writing logs
61125
_, err := redis.Bool(r.Do("HSET", fmt.Sprintf("%v:%v", md["date"], md["host"]), "username", md["username"], "src", md["src"]))
62126
if err != nil {
63127
r.Close()
64128
return err
65129
}
66130

131+
err = compileStats(s, parsedTime, md["src"], md["username"], md["host"])
132+
if err != nil {
133+
r.Close()
134+
return err
135+
}
136+
137+
return nil
138+
}
139+
140+
func compileStats(s *SshdParser, parsedTime time.Time, src string, username string, host string) error {
141+
r := *s.r1
142+
67143
// Pushing statistics in database 1
68144
if _, err := r.Do("SELECT", 1); err != nil {
69145
r.Close()
@@ -74,8 +150,7 @@ func (s *SshdParser) Parse(logline string) error {
74150
dstr := fmt.Sprintf("%v%v%v", parsedTime.Year(), fmt.Sprintf("%02d", int(parsedTime.Month())), fmt.Sprintf("%02d", int(parsedTime.Day())))
75151

76152
// Check current entry date as oldest if older than the current
77-
var oldest string
78-
if oldest, err = redis.String(r.Do("GET", "oldest")); err == redis.ErrNil {
153+
if oldest, err := redis.String(r.Do("GET", "oldest")); err == redis.ErrNil {
79154
r.Do("SET", "oldest", dstr)
80155
} else if err != nil {
81156
r.Close()
@@ -92,8 +167,7 @@ func (s *SshdParser) Parse(logline string) error {
92167
}
93168

94169
// Check current entry date as oldest if older than the current
95-
var newest string
96-
if newest, err = redis.String(r.Do("GET", "newest")); err == redis.ErrNil {
170+
if newest, err := redis.String(r.Do("GET", "newest")); err == redis.ErrNil {
97171
r.Do("SET", "newest", dstr)
98172
} else if err != nil {
99173
r.Close()
@@ -109,23 +183,23 @@ func (s *SshdParser) Parse(logline string) error {
109183
}
110184
}
111185

112-
err = compileStats(s, dstr, "daily", md["src"], md["username"], md["host"])
186+
err := compileStat(s, dstr, "daily", src, username, host)
113187
if err != nil {
114188
r.Close()
115189
return err
116190
}
117191

118192
// Monthly
119193
mstr := fmt.Sprintf("%v%v", parsedTime.Year(), fmt.Sprintf("%02d", int(parsedTime.Month())))
120-
err = compileStats(s, mstr, "daily", md["src"], md["username"], md["host"])
194+
err = compileStat(s, mstr, "daily", src, username, host)
121195
if err != nil {
122196
r.Close()
123197
return err
124198
}
125199

126200
// Yearly
127201
ystr := fmt.Sprintf("%v", parsedTime.Year())
128-
err = compileStats(s, ystr, "daily", md["src"], md["username"], md["host"])
202+
err = compileStat(s, ystr, "daily", src, username, host)
129203
if err != nil {
130204
r.Close()
131205
return err
@@ -134,7 +208,7 @@ func (s *SshdParser) Parse(logline string) error {
134208
return nil
135209
}
136210

137-
func compileStats(s *SshdParser, datestr string, mode string, src string, username string, host string) error {
211+
func compileStat(s *SshdParser, datestr string, mode string, src string, username string, host string) error {
138212
r := *s.r1
139213
_, err := redis.String(r.Do("ZINCRBY", fmt.Sprintf("%v:%v", datestr, "statssrc"), 1, src))
140214
if err != nil {
@@ -247,13 +321,9 @@ func (s *SshdParser) Compile() error {
247321
}
248322
#imageholder {
249323
background: black;
250-
color: white;
251-
padding: 1em;
252-
position: absolute;
253-
top: 50%;
254-
left: 50%;
255-
margin-right: -50%;
256-
transform: translate(-50%, -40%)
324+
margin: auto;
325+
width: 50%;
326+
padding: 10px;
257327
}
258328
span {
259329
float: left;
@@ -274,7 +344,7 @@ func (s *SshdParser) Compile() error {
274344
<option value="statshost">Hosts</option>
275345
</select>
276346
</span>
277-
<span id="imageholder"></span>
347+
<div id="imageholder"></div>
278348
</body>
279349
</html>`
280350

main.go

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ var (
4747
debug = flag.Bool("d", false, "debug info in logs")
4848
fromfile = flag.String("f", "", "parse from file on disk")
4949
retry = flag.Int("r", 1, "time in minute before retry on empty d4 queue")
50+
flush = flag.Bool("F", false, "Flush HTML output, recompile all statistic from redis logs, then quits")
5051
redisD4 redis.Conn
5152
redisParsers *redis.Pool
5253
parsers = [1]string{"sshd"}
@@ -111,36 +112,40 @@ func main() {
111112
log.SetFlags(log.LstdFlags | log.Lshortfile)
112113
}
113114

114-
// Parse Redis D4 Config
115-
tmp := config.ReadConfigFile(*confdir, "redis_d4")
116-
ss := strings.Split(string(tmp), "/")
117-
if len(ss) <= 1 {
118-
log.Fatal("Missing Database in Redis D4 config: should be host:port/database_name")
119-
}
120-
rd4.redisDB, _ = strconv.Atoi(ss[1])
121-
var ret bool
122-
ret, ss[0] = config.IsNet(ss[0])
123-
if !ret {
124-
sss := strings.Split(string(ss[0]), ":")
125-
rd4.redisHost = sss[0]
126-
rd4.redisPort = sss[1]
127-
}
128-
rd4.redisQueue = string(config.ReadConfigFile(*confdir, "redis_queue"))
129-
// Connect to D4 Redis
130-
// TODO use DialOptions to Dial with a timeout
131-
redisD4, err = redis.Dial("tcp", rd4.redisHost+":"+rd4.redisPort, redis.DialDatabase(rd4.redisDB))
132-
if err != nil {
133-
log.Fatal(err)
115+
// Dont't touch D4 server if Flushing
116+
if !*flush {
117+
// Parse Redis D4 Config
118+
tmp := config.ReadConfigFile(*confdir, "redis_d4")
119+
ss := strings.Split(string(tmp), "/")
120+
if len(ss) <= 1 {
121+
log.Fatal("Missing Database in Redis D4 config: should be host:port/database_name")
122+
}
123+
rd4.redisDB, _ = strconv.Atoi(ss[1])
124+
var ret bool
125+
ret, ss[0] = config.IsNet(ss[0])
126+
if !ret {
127+
sss := strings.Split(string(ss[0]), ":")
128+
rd4.redisHost = sss[0]
129+
rd4.redisPort = sss[1]
130+
}
131+
rd4.redisQueue = string(config.ReadConfigFile(*confdir, "redis_queue"))
132+
// Connect to D4 Redis
133+
// TODO use DialOptions to Dial with a timeout
134+
redisD4, err = redis.Dial("tcp", rd4.redisHost+":"+rd4.redisPort, redis.DialDatabase(rd4.redisDB))
135+
if err != nil {
136+
log.Fatal(err)
137+
}
138+
defer redisD4.Close()
134139
}
135-
defer redisD4.Close()
136140

137141
// Parse Redis Parsers Config
138-
tmp = config.ReadConfigFile(*confdir, "redis_parsers")
139-
ss = strings.Split(string(tmp), "/")
142+
tmp := config.ReadConfigFile(*confdir, "redis_parsers")
143+
ss := strings.Split(string(tmp), "/")
140144
if len(ss) <= 1 {
141145
log.Fatal("Missing Database Count in Redis config: should be host:port/max number of DB")
142146
}
143147
rp.redisDBCount, _ = strconv.Atoi(ss[1])
148+
var ret bool
144149
ret, ss[0] = config.IsNet(ss[0])
145150
if !ret {
146151
sss := strings.Split(string(ss[0]), ":")
@@ -177,8 +182,16 @@ func main() {
177182
log.Println("TODO should run specific parser here")
178183
}
179184

180-
// Parsing loop
181-
if *fromfile != "" {
185+
// If we flush, we bypass the parsing loop
186+
if *flush {
187+
for _, v := range torun {
188+
err := v.Flush()
189+
if err != nil {
190+
log.Fatal(err)
191+
}
192+
}
193+
// Parsing loop
194+
} else if *fromfile != "" {
182195
f, err = os.Open(*fromfile)
183196
if err != nil {
184197
log.Fatalf("Error opening seed file: %v", err)

0 commit comments

Comments
 (0)