|
@@ -69,29 +69,26 @@ func NewNetStatBuffer(recordCount int) (*NetStatBuffers, error) {
|
|
|
}
|
|
|
|
|
|
//Get the initial measurements of netstats
|
|
|
- go func(n *NetStatBuffers) {
|
|
|
- rx, tx, err := GetNetworkInterfaceStats()
|
|
|
+ rx, tx, err := GetNetworkInterfaceStats()
|
|
|
+ if err != nil {
|
|
|
+ log.Println("Unable to get NIC stats: ", err.Error())
|
|
|
+ }
|
|
|
+
|
|
|
+ retryCount := 0
|
|
|
+ for rx == 0 && tx == 0 && retryCount < 10 {
|
|
|
+ //Strange. Retry
|
|
|
+ log.Println("NIC stats return all 0. Retrying...")
|
|
|
+ rx, tx, err = GetNetworkInterfaceStats()
|
|
|
if err != nil {
|
|
|
log.Println("Unable to get NIC stats: ", err.Error())
|
|
|
}
|
|
|
+ retryCount++
|
|
|
+ }
|
|
|
|
|
|
- retryCount := 0
|
|
|
- for rx == 0 && tx == 0 && retryCount < 10 {
|
|
|
- //Strange. Retry
|
|
|
- log.Println("NIC stats return all 0. Retrying...")
|
|
|
- rx, tx, err = GetNetworkInterfaceStats()
|
|
|
- if err != nil {
|
|
|
- log.Println("Unable to get NIC stats: ", err.Error())
|
|
|
- }
|
|
|
- retryCount++
|
|
|
- }
|
|
|
-
|
|
|
- n.PreviousStat = &RawFlowStat{
|
|
|
- RX: rx,
|
|
|
- TX: tx,
|
|
|
- }
|
|
|
-
|
|
|
- }(&thisNetBuffer)
|
|
|
+ thisNetBuffer.PreviousStat = &RawFlowStat{
|
|
|
+ RX: rx,
|
|
|
+ TX: tx,
|
|
|
+ }
|
|
|
|
|
|
// Update the buffer every second
|
|
|
go func(n *NetStatBuffers) {
|
|
@@ -199,51 +196,75 @@ func HandleGetNetworkInterfaceStats(w http.ResponseWriter, r *http.Request) {
|
|
|
// Get network interface stats, return accumulated rx bits, tx bits and error if any
|
|
|
func GetNetworkInterfaceStats() (int64, int64, error) {
|
|
|
if runtime.GOOS == "windows" {
|
|
|
- cmd := exec.Command("wmic", "path", "Win32_PerfRawData_Tcpip_NetworkInterface", "Get", "BytesReceivedPersec,BytesSentPersec,BytesTotalPersec")
|
|
|
- out, err := cmd.Output()
|
|
|
- if err != nil {
|
|
|
- return 0, 0, err
|
|
|
+ //Windows wmic sometime freeze and not respond.
|
|
|
+ //The safer way is to make a bypass mechanism
|
|
|
+ //when timeout with channel
|
|
|
+
|
|
|
+ type wmicResult struct {
|
|
|
+ RX int64
|
|
|
+ TX int64
|
|
|
+ Err error
|
|
|
}
|
|
|
|
|
|
- //Spawn a timer to terminate the cmd process if timeout
|
|
|
- var timer *time.Timer
|
|
|
- timer = time.AfterFunc(3*time.Second, func() {
|
|
|
- timer.Stop()
|
|
|
- cmd.Process.Kill()
|
|
|
- })
|
|
|
-
|
|
|
- //Filter out the first line
|
|
|
- lines := strings.Split(strings.ReplaceAll(string(out), "\r\n", "\n"), "\n")
|
|
|
- if len(lines) >= 2 && len(lines[1]) >= 0 {
|
|
|
- dataLine := lines[1]
|
|
|
- for strings.Contains(dataLine, " ") {
|
|
|
- dataLine = strings.ReplaceAll(dataLine, " ", " ")
|
|
|
- }
|
|
|
- dataLine = strings.TrimSpace(dataLine)
|
|
|
- info := strings.Split(dataLine, " ")
|
|
|
- if len(info) != 3 {
|
|
|
- return 0, 0, errors.New("Invalid wmic results")
|
|
|
+ callbackChan := make(chan wmicResult)
|
|
|
+ cmd := exec.Command("wmic", "path", "Win32_PerfRawData_Tcpip_NetworkInterface", "Get", "BytesReceivedPersec,BytesSentPersec,BytesTotalPersec")
|
|
|
+ //Execute the cmd in goroutine
|
|
|
+ go func() {
|
|
|
+ out, err := cmd.Output()
|
|
|
+ if err != nil {
|
|
|
+ callbackChan <- wmicResult{0, 0, err}
|
|
|
}
|
|
|
- rxString := info[0]
|
|
|
- txString := info[1]
|
|
|
|
|
|
- rx := int64(0)
|
|
|
- tx := int64(0)
|
|
|
- if s, err := strconv.ParseInt(rxString, 10, 64); err == nil {
|
|
|
- rx = s
|
|
|
- }
|
|
|
+ //Filter out the first line
|
|
|
+ lines := strings.Split(strings.ReplaceAll(string(out), "\r\n", "\n"), "\n")
|
|
|
+ if len(lines) >= 2 && len(lines[1]) >= 0 {
|
|
|
+ dataLine := lines[1]
|
|
|
+ for strings.Contains(dataLine, " ") {
|
|
|
+ dataLine = strings.ReplaceAll(dataLine, " ", " ")
|
|
|
+ }
|
|
|
+ dataLine = strings.TrimSpace(dataLine)
|
|
|
+ info := strings.Split(dataLine, " ")
|
|
|
+ if len(info) != 3 {
|
|
|
+ callbackChan <- wmicResult{0, 0, errors.New("invalid wmic results length")}
|
|
|
+ }
|
|
|
+ rxString := info[0]
|
|
|
+ txString := info[1]
|
|
|
+
|
|
|
+ rx := int64(0)
|
|
|
+ tx := int64(0)
|
|
|
+ if s, err := strconv.ParseInt(rxString, 10, 64); err == nil {
|
|
|
+ rx = s
|
|
|
+ }
|
|
|
|
|
|
- if s, err := strconv.ParseInt(txString, 10, 64); err == nil {
|
|
|
- tx = s
|
|
|
+ if s, err := strconv.ParseInt(txString, 10, 64); err == nil {
|
|
|
+ tx = s
|
|
|
+ }
|
|
|
+
|
|
|
+ time.Sleep(100 * time.Millisecond)
|
|
|
+ callbackChan <- wmicResult{rx * 4, tx * 4, nil}
|
|
|
+ } else {
|
|
|
+ //Invalid data
|
|
|
+ callbackChan <- wmicResult{0, 0, errors.New("invalid wmic results")}
|
|
|
}
|
|
|
|
|
|
- time.Sleep(100 * time.Millisecond)
|
|
|
- return rx * 4, tx * 4, nil
|
|
|
- } else {
|
|
|
- //Invalid data
|
|
|
- return 0, 0, errors.New("Invalid wmic results")
|
|
|
+ }()
|
|
|
+
|
|
|
+ go func() {
|
|
|
+ //Spawn a timer to terminate the cmd process if timeout
|
|
|
+ var timer *time.Timer
|
|
|
+ timer = time.AfterFunc(3*time.Second, func() {
|
|
|
+ timer.Stop()
|
|
|
+ cmd.Process.Kill()
|
|
|
+ callbackChan <- wmicResult{0, 0, errors.New("wmic execution timeout")}
|
|
|
+ })
|
|
|
+ }()
|
|
|
+
|
|
|
+ result := wmicResult{}
|
|
|
+ result = <-callbackChan
|
|
|
+ if result.Err != nil {
|
|
|
+ log.Println("Unable to extract NIC info from wmic: " + result.Err.Error())
|
|
|
}
|
|
|
-
|
|
|
+ return result.RX, result.TX, result.Err
|
|
|
} else if runtime.GOOS == "linux" {
|
|
|
allIfaceRxByteFiles, err := filepath.Glob("/sys/class/net/*/statistics/rx_bytes")
|
|
|
if err != nil {
|