ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.38
Committed: Sun Oct 3 18:35:59 2010 UTC (14 years, 2 months ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.37: +30 -26 lines
Log Message:
Add support for AIX 5.x - 9.x.

Many thanks to Jens Rehsack <rehsack@googlemail.com> for providing the
patch for this work. Thanks!

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