ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.35
Committed: Wed Jul 13 13:01:24 2005 UTC (18 years, 10 months ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_12
Changes since 1.34: +15 -1 lines
Log Message:
Add more fs stats.

Patches provided by: Roman Neuhauser

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.35 * $Id: statgrab.c,v 1.34 2005/07/13 09:31:54 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     printf("%lld", *(long long *)v);
515     break;
516 ats 1.30 case BYTES:
517     llv = *(long long *)v;
518     if (bytes_scale_factor != 0) {
519     llv /= bytes_scale_factor;
520     }
521     printf("%lld", llv);
522     break;
523 tdb 1.1 case TIME_T:
524     /* FIXME option for formatted time? */
525 ats 1.30 lv = *(time_t *)v;
526     printf("%ld", lv);
527 tdb 1.1 break;
528     case FLOAT:
529     case DOUBLE:
530 ats 1.29 if (s->type == FLOAT) {
531     fv = *(float *)v;
532     } else {
533     fv = *(double *)v;
534     }
535     if (float_scale_factor != 0) {
536     printf("%ld", (long)(float_scale_factor * fv));
537     } else {
538     printf("%f", fv);
539     }
540 tdb 1.1 break;
541     case STRING:
542     /* FIXME escaping? */
543     printf("%s", *(char **)v);
544     break;
545     case INT:
546     printf("%d", *(int *)v);
547 ats 1.20 break;
548     case BOOL:
549     printf("%s", *(int *)v ? "true" : "false");
550 ats 1.18 break;
551     case DUPLEX:
552 ats 1.24 switch (*(sg_iface_duplex *) v) {
553     case SG_IFACE_DUPLEX_FULL:
554 ats 1.18 printf("full");
555     break;
556 ats 1.24 case SG_IFACE_DUPLEX_HALF:
557 ats 1.18 printf("half");
558     break;
559     default:
560     printf("unknown");
561     break;
562     }
563 tdb 1.1 break;
564     }
565     }
566    
567     /* Print the name and value of a stat. */
568     void print_stat(const stat *s) {
569     switch (display_mode) {
570     case DISPLAY_LINUX:
571     printf("%s = ", s->name);
572     break;
573     case DISPLAY_BSD:
574     printf("%s: ", s->name);
575     break;
576     case DISPLAY_MRTG:
577     case DISPLAY_PLAIN:
578     break;
579     }
580     print_stat_value(s);
581     printf("\n");
582     }
583    
584     /* Print stats as specified on the provided command line. */
585     void print_stats(int argc, char **argv) {
586     int i;
587    
588     if (argc == optind) {
589     /* Print all stats. */
590     for (i = 0; i < num_stats; i++)
591     print_stat(&stats[i]);
592     } else {
593     /* Print selected stats. */
594     for (i = optind; i < argc; i++) {
595 ats 1.9 char *name = argv[i];
596 tdb 1.1 stat key;
597 ats 1.9 const stat *s, *end;
598     int (*compare)(const void *, const void *);
599    
600     key.name = name;
601     if (name[strlen(name) - 1] == '.')
602     compare = stats_compare_prefix;
603     else
604     compare = stats_compare;
605 tdb 1.1
606 ats 1.21 if (stats == NULL) {
607     s = NULL;
608     } else {
609     s = (const stat *)bsearch(&key, stats,
610 tdb 1.26 num_stats,
611     sizeof *stats,
612     compare);
613 ats 1.21 }
614    
615 ats 1.9 if (s == NULL) {
616     printf("Unknown stat %s\n", name);
617     continue;
618     }
619    
620     /* Find the range of stats the user wanted. */
621     for (; s >= stats; s--) {
622     if (compare(&key, s) != 0)
623     break;
624     }
625     s++;
626     for (end = s; end < &stats[num_stats]; end++) {
627     if (compare(&key, end) != 0)
628     break;
629     }
630    
631     /* And print them. */
632     for (; s < end; s++) {
633 tdb 1.1 print_stat(s);
634     }
635     }
636     }
637     }
638    
639     void usage() {
640     printf("Usage: statgrab [OPTION]... [STAT]...\n"
641 ats 1.9 "Display system statistics.\n"
642     "\n"
643     "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
644     "statistics starting with that prefix.\n"
645 tdb 1.1 "\n");
646 ats 1.32 printf(" -l Linux sysctl-style output (default)\n"
647     " -b BSD sysctl-style output\n"
648     " -m MRTG-compatible output\n"
649     " -u Plain output (only show values)\n"
650     " -n Display cumulative stats once (default)\n"
651     " -s Display stat differences repeatedly\n"
652     " -o Display stat differences once\n"
653 tdb 1.1 " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
654 ats 1.32 " -p Display CPU usage differences as percentages rather than\n"
655     " absolute values\n"
656 tdb 1.33 " -f FACTOR Display floating-point values as integers scaled by FACTOR\n"
657 ats 1.30 " -K Display byte counts in kibibytes\n"
658 ats 1.31 " -M Display byte counts in mebibytes\n"
659 ats 1.30 " -G Display byte counts in gibibytes\n"
660 tdb 1.1 "\n");
661 tdb 1.2 printf("Version %s - report bugs to <%s>.\n",
662 ats 1.5 PACKAGE_VERSION, PACKAGE_BUGREPORT);
663 tdb 1.1 exit(1);
664     }
665    
666     int main(int argc, char **argv) {
667     opterr = 0;
668     while (1) {
669 ats 1.30 int c = getopt(argc, argv, "lbmunsot:pf:KMG");
670 tdb 1.1 if (c == -1)
671     break;
672     switch (c) {
673     case 'l':
674     display_mode = DISPLAY_LINUX;
675     break;
676     case 'b':
677     display_mode = DISPLAY_BSD;
678     break;
679     case 'm':
680     display_mode = DISPLAY_MRTG;
681     break;
682     case 'u':
683     display_mode = DISPLAY_PLAIN;
684     break;
685     case 'n':
686     repeat_mode = REPEAT_NONE;
687     break;
688     case 's':
689     repeat_mode = REPEAT_FOREVER;
690     break;
691     case 'o':
692     repeat_mode = REPEAT_ONCE;
693     break;
694     case 't':
695     repeat_time = atoi(optarg);
696     break;
697     case 'p':
698     use_cpu_percent = 1;
699 ats 1.29 break;
700     case 'f':
701     float_scale_factor = atol(optarg);
702 ats 1.30 break;
703     case 'K':
704     bytes_scale_factor = 1024;
705     break;
706     case 'M':
707     bytes_scale_factor = 1024 * 1024;
708     break;
709     case 'G':
710     bytes_scale_factor = 1024 * 1024 * 1024;
711 tdb 1.1 break;
712     default:
713     usage();
714     }
715     }
716    
717     if (display_mode == DISPLAY_MRTG) {
718     if ((argc - optind) != 2)
719     die("mrtg mode: must specify exactly two stats");
720 ats 1.5 if (repeat_mode == REPEAT_FOREVER)
721 tdb 1.1 die("mrtg mode: cannot repeat display");
722     }
723 ats 1.5
724     if (use_cpu_percent && repeat_mode == REPEAT_NONE)
725     die("CPU percentage usage display requires stat differences");
726 tdb 1.1
727 ats 1.8 if (repeat_mode == REPEAT_NONE)
728     use_diffs = 0;
729     else
730     use_diffs = 1;
731    
732     select_interesting(argc - optind, &argv[optind]);
733    
734 tdb 1.28 /* We don't care if sg_init fails, because we can just display
735 ats 1.12 the statistics that can be read as non-root. */
736 ats 1.24 sg_init();
737     if (sg_drop_privileges() != 0)
738 ats 1.15 die("Failed to drop setuid/setgid privileges");
739 ats 1.12
740 tdb 1.1 switch (repeat_mode) {
741     case REPEAT_NONE:
742 ats 1.8 get_stats();
743 tdb 1.1 print_stats(argc, argv);
744     break;
745     case REPEAT_ONCE:
746 ats 1.8 get_stats();
747 tdb 1.1 sleep(repeat_time);
748 ats 1.8 get_stats();
749 tdb 1.1 print_stats(argc, argv);
750     break;
751     case REPEAT_FOREVER:
752     while (1) {
753 ats 1.8 get_stats();
754 tdb 1.1 print_stats(argc, argv);
755     printf("\n");
756     sleep(repeat_time);
757     }
758     }
759    
760     if (display_mode == DISPLAY_MRTG) {
761     printf("\n");
762     printf("statgrab\n");
763     }
764    
765     return 0;
766     }
767