aboutsummaryrefslogtreecommitdiff
path: root/statistics.cpp
blob: 800a4e55c9c8c3f7428a89da94275f0f28fd176d (plain)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "config.h"
#include "statistics.hpp"
#include <algorithm>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <sys/time.h>

#include <iostream>
#include <sstream>

#define PROC_NET_DEV "/proc/net/dev"

namespace statistics {

Interface::Interface(const std::string& name) : name_(name), updated_(false) {}

class InterfaceNameMatchesPredicate {
    public:
	InterfaceNameMatchesPredicate(const std::string& name) : name_(name) {}

	bool operator() (const Interface& interface) const { 
	    return name_ == interface.getName(); 
	}
    private:
	std::string name_;
};

class InterfaceNotUpdatedPredicate {
    public:
	bool operator() (const Interface& interface) const { 
	    return !interface.getUpdated(); 
	}
};

void Interface::update(const Statistics& statistics) {
    memcpy(statistics_ + 1, statistics_ + 0, sizeof(Statistics));
    memcpy(statistics_, &statistics, sizeof(Statistics));
   
    const Statistics& x0 = statistics_[1];
    const Statistics& x1 = statistics_[0];
    
    double timeDelta = 
	(x1.timestamp.tv_sec - x0.timestamp.tv_sec) * 1.
      + (x1.timestamp.tv_usec - x0.timestamp.tv_usec) * .000001;

    receiveSpeed_ = (x1.rx_bytes - x0.rx_bytes) / timeDelta;
    transmitSpeed_ = (x1.tx_bytes - x0.tx_bytes) / timeDelta;

    updated_ = true;
}

void Reader::update() {
    // Open /proc/net/dev
    FILE *dev = fopen(PROC_NET_DEV, "r");
    if (!dev) {
		std::cerr << "cannot open " << PROC_NET_DEV << "\n";
		exit(EXIT_FAILURE);
	}

    try {
	// Clear the 'updated' flag for all interfaces
	for (Interfaces::iterator interface = interfaces_.begin();
		interface != interfaces_.end(); ++interface)
	    interface->setUpdated(false);

	// Read /proc/dev/net
	while (!feof(dev)) {
	    char buf[500];
	    char *name, *statistics_string;
	    Statistics stats;
	    struct timezone unused_timezone;

	    // Read a line
	    fgets(buf, sizeof(buf), dev);
	    gettimeofday(&stats.timestamp, &unused_timezone);

	    // Find the colon separating the name from the statistics
	    statistics_string = strchr(buf, ':');
	    if (!statistics_string) continue;

	    // Get a pointer to the statistics
	    *statistics_string = 0;
	    ++statistics_string;

	    // Remove leading whitespace from the name
	    name = buf;
	    while (*name == ' ' || *name == '\t') ++name;
	    
	    // Parse the statistics
	    if (sscanf(statistics_string, "%Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu "
					  "%Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
			    &stats.rx_bytes, &stats.rx_packets, 
			    &stats.rx_errs, &stats.rx_drop, 
			    &stats.rx_fifo, &stats.rx_frame, 
			    &stats.rx_compressed, &stats.rx_multicast,
			    &stats.tx_bytes, &stats.tx_packets, 
			    &stats.tx_errs, &stats.tx_drop, 
			    &stats.tx_fifo, &stats.tx_frame, 
			    &stats.tx_compressed, &stats.tx_multicast) 
		    == 16) {
		
		// Find the interface with the same name
		Interfaces::iterator interface 
		    = std::find_if(interfaces_.begin(), interfaces_.end(), 
			InterfaceNameMatchesPredicate(name));
		
		if (interface == interfaces_.end()) {
		    // This is a new interface; add it to the list
		    interfaces_.push_back(Interface(name));
		    interface = interfaces_.end();
		    --interface;
		}
		
		// Update the interface
		interface->update(stats);
	    }
	}

	// Remove all interfaces for which the updated flag was not set
	interfaces_.remove_if(InterfaceNotUpdatedPredicate());
	
	// Close the file
	fclose(dev);
    }
    catch (...) {
	fclose(dev);
	throw;
    }
}

void Interface::setUpdated(bool updated) {
    updated_ = updated;
}

bool Interface::getUpdated() const {
    return updated_;
}

const std::string& Interface::getName() const {
    return name_;
}

double Interface::getReceiveSpeed() const {
    return receiveSpeed_;
}

double Interface::getTransmitSpeed() const {
    return transmitSpeed_;
}

const Reader::Interfaces& Reader::getInterfaces() const {
    return interfaces_;
}

} // namespace statistics