ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.6
Committed: Fri Aug 29 06:48:04 2003 UTC (21 years, 4 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.5: +1 -1 lines
Log Message:
Fill in time_taken correctly.

File Contents

# Content
1 /*
2 * i-scream central monitoring system
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2003 i-scream
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <statgrab.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31
32 typedef enum {
33 LONG_LONG = 0,
34 TIME_T,
35 FLOAT,
36 DOUBLE,
37 STRING,
38 INT
39 } stat_type;
40
41 typedef enum {
42 DISPLAY_LINUX = 0,
43 DISPLAY_BSD,
44 DISPLAY_MRTG,
45 DISPLAY_PLAIN
46 } display_mode_type;
47
48 typedef enum {
49 REPEAT_NONE = 0,
50 REPEAT_ONCE,
51 REPEAT_FOREVER
52 } repeat_mode_type;
53
54 typedef struct {
55 char *name;
56 stat_type type;
57 void *stat;
58 } stat;
59
60 stat *stats = NULL;
61 int num_stats = 0;
62 int alloc_stats = 0;
63 #define INCREMENT_STATS 64
64
65 display_mode_type display_mode = DISPLAY_LINUX;
66 repeat_mode_type repeat_mode = REPEAT_NONE;
67 int repeat_time = 1;
68 int use_cpu_percent = 0;
69
70 /* Exit with an error message. */
71 void die(const char *s) {
72 fprintf(stderr, "fatal: %s\n", s);
73 exit(1);
74 }
75
76 /* Remove all the recorded stats. */
77 void clear_stats() {
78 int i;
79
80 for (i = 0; i < num_stats; i++)
81 free(stats[i].name);
82 num_stats = 0;
83 }
84
85 /* Add a stat. The varargs make up the name, joined with dots; the name is
86 terminated with a NULL. */
87 void add_stat(stat_type type, void *stat, ...) {
88 va_list ap;
89 int len = 0;
90 char *name, *p;
91
92 /* Figure out how long the name will be, including dots and trailing
93 \0. */
94 va_start(ap, stat);
95 while (1) {
96 const char *part = va_arg(ap, const char *);
97 if (part == NULL)
98 break;
99 len += 1 + strlen(part);
100 }
101 va_end(ap);
102
103 /* Paste the name together. */
104 name = malloc(len);
105 if (name == NULL)
106 die("out of memory");
107 p = name;
108 va_start(ap, stat);
109 while (1) {
110 const char *part = va_arg(ap, const char *);
111 int partlen;
112 if (part == NULL)
113 break;
114 partlen = strlen(part);
115 memcpy(p, part, partlen);
116 p += partlen;
117 *p++ = '.';
118 }
119 va_end(ap);
120 *--p = '\0';
121
122 /* Replace spaces with underscores. */
123 for (p = name; *p != '\0'; p++) {
124 if (*p == ' ')
125 *p = '_';
126 }
127
128 /* Stretch the stats array if necessary. */
129 if (num_stats >= alloc_stats) {
130 alloc_stats += INCREMENT_STATS;
131 stats = realloc(stats, alloc_stats * sizeof *stats);
132 if (stats == NULL)
133 die("out of memory");
134 }
135
136 stats[num_stats].name = name;
137 stats[num_stats].type = type;
138 stats[num_stats].stat = stat;
139 ++num_stats;
140 }
141
142 /* Compare two stats by name, for sorting purposes. */
143 int stats_compare(const void *a, const void *b) {
144 return strcmp(((stat *)a)->name, ((stat *)b)->name);
145 }
146
147 /* Clear and rebuild the stats array. */
148 void get_stats(int use_diffs) {
149 cpu_states_t *cpu_s;
150 cpu_percent_t *cpu_p;
151 mem_stat_t *mem;
152 load_stat_t *load;
153 user_stat_t *user;
154 swap_stat_t *swap;
155 general_stat_t *gen;
156 disk_stat_t *disk;
157 diskio_stat_t *diskio;
158 process_stat_t *proc;
159 network_stat_t *net;
160 page_stat_t *page;
161 static int zero = 0;
162 int n, i;
163
164 clear_stats();
165
166 /* Constants, for use with MRTG mode. */
167 add_stat(INT, &zero, "const", "0", NULL);
168
169 /* FIXME when only fetching some stats, it'd be more efficient to only
170 do the libstatgrab calls needed, rather than fetching everything. */
171
172 if (use_cpu_percent) {
173 cpu_p = cpu_percent_usage();
174 if (cpu_p != NULL) {
175 add_stat(FLOAT, &cpu_p->user,
176 "cpu", "user", NULL);
177 add_stat(FLOAT, &cpu_p->kernel,
178 "cpu", "kernel", NULL);
179 add_stat(FLOAT, &cpu_p->idle,
180 "cpu", "idle", NULL);
181 add_stat(FLOAT, &cpu_p->iowait,
182 "cpu", "iowait", NULL);
183 add_stat(FLOAT, &cpu_p->swap,
184 "cpu", "swap", NULL);
185 add_stat(FLOAT, &cpu_p->nice,
186 "cpu", "nice", NULL);
187 add_stat(TIME_T, &cpu_p->systime,
188 "cpu", "time_taken", NULL);
189 }
190 } else {
191 cpu_s = use_diffs ? get_cpu_diff() : get_cpu_totals();
192 if (cpu_s != NULL) {
193 add_stat(LONG_LONG, &cpu_s->user,
194 "cpu", "user", NULL);
195 add_stat(LONG_LONG, &cpu_s->kernel,
196 "cpu", "kernel", NULL);
197 add_stat(LONG_LONG, &cpu_s->idle,
198 "cpu", "idle", NULL);
199 add_stat(LONG_LONG, &cpu_s->iowait,
200 "cpu", "iowait", NULL);
201 add_stat(LONG_LONG, &cpu_s->swap,
202 "cpu", "swap", NULL);
203 add_stat(LONG_LONG, &cpu_s->nice,
204 "cpu", "nice", NULL);
205 add_stat(LONG_LONG, &cpu_s->total,
206 "cpu", "total", NULL);
207 add_stat(TIME_T, &cpu_s->systime,
208 "cpu", "systime", NULL);
209 }
210 }
211
212 mem = get_memory_stats();
213 if (mem != NULL) {
214 add_stat(LONG_LONG, &mem->total, "mem", "total", NULL);
215 add_stat(LONG_LONG, &mem->free, "mem", "free", NULL);
216 add_stat(LONG_LONG, &mem->used, "mem", "used", NULL);
217 add_stat(LONG_LONG, &mem->cache, "mem", "cache", NULL);
218 }
219
220 load = get_load_stats();
221 if (load != NULL) {
222 add_stat(DOUBLE, &load->min1, "load", "min1", NULL);
223 add_stat(DOUBLE, &load->min5, "load", "min5", NULL);
224 add_stat(DOUBLE, &load->min15, "load", "min15", NULL);
225 }
226
227 user = get_user_stats();
228 if (user != NULL) {
229 add_stat(INT, &user->num_entries, "user", "num", NULL);
230 add_stat(STRING, &user->name_list, "user", "names", NULL);
231 }
232
233 swap = get_swap_stats();
234 if (swap != NULL) {
235 add_stat(LONG_LONG, &swap->total, "swap", "total", NULL);
236 add_stat(LONG_LONG, &swap->used, "swap", "used", NULL);
237 add_stat(LONG_LONG, &swap->free, "swap", "free", NULL);
238 }
239
240 gen = get_general_stats();
241 if (gen != NULL) {
242 add_stat(STRING, &gen->os_name,
243 "general", "os_name", NULL);
244 add_stat(STRING, &gen->os_release,
245 "general", "os_release", NULL);
246 add_stat(STRING, &gen->os_version,
247 "general", "os_version", NULL);
248 add_stat(STRING, &gen->platform, "general", "platform", NULL);
249 add_stat(STRING, &gen->hostname, "general", "hostname", NULL);
250 add_stat(TIME_T, &gen->uptime, "general", "uptime", NULL);
251 }
252
253 disk = get_disk_stats(&n);
254 if (disk != NULL) {
255 for (i = 0; i < n; i++) {
256 /* FIXME it'd be nicer if libstatgrab did this */
257 const char *name = disk[i].device_name,
258 *p = strrchr(name, '/');
259 if (p != NULL)
260 name = p + 1;
261 if (*name == '\0')
262 name = "root";
263
264 add_stat(STRING, &disk[i].device_name,
265 "fs", name, "device_name", NULL);
266 add_stat(STRING, &disk[i].fs_type,
267 "fs", name, "fs_type", NULL);
268 add_stat(STRING, &disk[i].mnt_point,
269 "fs", name, "mnt_point", NULL);
270 add_stat(LONG_LONG, &disk[i].size,
271 "fs", name, "size", NULL);
272 add_stat(LONG_LONG, &disk[i].used,
273 "fs", name, "used", NULL);
274 add_stat(LONG_LONG, &disk[i].avail,
275 "fs", name, "avail", NULL);
276 add_stat(LONG_LONG, &disk[i].total_inodes,
277 "fs", name, "total_inodes", NULL);
278 add_stat(LONG_LONG, &disk[i].used_inodes,
279 "fs", name, "used_inodes", NULL);
280 add_stat(LONG_LONG, &disk[i].free_inodes,
281 "fs", name, "free_inodes", NULL);
282 }
283 }
284
285 diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n);
286 if (diskio != NULL) {
287 for (i = 0; i < n; i++) {
288 const char *name = diskio[i].disk_name;
289
290 add_stat(STRING, &diskio[i].disk_name,
291 "disk", name, "disk_name", NULL);
292 add_stat(LONG_LONG, &diskio[i].read_bytes,
293 "disk", name, "read_bytes", NULL);
294 add_stat(LONG_LONG, &diskio[i].write_bytes,
295 "disk", name, "write_bytes", NULL);
296 add_stat(TIME_T, &diskio[i].systime,
297 "disk", name, "systime", NULL);
298 }
299 }
300
301 proc = get_process_stats();
302 if (proc != NULL) {
303 add_stat(INT, &proc->total, "proc", "total", NULL);
304 add_stat(INT, &proc->running, "proc", "running", NULL);
305 add_stat(INT, &proc->sleeping, "proc", "sleeping", NULL);
306 add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
307 add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
308 }
309
310 net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n);
311 if (net != NULL) {
312 for (i = 0; i < n; i++) {
313 const char *name = net[i].interface_name;
314
315 add_stat(STRING, &net[i].interface_name,
316 "net", name, "interface_name", NULL);
317 add_stat(LONG_LONG, &net[i].tx,
318 "net", name, "tx", NULL);
319 add_stat(LONG_LONG, &net[i].rx,
320 "net", name, "rx", NULL);
321 add_stat(TIME_T, &net[i].systime,
322 "net", name, "systime", NULL);
323 }
324 }
325
326 page = use_diffs ? get_page_stats_diff() : get_page_stats();
327 if (page != NULL) {
328 add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
329 add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
330 add_stat(LONG_LONG, &page->systime, "page", "systime", NULL);
331 }
332
333 qsort(stats, num_stats, sizeof *stats, stats_compare);
334 }
335
336 /* Print the value of a stat. */
337 void print_stat_value(const stat *s) {
338 void *v = s->stat;
339
340 switch (s->type) {
341 case LONG_LONG:
342 printf("%lld", *(long long *)v);
343 break;
344 case TIME_T:
345 /* FIXME option for formatted time? */
346 printf("%ld", *(time_t *)v);
347 break;
348 case FLOAT:
349 printf("%f", *(float *)v);
350 break;
351 case DOUBLE:
352 printf("%f", *(double *)v);
353 break;
354 case STRING:
355 /* FIXME escaping? */
356 printf("%s", *(char **)v);
357 break;
358 case INT:
359 printf("%d", *(int *)v);
360 break;
361 }
362 }
363
364 /* Print the name and value of a stat. */
365 void print_stat(const stat *s) {
366 switch (display_mode) {
367 case DISPLAY_LINUX:
368 printf("%s = ", s->name);
369 break;
370 case DISPLAY_BSD:
371 printf("%s: ", s->name);
372 break;
373 case DISPLAY_MRTG:
374 case DISPLAY_PLAIN:
375 break;
376 }
377 print_stat_value(s);
378 printf("\n");
379 }
380
381 /* Print stats as specified on the provided command line. */
382 void print_stats(int argc, char **argv) {
383 int i;
384
385 if (argc == optind) {
386 /* Print all stats. */
387 for (i = 0; i < num_stats; i++)
388 print_stat(&stats[i]);
389 } else {
390 /* Print selected stats. */
391 for (i = optind; i < argc; i++) {
392 stat key;
393 const stat *s;
394
395 key.name = argv[i];
396 s = (const stat *)bsearch(&key, stats, num_stats,
397 sizeof *stats,
398 stats_compare);
399 if (s != NULL) {
400 print_stat(s);
401 }
402 }
403 }
404 }
405
406 void usage() {
407 printf("Usage: statgrab [OPTION]... [STAT]...\n"
408 "Display system statistics (all statistics by default).\n"
409 "\n");
410 printf(" -l Linux sysctl-style output (default)\n"
411 " -b BSD sysctl-style output\n"
412 " -m MRTG-compatible output\n"
413 " -u Plain output (only show values)\n"
414 " -n Display cumulative stats once (default)\n"
415 " -s Display stat differences repeatedly\n"
416 " -o Display stat differences once\n"
417 " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
418 " -p Display CPU usage differences as percentages rather than\n"
419 " absolute values\n"
420 "\n");
421 printf("Version %s - report bugs to <%s>.\n",
422 PACKAGE_VERSION, PACKAGE_BUGREPORT);
423 exit(1);
424 }
425
426 int main(int argc, char **argv) {
427 opterr = 0;
428 while (1) {
429 int c = getopt(argc, argv, "lbmunsot:p");
430 if (c == -1)
431 break;
432 switch (c) {
433 case 'l':
434 display_mode = DISPLAY_LINUX;
435 break;
436 case 'b':
437 display_mode = DISPLAY_BSD;
438 break;
439 case 'm':
440 display_mode = DISPLAY_MRTG;
441 break;
442 case 'u':
443 display_mode = DISPLAY_PLAIN;
444 break;
445 case 'n':
446 repeat_mode = REPEAT_NONE;
447 break;
448 case 's':
449 repeat_mode = REPEAT_FOREVER;
450 break;
451 case 'o':
452 repeat_mode = REPEAT_ONCE;
453 break;
454 case 't':
455 repeat_time = atoi(optarg);
456 break;
457 case 'p':
458 use_cpu_percent = 1;
459 break;
460 default:
461 usage();
462 }
463 }
464
465 if (display_mode == DISPLAY_MRTG) {
466 if ((argc - optind) != 2)
467 die("mrtg mode: must specify exactly two stats");
468 if (repeat_mode == REPEAT_FOREVER)
469 die("mrtg mode: cannot repeat display");
470 }
471
472 if (use_cpu_percent && repeat_mode == REPEAT_NONE)
473 die("CPU percentage usage display requires stat differences");
474
475 switch (repeat_mode) {
476 case REPEAT_NONE:
477 get_stats(0);
478 print_stats(argc, argv);
479 break;
480 case REPEAT_ONCE:
481 get_stats(1);
482 sleep(repeat_time);
483 get_stats(1);
484 print_stats(argc, argv);
485 break;
486 case REPEAT_FOREVER:
487 while (1) {
488 get_stats(1);
489 print_stats(argc, argv);
490 printf("\n");
491 sleep(repeat_time);
492 }
493 }
494
495 if (display_mode == DISPLAY_MRTG) {
496 printf("\n");
497 printf("statgrab\n");
498 }
499
500 return 0;
501 }
502