ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
Revision: 1.30
Committed: Tue Aug 10 18:58:42 2004 UTC (20 years, 3 months ago) by ats
Content type: text/plain
Branch: MAIN
Changes since 1.29: +41 -19 lines
Log Message:
Add -K, -M and -G to display byte counts in kibibytes, mibibytes and
gibibytes (mostly for MRTG use, since it can't deal with numbers bigger
than 32 bits).

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.30 * $Id: statgrab.c,v 1.29 2004/08/10 18:50:37 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     } 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 ats 1.10
317     free(buf);
318 tdb 1.1 }
319     }
320 ats 1.8 }
321    
322     void populate_disk() {
323     int n, i;
324 ats 1.24 sg_disk_io_stats *diskio;
325 tdb 1.1
326 ats 1.24 diskio = use_diffs ? sg_get_disk_io_stats_diff(&n)
327 tdb 1.26 : sg_get_disk_io_stats(&n);
328 tdb 1.1 if (diskio != NULL) {
329     for (i = 0; i < n; i++) {
330     const char *name = diskio[i].disk_name;
331    
332     add_stat(STRING, &diskio[i].disk_name,
333 tdb 1.26 "disk", name, "disk_name", NULL);
334 ats 1.30 add_stat(BYTES, &diskio[i].read_bytes,
335 tdb 1.26 "disk", name, "read_bytes", NULL);
336 ats 1.30 add_stat(BYTES, &diskio[i].write_bytes,
337 tdb 1.26 "disk", name, "write_bytes", NULL);
338 tdb 1.1 add_stat(TIME_T, &diskio[i].systime,
339 tdb 1.26 "disk", name, "systime", NULL);
340 tdb 1.1 }
341     }
342 ats 1.8 }
343    
344     void populate_proc() {
345 ats 1.24 /* FIXME expose individual process info too */
346     sg_process_count *proc = sg_get_process_count();
347 tdb 1.1
348     if (proc != NULL) {
349     add_stat(INT, &proc->total, "proc", "total", NULL);
350     add_stat(INT, &proc->running, "proc", "running", NULL);
351     add_stat(INT, &proc->sleeping, "proc", "sleeping", NULL);
352     add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
353     add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
354     }
355 ats 1.8 }
356    
357     void populate_net() {
358 ats 1.27 int num_io, num_iface, i;
359 ats 1.24 sg_network_io_stats *io;
360     sg_network_iface_stats *iface;
361 tdb 1.1
362 ats 1.27 io = use_diffs ? sg_get_network_io_stats_diff(&num_io)
363     : sg_get_network_io_stats(&num_io);
364 ats 1.24 if (io != NULL) {
365 ats 1.27 for (i = 0; i < num_io; i++) {
366 ats 1.24 const char *name = io[i].interface_name;
367 tdb 1.1
368 ats 1.24 add_stat(STRING, &io[i].interface_name,
369 tdb 1.26 "net", name, "interface_name", NULL);
370 ats 1.30 add_stat(BYTES, &io[i].tx,
371 tdb 1.26 "net", name, "tx", NULL);
372 ats 1.30 add_stat(BYTES, &io[i].rx,
373 tdb 1.26 "net", name, "rx", NULL);
374 ats 1.24 add_stat(LONG_LONG, &io[i].ipackets,
375 tdb 1.26 "net", name, "ipackets", NULL);
376 ats 1.24 add_stat(LONG_LONG, &io[i].opackets,
377 tdb 1.26 "net", name, "opackets", NULL);
378 ats 1.24 add_stat(LONG_LONG, &io[i].ierrors,
379 tdb 1.26 "net", name, "ierrors", NULL);
380 ats 1.24 add_stat(LONG_LONG, &io[i].oerrors,
381 tdb 1.26 "net", name, "oerrors", NULL);
382 ats 1.24 add_stat(LONG_LONG, &io[i].collisions,
383 tdb 1.26 "net", name, "collisions", NULL);
384 ats 1.24 add_stat(TIME_T, &io[i].systime,
385 tdb 1.26 "net", name, "systime", NULL);
386 tdb 1.1 }
387     }
388 ats 1.18
389 ats 1.27 iface = sg_get_network_iface_stats(&num_iface);
390 ats 1.18 if (iface != NULL) {
391 ats 1.27 for (i = 0; i < num_iface; i++) {
392 ats 1.18 const char *name = iface[i].interface_name;
393 ats 1.27 int had_io = 0, j;
394    
395     /* If there wasn't a corresponding io stat,
396     add interface_name from here. */
397     if (io != NULL) {
398     for (j = 0; j < num_io; j++) {
399     if (strcmp(io[j].interface_name,
400     name) == 0) {
401     had_io = 1;
402     break;
403     }
404     }
405     }
406     if (!had_io) {
407     add_stat(STRING, &iface[i].interface_name,
408     "net", name, "interface_name", NULL);
409     }
410 ats 1.18
411     add_stat(INT, &iface[i].speed,
412 tdb 1.26 "net", name, "speed", NULL);
413 ats 1.20 add_stat(BOOL, &iface[i].up,
414 tdb 1.26 "net", name, "up", NULL);
415 ats 1.18 add_stat(DUPLEX, &iface[i].dup,
416 tdb 1.26 "net", name, "duplex", NULL);
417 ats 1.18 }
418     }
419 ats 1.8 }
420    
421     void populate_page() {
422 ats 1.24 sg_page_stats *page;
423 tdb 1.1
424 ats 1.24 page = use_diffs ? sg_get_page_stats_diff() : sg_get_page_stats();
425 tdb 1.1 if (page != NULL) {
426     add_stat(LONG_LONG, &page->pages_pagein, "page", "in", NULL);
427     add_stat(LONG_LONG, &page->pages_pageout, "page", "out", NULL);
428 ats 1.11 add_stat(TIME_T, &page->systime, "page", "systime", NULL);
429 tdb 1.1 }
430 ats 1.8 }
431    
432     typedef struct {
433     const char *name;
434     void (*populate)();
435     int interesting;
436     } toplevel;
437     toplevel toplevels[] = {
438     {"const.", populate_const, 0},
439     {"cpu.", populate_cpu, 0},
440     {"mem.", populate_mem, 0},
441     {"load.", populate_load, 0},
442     {"user.", populate_user, 0},
443     {"swap.", populate_swap, 0},
444     {"general.", populate_general, 0},
445     {"fs.", populate_fs, 0},
446     {"disk.", populate_disk, 0},
447     {"proc.", populate_proc, 0},
448     {"net.", populate_net, 0},
449     {"page.", populate_page, 0},
450     {NULL, NULL, 0}
451     };
452    
453     /* Set the "interesting" flag on the sections that we actually need to
454     fetch. */
455     void select_interesting(int argc, char **argv) {
456     toplevel *t;
457    
458     if (argc == 0) {
459     for (t = &toplevels[0]; t->name != NULL; t++)
460     t->interesting = 1;
461     } else {
462     int i;
463    
464     for (i = 0; i < argc; i++) {
465     for (t = &toplevels[0]; t->name != NULL; t++) {
466     if (strncmp(argv[i], t->name,
467 tdb 1.26 strlen(t->name)) == 0) {
468 ats 1.8 t->interesting = 1;
469     break;
470     }
471     }
472     }
473     }
474     }
475    
476     /* Clear and rebuild the stats array. */
477     void get_stats() {
478     toplevel *t;
479    
480     clear_stats();
481    
482     for (t = &toplevels[0]; t->name != NULL; t++) {
483     if (t->interesting)
484     t->populate();
485     }
486 tdb 1.1
487 ats 1.21 if (stats != NULL)
488     qsort(stats, num_stats, sizeof *stats, stats_compare);
489 tdb 1.1 }
490    
491     /* Print the value of a stat. */
492     void print_stat_value(const stat *s) {
493     void *v = s->stat;
494 ats 1.29 double fv;
495 ats 1.30 long lv;
496     long long llv;
497 tdb 1.1
498     switch (s->type) {
499     case LONG_LONG:
500     printf("%lld", *(long long *)v);
501     break;
502 ats 1.30 case BYTES:
503     llv = *(long long *)v;
504     if (bytes_scale_factor != 0) {
505     llv /= bytes_scale_factor;
506     }
507     printf("%lld", llv);
508     break;
509 tdb 1.1 case TIME_T:
510     /* FIXME option for formatted time? */
511 ats 1.30 lv = *(time_t *)v;
512     printf("%ld", lv);
513 tdb 1.1 break;
514     case FLOAT:
515     case DOUBLE:
516 ats 1.29 if (s->type == FLOAT) {
517     fv = *(float *)v;
518     } else {
519     fv = *(double *)v;
520     }
521     if (float_scale_factor != 0) {
522     printf("%ld", (long)(float_scale_factor * fv));
523     } else {
524     printf("%f", fv);
525     }
526 tdb 1.1 break;
527     case STRING:
528     /* FIXME escaping? */
529     printf("%s", *(char **)v);
530     break;
531     case INT:
532     printf("%d", *(int *)v);
533 ats 1.20 break;
534     case BOOL:
535     printf("%s", *(int *)v ? "true" : "false");
536 ats 1.18 break;
537     case DUPLEX:
538 ats 1.24 switch (*(sg_iface_duplex *) v) {
539     case SG_IFACE_DUPLEX_FULL:
540 ats 1.18 printf("full");
541     break;
542 ats 1.24 case SG_IFACE_DUPLEX_HALF:
543 ats 1.18 printf("half");
544     break;
545     default:
546     printf("unknown");
547     break;
548     }
549 tdb 1.1 break;
550     }
551     }
552    
553     /* Print the name and value of a stat. */
554     void print_stat(const stat *s) {
555     switch (display_mode) {
556     case DISPLAY_LINUX:
557     printf("%s = ", s->name);
558     break;
559     case DISPLAY_BSD:
560     printf("%s: ", s->name);
561     break;
562     case DISPLAY_MRTG:
563     case DISPLAY_PLAIN:
564     break;
565     }
566     print_stat_value(s);
567     printf("\n");
568     }
569    
570     /* Print stats as specified on the provided command line. */
571     void print_stats(int argc, char **argv) {
572     int i;
573    
574     if (argc == optind) {
575     /* Print all stats. */
576     for (i = 0; i < num_stats; i++)
577     print_stat(&stats[i]);
578     } else {
579     /* Print selected stats. */
580     for (i = optind; i < argc; i++) {
581 ats 1.9 char *name = argv[i];
582 tdb 1.1 stat key;
583 ats 1.9 const stat *s, *end;
584     int (*compare)(const void *, const void *);
585    
586     key.name = name;
587     if (name[strlen(name) - 1] == '.')
588     compare = stats_compare_prefix;
589     else
590     compare = stats_compare;
591 tdb 1.1
592 ats 1.21 if (stats == NULL) {
593     s = NULL;
594     } else {
595     s = (const stat *)bsearch(&key, stats,
596 tdb 1.26 num_stats,
597     sizeof *stats,
598     compare);
599 ats 1.21 }
600    
601 ats 1.9 if (s == NULL) {
602     printf("Unknown stat %s\n", name);
603     continue;
604     }
605    
606     /* Find the range of stats the user wanted. */
607     for (; s >= stats; s--) {
608     if (compare(&key, s) != 0)
609     break;
610     }
611     s++;
612     for (end = s; end < &stats[num_stats]; end++) {
613     if (compare(&key, end) != 0)
614     break;
615     }
616    
617     /* And print them. */
618     for (; s < end; s++) {
619 tdb 1.1 print_stat(s);
620     }
621     }
622     }
623     }
624    
625     void usage() {
626     printf("Usage: statgrab [OPTION]... [STAT]...\n"
627 ats 1.9 "Display system statistics.\n"
628     "\n"
629     "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
630     "statistics starting with that prefix.\n"
631 tdb 1.1 "\n");
632 tdb 1.26 printf(" -l Linux sysctl-style output (default)\n"
633     " -b BSD sysctl-style output\n"
634     " -m MRTG-compatible output\n"
635     " -u Plain output (only show values)\n"
636     " -n Display cumulative stats once (default)\n"
637     " -s Display stat differences repeatedly\n"
638     " -o Display stat differences once\n"
639 tdb 1.1 " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
640 tdb 1.26 " -p Display CPU usage differences as percentages rather than\n"
641     " absolute values\n"
642 ats 1.29 " -f SCALE Display floating-point values as integers scaled by FACTOR\n"
643 ats 1.30 " -K Display byte counts in kibibytes\n"
644     " -M Display byte counts in mibibytes\n"
645     " -G Display byte counts in gibibytes\n"
646 tdb 1.1 "\n");
647 tdb 1.2 printf("Version %s - report bugs to <%s>.\n",
648 ats 1.5 PACKAGE_VERSION, PACKAGE_BUGREPORT);
649 tdb 1.1 exit(1);
650     }
651    
652     int main(int argc, char **argv) {
653     opterr = 0;
654     while (1) {
655 ats 1.30 int c = getopt(argc, argv, "lbmunsot:pf:KMG");
656 tdb 1.1 if (c == -1)
657     break;
658     switch (c) {
659     case 'l':
660     display_mode = DISPLAY_LINUX;
661     break;
662     case 'b':
663     display_mode = DISPLAY_BSD;
664     break;
665     case 'm':
666     display_mode = DISPLAY_MRTG;
667     break;
668     case 'u':
669     display_mode = DISPLAY_PLAIN;
670     break;
671     case 'n':
672     repeat_mode = REPEAT_NONE;
673     break;
674     case 's':
675     repeat_mode = REPEAT_FOREVER;
676     break;
677     case 'o':
678     repeat_mode = REPEAT_ONCE;
679     break;
680     case 't':
681     repeat_time = atoi(optarg);
682     break;
683     case 'p':
684     use_cpu_percent = 1;
685 ats 1.29 break;
686     case 'f':
687     float_scale_factor = atol(optarg);
688 ats 1.30 break;
689     case 'K':
690     bytes_scale_factor = 1024;
691     break;
692     case 'M':
693     bytes_scale_factor = 1024 * 1024;
694     break;
695     case 'G':
696     bytes_scale_factor = 1024 * 1024 * 1024;
697 tdb 1.1 break;
698     default:
699     usage();
700     }
701     }
702    
703     if (display_mode == DISPLAY_MRTG) {
704     if ((argc - optind) != 2)
705     die("mrtg mode: must specify exactly two stats");
706 ats 1.5 if (repeat_mode == REPEAT_FOREVER)
707 tdb 1.1 die("mrtg mode: cannot repeat display");
708     }
709 ats 1.5
710     if (use_cpu_percent && repeat_mode == REPEAT_NONE)
711     die("CPU percentage usage display requires stat differences");
712 tdb 1.1
713 ats 1.8 if (repeat_mode == REPEAT_NONE)
714     use_diffs = 0;
715     else
716     use_diffs = 1;
717    
718     select_interesting(argc - optind, &argv[optind]);
719    
720 tdb 1.28 /* We don't care if sg_init fails, because we can just display
721 ats 1.12 the statistics that can be read as non-root. */
722 ats 1.24 sg_init();
723     if (sg_drop_privileges() != 0)
724 ats 1.15 die("Failed to drop setuid/setgid privileges");
725 ats 1.12
726 tdb 1.1 switch (repeat_mode) {
727     case REPEAT_NONE:
728 ats 1.8 get_stats();
729 tdb 1.1 print_stats(argc, argv);
730     break;
731     case REPEAT_ONCE:
732 ats 1.8 get_stats();
733 tdb 1.1 sleep(repeat_time);
734 ats 1.8 get_stats();
735 tdb 1.1 print_stats(argc, argv);
736     break;
737     case REPEAT_FOREVER:
738     while (1) {
739 ats 1.8 get_stats();
740 tdb 1.1 print_stats(argc, argv);
741     printf("\n");
742     sleep(repeat_time);
743     }
744     }
745    
746     if (display_mode == DISPLAY_MRTG) {
747     printf("\n");
748     printf("statgrab\n");
749     }
750    
751     return 0;
752     }
753