ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.1
Committed: Tue Aug 26 12:09:44 2003 UTC (21 years, 2 months ago) by tdb
Content type: text/plain
Branch: MAIN
Log Message:
A sysctl-style program to display system statistics collected through
libstatgrab. Contributed by Adam Sampson, azz@us-lot.org.

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