1/*
2 KSysGuard, the KDE System Guard
3
4 Copyright (c) 1999 - 2001 Chris Schlaeger <cs@kde.org>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of version 2 of the GNU General Public
8 License as published by the Free Software Foundation.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19*/
20
21#include <config-workspace.h>
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <sys/time.h>
26#include <fcntl.h>
27#include <unistd.h>
28#include <stdio.h>
29#include <string.h>
30
31#include "Command.h"
32#include "ksysguardd.h"
33
34#include "netdev.h"
35
36#define MON_SIZE 128
37
38#define CALC( a, b, c, d, e, f ) \
39{ \
40 if (f){ \
41 if( NetDevs[i].oldInitialised) {\
42 if( a >= NetDevs[i].a ) \
43 NetDevs[ i ].delta##a = a - NetDevs[ i ].a; \
44 else \
45 NetDevs[ i ].delta##a = a; \
46 } else \
47 NetDevs[ i ].delta##a = 0; \
48 } \
49 NetDevs[ i ].a = a; \
50}
51
52#define REGISTERSENSOR( a, b, c, d, e, f ) \
53{ \
54 snprintf( mon, MON_SIZE, "network/interfaces/%s/%s", tag, b ); \
55 registerMonitor( mon, "float", printNetDev##a##0, printNetDev##a##0Info, NetDevSM ); \
56 if(f) { \
57 snprintf( mon, MON_SIZE, "network/interfaces/%s/%sTotal", tag, b ); \
58 registerMonitor( mon, "float", printNetDev##a##1, printNetDev##a##1Info, NetDevSM ); \
59 } \
60}
61
62#define UNREGISTERSENSOR( a, b, c, d, e, f ) \
63{ \
64 snprintf( mon, MON_SIZE, "network/interfaces/%s/%s", NetDevs[ i ].name, b ); \
65 removeMonitor( mon ); \
66 if(f) { \
67 snprintf( mon, MON_SIZE, "network/interfaces/%s/%sTotal", NetDevs[ i ].name, b ); \
68 removeMonitor( mon ); \
69 } \
70}
71
72#define DEFMEMBERS( a, b, c, d, e, f ) \
73unsigned long long delta##a; \
74unsigned long long a; \
75unsigned long a##Scale;
76
77#define DEFWIFIMEMBERS( a, b, c, d, e, f ) \
78signed long long delta##a; \
79signed long long a; \
80signed long a##Scale;
81
82#define DEFVARS( a, b, c, d, e, f) \
83unsigned long long a;
84
85#define DEFWIFIVARS( a, b, c, d, e, f) \
86signed long long a;
87
88/* The sixth variable is 1 if the quantity variation must be provided, 0 if the absolute value must be provided */
89#define FORALL( a ) \
90 a( recBytes, "receiver/data", "Received Data", "KB", 1024, 1) \
91 a( recPacks, "receiver/packets", "Received Packets", "", 1, 1 ) \
92 a( recErrs, "receiver/errors", "Receiver Errors", "", 1, 1 ) \
93 a( recDrop, "receiver/drops", "Receiver Drops", "", 1, 1 ) \
94 a( recFifo, "receiver/fifo", "Receiver FIFO Overruns", "", 1, 1 ) \
95 a( recFrame, "receiver/frame", "Receiver Frame Errors", "", 1, 1 ) \
96 a( recCompressed, "receiver/compressed", "Received Compressed Packets", "", 1, 1 ) \
97 a( recMulticast, "receiver/multicast", "Received Multicast Packets", "", 1, 1 ) \
98 a( sentBytes, "transmitter/data", "Sent Data", "KB", 1024, 1 ) \
99 a( sentPacks, "transmitter/packets", "Sent Packets", "", 1, 1 ) \
100 a( sentErrs, "transmitter/errors", "Transmitter Errors", "", 1, 1 ) \
101 a( sentDrop, "transmitter/drops", "Transmitter Drops", "", 1, 1 ) \
102 a( sentFifo, "transmitter/fifo", "Transmitter FIFO overruns", "", 1, 1 ) \
103 a( sentColls, "transmitter/collisions", "Transmitter Collisions", "", 1, 1 ) \
104 a( sentCarrier, "transmitter/carrier", "Transmitter Carrier losses", "", 1, 1 ) \
105 a( sentCompressed, "transmitter/compressed", "Transmitter Compressed Packets", "", 1, 1 )
106
107#define FORALLWIFI( a ) \
108 a( linkQuality, "wifi/quality", "Link Quality", "", 1, 0) \
109 a( signalLevel, "wifi/signal", "Signal Level", "dBm", 1, 0) \
110 a( noiseLevel, "wifi/noise", "Noise Level", "dBm", 1, 0) \
111 a( nwid, "wifi/nwid", "Rx Invalid Nwid Packets", "", 1, 1) \
112 a( RxCrypt, "wifi/crypt", "Rx Invalid Crypt Packets", "", 1, 1) \
113 a( frag, "wifi/frag", "Rx Invalid Frag Packets", "", 1, 1) \
114 a( retry, "wifi/retry", "Tx Excessive Retries Packets", "", 1, 1) \
115 a( misc, "wifi/misc", "Invalid Misc Packets", "", 1, 1) \
116 a( beacon, "wifi/beacon", "Missed Beacon", "", 1, 1)
117
118#define SETZERO( a, b, c, d, e, f ) \
119a = 0;
120
121#define SETMEMBERZERO( a, b, c, d, e, f ) \
122NetDevs[ i ].a = 0; \
123NetDevs[ i ].delta##a = 0; \
124NetDevs[ i ].a##Scale = e;
125
126#define DECLAREFUNC( a, b, c, d, e, f) \
127void printNetDev##a##0( const char* cmd ); \
128void printNetDev##a##0Info( const char* cmd ); \
129void printNetDev##a##1( const char* cmd ); \
130void printNetDev##a##1Info( const char* cmd ); \
131
132typedef struct
133{
134 FORALL( DEFMEMBERS )
135 FORALLWIFI( DEFWIFIMEMBERS )
136 char name[ 32 ];
137 int isWifi;
138 int oldInitialised;
139} NetDevInfo;
140
141/* We have observed deviations of up to 5% in the accuracy of the timer
142 * interrupts. So we try to measure the interrupt interval and use this
143 * value to calculate timing dependant values. */
144static float timeInterval = 0;
145static struct timeval lastSampling;
146static struct timeval currSampling;
147static struct SensorModul* NetDevSM;
148
149#define NETDEVBUFSIZE 4096
150static char NetDevBuf[ NETDEVBUFSIZE ];
151static char NetDevWifiBuf[ NETDEVBUFSIZE ];
152static int NetDevCnt = 0;
153static int Dirty = 0;
154static long OldHash = 0;
155
156#define MAXNETDEVS 64
157static NetDevInfo NetDevs[ MAXNETDEVS ];
158
159void processNetDev( void );
160
161FORALL( DECLAREFUNC )
162FORALLWIFI( DECLAREFUNC )
163
164static int processNetDev_( void )
165{
166 int i, j;
167 char format[ 32 ];
168 char devFormat[ 16 ];
169 char buf[ 1024 ];
170 char tag[ 64 ];
171 char* netDevBufP = NetDevBuf;
172 char* netDevWifiBufP = NetDevWifiBuf;
173
174 sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
175 sprintf( devFormat, "%%%ds", (int)sizeof( tag ) - 1 );
176
177 /*Update the values for the wifi interfaces if there is a /proc/net/wireless file*/
178 if (*netDevBufP != '\0') {
179 /* skip 2 first lines */
180 for (i = 0; i < 2; i++) {
181 sscanf(netDevBufP, format, buf);
182 buf[sizeof(buf) - 1] = '\0';
183 netDevBufP += strlen(buf) + 1; /* move netDevBufP to next line */
184 }
185
186 for (i = 0; sscanf(netDevBufP, format, buf) == 1; ++i) {
187 buf[sizeof(buf) - 1] = '\0';
188 netDevBufP += strlen(buf) + 1; /* move netDevBufP to next line */
189
190 if (sscanf(buf, devFormat, tag)) {
191 char* pos = strchr(tag, ':');
192 if (pos) {
193 FORALL( DEFVARS );
194 *pos = '\0';
195 FORALL( SETZERO );
196 sscanf(strchr(buf, ':') + 1, "%llu %llu %llu %llu %llu %llu %llu %llu "
197 "%llu %llu %llu %llu %llu %llu %llu %llu",
198 &recBytes, &recPacks, &recErrs, &recDrop, &recFifo,
199 &recFrame, &recCompressed, &recMulticast,
200 &sentBytes, &sentPacks, &sentErrs, &sentDrop,
201 &sentFifo, &sentColls, &sentCarrier, &sentCompressed);
202
203 if (i >= NetDevCnt || strcmp(NetDevs[i].name, tag) != 0) {
204 /* The network device configuration has changed. We
205 * need to reconfigure the netdev module. */
206 return -1;
207 } else {
208 FORALL( CALC );
209 if (!NetDevs[i].isWifi)
210 NetDevs[i].oldInitialised = 1;
211 }
212 }
213 }
214 }
215 if ( i != NetDevCnt )
216 return -1;
217 }
218
219
220
221 /*Update the values for the wifi interfaces if there is a /proc/net/wireless file*/
222 if (*netDevWifiBufP != '\0') {
223
224 /* skip 2 first lines */
225 for (i = 0; i < 2; i++) {
226 sscanf(netDevWifiBufP, format, buf);
227 buf[sizeof(buf) - 1] = '\0';
228 netDevWifiBufP += strlen(buf) + 1; /* move netDevWifiBufP to next line */
229 }
230
231 for (j = 0; sscanf(netDevWifiBufP, format, buf) == 1; ++j) {
232 buf[sizeof(buf) - 1] = '\0';
233 netDevWifiBufP += strlen(buf) + 1; /* move netDevWifiBufP to next line */
234
235 if (sscanf(buf, devFormat, tag)) {
236 char* pos = strchr(tag, ':');
237 if (pos) {
238 FORALLWIFI( DEFWIFIVARS );
239 *pos = '\0';
240
241 for (i = 0; i < NetDevCnt; ++i) { /*find the corresponding interface*/
242 if (strcmp(tag, NetDevs[i].name) == 0) {
243 break;
244 }
245 }
246 unsigned int wifiStatus;
247 sscanf(strchr(buf, ':') + 1, " %d %lli. %lli. %lli. %lli %lli %lli %lli %lli %lli",
248 &wifiStatus, &linkQuality, &signalLevel, &noiseLevel, &nwid,
249 &RxCrypt, &frag, &retry, &misc, &beacon);
250 signalLevel -= 256; /*the units are dBm*/
251 noiseLevel -= 256;
252 FORALLWIFI( CALC );
253 NetDevs[i].oldInitialised = 1;
254 }
255 }
256 }
257 }
258
259 /* save exact time inverval between this and the last read of
260 * /proc/net/dev */
261 timeInterval = currSampling.tv_sec - lastSampling.tv_sec +
262 ( currSampling.tv_usec - lastSampling.tv_usec ) / 1000000.0;
263 lastSampling = currSampling;
264 Dirty = 0;
265
266 return 0;
267}
268
269void processNetDev( void )
270{
271 int i;
272
273 if ( NetDevCnt == 0 )
274 return;
275
276 for ( i = 0; i < 5 && processNetDev_() < 0; ++i ) {
277 /* Values for other network devices are lost, but it is still better
278 * than not detecting any new devices */
279 exitNetDev();
280 initNetDev( NetDevSM );
281 }
282
283 /* If 5 reconfiguration attemts failed, something is very wrong and
284 * we close the netdev module for further use. */
285 if ( i == 5 )
286 exitNetDev();
287}
288
289/*
290================================ public part =================================
291*/
292
293void initNetDev( struct SensorModul* sm )
294{
295 int i, j;
296 char format[ 32 ];
297 char devFormat[ 16 ];
298 char buf[ 1024 ];
299 char tag[ 64 ];
300 char* netDevBufP = NetDevBuf;
301 char* netDevWifiBufP = NetDevWifiBuf;
302
303 NetDevSM = sm;
304
305 if ( updateNetDev() < 0 )
306 return;
307
308 sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
309 sprintf( devFormat, "%%%ds", (int)sizeof( tag ) - 1 );
310
311 /* skip 2 first lines */
312 for ( i = 0; i < 2; i++ ) {
313 sscanf( netDevBufP, format, buf );
314 buf[ sizeof( buf ) - 1 ] = '\0';
315 netDevBufP += strlen( buf ) + 1; /* move netDevBufP to next line */
316 }
317
318 for ( i = 0; sscanf( netDevBufP, format, buf ) == 1; ++i ) {
319 buf[ sizeof( buf ) - 1 ] = '\0';
320 netDevBufP += strlen( buf ) + 1; /* move netDevBufP to next line */
321
322 NetDevs[i].oldInitialised = 0;
323 if ( sscanf( buf, devFormat, tag ) ) {
324 char* pos = strchr( tag, ':' );
325 FORALL( SETMEMBERZERO );
326 if ( pos ) {
327 char mon[ MON_SIZE ];
328 *pos = '\0';
329 strncpy( NetDevs[ i ].name, tag, sizeof( NetDevs[ i ].name ) );
330 NetDevs[ i ].name[ sizeof( NetDevs[ i ].name )-1] = 0;
331 FORALL( REGISTERSENSOR );
332 sscanf( pos + 1, "%lli %lli %lli %lli %lli %lli %lli %lli"
333 "%lli %lli %lli %lli %lli %lli %lli %lli",
334 &NetDevs[ i ].recBytes, &NetDevs[ i ].recPacks,
335 &NetDevs[ i ].recErrs, &NetDevs[ i ].recDrop,
336 &NetDevs[ i ].recFifo, &NetDevs[ i ].recFrame,
337 &NetDevs[ i ].recCompressed, &NetDevs[ i ].recMulticast,
338 &NetDevs[ i ].sentBytes, &NetDevs[ i ].sentPacks,
339 &NetDevs[ i ].sentErrs, &NetDevs[ i ].sentDrop,
340 &NetDevs[ i ].sentFifo, &NetDevs[ i ].sentColls,
341 &NetDevs[ i ].sentCarrier, &NetDevs[ i ].sentCompressed );
342 NetDevCnt++;
343 }
344 }
345 }
346
347 /* detect the wifi interfaces*/
348 /* skip 2 first lines */
349 for ( i = 0; i < 2; i++ ) {
350 sscanf( netDevWifiBufP, format, buf );
351 buf[ sizeof( buf ) - 1 ] = '\0';
352 netDevWifiBufP += strlen( buf ) + 1; /* move netDevWifiBufP to next line */
353 }
354
355 for ( j = 0; sscanf( netDevWifiBufP, format, buf ) == 1; ++j ) {
356 buf[ sizeof( buf ) - 1 ] = '\0';
357 netDevWifiBufP += strlen( buf ) + 1; /* move netDevWifiBufP to next line */
358
359 if ( sscanf( buf, devFormat, tag ) ) {
360 char * pos = strchr( tag, ':' );
361 if ( pos ) {
362 char mon[ MON_SIZE ];
363 *pos = '\0';
364 /*find and tag the corresponding NetDev as wifi enabled.
365 At the end of the loop, i is the index of the device.
366 This variable i is used in some macro */
367 for (i = 0 ; i < NetDevCnt ; ++i){
368 if ( strcmp(tag,NetDevs[ i ].name)==0){
369 NetDevs[ i ].isWifi = 1;
370 break;
371 }
372 }
373 FORALLWIFI( REGISTERSENSOR );
374 }
375 FORALLWIFI( SETMEMBERZERO ); /* the variable i must point to the corrrect NetDevs[i]*/
376 }
377 }
378
379 /* Call processNetDev to elimitate initial peek values. */
380 processNetDev();
381}
382
383void exitNetDev( void )
384{
385 int i;
386
387 for ( i = 0; i < NetDevCnt; ++i ) {
388 char mon[ MON_SIZE ];
389 FORALL( UNREGISTERSENSOR );
390 if (NetDevs[ i ].isWifi)
391 FORALLWIFI( UNREGISTERSENSOR );
392 }
393 NetDevCnt = 0;
394}
395
396int updateNetDev( void )
397{
398 /* We read the information about the network interfaces from
399 /proc/net/dev. The file should look like this:
400
401 Inter-| Receive | Transmit
402 face | bytes packets errs drop fifo frame compressed multicast| bytes packets errs drop fifo colls carrier compressed
403 lo:275135772 1437448 0 0 0 0 0 0 275135772 1437448 0 0 0 0 0 0
404 eth0:123648812 655251 0 0 0 0 0 0 246847871 889636 0 0 0 0 0 0
405 */
406
407 size_t n;
408 int fd;
409 long hash;
410 char* p;
411
412 if ((fd = open("/proc/net/dev", O_RDONLY)) > 0) {
413 n = read(fd, NetDevBuf, NETDEVBUFSIZE - 1);
414 if (n == NETDEVBUFSIZE - 1 || n <= 0) {
415 log_error("Internal buffer too small to read \'/proc/net/dev\'");
416 close(fd);
417 return -1;
418 }
419
420 gettimeofday(&currSampling, 0);
421 close(fd);
422 NetDevBuf[n] = '\0';
423
424 /* Calculate hash over the first 7 characters of each line starting
425 * after the first newline. This will detect whether any interfaces
426 * have either appeared or disappeared. */
427 for (p = NetDevBuf, hash = 0; *p; ++p)
428 if (*p == '\n')
429 for (++p; *p && *p != ':' && *p != '|'; ++p)
430 hash = ((hash << 6) + *p) % 390389;
431
432 if (OldHash != 0 && OldHash != hash) {
433 print_error("RECONFIGURE");
434 CheckSetupFlag = 1;
435 }
436 OldHash = hash;
437 }
438
439 /* We read the information about the wifi from /proc/net/wireless and store it into NetDevWifiBuf */
440 if ( ( fd = open( "/proc/net/wireless", O_RDONLY ) ) < 0 ) {
441 /* /proc/net/wireless may not exist on some machines. */
442 NetDevWifiBuf[0]='\0';
443 } else if ( ( n = read( fd, NetDevWifiBuf, NETDEVBUFSIZE - 1 ) ) == NETDEVBUFSIZE - 1 ) {
444 log_error( "Internal buffer too small to read \'/proc/net/wireless\'" );
445 close( fd );
446 return -1;
447 } else {
448 close( fd );
449 NetDevWifiBuf[ n ] = '\0';
450 }
451 Dirty = 1;
452
453 return 0;
454}
455
456void checkNetDev( void )
457{
458 updateNetDev();
459}
460
461#define PRINTFUNC( a, b, c, d, e, f ) \
462void printNetDev##a##0( const char* cmd ) \
463{ \
464 int i; \
465 char* beg; \
466 char* end; \
467 char dev[ 64 ]; \
468 \
469 beg = strchr( cmd, '/' ); \
470 beg = strchr( beg + 1, '/' ); \
471 end = strchr( beg + 1, '/' ); \
472 strncpy( dev, beg + 1, end - beg - 1 ); \
473 dev[ end - beg - 1 ] = '\0'; \
474 \
475 if ( Dirty ) \
476 processNetDev(); \
477 \
478 for ( i = 0; i < MAXNETDEVS; ++i ) \
479 if ( strcmp( NetDevs[ i ].name, dev ) == 0) { \
480 if (f && timeInterval < 0.01) \
481 /*Time interval is very small. Can we really get an accurate value from this? Assume not*/ \
482 output( "0\n"); \
483 else if(f) \
484 output( "%li\n", (long) \
485 ( NetDevs[ i ].delta##a / ( NetDevs[ i ].a##Scale * timeInterval ) ) ); \
486 else \
487 output( "%li\n", (long) NetDevs[ i ].a ); \
488 return; \
489 } \
490 \
491 output( "0\n" ); \
492} \
493void printNetDev##a##0##Info( const char* cmd ) \
494{ \
495 char* beg; \
496 char* end; \
497 char dev[ 64 ]; \
498 \
499 beg = strchr( cmd, '/' ); \
500 beg = strchr( beg + 1, '/' ); \
501 end = strchr( beg + 1, '/' ); \
502 strncpy( dev, beg + 1, end - beg - 1 ); \
503 dev[ end - beg - 1 ] = '\0'; \
504\
505 if(f && d[0] == 0) \
506 output( "%s %s Rate\t0\t0\t1/s\n", dev, c); \
507 else if(f) \
508 output( "%s %s Rate\t0\t0\t%s/s\n", dev, c, d ); \
509 else \
510 output( "%s %s\t0\t0\t%s\n", dev, c, d ); \
511} \
512void printNetDev##a##1( const char* cmd ) \
513{ \
514 if(f) { \
515 int i; \
516 char* beg; \
517 char* end; \
518 char dev[ 64 ]; \
519 \
520 beg = strchr( cmd, '/' ); \
521 beg = strchr( beg + 1, '/' ); \
522 end = strchr( beg + 1, '/' ); \
523 strncpy( dev, beg + 1, end - beg - 1 ); \
524 dev[ end - beg - 1 ] = '\0'; \
525 \
526 if ( Dirty ) \
527 processNetDev(); \
528 \
529 for ( i = 0; i < MAXNETDEVS; ++i ) \
530 if ( strcmp( NetDevs[ i ].name, dev ) == 0) { \
531 output( "%li\n", (long) NetDevs[ i ].a / ( NetDevs[ i ].a##Scale) ); \
532 return; \
533 } \
534 \
535 output( "0\n" ); \
536 } \
537} \
538void printNetDev##a##1##Info( const char* cmd ) \
539{ \
540 if(f) { \
541 char* beg; \
542 char* end; \
543 char dev[ 64 ]; \
544 \
545 beg = strchr( cmd, '/' ); \
546 beg = strchr( beg + 1, '/' ); \
547 end = strchr( beg + 1, '/' ); \
548 strncpy( dev, beg + 1, end - beg - 1 ); \
549 dev[ end - beg - 1 ] = '\0'; \
550\
551 output( "%s %s\t0\t0\t%s\n", dev, c, d ); \
552 } \
553}
554
555
556FORALL( PRINTFUNC )
557FORALLWIFI( PRINTFUNC )
558