1/*
2 KSysGuard, the KDE System Guard
3
4 Copyright (c) 1999, 2000 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 * stat.c is used to read from /proc/[pid]/stat
21*/
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 <stdlib.h>
30#include <string.h>
31#include <ctype.h>
32
33#include "Command.h"
34#include "ksysguardd.h"
35
36#include "stat.h"
37
38typedef struct {
39 /* A CPU can be loaded with user processes, reniced processes and
40 * system processes. Unused processing time is called idle load.
41 * These variable store the percentage of each load type. */
42 float userLoad;
43 float niceLoad;
44 float sysLoad;
45 float idleLoad;
46 float waitLoad;
47
48 /* To calculate the loads we need to remember the tick values for each
49 * load type. */
50 unsigned long userTicks;
51 unsigned long niceTicks;
52 unsigned long sysTicks;
53 unsigned long idleTicks;
54 unsigned long waitTicks;
55} CPULoadInfo;
56
57typedef struct {
58 unsigned long delta;
59 unsigned long old;
60} DiskLoadSample;
61
62typedef struct {
63 /* 5 types of samples are taken:
64 total, rio, wio, rBlk, wBlk */
65 DiskLoadSample s[ 5 ];
66} DiskLoadInfo;
67
68typedef struct DiskIOInfo {
69 int major;
70 int minor;
71 char* devname;
72
73 int alive;
74 DiskLoadSample total;
75 DiskLoadSample rio;
76 DiskLoadSample wio;
77 DiskLoadSample rblk;
78 DiskLoadSample wblk;
79 struct DiskIOInfo* next;
80} DiskIOInfo;
81
82#define DISKDEVNAMELEN 16
83
84static int StatDirty = 0;
85
86/* We have observed deviations of up to 5% in the accuracy of the timer
87* interrupts. So we try to measure the interrupt interval and use this
88* value to calculate timing dependant values. */
89static float timeInterval = 0;
90static struct timeval lastSampling;
91static struct timeval currSampling;
92static struct SensorModul* StatSM;
93
94static CPULoadInfo CPULoad;
95static CPULoadInfo* SMPLoad = 0;
96static unsigned CPUCount = 0;
97static DiskLoadInfo* DiskLoad = 0;
98static unsigned DiskCount = 0;
99static DiskIOInfo* DiskIO = 0;
100static unsigned long PageIn = 0;
101static unsigned long OldPageIn = 0;
102static unsigned long PageOut = 0;
103static unsigned long OldPageOut = 0;
104static unsigned long Ctxt = 0;
105static unsigned long OldCtxt = 0;
106static unsigned int NumOfInts = 0;
107static unsigned long* OldIntr = 0;
108static unsigned long* Intr = 0;
109
110static int initStatDisk( char* tag, char* buf, const char* label, const char* shortLabel,
111 int idx, cmdExecutor ex, cmdExecutor iq );
112static void updateCPULoad( const char* line, CPULoadInfo* load );
113static int process24Disk( char* tag, char* buf, const char* label, int idx );
114static void processStat( void );
115static int process24DiskIO( const char* buf );
116static void cleanup24DiskList( void );
117
118static int initStatDisk( char* tag, char* buf, const char* label,
119 const char* shortLabel, int idx, cmdExecutor ex, cmdExecutor iq )
120{
121 char sensorName[ 128 ];
122
123 gettimeofday( &lastSampling, 0 );
124
125 if ( strcmp( label, tag ) == 0 ) {
126 unsigned int i;
127 buf = buf + strlen( label ) + 1;
128
129 for ( i = 0; i < DiskCount; ++i ) {
130 sscanf( buf, "%lu", &DiskLoad[ i ].s[ idx ].old );
131 while ( *buf && isblank( *buf++ ) );
132 while ( *buf && isdigit( *buf++ ) );
133 sprintf( sensorName, "disk/disk%d/%s", i, shortLabel );
134 registerMonitor( sensorName, "float", ex, iq, StatSM );
135 }
136
137 return 1;
138 }
139
140 return 0;
141}
142
143/**
144 * updateCPULoad
145 *
146 * Parses the total cpu status line from /proc/stat
147 */
148static void updateCPULoad( const char* line, CPULoadInfo* load ) {
149 unsigned long currUserTicks, currSysTicks, currNiceTicks;
150 unsigned long currIdleTicks, currWaitTicks, totalTicks;
151
152 if(sscanf( line, "%*s %lu %lu %lu %lu %lu", &currUserTicks, &currNiceTicks,
153 &currSysTicks, &currIdleTicks, &currWaitTicks ) != 5) {
154 return;
155 }
156
157 totalTicks = ( currUserTicks - load->userTicks ) +
158 ( currSysTicks - load->sysTicks ) +
159 ( currNiceTicks - load->niceTicks ) +
160 ( currIdleTicks - load->idleTicks ) +
161 ( currWaitTicks - load->waitTicks );
162
163 if ( totalTicks > 10 ) {
164 load->userLoad = ( 100.0 * ( currUserTicks - load->userTicks ) ) / totalTicks;
165 load->sysLoad = ( 100.0 * ( currSysTicks - load->sysTicks ) ) / totalTicks;
166 load->niceLoad = ( 100.0 * ( currNiceTicks - load->niceTicks ) ) / totalTicks;
167 load->idleLoad = ( 100.0 * ( currIdleTicks - load->idleTicks ) ) / totalTicks;
168 load->waitLoad = ( 100.0 * ( currWaitTicks - load->waitTicks ) ) / totalTicks;
169 }
170 else
171 load->userLoad = load->sysLoad = load->niceLoad = load->idleLoad = load->waitLoad = 0.0;
172
173 load->userTicks = currUserTicks;
174 load->sysTicks = currSysTicks;
175 load->niceTicks = currNiceTicks;
176 load->idleTicks = currIdleTicks;
177 load->waitTicks = currWaitTicks;
178}
179
180static int process24Disk( char* tag, char* buf, const char* label, int idx ) {
181 if ( strcmp( label, tag ) == 0 ) {
182 unsigned long val;
183 unsigned int i;
184 buf = buf + strlen( label ) + 1;
185
186 for ( i = 0; i < DiskCount; ++i ) {
187 sscanf( buf, "%lu", &val );
188 while ( *buf && isblank( *buf++ ) );
189 while ( *buf && isdigit( *buf++ ) );
190 DiskLoad[ i ].s[ idx ].delta = val - DiskLoad[ i ].s[ idx ].old;
191 DiskLoad[ i ].s[ idx ].old = val;
192 }
193
194 return 1;
195 }
196
197 return 0;
198}
199
200static int process24DiskIO( const char* buf ) {
201 /* Process disk_io lines as provided by 2.4.x kernels.
202 * disk_io: (2,0):(3,3,6,0,0) (3,0):(1413012,511622,12155382,901390,26486215) */
203 int major, minor;
204 unsigned long total, rblk, rio, wblk, wio;
205 DiskIOInfo* ptr = DiskIO;
206 DiskIOInfo* last = 0;
207 char sensorName[ 128 ];
208 const char* p;
209
210 p = buf + strlen( "disk_io: " );
211 while ( p && *p ) {
212 if ( sscanf( p, "(%d,%d):(%lu,%lu,%lu,%lu,%lu)", &major, &minor,
213 &total, &rio, &rblk, &wio, &wblk ) != 7 )
214 return -1;
215
216 last = 0;
217 ptr = DiskIO;
218 while ( ptr ) {
219 if ( ptr->major == major && ptr->minor == minor ) {
220 /* The IO device has already been registered. */
221 ptr->total.delta = total - ptr->total.old;
222 ptr->total.old = total;
223 ptr->rio.delta = rio - ptr->rio.old;
224 ptr->rio.old = rio;
225 ptr->wio.delta = wio - ptr->wio.old;
226 ptr->wio.old = wio;
227 ptr->rblk.delta = rblk - ptr->rblk.old;
228 ptr->rblk.old = rblk;
229 ptr->wblk.delta = wblk - ptr->wblk.old;
230 ptr->wblk.old = wblk;
231 ptr->alive = 1;
232 break;
233 }
234
235 last = ptr;
236 ptr = ptr->next;
237 }
238
239 if ( !ptr ) {
240 /* The IO device has not been registered yet. We need to add it. */
241 ptr = (DiskIOInfo*)malloc( sizeof( DiskIOInfo ) );
242 ptr->major = major;
243 ptr->minor = minor;
244
245 /* 2.6 gives us a nice device name. On 2.4 we get nothing */
246 ptr->devname = (char *)malloc( DISKDEVNAMELEN );
247 memset( ptr->devname, 0, DISKDEVNAMELEN );
248
249 ptr->total.delta = 0;
250 ptr->total.old = total;
251 ptr->rio.delta = 0;
252 ptr->rio.old = rio;
253 ptr->wio.delta = 0;
254 ptr->wio.old = wio;
255 ptr->rblk.delta = 0;
256 ptr->rblk.old = rblk;
257 ptr->wblk.delta = 0;
258 ptr->wblk.old = wblk;
259 ptr->alive = 1;
260 ptr->next = 0;
261 if ( last ) {
262 /* Append new entry at end of list. */
263 last->next = ptr;
264 }
265 else {
266 /* List is empty, so we insert the fist element into the list. */
267 DiskIO = ptr;
268 }
269
270 sprintf( sensorName, "disk/%s_(%d:%d)24/total", ptr->devname, major, minor );
271 registerMonitor( sensorName, "float", print24DiskIO, print24DiskIOInfo, StatSM );
272 sprintf( sensorName, "disk/%s_(%d:%d)24/rio", ptr->devname, major, minor );
273 registerMonitor( sensorName, "float", print24DiskIO, print24DiskIOInfo, StatSM );
274 sprintf( sensorName, "disk/%s_(%d:%d)24/wio", ptr->devname, major, minor );
275 registerMonitor( sensorName, "float", print24DiskIO, print24DiskIOInfo, StatSM );
276 sprintf( sensorName, "disk/%s_(%d:%d)24/rblk", ptr->devname, major, minor );
277 registerMonitor( sensorName, "float", print24DiskIO, print24DiskIOInfo, StatSM );
278 sprintf( sensorName, "disk/%s_(%d:%d)24/wblk", ptr->devname, major, minor );
279 registerMonitor( sensorName, "float", print24DiskIO, print24DiskIOInfo, StatSM );
280 }
281
282 /* Move p after the second ')'. We can safely assume that
283 * those two ')' exist. */
284 p = strchr( p, ')' ) + 1;
285 p = strchr( p, ')' ) + 1;
286 if ( p && *p )
287 p = strchr( p, '(' );
288 }
289
290 return 0;
291}
292
293static void cleanup24DiskList( void ) {
294 DiskIOInfo* ptr = DiskIO;
295 DiskIOInfo* last = 0;
296
297 while ( ptr ) {
298 if ( ptr->alive == 0 ) {
299 DiskIOInfo* newPtr;
300 char sensorName[ 128 ];
301
302 /* Disk device has disappeared. We have to remove it from
303 * the list and unregister the monitors. */
304 sprintf( sensorName, "disk/%s_(%d:%d)24/total", ptr->devname, ptr->major, ptr->minor );
305 removeMonitor( sensorName );
306 sprintf( sensorName, "disk/%s_(%d:%d)24/rio", ptr->devname, ptr->major, ptr->minor );
307 removeMonitor( sensorName );
308 sprintf( sensorName, "disk/%s_(%d:%d)24/wio", ptr->devname, ptr->major, ptr->minor );
309 removeMonitor( sensorName );
310 sprintf( sensorName, "disk/%s_(%d:%d)24/rblk", ptr->devname, ptr->major, ptr->minor );
311 removeMonitor( sensorName );
312 sprintf( sensorName, "disk/%s_(%d:%d)24/wblk", ptr->devname, ptr->major, ptr->minor );
313 removeMonitor( sensorName );
314 if ( last ) {
315 last->next = ptr->next;
316 newPtr = ptr->next;
317 }
318 else {
319 DiskIO = ptr->next;
320 newPtr = DiskIO;
321 last = 0;
322 }
323
324 free ( ptr );
325 ptr = newPtr;
326 }
327 else {
328 ptr->alive = 0;
329 last = ptr;
330 ptr = ptr->next;
331 }
332 }
333}
334
335static void processStat( void ) {
336 char format[ 32 ];
337 char tagFormat[ 16 ];
338 char buf[ 1024 ];
339 char tag[ 32 ];
340
341 sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
342 sprintf( tagFormat, "%%%ds", (int)sizeof( tag ) - 1 );
343
344 gettimeofday( &currSampling, 0 );
345 StatDirty = 0;
346
347 FILE *stat = fopen("/proc/stat", "r");
348 if(!stat) {
349 print_error( "Cannot open file \'/proc/stat\'!\n"
350 "The kernel needs to be compiled with support\n"
351 "for /proc file system enabled!\n" );
352 return;
353 }
354
355 while ( fscanf( stat, format, buf ) == 1 ) {
356 buf[ sizeof( buf ) - 1 ] = '\0';
357 sscanf( buf, tagFormat, tag );
358
359 if ( strcmp( "cpu", tag ) == 0 ) {
360 /* Total CPU load */
361 updateCPULoad( buf, &CPULoad );
362 }
363 else if ( strncmp( "cpu", tag, 3 ) == 0 ) {
364 /* Load for each SMP CPU */
365 int id;
366 sscanf( tag + 3, "%d", &id );
367 updateCPULoad( buf, &SMPLoad[ id ] );
368 }
369 else if ( process24Disk( tag, buf, "disk", 0 ) ) {
370 }
371 else if ( process24Disk( tag, buf, "disk_rio", 1 ) ) {
372 }
373 else if ( process24Disk( tag, buf, "disk_wio", 2 ) ) {
374 }
375 else if ( process24Disk( tag, buf, "disk_rblk", 3 ) ) {
376 }
377 else if ( process24Disk( tag, buf, "disk_wblk", 4 ) ) {
378 }
379 else if ( strcmp( "disk_io:", tag ) == 0 ) {
380 process24DiskIO( buf );
381 }
382 else if ( strcmp( "page", tag ) == 0 ) {
383 unsigned long v1, v2;
384 sscanf( buf + 5, "%lu %lu", &v1, &v2 );
385 PageIn = v1 - OldPageIn;
386 OldPageIn = v1;
387 PageOut = v2 - OldPageOut;
388 OldPageOut = v2;
389 }
390 else if ( strcmp( "intr", tag ) == 0 ) {
391 unsigned int i = 0;
392 char* p = buf + 5;
393
394 for ( i = 0; i < NumOfInts; i++ ) {
395 unsigned long val;
396
397 sscanf( p, "%lu", &val );
398 Intr[ i ] = val - OldIntr[ i ];
399 OldIntr[ i ] = val;
400 while ( *p && *p != ' ' )
401 p++;
402 while ( *p && *p == ' ' )
403 p++;
404 }
405 } else if ( strcmp( "ctxt", tag ) == 0 ) {
406 unsigned long val;
407
408 sscanf( buf + 5, "%lu", &val );
409 Ctxt = val - OldCtxt;
410 OldCtxt = val;
411 }
412 }
413 fclose(stat);
414
415 /* Read Linux 2.5.x /proc/vmstat */
416 stat = fopen("/proc/vmstat", "r");
417 if(stat) {
418 while ( fscanf( stat, format, buf ) == 1 ) {
419 buf[ sizeof( buf ) - 1 ] = '\0';
420 sscanf( buf, tagFormat, tag );
421
422 if ( strcmp( "pgpgin", tag ) == 0 ) {
423 unsigned long v1;
424 sscanf( buf + 7, "%lu", &v1 );
425 PageIn = v1 - OldPageIn;
426 OldPageIn = v1;
427 }
428 else if ( strcmp( "pgpgout", tag ) == 0 ) {
429 unsigned long v1;
430 sscanf( buf + 7, "%lu", &v1 );
431 PageOut = v1 - OldPageOut;
432 OldPageOut = v1;
433 }
434 }
435 fclose(stat);
436 }
437
438 /* save exact time interval between this and the last read of /proc/stat */
439 timeInterval = currSampling.tv_sec - lastSampling.tv_sec +
440 ( currSampling.tv_usec - lastSampling.tv_usec ) / 1000000.0;
441 lastSampling = currSampling;
442
443 cleanup24DiskList();
444
445}
446
447/*
448================================ public part =================================
449*/
450
451void initStat( struct SensorModul* sm ) {
452 /* The CPU load is calculated from the values in /proc/stat. The cpu
453 * entry contains 7 counters. These counters count the number of ticks
454 * the system has spend on user processes, system processes, nice
455 * processes, idle and IO-wait time, hard and soft interrupts.
456 *
457 * SMP systems will have cpu1 to cpuN lines right after the cpu info. The
458 * format is identical to cpu and reports the information for each cpu.
459 * Linux kernels <= 2.0 do not provide this information!
460 *
461 * The /proc/stat file looks like this:
462 *
463 * cpu <user> <nice> <system> <idling> <waiting> <hardinterrupt> <softinterrupt>
464 * disk 7797 0 0 0
465 * disk_rio 6889 0 0 0
466 * disk_wio 908 0 0 0
467 * disk_rblk 13775 0 0 0
468 * disk_wblk 1816 0 0 0
469 * page 27575 1330
470 * swap 1 0
471 * intr 50444 38672 2557 0 0 0 0 2 0 2 0 0 3 1429 1 7778 0
472 * ctxt 54155
473 * btime 917379184
474 * processes 347
475 *
476 * Linux kernel >= 2.4.0 have one or more disk_io: lines instead of
477 * the disk_* lines.
478 *
479 * Linux kernel >= 2.6.x(?) have disk I/O stats in /proc/diskstats
480 * and no disk relevant lines are found in /proc/stat
481 */
482
483 char format[ 32 ];
484 char tagFormat[ 16 ];
485 char buf[ 1024 ];
486 char tag[ 32 ];
487
488 StatSM = sm;
489
490 sprintf( format, "%%%d[^\n]\n", (int)sizeof( buf ) - 1 );
491 sprintf( tagFormat, "%%%ds", (int)sizeof( tag ) - 1 );
492
493 FILE *stat = fopen("/proc/stat", "r");
494 if(!stat) {
495 print_error( "Cannot open file \'/proc/stat\'!\n"
496 "The kernel needs to be compiled with support\n"
497 "for /proc file system enabled!\n" );
498 return;
499 }
500 while ( fscanf( stat, format, buf ) == 1 ) {
501 buf[ sizeof( buf ) - 1 ] = '\0';
502 sscanf( buf, tagFormat, tag );
503
504 if ( strcmp( "cpu", tag ) == 0 ) {
505 /* Total CPU load */
506 registerMonitor( "cpu/system/user", "float", printCPUUser, printCPUUserInfo, StatSM );
507 registerMonitor( "cpu/system/nice", "float", printCPUNice, printCPUNiceInfo, StatSM );
508 registerMonitor( "cpu/system/sys", "float", printCPUSys, printCPUSysInfo, StatSM );
509 registerMonitor( "cpu/system/TotalLoad", "float", printCPUTotalLoad, printCPUTotalLoadInfo, StatSM );
510 registerMonitor( "cpu/system/idle", "float", printCPUIdle, printCPUIdleInfo, StatSM );
511 registerMonitor( "cpu/system/wait", "float", printCPUWait, printCPUWaitInfo, StatSM );
512
513 /* Monitor names changed from kde3 => kde4. Remain compatible with legacy requests when possible. */
514 registerLegacyMonitor( "cpu/user", "float", printCPUUser, printCPUUserInfo, StatSM );
515 registerLegacyMonitor( "cpu/nice", "float", printCPUNice, printCPUNiceInfo, StatSM );
516 registerLegacyMonitor( "cpu/sys", "float", printCPUSys, printCPUSysInfo, StatSM );
517 registerLegacyMonitor( "cpu/TotalLoad", "float", printCPUTotalLoad, printCPUTotalLoadInfo, StatSM );
518 registerLegacyMonitor( "cpu/idle", "float", printCPUIdle, printCPUIdleInfo, StatSM );
519 registerLegacyMonitor( "cpu/wait", "float", printCPUWait, printCPUWaitInfo, StatSM );
520 }
521 else if ( strncmp( "cpu", tag, 3 ) == 0 ) {
522 char cmdName[ 24 ];
523 /* Load for each SMP CPU */
524 int id;
525
526 sscanf( tag + 3, "%d", &id );
527 CPUCount++;
528 sprintf( cmdName, "cpu/cpu%d/user", id );
529 registerMonitor( cmdName, "float", printCPUxUser, printCPUxUserInfo, StatSM );
530 sprintf( cmdName, "cpu/cpu%d/nice", id );
531 registerMonitor( cmdName, "float", printCPUxNice, printCPUxNiceInfo, StatSM );
532 sprintf( cmdName, "cpu/cpu%d/sys", id );
533 registerMonitor( cmdName, "float", printCPUxSys, printCPUxSysInfo, StatSM );
534 sprintf( cmdName, "cpu/cpu%d/TotalLoad", id );
535 registerMonitor( cmdName, "float", printCPUxTotalLoad, printCPUxTotalLoadInfo, StatSM );
536 sprintf( cmdName, "cpu/cpu%d/idle", id );
537 registerMonitor( cmdName, "float", printCPUxIdle, printCPUxIdleInfo, StatSM );
538 sprintf( cmdName, "cpu/cpu%d/wait", id );
539 registerMonitor( cmdName, "float", printCPUxWait, printCPUxWaitInfo, StatSM );
540 }
541 else if ( strcmp( "disk", tag ) == 0 ) {
542 unsigned long val;
543 char* b = buf + 5;
544
545 /* Count the number of registered disks */
546 for ( DiskCount = 0; *b && sscanf( b, "%lu", &val ) == 1; DiskCount++ ) {
547 while ( *b && isblank( *b++ ) );
548 while ( *b && isdigit( *b++ ) );
549 }
550
551 if ( DiskCount > 0 )
552 DiskLoad = (DiskLoadInfo*)malloc( sizeof( DiskLoadInfo ) * DiskCount );
553
554 initStatDisk( tag, buf, "disk", "disk", 0, print24DiskTotal, print24DiskTotalInfo );
555 }
556 else if ( initStatDisk( tag, buf, "disk_rio", "rio", 1, print24DiskRIO, print24DiskRIOInfo ) );
557 else if ( initStatDisk( tag, buf, "disk_wio", "wio", 2, print24DiskWIO, print24DiskWIOInfo ) );
558 else if ( initStatDisk( tag, buf, "disk_rblk", "rblk", 3, print24DiskRBlk, print24DiskRBlkInfo ) );
559 else if ( initStatDisk( tag, buf, "disk_wblk", "wblk", 4, print24DiskWBlk, print24DiskWBlkInfo ) );
560 else if ( strcmp( "disk_io:", tag ) == 0 )
561 process24DiskIO( buf );
562 else if ( strcmp( "page", tag ) == 0 ) {
563 sscanf( buf + 5, "%lu %lu", &OldPageIn, &OldPageOut );
564 registerMonitor( "cpu/pageIn", "float", printPageIn, printPageInInfo, StatSM );
565 registerMonitor( "cpu/pageOut", "float", printPageOut, printPageOutInfo, StatSM );
566 }
567 else if ( strcmp( "intr", tag ) == 0 ) {
568 unsigned int i;
569 char cmdName[ 32 ];
570 char* p = buf + 5;
571
572 /* Count the number of listed values in the intr line. */
573 NumOfInts = 0;
574 while ( *p )
575 if ( *p++ == ' ' )
576 NumOfInts++;
577
578 /* It looks like anything above 24 is always 0. So let's just
579 * ignore this for the time being. */
580 if ( NumOfInts > 25 )
581 NumOfInts = 25;
582 OldIntr = (unsigned long*)malloc( NumOfInts * sizeof( unsigned long ) );
583 Intr = (unsigned long*)malloc( NumOfInts * sizeof( unsigned long ) );
584 i = 0;
585 p = buf + 5;
586 for ( i = 0; p && i < NumOfInts; i++ ) {
587 sscanf( p, "%lu", &OldIntr[ i ] );
588 while ( *p && *p != ' ' )
589 p++;
590 while ( *p && *p == ' ' )
591 p++;
592 sprintf( cmdName, "cpu/interrupts/int%02d", i );
593 registerMonitor( cmdName, "float", printInterruptx, printInterruptxInfo, StatSM );
594 }
595 }
596 else if ( strcmp( "ctxt", tag ) == 0 ) {
597 sscanf( buf + 5, "%lu", &OldCtxt );
598 registerMonitor( "cpu/context", "float", printCtxt, printCtxtInfo, StatSM );
599 }
600 }
601 fclose(stat);
602
603 stat = fopen("/proc/vmstat", "r");
604 if(!stat) {
605 print_error( "Cannot open file \'/proc/vmstat\'\n");
606 } else {
607 while ( fscanf( stat, format, buf ) == 1 ) {
608 buf[ sizeof( buf ) - 1 ] = '\0';
609 sscanf( buf, tagFormat, tag );
610
611 if ( strcmp( "pgpgin", tag ) == 0 ) {
612 sscanf( buf + 7, "%lu", &OldPageIn );
613 registerMonitor( "cpu/pageIn", "float", printPageIn, printPageInInfo, StatSM );
614 }
615 else if ( strcmp( "pgpgout", tag ) == 0 ) {
616 sscanf( buf + 7, "%lu", &OldPageOut );
617 registerMonitor( "cpu/pageOut", "float", printPageOut, printPageOutInfo, StatSM );
618 }
619 }
620 fclose(stat);
621 }
622 if ( CPUCount > 0 )
623 SMPLoad = (CPULoadInfo*)calloc( CPUCount, sizeof( CPULoadInfo ) );
624
625 /* Call processStat to eliminate initial peek values. */
626 processStat();
627}
628
629void exitStat( void ) {
630 free( DiskLoad );
631 DiskLoad = 0;
632
633 free( SMPLoad );
634 SMPLoad = 0;
635
636 free( OldIntr );
637 OldIntr = 0;
638
639 free( Intr );
640 Intr = 0;
641
642 removeMonitor("cpu/system/user");
643 removeMonitor("cpu/system/nice");
644 removeMonitor("cpu/system/sys");
645 removeMonitor("cpu/system/idle");
646
647 /* Todo: Dynamically registered monitors (per cpu, per disk) are not removed yet) */
648
649 /* These were registered as legacy monitors */
650 removeMonitor("cpu/user");
651 removeMonitor("cpu/nice");
652 removeMonitor("cpu/sys");
653 removeMonitor("cpu/idle");
654}
655
656int updateStat( void )
657{
658 StatDirty = 1;
659 return 0;
660}
661
662void printCPUUser( const char* cmd ) {
663 (void)cmd;
664
665 if ( StatDirty )
666 processStat();
667
668 output( "%f\n", CPULoad.userLoad );
669}
670
671void printCPUUserInfo( const char* cmd ) {
672 (void)cmd;
673
674 output( "CPU User Load\t0\t100\t%%\n" );
675}
676
677void printCPUNice( const char* cmd ) {
678 (void)cmd;
679
680 if ( StatDirty )
681 processStat();
682
683 output( "%f\n", CPULoad.niceLoad );
684}
685
686void printCPUNiceInfo( const char* cmd ) {
687 (void)cmd;
688
689 output( "CPU Nice Load\t0\t100\t%%\n" );
690}
691
692void printCPUSys( const char* cmd ) {
693 (void)cmd;
694
695 if ( StatDirty )
696 processStat();
697
698 output( "%f\n", CPULoad.sysLoad );
699}
700
701void printCPUSysInfo( const char* cmd ) {
702 (void)cmd;
703
704 output( "CPU System Load\t0\t100\t%%\n" );
705}
706
707void printCPUTotalLoad( const char* cmd ) {
708 (void)cmd;
709
710 if ( StatDirty )
711 processStat();
712
713 output( "%f\n", CPULoad.userLoad + CPULoad.sysLoad + CPULoad.niceLoad + CPULoad.waitLoad );
714}
715
716void printCPUTotalLoadInfo( const char* cmd ) {
717 (void)cmd;
718
719 output( "CPU Total Load\t0\t100\t%%\n" );
720}
721
722void printCPUIdle( const char* cmd ) {
723 (void)cmd;
724
725 if ( StatDirty )
726 processStat();
727
728 output( "%f\n", CPULoad.idleLoad );
729}
730
731void printCPUIdleInfo( const char* cmd ) {
732 (void)cmd;
733
734 output( "CPU Idle Load\t0\t100\t%%\n" );
735}
736
737void printCPUWait( const char* cmd )
738{
739 (void)cmd;
740
741 if ( StatDirty )
742 processStat();
743
744 output( "%f\n", CPULoad.waitLoad );
745}
746
747void printCPUWaitInfo( const char* cmd )
748{
749 (void)cmd;
750 output( "CPU Wait Load\t0\t100\t%%\n" );
751}
752
753void printCPUxUser( const char* cmd ) {
754 int id;
755
756 if ( StatDirty )
757 processStat();
758
759 sscanf( cmd + 7, "%d", &id );
760 output( "%f\n", SMPLoad[ id ].userLoad );
761}
762
763void printCPUxUserInfo( const char* cmd ) {
764 int id;
765
766 sscanf( cmd + 7, "%d", &id );
767 output( "CPU %d User Load\t0\t100\t%%\n", id+1 );
768}
769
770void printCPUxNice( const char* cmd ) {
771 int id;
772
773 if ( StatDirty )
774 processStat();
775
776 sscanf( cmd + 7, "%d", &id );
777 output( "%f\n", SMPLoad[ id ].niceLoad );
778}
779
780void printCPUxNiceInfo( const char* cmd ) {
781 int id;
782
783 sscanf( cmd + 7, "%d", &id );
784 output( "CPU %d Nice Load\t0\t100\t%%\n", id+1 );
785}
786
787void printCPUxSys( const char* cmd ) {
788 int id;
789
790 if ( StatDirty )
791 processStat();
792
793 sscanf( cmd + 7, "%d", &id );
794 output( "%f\n", SMPLoad[ id ].sysLoad );
795}
796
797void printCPUxSysInfo( const char* cmd ) {
798 int id;
799
800 sscanf( cmd + 7, "%d", &id );
801 output( "CPU %d System Load\t0\t100\t%%\n", id+1 );
802}
803
804void printCPUxTotalLoad( const char* cmd ) {
805 int id;
806
807 if ( StatDirty )
808 processStat();
809
810 sscanf( cmd + 7, "%d", &id );
811 output( "%f\n", SMPLoad[ id ].userLoad + SMPLoad[ id ].sysLoad + SMPLoad[ id ].niceLoad + SMPLoad[ id ].waitLoad );
812}
813
814void printCPUxTotalLoadInfo( const char* cmd ) {
815 int id;
816
817 sscanf( cmd + 7, "%d", &id );
818 output( "CPU %d\t0\t100\t%%\n", id+1 );
819}
820
821void printCPUxIdle( const char* cmd ) {
822 int id;
823
824 if ( StatDirty )
825 processStat();
826
827 sscanf( cmd + 7, "%d", &id );
828 output( "%f\n", SMPLoad[ id ].idleLoad );
829}
830
831void printCPUxIdleInfo( const char* cmd ) {
832 int id;
833
834 sscanf( cmd + 7, "%d", &id );
835 output( "CPU %d Idle Load\t0\t100\t%%\n", id+1 );
836}
837
838void printCPUxWait( const char* cmd )
839{
840 int id;
841
842 if ( StatDirty )
843 processStat();
844
845 sscanf( cmd + 7, "%d", &id );
846 output( "%f\n", SMPLoad[ id ].waitLoad );
847}
848
849void printCPUxWaitInfo( const char* cmd )
850{
851 int id;
852
853 sscanf( cmd + 7, "%d", &id );
854 output( "CPU %d Wait Load\t0\t100\t%%\n", id+1 );
855}
856
857void print24DiskTotal( const char* cmd ) {
858 int id;
859
860 if ( StatDirty )
861 processStat();
862
863 sscanf( cmd + 9, "%d", &id );
864 output( "%f\n", (float)( DiskLoad[ id ].s[ 0 ].delta
865 / timeInterval ) );
866}
867
868void print24DiskTotalInfo( const char* cmd ) {
869 int id;
870
871 sscanf( cmd + 9, "%d", &id );
872 output( "Disk %d Total Load\t0\t0\tKB/s\n", id );
873}
874
875void print24DiskRIO( const char* cmd ) {
876 int id;
877
878 if ( StatDirty )
879 processStat();
880
881 sscanf( cmd + 9, "%d", &id );
882 output( "%f\n", (float)( DiskLoad[ id ].s[ 1 ].delta
883 / timeInterval ) );
884}
885
886void print24DiskRIOInfo( const char* cmd ) {
887 int id;
888
889 sscanf( cmd + 9, "%d", &id );
890 output( "Disk %d Read\t0\t0\tKB/s\n", id );
891}
892
893void print24DiskWIO( const char* cmd ) {
894 int id;
895
896 if ( StatDirty )
897 processStat();
898
899 sscanf( cmd + 9, "%d", &id );
900 output( "%f\n", (float)( DiskLoad[ id ].s[ 2 ].delta
901 / timeInterval ) );
902}
903
904void print24DiskWIOInfo( const char* cmd ) {
905 int id;
906
907 sscanf( cmd + 9, "%d", &id );
908 output( "Disk %d Write\t0\t0\tKB/s\n", id );
909}
910
911void print24DiskRBlk( const char* cmd ) {
912 int id;
913
914 if ( StatDirty )
915 processStat();
916
917 sscanf( cmd + 9, "%d", &id );
918 /* a block is 512 bytes or 1/2 kBytes */
919 output( "%f\n", (float)( DiskLoad[ id ].s[ 3 ].delta / timeInterval * 2 ) );
920}
921
922void print24DiskRBlkInfo( const char* cmd ) {
923 int id;
924
925 sscanf( cmd + 9, "%d", &id );
926 output( "Disk %d Read Data\t0\t0\tKB/s\n", id );
927}
928
929void print24DiskWBlk( const char* cmd ) {
930 int id;
931
932 if ( StatDirty )
933 processStat();
934
935 sscanf( cmd + 9, "%d", &id );
936 /* a block is 512 bytes or 1/2 kBytes */
937 output( "%f\n", (float)( DiskLoad[ id ].s[ 4 ].delta / timeInterval * 2 ) );
938}
939
940void print24DiskWBlkInfo( const char* cmd ) {
941 int id;
942
943 sscanf( cmd + 9, "%d", &id );
944 output( "Disk %d Write Data\t0\t0\tKB/s\n", id );
945}
946
947void printPageIn( const char* cmd ) {
948 (void)cmd;
949
950 if ( StatDirty )
951 processStat();
952
953 output( "%f\n", (float)( PageIn / timeInterval ) );
954}
955
956void printPageInInfo( const char* cmd ) {
957 (void)cmd;
958
959 output( "Paged in Pages\t0\t0\t1/s\n" );
960}
961
962void printPageOut( const char* cmd ) {
963 (void)cmd;
964
965 if ( StatDirty )
966 processStat();
967
968 output( "%f\n", (float)( PageOut / timeInterval ) );
969}
970
971void printPageOutInfo( const char* cmd ) {
972 (void)cmd;
973
974 output( "Paged out Pages\t0\t0\t1/s\n" );
975}
976
977void printInterruptx( const char* cmd ) {
978 int id;
979
980 if ( StatDirty )
981 processStat();
982
983 sscanf( cmd + strlen( "cpu/interrupts/int" ), "%d", &id );
984 output( "%f\n", (float)( Intr[ id ] / timeInterval ) );
985}
986
987void printInterruptxInfo( const char* cmd ) {
988 int id;
989
990 sscanf( cmd + strlen( "cpu/interrupt/int" ), "%d", &id );
991 output( "Interrupt %d\t0\t0\t1/s\n", id );
992}
993
994void printCtxt( const char* cmd ) {
995 (void)cmd;
996
997 if ( StatDirty )
998 processStat();
999
1000 output( "%f\n", (float)( Ctxt / timeInterval ) );
1001}
1002
1003void printCtxtInfo( const char* cmd ) {
1004 (void)cmd;
1005
1006 output( "Context switches\t0\t0\t1/s\n" );
1007}
1008
1009void print24DiskIO( const char* cmd ) {
1010 int major, minor;
1011 char devname[DISKDEVNAMELEN];
1012 char name[ 17 ];
1013 DiskIOInfo* ptr;
1014
1015 sscanf( cmd, "disk/%[^_]_(%d:%d)/%16s", devname, &major, &minor, name );
1016
1017 if ( StatDirty )
1018 processStat();
1019
1020 ptr = DiskIO;
1021 while ( ptr && ( ptr->major != major || ptr->minor != minor ) )
1022 ptr = ptr->next;
1023
1024 if ( !ptr ) {
1025 print_error( "RECONFIGURE" );
1026 output( "0\n" );
1027
1028 log_error( "Disk device disappeared" );
1029 return;
1030 }
1031
1032 if ( strcmp( name, "total" ) == 0 )
1033 output( "%f\n", (float)( ptr->total.delta / timeInterval ) );
1034 else if ( strcmp( name, "rio" ) == 0 )
1035 output( "%f\n", (float)( ptr->rio.delta / timeInterval ) );
1036 else if ( strcmp( name, "wio" ) == 0 )
1037 output( "%f\n", (float)( ptr->wio.delta / timeInterval ) );
1038 else if ( strcmp( name, "rblk" ) == 0 )
1039 output( "%f\n", (float)( ptr->rblk.delta / ( timeInterval * 2 ) ) );
1040 else if ( strcmp( name, "wblk" ) == 0 )
1041 output( "%f\n", (float)( ptr->wblk.delta / ( timeInterval * 2 ) ) );
1042 else {
1043 output( "0\n" );
1044 log_error( "Unknown disk device property \'%s\'", name );
1045 }
1046}
1047
1048void print24DiskIOInfo( const char* cmd ) {
1049 int major, minor;
1050 char devname[DISKDEVNAMELEN];
1051 char name[ 17 ];
1052 DiskIOInfo* ptr = DiskIO;
1053
1054 sscanf( cmd, "disk/%[^_]_(%d:%d)/%16s", devname, &major, &minor, name );
1055
1056 while ( ptr && ( ptr->major != major || ptr->minor != minor ) )
1057 ptr = ptr->next;
1058
1059 if ( !ptr ) {
1060 /* Disk device has disappeared. Print a dummy answer. */
1061 output( "Dummy\t0\t0\t\n" );
1062 return;
1063 }
1064
1065 /* remove trailing '?' */
1066 name[ strlen( name ) - 1 ] = '\0';
1067
1068 if ( strcmp( name, "total" ) == 0 )
1069 output( "Total accesses device %s (%d:%d)\t0\t0\t1/s\n",
1070 devname, major, minor );
1071 else if ( strcmp( name, "rio" ) == 0 )
1072 output( "Read data device %s (%d:%d)\t0\t0\t1/s\n",
1073 devname, major, minor );
1074 else if ( strcmp( name, "wio" ) == 0 )
1075 output( "Write data device %s (%d:%d)\t0\t0\t1/s\n",
1076 devname, major, minor );
1077 else if ( strcmp( name, "rblk" ) == 0 )
1078 output( "Read accesses device %s (%d:%d)\t0\t0\tKB/s\n",
1079 devname, major, minor );
1080 else if ( strcmp( name, "wblk" ) == 0 )
1081 output( "Write accesses device %s (%d:%d)\t0\t0\tKB/s\n",
1082 devname, major, minor );
1083 else {
1084 output( "Dummy\t0\t0\t\n" );
1085 log_error( "Request for unknown device property \'%s\'", name );
1086 }
1087}
1088