ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.37
Committed: Fri Mar 17 13:23:05 2006 UTC (18 years, 9 months ago) by ats
Content type: text/plain
Branch: MAIN
CVS Tags: LIBSTATGRAB_0_17, LIBSTATGRAB_0_16, LIBSTATGRAB_0_15, LIBSTATGRAB_0_14, LIBSTATGRAB_0_13
Changes since 1.36: +9 -8 lines
Log Message:
Replace both spaces and dots with underscores in name components, so
that filesystem paths containing dots work correctly.

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