ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.36
Committed: Sat Sep 24 13:29:25 2005 UTC (18 years, 8 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.35: +14 -1 lines
Log Message:
Add WIN32 support via MINGW. We'll need to add stuff to the README file
about what this requires to build.

All the hard work done by: skel

File Contents

# User Rev Content
1 tdb 1.1 /*
2 tdb 1.25 * i-scream libstatgrab
3 tdb 1.1 * http://www.i-scream.org
4 tdb 1.16 * Copyright (C) 2000-2004 i-scream
5 tdb 1.1 *
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 tdb 1.17 *
20 tdb 1.36 * $Id: statgrab.c,v 1.35 2005/07/13 13:01:24 tdb Exp $
21 tdb 1.1 */
22    
23 tdb 1.3 #ifdef HAVE_CONFIG_H
24     #include "config.h"
25     #endif
26    
27 tdb 1.1 #include <statgrab.h>
28     #include <string.h>
29     #include <stdio.h>
30     #include <stdarg.h>
31     #include <stdlib.h>
32     #include <unistd.h>
33    
34     typedef enum {
35     LONG_LONG = 0,
36 ats 1.30 BYTES,
37 tdb 1.1 TIME_T,
38     FLOAT,
39     DOUBLE,
40     STRING,
41 ats 1.18 INT,
42 ats 1.20 BOOL,
43 ats 1.18 DUPLEX
44 tdb 1.1 } stat_type;
45    
46     typedef enum {
47     DISPLAY_LINUX = 0,
48     DISPLAY_BSD,
49     DISPLAY_MRTG,
50     DISPLAY_PLAIN
51     } display_mode_type;
52    
53     typedef enum {
54     REPEAT_NONE = 0,
55     REPEAT_ONCE,
56     REPEAT_FOREVER
57     } repeat_mode_type;
58    
59     typedef struct {
60     char *name;
61     stat_type type;
62     void *stat;
63     } stat;
64    
65     stat *stats = NULL;
66     int num_stats = 0;
67     int alloc_stats = 0;
68     #define INCREMENT_STATS 64
69    
70     display_mode_type display_mode = DISPLAY_LINUX;
71     repeat_mode_type repeat_mode = REPEAT_NONE;
72     int repeat_time = 1;
73     int use_cpu_percent = 0;
74 ats 1.8 int use_diffs = 0;
75 ats 1.29 long float_scale_factor = 0;
76 ats 1.30 long long bytes_scale_factor = 0;
77 tdb 1.1
78     /* Exit with an error message. */
79     void die(const char *s) {
80     fprintf(stderr, "fatal: %s\n", s);
81     exit(1);
82     }
83    
84     /* Remove all the recorded stats. */
85     void clear_stats() {
86     int i;
87    
88     for (i = 0; i < num_stats; i++)
89     free(stats[i].name);
90     num_stats = 0;
91     }
92    
93     /* Add a stat. The varargs make up the name, joined with dots; the name is
94     terminated with a NULL. */
95     void add_stat(stat_type type, void *stat, ...) {
96     va_list ap;
97     int len = 0;
98     char *name, *p;
99    
100     /* Figure out how long the name will be, including dots and trailing
101     \0. */
102     va_start(ap, stat);
103     while (1) {
104     const char *part = va_arg(ap, const char *);
105     if (part == NULL)
106     break;
107     len += 1 + strlen(part);
108     }
109     va_end(ap);
110    
111     /* Paste the name together. */
112     name = malloc(len);
113     if (name == NULL)
114     die("out of memory");
115     p = name;
116     va_start(ap, stat);
117     while (1) {
118     const char *part = va_arg(ap, const char *);
119     int partlen;
120     if (part == NULL)
121     break;
122     partlen = strlen(part);
123     memcpy(p, part, partlen);
124     p += partlen;
125     *p++ = '.';
126     }
127     va_end(ap);
128     *--p = '\0';
129    
130     /* Replace spaces with underscores. */
131     for (p = name; *p != '\0'; p++) {
132     if (*p == ' ')
133     *p = '_';
134     }
135    
136     /* Stretch the stats array if necessary. */
137     if (num_stats >= alloc_stats) {
138     alloc_stats += INCREMENT_STATS;
139     stats = realloc(stats, alloc_stats * sizeof *stats);
140     if (stats == NULL)
141     die("out of memory");
142     }
143    
144     stats[num_stats].name = name;
145     stats[num_stats].type = type;
146     stats[num_stats].stat = stat;
147     ++num_stats;
148     }
149    
150 ats 1.9 /* Compare two stats by name for qsort and bsearch. */
151 tdb 1.1 int stats_compare(const void *a, const void *b) {
152     return strcmp(((stat *)a)->name, ((stat *)b)->name);
153     }
154    
155 ats 1.9 /* Compare up to the length of the key for bsearch. */
156     int stats_compare_prefix(const void *key, const void *item) {
157     const char *kn = ((stat *)key)->name;
158     const char *in = ((stat *)item)->name;
159    
160     return strncmp(kn, in, strlen(kn));
161     }
162    
163 ats 1.8 void populate_const() {
164 tdb 1.1 static int zero = 0;
165    
166     /* Constants, for use with MRTG mode. */
167     add_stat(INT, &zero, "const", "0", NULL);
168 ats 1.8 }
169 tdb 1.1
170 ats 1.8 void populate_cpu() {
171     if (use_cpu_percent) {
172 ats 1.24 sg_cpu_percents *cpu_p = sg_get_cpu_percents();
173 tdb 1.1
174     if (cpu_p != NULL) {
175     add_stat(FLOAT, &cpu_p->user,
176 tdb 1.26 "cpu", "user", NULL);
177 tdb 1.1 add_stat(FLOAT, &cpu_p->kernel,
178 tdb 1.26 "cpu", "kernel", NULL);
179 tdb 1.1 add_stat(FLOAT, &cpu_p->idle,
180 tdb 1.26 "cpu", "idle", NULL);
181 tdb 1.1 add_stat(FLOAT, &cpu_p->iowait,
182 tdb 1.26 "cpu", "iowait", NULL);
183 tdb 1.1 add_stat(FLOAT, &cpu_p->swap,
184 tdb 1.26 "cpu", "swap", NULL);
185 tdb 1.1 add_stat(FLOAT, &cpu_p->nice,
186 tdb 1.26 "cpu", "nice", NULL);
187 ats 1.7 add_stat(TIME_T, &cpu_p->time_taken,
188 tdb 1.26 "cpu", "time_taken", NULL);
189 tdb 1.1 }
190     } else {
191 ats 1.24 sg_cpu_stats *cpu_s;
192 ats 1.8
193 ats 1.24 cpu_s = use_diffs ? sg_get_cpu_stats_diff()
194 tdb 1.26 : sg_get_cpu_stats();
195 tdb 1.1 if (cpu_s != NULL) {
196     add_stat(LONG_LONG, &cpu_s->user,
197 tdb 1.26 "cpu", "user", NULL);
198 tdb 1.1 add_stat(LONG_LONG, &cpu_s->kernel,
199 tdb 1.26 "cpu", "kernel", NULL);
200 tdb 1.1 add_stat(LONG_LONG, &cpu_s->idle,
201 tdb 1.26 "cpu", "idle", NULL);
202 tdb 1.1 add_stat(LONG_LONG, &cpu_s->iowait,
203 tdb 1.26 "cpu", "iowait", NULL);
204 tdb 1.1 add_stat(LONG_LONG, &cpu_s->swap,
205 tdb 1.26 "cpu", "swap", NULL);
206 tdb 1.1 add_stat(LONG_LONG, &cpu_s->nice,
207 tdb 1.26 "cpu", "nice", NULL);
208 tdb 1.1 add_stat(LONG_LONG, &cpu_s->total,
209 tdb 1.26 "cpu", "total", NULL);
210 tdb 1.1 add_stat(TIME_T, &cpu_s->systime,
211 tdb 1.26 "cpu", "systime", NULL);
212 tdb 1.1 }
213     }
214 ats 1.8 }
215    
216     void populate_mem() {
217 ats 1.24 sg_mem_stats *mem = sg_get_mem_stats();
218 tdb 1.1
219     if (mem != NULL) {
220 ats 1.30 add_stat(BYTES, &mem->total, "mem", "total", NULL);
221     add_stat(BYTES, &mem->free, "mem", "free", NULL);
222     add_stat(BYTES, &mem->used, "mem", "used", NULL);
223     add_stat(BYTES, &mem->cache, "mem", "cache", NULL);
224 tdb 1.1 }
225 ats 1.8 }
226    
227     void populate_load() {
228 ats 1.24 sg_load_stats *load = sg_get_load_stats();
229 tdb 1.1
230     if (load != NULL) {
231     add_stat(DOUBLE, &load->min1, "load", "min1", NULL);
232     add_stat(DOUBLE, &load->min5, "load", "min5", NULL);
233     add_stat(DOUBLE, &load->min15, "load", "min15", NULL);
234     }
235 ats 1.8 }
236    
237     void populate_user() {
238 ats 1.24 sg_user_stats *user = sg_get_user_stats();
239 tdb 1.1
240     if (user != NULL) {
241     add_stat(INT, &user->num_entries, "user", "num", NULL);
242     add_stat(STRING, &user->name_list, "user", "names", NULL);
243     }
244 ats 1.8 }
245    
246     void populate_swap() {
247 ats 1.24 sg_swap_stats *swap = sg_get_swap_stats();
248 tdb 1.1
249     if (swap != NULL) {
250 ats 1.30 add_stat(BYTES, &swap->total, "swap", "total", NULL);
251     add_stat(BYTES, &swap->used, "swap", "used", NULL);
252     add_stat(BYTES, &swap->free, "swap", "free", NULL);
253 tdb 1.1 }
254 ats 1.8 }
255    
256     void populate_general() {
257 ats 1.24 /* FIXME this should be renamed to host. */
258     sg_host_info *host = sg_get_host_info();
259 tdb 1.1
260 ats 1.24 if (host != NULL) {
261     add_stat(STRING, &host->os_name,
262 tdb 1.26 "general", "os_name", NULL);
263 ats 1.24 add_stat(STRING, &host->os_release,
264 tdb 1.26 "general", "os_release", NULL);
265 ats 1.24 add_stat(STRING, &host->os_version,
266 tdb 1.26 "general", "os_version", NULL);
267 ats 1.24 add_stat(STRING, &host->platform, "general", "platform", NULL);
268     add_stat(STRING, &host->hostname, "general", "hostname", NULL);
269     add_stat(TIME_T, &host->uptime, "general", "uptime", NULL);
270 tdb 1.1 }
271 ats 1.8 }
272    
273     void populate_fs() {
274     int n, i;
275 ats 1.24 sg_fs_stats *disk = sg_get_fs_stats(&n);
276 tdb 1.1
277     if (disk != NULL) {
278     for (i = 0; i < n; i++) {
279     /* FIXME it'd be nicer if libstatgrab did this */
280 ats 1.10 char *buf, *name, *p;
281     const char *device = disk[i].device_name;
282    
283     if (strcmp(device, "/") == 0)
284     device = "root";
285    
286     buf = strdup(device);
287     if (buf == NULL)
288     die("out of memory");
289    
290     name = buf;
291 ats 1.13 if (strlen(name) == 2 && name[1] == ':')
292     name[1] = '\0';
293 ats 1.10 if (strncmp(name, "/dev/", 5) == 0)
294     name += 5;
295     while ((p = strchr(name, '/')) != NULL)
296     *p = '_';
297    
298 tdb 1.1 add_stat(STRING, &disk[i].device_name,
299 tdb 1.26 "fs", name, "device_name", NULL);
300 tdb 1.1 add_stat(STRING, &disk[i].fs_type,
301 tdb 1.26 "fs", name, "fs_type", NULL);
302 tdb 1.1 add_stat(STRING, &disk[i].mnt_point,
303 tdb 1.26 "fs", name, "mnt_point", NULL);
304 ats 1.30 add_stat(BYTES, &disk[i].size,
305 tdb 1.26 "fs", name, "size", NULL);
306 ats 1.30 add_stat(BYTES, &disk[i].used,
307 tdb 1.26 "fs", name, "used", NULL);
308 ats 1.30 add_stat(BYTES, &disk[i].avail,
309 tdb 1.26 "fs", name, "avail", NULL);
310 tdb 1.1 add_stat(LONG_LONG, &disk[i].total_inodes,
311 tdb 1.26 "fs", name, "total_inodes", NULL);
312 tdb 1.1 add_stat(LONG_LONG, &disk[i].used_inodes,
313 tdb 1.26 "fs", name, "used_inodes", NULL);
314 tdb 1.1 add_stat(LONG_LONG, &disk[i].free_inodes,
315 tdb 1.26 "fs", name, "free_inodes", NULL);
316 tdb 1.35 add_stat(LONG_LONG, &disk[i].avail_inodes,
317     "fs", name, "avail_inodes", NULL);
318     add_stat(LONG_LONG, &disk[i].io_size,
319     "fs", name, "io_size", NULL);
320     add_stat(LONG_LONG, &disk[i].block_size,
321     "fs", name, "block_size", NULL);
322     add_stat(LONG_LONG, &disk[i].total_blocks,
323     "fs", name, "total_blocks", NULL);
324     add_stat(LONG_LONG, &disk[i].free_blocks,
325     "fs", name, "free_blocks", NULL);
326     add_stat(LONG_LONG, &disk[i].avail_blocks,
327     "fs", name, "avail_blocks", NULL);
328     add_stat(LONG_LONG, &disk[i].used_blocks,
329     "fs", name, "used_blocks", NULL);
330 ats 1.10
331     free(buf);
332 tdb 1.1 }
333     }
334 ats 1.8 }
335    
336     void populate_disk() {
337     int n, i;
338 ats 1.24 sg_disk_io_stats *diskio;
339 tdb 1.1
340 ats 1.24 diskio = use_diffs ? sg_get_disk_io_stats_diff(&n)
341 tdb 1.26 : sg_get_disk_io_stats(&n);
342 tdb 1.1 if (diskio != NULL) {
343     for (i = 0; i < n; i++) {
344     const char *name = diskio[i].disk_name;
345    
346     add_stat(STRING, &diskio[i].disk_name,
347 tdb 1.26 "disk", name, "disk_name", NULL);
348 ats 1.30 add_stat(BYTES, &diskio[i].read_bytes,
349 tdb 1.26 "disk", name, "read_bytes", NULL);
350 ats 1.30 add_stat(BYTES, &diskio[i].write_bytes,
351 tdb 1.26 "disk", name, "write_bytes", NULL);
352 tdb 1.1 add_stat(TIME_T, &diskio[i].systime,
353 tdb 1.26 "disk", name, "systime", NULL);
354 tdb 1.1 }
355     }
356 ats 1.8 }
357    
358     void populate_proc() {
359 ats 1.24 /* FIXME expose individual process info too */
360     sg_process_count *proc = sg_get_process_count();
361 tdb 1.1
362     if (proc != NULL) {
363     add_stat(INT, &proc->total, "proc", "total", NULL);
364     add_stat(INT, &proc->running, "proc", "running", NULL);
365     add_stat(INT, &proc->sleeping, "proc", "sleeping", NULL);
366     add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
367     add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
368     }
369 ats 1.8 }
370    
371     void populate_net() {
372 ats 1.27 int num_io, num_iface, i;
373 ats 1.24 sg_network_io_stats *io;
374     sg_network_iface_stats *iface;
375 tdb 1.1
376 ats 1.27 io = use_diffs ? sg_get_network_io_stats_diff(&num_io)
377     : sg_get_network_io_stats(&num_io);
378 ats 1.24 if (io != NULL) {
379 ats 1.27 for (i = 0; i < num_io; i++) {
380 ats 1.24 const char *name = io[i].interface_name;
381 tdb 1.1
382 ats 1.24 add_stat(STRING, &io[i].interface_name,
383 tdb 1.26 "net", name, "interface_name", NULL);
384 ats 1.30 add_stat(BYTES, &io[i].tx,
385 tdb 1.26 "net", name, "tx", NULL);
386 ats 1.30 add_stat(BYTES, &io[i].rx,
387 tdb 1.26 "net", name, "rx", NULL);
388 ats 1.24 add_stat(LONG_LONG, &io[i].ipackets,
389 tdb 1.26 "net", name, "ipackets", NULL);
390 ats 1.24 add_stat(LONG_LONG, &io[i].opackets,
391 tdb 1.26 "net", name, "opackets", NULL);
392 ats 1.24 add_stat(LONG_LONG, &io[i].ierrors,
393 tdb 1.26 "net", name, "ierrors", NULL);
394 ats 1.24 add_stat(LONG_LONG, &io[i].oerrors,
395 tdb 1.26 "net", name, "oerrors", NULL);
396 ats 1.24 add_stat(LONG_LONG, &io[i].collisions,
397 tdb 1.26 "net", name, "collisions", NULL);
398 ats 1.24 add_stat(TIME_T, &io[i].systime,
399 tdb 1.26 "net", name, "systime", NULL);
400 tdb 1.1 }
401     }
402 ats 1.18
403 ats 1.27 iface = sg_get_network_iface_stats(&num_iface);
404 ats 1.18 if (iface != NULL) {
405 ats 1.27 for (i = 0; i < num_iface; i++) {
406 ats 1.18 const char *name = iface[i].interface_name;
407 ats 1.27 int had_io = 0, j;
408    
409     /* If there wasn't a corresponding io stat,
410     add interface_name from here. */
411     if (io != NULL) {
412     for (j = 0; j < num_io; j++) {
413     if (strcmp(io[j].interface_name,
414     name) == 0) {
415     had_io = 1;
416     break;
417     }
418     }
419     }
420     if (!had_io) {
421     add_stat(STRING, &iface[i].interface_name,
422     "net", name, "interface_name", NULL);
423     }
424 ats 1.18
425     add_stat(INT, &iface[i].speed,
426 tdb 1.26 "net", name, "speed", NULL);
427 ats 1.20 add_stat(BOOL, &iface[i].up,
428 tdb 1.26 "net", name, "up", NULL);
429 tdb 1.34 add_stat(DUPLEX, &iface[i].duplex,
430 tdb 1.26 "net", name, "duplex", NULL);
431 ats 1.18 }
432     }
433 ats 1.8 }
434    
435     void populate_page() {
436 ats 1.24 sg_page_stats *page;
437 tdb 1.1
438 ats 1.24 page = use_diffs ? sg_get_page_stats_diff() : sg_get_page_stats();
439 tdb 1.1 if (page != NULL) {
440     add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
441     add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
442 ats 1.11 add_stat(TIME_T, &page->systime, "page", "systime", NULL);
443 tdb 1.1 }
444 ats 1.8 }
445    
446     typedef struct {
447     const char *name;
448     void (*populate)();
449     int interesting;
450     } toplevel;
451     toplevel toplevels[] = {
452     {"const.", populate_const, 0},
453     {"cpu.", populate_cpu, 0},
454     {"mem.", populate_mem, 0},
455     {"load.", populate_load, 0},
456     {"user.", populate_user, 0},
457     {"swap.", populate_swap, 0},
458     {"general.", populate_general, 0},
459     {"fs.", populate_fs, 0},
460     {"disk.", populate_disk, 0},
461     {"proc.", populate_proc, 0},
462     {"net.", populate_net, 0},
463     {"page.", populate_page, 0},
464     {NULL, NULL, 0}
465     };
466    
467     /* Set the "interesting" flag on the sections that we actually need to
468     fetch. */
469     void select_interesting(int argc, char **argv) {
470     toplevel *t;
471    
472     if (argc == 0) {
473     for (t = &toplevels[0]; t->name != NULL; t++)
474     t->interesting = 1;
475     } else {
476     int i;
477    
478     for (i = 0; i < argc; i++) {
479     for (t = &toplevels[0]; t->name != NULL; t++) {
480     if (strncmp(argv[i], t->name,
481 tdb 1.26 strlen(t->name)) == 0) {
482 ats 1.8 t->interesting = 1;
483     break;
484     }
485     }
486     }
487     }
488     }
489    
490     /* Clear and rebuild the stats array. */
491     void get_stats() {
492     toplevel *t;
493    
494     clear_stats();
495    
496     for (t = &toplevels[0]; t->name != NULL; t++) {
497     if (t->interesting)
498     t->populate();
499     }
500 tdb 1.1
501 ats 1.21 if (stats != NULL)
502     qsort(stats, num_stats, sizeof *stats, stats_compare);
503 tdb 1.1 }
504    
505     /* Print the value of a stat. */
506     void print_stat_value(const stat *s) {
507     void *v = s->stat;
508 ats 1.29 double fv;
509 ats 1.30 long lv;
510     long long llv;
511 tdb 1.1
512     switch (s->type) {
513     case LONG_LONG:
514 tdb 1.36 #ifdef WIN32 /* Windows printf does not understand %lld, so use %I64d instead */
515     printf("%I64d", *(long long *)v);
516     #else
517 tdb 1.1 printf("%lld", *(long long *)v);
518 tdb 1.36 #endif
519 tdb 1.1 break;
520 ats 1.30 case BYTES:
521     llv = *(long long *)v;
522     if (bytes_scale_factor != 0) {
523     llv /= bytes_scale_factor;
524     }
525 tdb 1.36 #ifdef WIN32
526     printf("%I64d", llv);
527     #else
528 ats 1.30 printf("%lld", llv);
529 tdb 1.36 #endif
530 ats 1.30 break;
531 tdb 1.1 case TIME_T:
532     /* FIXME option for formatted time? */
533 ats 1.30 lv = *(time_t *)v;
534     printf("%ld", lv);
535 tdb 1.1 break;
536     case FLOAT:
537     case DOUBLE:
538 ats 1.29 if (s->type == FLOAT) {
539     fv = *(float *)v;
540     } else {
541     fv = *(double *)v;
542     }
543     if (float_scale_factor != 0) {
544     printf("%ld", (long)(float_scale_factor * fv));
545     } else {
546     printf("%f", fv);
547     }
548 tdb 1.1 break;
549     case STRING:
550     /* FIXME escaping? */
551     printf("%s", *(char **)v);
552     break;
553     case INT:
554     printf("%d", *(int *)v);
555 ats 1.20 break;
556     case BOOL:
557     printf("%s", *(int *)v ? "true" : "false");
558 ats 1.18 break;
559     case DUPLEX:
560 ats 1.24 switch (*(sg_iface_duplex *) v) {
561     case SG_IFACE_DUPLEX_FULL:
562 ats 1.18 printf("full");
563     break;
564 ats 1.24 case SG_IFACE_DUPLEX_HALF:
565 ats 1.18 printf("half");
566     break;
567     default:
568     printf("unknown");
569     break;
570     }
571 tdb 1.1 break;
572     }
573     }
574    
575     /* Print the name and value of a stat. */
576     void print_stat(const stat *s) {
577     switch (display_mode) {
578     case DISPLAY_LINUX:
579     printf("%s = ", s->name);
580     break;
581     case DISPLAY_BSD:
582     printf("%s: ", s->name);
583     break;
584     case DISPLAY_MRTG:
585     case DISPLAY_PLAIN:
586     break;
587     }
588     print_stat_value(s);
589     printf("\n");
590     }
591    
592     /* Print stats as specified on the provided command line. */
593     void print_stats(int argc, char **argv) {
594     int i;
595    
596     if (argc == optind) {
597     /* Print all stats. */
598     for (i = 0; i < num_stats; i++)
599     print_stat(&stats[i]);
600     } else {
601     /* Print selected stats. */
602     for (i = optind; i < argc; i++) {
603 ats 1.9 char *name = argv[i];
604 tdb 1.1 stat key;
605 ats 1.9 const stat *s, *end;
606     int (*compare)(const void *, const void *);
607    
608     key.name = name;
609     if (name[strlen(name) - 1] == '.')
610     compare = stats_compare_prefix;
611     else
612     compare = stats_compare;
613 tdb 1.1
614 ats 1.21 if (stats == NULL) {
615     s = NULL;
616     } else {
617     s = (const stat *)bsearch(&key, stats,
618 tdb 1.26 num_stats,
619     sizeof *stats,
620     compare);
621 ats 1.21 }
622    
623 ats 1.9 if (s == NULL) {
624     printf("Unknown stat %s\n", name);
625     continue;
626     }
627    
628     /* Find the range of stats the user wanted. */
629     for (; s >= stats; s--) {
630     if (compare(&key, s) != 0)
631     break;
632     }
633     s++;
634     for (end = s; end < &stats[num_stats]; end++) {
635     if (compare(&key, end) != 0)
636     break;
637     }
638    
639     /* And print them. */
640     for (; s < end; s++) {
641 tdb 1.1 print_stat(s);
642     }
643     }
644     }
645     }
646    
647     void usage() {
648     printf("Usage: statgrab [OPTION]... [STAT]...\n"
649 ats 1.9 "Display system statistics.\n"
650     "\n"
651     "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
652     "statistics starting with that prefix.\n"
653 tdb 1.1 "\n");
654 ats 1.32 printf(" -l Linux sysctl-style output (default)\n"
655     " -b BSD sysctl-style output\n"
656     " -m MRTG-compatible output\n"
657     " -u Plain output (only show values)\n"
658     " -n Display cumulative stats once (default)\n"
659     " -s Display stat differences repeatedly\n"
660     " -o Display stat differences once\n"
661 tdb 1.1 " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
662 ats 1.32 " -p Display CPU usage differences as percentages rather than\n"
663     " absolute values\n"
664 tdb 1.33 " -f FACTOR Display floating-point values as integers scaled by FACTOR\n"
665 ats 1.30 " -K Display byte counts in kibibytes\n"
666 ats 1.31 " -M Display byte counts in mebibytes\n"
667 ats 1.30 " -G Display byte counts in gibibytes\n"
668 tdb 1.1 "\n");
669 tdb 1.2 printf("Version %s - report bugs to <%s>.\n",
670 ats 1.5 PACKAGE_VERSION, PACKAGE_BUGREPORT);
671 tdb 1.1 exit(1);
672     }
673    
674     int main(int argc, char **argv) {
675     opterr = 0;
676     while (1) {
677 ats 1.30 int c = getopt(argc, argv, "lbmunsot:pf:KMG");
678 tdb 1.1 if (c == -1)
679     break;
680     switch (c) {
681     case 'l':
682     display_mode = DISPLAY_LINUX;
683     break;
684     case 'b':
685     display_mode = DISPLAY_BSD;
686     break;
687     case 'm':
688     display_mode = DISPLAY_MRTG;
689     break;
690     case 'u':
691     display_mode = DISPLAY_PLAIN;
692     break;
693     case 'n':
694     repeat_mode = REPEAT_NONE;
695     break;
696     case 's':
697     repeat_mode = REPEAT_FOREVER;
698     break;
699     case 'o':
700     repeat_mode = REPEAT_ONCE;
701     break;
702     case 't':
703     repeat_time = atoi(optarg);
704     break;
705     case 'p':
706     use_cpu_percent = 1;
707 ats 1.29 break;
708     case 'f':
709     float_scale_factor = atol(optarg);
710 ats 1.30 break;
711     case 'K':
712     bytes_scale_factor = 1024;
713     break;
714     case 'M':
715     bytes_scale_factor = 1024 * 1024;
716     break;
717     case 'G':
718     bytes_scale_factor = 1024 * 1024 * 1024;
719 tdb 1.1 break;
720     default:
721     usage();
722     }
723     }
724    
725     if (display_mode == DISPLAY_MRTG) {
726     if ((argc - optind) != 2)
727     die("mrtg mode: must specify exactly two stats");
728 ats 1.5 if (repeat_mode == REPEAT_FOREVER)
729 tdb 1.1 die("mrtg mode: cannot repeat display");
730     }
731 ats 1.5
732     if (use_cpu_percent && repeat_mode == REPEAT_NONE)
733     die("CPU percentage usage display requires stat differences");
734 tdb 1.1
735 ats 1.8 if (repeat_mode == REPEAT_NONE)
736     use_diffs = 0;
737     else
738     use_diffs = 1;
739    
740     select_interesting(argc - optind, &argv[optind]);
741    
742 tdb 1.28 /* We don't care if sg_init fails, because we can just display
743 ats 1.12 the statistics that can be read as non-root. */
744 ats 1.24 sg_init();
745 tdb 1.36 sg_snapshot();
746 ats 1.24 if (sg_drop_privileges() != 0)
747 ats 1.15 die("Failed to drop setuid/setgid privileges");
748 ats 1.12
749 tdb 1.1 switch (repeat_mode) {
750     case REPEAT_NONE:
751 ats 1.8 get_stats();
752 tdb 1.1 print_stats(argc, argv);
753     break;
754     case REPEAT_ONCE:
755 ats 1.8 get_stats();
756 tdb 1.1 sleep(repeat_time);
757 tdb 1.36 sg_snapshot();
758 ats 1.8 get_stats();
759 tdb 1.1 print_stats(argc, argv);
760     break;
761     case REPEAT_FOREVER:
762     while (1) {
763 ats 1.8 get_stats();
764 tdb 1.1 print_stats(argc, argv);
765     printf("\n");
766     sleep(repeat_time);
767 tdb 1.36 sg_snapshot();
768 tdb 1.1 }
769     }
770    
771     if (display_mode == DISPLAY_MRTG) {
772     printf("\n");
773     printf("statgrab\n");
774     }
775 tdb 1.36
776     sg_shutdown();
777 tdb 1.1
778     return 0;
779     }
780