Skip to content

Commit

Permalink
refactored CIFS parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Rebischke committed Sep 16, 2018
1 parent 70f5fe1 commit 3040ba7
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 210 deletions.
7 changes: 4 additions & 3 deletions cifs/cifs.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ type SessionIDs struct {

// model for the CIFS header statistics
type ClientStats struct {
Header map[string]uint64
SMBStats []*SMBStats
Header map[string]uint64
SMBStatsList []*SMBStats
}

// Array with fixed regex for parsing the SMB stats header
Expand All @@ -51,6 +51,8 @@ var regexpHeaders = [...]*regexp.Regexp{
// Array with regex for parsing SMB
var regexpSMBs = [...]*regexp.Regexp{
regexp.MustCompile(`(?P<sessionID>\d+)\) \\\\(?P<server>[A-Za-z1-9-.]+)(?P<share>.+)`),
// Match SMB2 "flushes" line first. Otherwise we will get a mismatch.
regexp.MustCompile(`Flushes: (?P<flushesSent>\d+) sent (?P<flushesFailed>\d+) failed`),
regexp.MustCompile(`SMBs: (?P<smbs>\d+) Oplocks breaks: (?P<breaks>\d+)`),
regexp.MustCompile(`Reads: (?P<reads>\d+) Bytes: (?P<readsBytes>\d+)`),
regexp.MustCompile(`Writes: (?P<writes>\d+) Bytes: (?P<writesBytes>\d+)`),
Expand All @@ -69,7 +71,6 @@ var regexpSMBs = [...]*regexp.Regexp{
regexp.MustCompile(`TreeDisconnects: (?P<treeDisconnectsSent>\d+) sent (?P<treeDisconnectsFailed>\d+) failed`),
regexp.MustCompile(`Creates: (?P<createsSent>\d+) sent (?P<createsFailed>\d+) failed`),
regexp.MustCompile(`Closes: (?P<closesSent>\d+) sent (?P<closesFailed>\d+) failed`),
regexp.MustCompile(`Flushes: (?P<flushesSent>\d+) sent (?P<flushesFailed>\d+) failed`),
regexp.MustCompile(`Reads: (?P<readsSent>\d+) sent (?P<readsFailed>\d+) failed`),
regexp.MustCompile(`Writes: (?P<writesSent>\d+) sent (?P<writesFailed>\d+) failed`),
regexp.MustCompile(`Locks: (?P<locksSent>\d+) sent (?P<locksFailed>\d+) failed`),
Expand Down
218 changes: 59 additions & 159 deletions cifs/parse_cifs.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import (
"bufio"
"fmt"
"io"
"regexp"
"strconv"
"strings"
)

// parseHeader parses our SMB header
func parseHeader(line string, header map[string]uint64) error {
for _, regexpHeader := range regexpHeaders {
match := regexpHeader.FindStringSubmatch(line)
Expand All @@ -42,173 +42,73 @@ func parseHeader(line string, header map[string]uint64) error {
return nil
}

// parseSMBStats parses a SMB block
func parseSMBStats(line string, stats map[string]uint64, sessionIDs *SessionIDs) error {
for _, regexpSMB := range regexpSMBs {
match := regexpSMB.FindStringSubmatch(line)
if match == nil {
continue
}
for index, name := range regexpSMB.SubexpNames() {
if index == 0 || name == "" {
continue
}
switch name {
case "sessionID":
value, err := strconv.ParseUint(match[index], 10, 64)
if nil != err {
return fmt.Errorf("Type mismatch for sessionID")
}
sessionIDs.SessionID = value
case "server":
if match[index] != "" {
sessionIDs.Server = match[index]
}
case "share":
if match[index] != "" {
sessionIDs.Share = match[index]
}
default:
value, err := strconv.ParseUint(match[index], 10, 64)
if nil != err {
return fmt.Errorf("Invalid value in SMB Statistics")
}
stats[name] = value
}
}
return nil
}
return nil
}

// ParseClientStats returns stats read from /proc/fs/cifs/Stats
func ParseClientStats(r io.Reader) (*ClientStats, error) {
stats := &ClientStats{}
stats.Header = make(map[string]uint64)
scanner := bufio.NewScanner(r)
var tmpSMB1Stats *SMB1Stats
var tmpSMB2Stats *SMB2Stats
var tmpSessionIDs *SessionIDs
// The legacy variable sets the current context. True for SMB1, False for SMB2
legacy := true
var currentSMBBlock *SMBStats
var currentSMBMetrics map[string]uint64
var currentSMBSessionIDs *SessionIDs
for scanner.Scan() {
line := scanner.Text()
// if line is empty we can go back to start
if line == "" {
continue
}
parseHeader(line, stats.Header)
if legacy {
// This part manages the parsing of SMB1
for _, regexpSMB1 := range regexpSMB1s {
match := regexpSMB1.FindStringSubmatch(line)
if 0 == len(match) {
// Check for SMB1 Line: "SMBs: 9 Oplocks breaks: 0"
// If this Check fails we change to SMB2 Statistics
if strings.HasPrefix(line, "SMBs:") && !(strings.Contains(line, "breaks")) {
legacy = false
tmpSMB2Stats = &SMB2Stats{
Stats: make(map[string]map[string]uint64),
}
stats.SMB2Stats = append(stats.SMB2Stats, tmpSMB2Stats)
re := regexp.MustCompile("[0-9]+")
find_smb := re.FindAllString(line, 1)
tmpSMB2Stats.Stats["smbs"] = make(map[string]uint64)
value, err := strconv.ParseUint(find_smb[0], 10, 64)
if nil != err {
continue
}
tmpSMB2Stats.Stats["smbs"]["smbs"] = value
break
}
continue
}
for index, name := range regexpSMB1.SubexpNames() {
if 0 == index || "" == name {
continue
}
switch name {
case "sessionID":
value, err := strconv.ParseUint(match[index], 10, 64)
if nil != err {
continue
}
tmpSessionIDs = &SessionIDs{
SessionID: value,
}
case "server":
if "" != match[index] {
tmpSessionIDs.Server = match[index]
}
case "share":
if "" != match[index] {
tmpSessionIDs.Share = match[index]
}
case "smbs":
tmpSMB1Stats = &SMB1Stats{
Stats: make(map[string]uint64),
}
stats.SMB1Stats = append(stats.SMB1Stats, tmpSMB1Stats)
value, err := strconv.ParseUint(match[index], 10, 64)
if nil != err {
continue
}
tmpSMB1Stats.Stats[name] = value
default:
value, err := strconv.ParseUint(match[index], 10, 64)
if nil != err {
continue
}
if 0 == tmpSMB1Stats.SessionIDs.SessionID {
tmpSMB1Stats.SessionIDs.SessionID = tmpSessionIDs.SessionID
tmpSMB1Stats.SessionIDs.Server = tmpSessionIDs.Server
tmpSMB1Stats.SessionIDs.Share = tmpSessionIDs.Share

}
tmpSMB1Stats.Stats[name] = value
}
}
break
}
} else {
// This part manages the parsing of SMB2 Shares
var keyword string
for _, regexpSMB2 := range regexpSMB2s {
match := regexpSMB2.FindStringSubmatch(line)
if 0 == len(match) {
// Check for SMB2 Line: "SMBs: 9"
// If this Check fails we change to SMB1 Statistics
if strings.HasPrefix(line, "SMBs:") && strings.Contains(line, "breaks") {
legacy = true
tmpSMB1Stats = &SMB1Stats{
Stats: make(map[string]uint64),
}
stats.SMB1Stats = append(stats.SMB1Stats, tmpSMB1Stats)
re := regexp.MustCompile("[0-9]+")
find_smb := re.FindAllString(line, 2)
smbs, err := strconv.ParseUint(find_smb[0], 10, 64)
if nil != err {
continue
}
breaks, err := strconv.ParseUint(find_smb[1], 10, 64)
if nil != err {
continue
}
tmpSMB1Stats.Stats["smbs"] = smbs
tmpSMB1Stats.Stats["breaks"] = breaks

break
}
continue
}
for index, name := range regexpSMB2.SubexpNames() {
if 0 == index || "" == name {
continue
}
switch name {
case "sessionID":
value, err := strconv.ParseUint(match[index], 10, 64)
if nil != err {
continue
}
tmpSessionIDs = &SessionIDs{
SessionID: value,
}
case "server":
if "" != match[index] {
tmpSessionIDs.Server = match[index]
}
case "share":
if "" != match[index] {
tmpSessionIDs.Share = match[index]
}
case "smbs":
tmpSMB2Stats = &SMB2Stats{
Stats: make(map[string]map[string]uint64),
}
stats.SMB2Stats = append(stats.SMB2Stats, tmpSMB2Stats)
value, err := strconv.ParseUint(match[index], 10, 64)
if nil != err {
continue
}
tmpSMB2Stats.Stats[name] = make(map[string]uint64)
tmpSMB2Stats.Stats[name][name] = value

default:
value, err := strconv.ParseUint(match[index], 10, 64)
if nil != err {
keyword = match[index]
tmpSMB2Stats.Stats[keyword] = make(map[string]uint64)
continue
}
if 0 == tmpSMB2Stats.SessionIDs.SessionID {
tmpSMB2Stats.SessionIDs.SessionID = tmpSessionIDs.SessionID
tmpSMB2Stats.SessionIDs.Server = tmpSessionIDs.Server
tmpSMB2Stats.SessionIDs.Share = tmpSessionIDs.Share

}
tmpSMB2Stats.Stats[keyword][name] = value
}
}
break
// If we see a new SMB block we are initializing all necessary structs and hashmaps
if strings.Contains(line, ") \\") {
currentSMBMetrics = make(map[string]uint64)
currentSMBSessionIDs = &SessionIDs{}
currentSMBBlock = &SMBStats{
SessionIDs: *currentSMBSessionIDs,
Stats: currentSMBMetrics,
}
stats.SMBStatsList = append(stats.SMBStatsList, currentSMBBlock)
}
// Only parseSMBStats if we have a SMB block
if currentSMBSessionIDs != nil {
parseSMBStats(line, currentSMBMetrics, &currentSMBBlock.SessionIDs)
}
}

Expand Down

0 comments on commit 3040ba7

Please sign in to comment.