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

All the hard work done by: skel

File Contents

# Content
1 /*
2 * i-scream libstatgrab
3 * http://www.i-scream.org
4 * Copyright (C) 2000-2004 i-scream
5 *
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 *
20 * $Id: statgrab.c,v 1.35 2005/07/13 13:01:24 tdb Exp $
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #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 BYTES,
37 TIME_T,
38 FLOAT,
39 DOUBLE,
40 STRING,
41 INT,
42 BOOL,
43 DUPLEX
44 } 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 int use_diffs = 0;
75 long float_scale_factor = 0;
76 long long bytes_scale_factor = 0;
77
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 /* Compare two stats by name for qsort and bsearch. */
151 int stats_compare(const void *a, const void *b) {
152 return strcmp(((stat *)a)->name, ((stat *)b)->name);
153 }
154
155 /* 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 void populate_const() {
164 static int zero = 0;
165
166 /* Constants, for use with MRTG mode. */
167 add_stat(INT, &zero, "const", "0", NULL);
168 }
169
170 void populate_cpu() {
171 if (use_cpu_percent) {
172 sg_cpu_percents *cpu_p = sg_get_cpu_percents();
173
174 if (cpu_p != NULL) {
175 add_stat(FLOAT, &cpu_p->user,
176 "cpu", "user", NULL);
177 add_stat(FLOAT, &cpu_p->kernel,
178 "cpu", "kernel", NULL);
179 add_stat(FLOAT, &cpu_p->idle,
180 "cpu", "idle", NULL);
181 add_stat(FLOAT, &cpu_p->iowait,
182 "cpu", "iowait", NULL);
183 add_stat(FLOAT, &cpu_p->swap,
184 "cpu", "swap", NULL);
185 add_stat(FLOAT, &cpu_p->nice,
186 "cpu", "nice", NULL);
187 add_stat(TIME_T, &cpu_p->time_taken,
188 "cpu", "time_taken", NULL);
189 }
190 } else {
191 sg_cpu_stats *cpu_s;
192
193 cpu_s = use_diffs ? sg_get_cpu_stats_diff()
194 : sg_get_cpu_stats();
195 if (cpu_s != NULL) {
196 add_stat(LONG_LONG, &cpu_s->user,
197 "cpu", "user", NULL);
198 add_stat(LONG_LONG, &cpu_s->kernel,
199 "cpu", "kernel", NULL);
200 add_stat(LONG_LONG, &cpu_s->idle,
201 "cpu", "idle", NULL);
202 add_stat(LONG_LONG, &cpu_s->iowait,
203 "cpu", "iowait", NULL);
204 add_stat(LONG_LONG, &cpu_s->swap,
205 "cpu", "swap", NULL);
206 add_stat(LONG_LONG, &cpu_s->nice,
207 "cpu", "nice", NULL);
208 add_stat(LONG_LONG, &cpu_s->total,
209 "cpu", "total", NULL);
210 add_stat(TIME_T, &cpu_s->systime,
211 "cpu", "systime", NULL);
212 }
213 }
214 }
215
216 void populate_mem() {
217 sg_mem_stats *mem = sg_get_mem_stats();
218
219 if (mem != NULL) {
220 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 }
225 }
226
227 void populate_load() {
228 sg_load_stats *load = sg_get_load_stats();
229
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 }
236
237 void populate_user() {
238 sg_user_stats *user = sg_get_user_stats();
239
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 }
245
246 void populate_swap() {
247 sg_swap_stats *swap = sg_get_swap_stats();
248
249 if (swap != NULL) {
250 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 }
254 }
255
256 void populate_general() {
257 /* FIXME this should be renamed to host. */
258 sg_host_info *host = sg_get_host_info();
259
260 if (host != NULL) {
261 add_stat(STRING, &host->os_name,
262 "general", "os_name", NULL);
263 add_stat(STRING, &host->os_release,
264 "general", "os_release", NULL);
265 add_stat(STRING, &host->os_version,
266 "general", "os_version", NULL);
267 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 }
271 }
272
273 void populate_fs() {
274 int n, i;
275 sg_fs_stats *disk = sg_get_fs_stats(&n);
276
277 if (disk != NULL) {
278 for (i = 0; i < n; i++) {
279 /* FIXME it'd be nicer if libstatgrab did this */
280 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 if (strlen(name) == 2 && name[1] == ':')
292 name[1] = '\0';
293 if (strncmp(name, "/dev/", 5) == 0)
294 name += 5;
295 while ((p = strchr(name, '/')) != NULL)
296 *p = '_';
297
298 add_stat(STRING, &disk[i].device_name,
299 "fs", name, "device_name", NULL);
300 add_stat(STRING, &disk[i].fs_type,
301 "fs", name, "fs_type", NULL);
302 add_stat(STRING, &disk[i].mnt_point,
303 "fs", name, "mnt_point", NULL);
304 add_stat(BYTES, &disk[i].size,
305 "fs", name, "size", NULL);
306 add_stat(BYTES, &disk[i].used,
307 "fs", name, "used", NULL);
308 add_stat(BYTES, &disk[i].avail,
309 "fs", name, "avail", NULL);
310 add_stat(LONG_LONG, &disk[i].total_inodes,
311 "fs", name, "total_inodes", NULL);
312 add_stat(LONG_LONG, &disk[i].used_inodes,
313 "fs", name, "used_inodes", NULL);
314 add_stat(LONG_LONG, &disk[i].free_inodes,
315 "fs", name, "free_inodes", NULL);
316 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
331 free(buf);
332 }
333 }
334 }
335
336 void populate_disk() {
337 int n, i;
338 sg_disk_io_stats *diskio;
339
340 diskio = use_diffs ? sg_get_disk_io_stats_diff(&n)
341 : sg_get_disk_io_stats(&n);
342 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 "disk", name, "disk_name", NULL);
348 add_stat(BYTES, &diskio[i].read_bytes,
349 "disk", name, "read_bytes", NULL);
350 add_stat(BYTES, &diskio[i].write_bytes,
351 "disk", name, "write_bytes", NULL);
352 add_stat(TIME_T, &diskio[i].systime,
353 "disk", name, "systime", NULL);
354 }
355 }
356 }
357
358 void populate_proc() {
359 /* FIXME expose individual process info too */
360 sg_process_count *proc = sg_get_process_count();
361
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 }
370
371 void populate_net() {
372 int num_io, num_iface, i;
373 sg_network_io_stats *io;
374 sg_network_iface_stats *iface;
375
376 io = use_diffs ? sg_get_network_io_stats_diff(&num_io)
377 : sg_get_network_io_stats(&num_io);
378 if (io != NULL) {
379 for (i = 0; i < num_io; i++) {
380 const char *name = io[i].interface_name;
381
382 add_stat(STRING, &io[i].interface_name,
383 "net", name, "interface_name", NULL);
384 add_stat(BYTES, &io[i].tx,
385 "net", name, "tx", NULL);
386 add_stat(BYTES, &io[i].rx,
387 "net", name, "rx", NULL);
388 add_stat(LONG_LONG, &io[i].ipackets,
389 "net", name, "ipackets", NULL);
390 add_stat(LONG_LONG, &io[i].opackets,
391 "net", name, "opackets", NULL);
392 add_stat(LONG_LONG, &io[i].ierrors,
393 "net", name, "ierrors", NULL);
394 add_stat(LONG_LONG, &io[i].oerrors,
395 "net", name, "oerrors", NULL);
396 add_stat(LONG_LONG, &io[i].collisions,
397 "net", name, "collisions", NULL);
398 add_stat(TIME_T, &io[i].systime,
399 "net", name, "systime", NULL);
400 }
401 }
402
403 iface = sg_get_network_iface_stats(&num_iface);
404 if (iface != NULL) {
405 for (i = 0; i < num_iface; i++) {
406 const char *name = iface[i].interface_name;
407 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
425 add_stat(INT, &iface[i].speed,
426 "net", name, "speed", NULL);
427 add_stat(BOOL, &iface[i].up,
428 "net", name, "up", NULL);
429 add_stat(DUPLEX, &iface[i].duplex,
430 "net", name, "duplex", NULL);
431 }
432 }
433 }
434
435 void populate_page() {
436 sg_page_stats *page;
437
438 page = use_diffs ? sg_get_page_stats_diff() : sg_get_page_stats();
439 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 add_stat(TIME_T, &page->systime, "page", "systime", NULL);
443 }
444 }
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 strlen(t->name)) == 0) {
482 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
501 if (stats != NULL)
502 qsort(stats, num_stats, sizeof *stats, stats_compare);
503 }
504
505 /* Print the value of a stat. */
506 void print_stat_value(const stat *s) {
507 void *v = s->stat;
508 double fv;
509 long lv;
510 long long llv;
511
512 switch (s->type) {
513 case LONG_LONG:
514 #ifdef WIN32 /* Windows printf does not understand %lld, so use %I64d instead */
515 printf("%I64d", *(long long *)v);
516 #else
517 printf("%lld", *(long long *)v);
518 #endif
519 break;
520 case BYTES:
521 llv = *(long long *)v;
522 if (bytes_scale_factor != 0) {
523 llv /= bytes_scale_factor;
524 }
525 #ifdef WIN32
526 printf("%I64d", llv);
527 #else
528 printf("%lld", llv);
529 #endif
530 break;
531 case TIME_T:
532 /* FIXME option for formatted time? */
533 lv = *(time_t *)v;
534 printf("%ld", lv);
535 break;
536 case FLOAT:
537 case DOUBLE:
538 if (s->type == FLOAT) {
539 fv = *(float *)v;
540 } else {
541 fv = *(double *)v;
542 }
543 if (float_scale_factor != 0) {
544 printf("%ld", (long)(float_scale_factor * fv));
545 } else {
546 printf("%f", fv);
547 }
548 break;
549 case STRING:
550 /* FIXME escaping? */
551 printf("%s", *(char **)v);
552 break;
553 case INT:
554 printf("%d", *(int *)v);
555 break;
556 case BOOL:
557 printf("%s", *(int *)v ? "true" : "false");
558 break;
559 case DUPLEX:
560 switch (*(sg_iface_duplex *) v) {
561 case SG_IFACE_DUPLEX_FULL:
562 printf("full");
563 break;
564 case SG_IFACE_DUPLEX_HALF:
565 printf("half");
566 break;
567 default:
568 printf("unknown");
569 break;
570 }
571 break;
572 }
573 }
574
575 /* Print the name and value of a stat. */
576 void print_stat(const stat *s) {
577 switch (display_mode) {
578 case DISPLAY_LINUX:
579 printf("%s = ", s->name);
580 break;
581 case DISPLAY_BSD:
582 printf("%s: ", s->name);
583 break;
584 case DISPLAY_MRTG:
585 case DISPLAY_PLAIN:
586 break;
587 }
588 print_stat_value(s);
589 printf("\n");
590 }
591
592 /* Print stats as specified on the provided command line. */
593 void print_stats(int argc, char **argv) {
594 int i;
595
596 if (argc == optind) {
597 /* Print all stats. */
598 for (i = 0; i < num_stats; i++)
599 print_stat(&stats[i]);
600 } else {
601 /* Print selected stats. */
602 for (i = optind; i < argc; i++) {
603 char *name = argv[i];
604 stat key;
605 const stat *s, *end;
606 int (*compare)(const void *, const void *);
607
608 key.name = name;
609 if (name[strlen(name) - 1] == '.')
610 compare = stats_compare_prefix;
611 else
612 compare = stats_compare;
613
614 if (stats == NULL) {
615 s = NULL;
616 } else {
617 s = (const stat *)bsearch(&key, stats,
618 num_stats,
619 sizeof *stats,
620 compare);
621 }
622
623 if (s == NULL) {
624 printf("Unknown stat %s\n", name);
625 continue;
626 }
627
628 /* Find the range of stats the user wanted. */
629 for (; s >= stats; s--) {
630 if (compare(&key, s) != 0)
631 break;
632 }
633 s++;
634 for (end = s; end < &stats[num_stats]; end++) {
635 if (compare(&key, end) != 0)
636 break;
637 }
638
639 /* And print them. */
640 for (; s < end; s++) {
641 print_stat(s);
642 }
643 }
644 }
645 }
646
647 void usage() {
648 printf("Usage: statgrab [OPTION]... [STAT]...\n"
649 "Display system statistics.\n"
650 "\n"
651 "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
652 "statistics starting with that prefix.\n"
653 "\n");
654 printf(" -l Linux sysctl-style output (default)\n"
655 " -b BSD sysctl-style output\n"
656 " -m MRTG-compatible output\n"
657 " -u Plain output (only show values)\n"
658 " -n Display cumulative stats once (default)\n"
659 " -s Display stat differences repeatedly\n"
660 " -o Display stat differences once\n"
661 " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
662 " -p Display CPU usage differences as percentages rather than\n"
663 " absolute values\n"
664 " -f FACTOR Display floating-point values as integers scaled by FACTOR\n"
665 " -K Display byte counts in kibibytes\n"
666 " -M Display byte counts in mebibytes\n"
667 " -G Display byte counts in gibibytes\n"
668 "\n");
669 printf("Version %s - report bugs to <%s>.\n",
670 PACKAGE_VERSION, PACKAGE_BUGREPORT);
671 exit(1);
672 }
673
674 int main(int argc, char **argv) {
675 opterr = 0;
676 while (1) {
677 int c = getopt(argc, argv, "lbmunsot:pf:KMG");
678 if (c == -1)
679 break;
680 switch (c) {
681 case 'l':
682 display_mode = DISPLAY_LINUX;
683 break;
684 case 'b':
685 display_mode = DISPLAY_BSD;
686 break;
687 case 'm':
688 display_mode = DISPLAY_MRTG;
689 break;
690 case 'u':
691 display_mode = DISPLAY_PLAIN;
692 break;
693 case 'n':
694 repeat_mode = REPEAT_NONE;
695 break;
696 case 's':
697 repeat_mode = REPEAT_FOREVER;
698 break;
699 case 'o':
700 repeat_mode = REPEAT_ONCE;
701 break;
702 case 't':
703 repeat_time = atoi(optarg);
704 break;
705 case 'p':
706 use_cpu_percent = 1;
707 break;
708 case 'f':
709 float_scale_factor = atol(optarg);
710 break;
711 case 'K':
712 bytes_scale_factor = 1024;
713 break;
714 case 'M':
715 bytes_scale_factor = 1024 * 1024;
716 break;
717 case 'G':
718 bytes_scale_factor = 1024 * 1024 * 1024;
719 break;
720 default:
721 usage();
722 }
723 }
724
725 if (display_mode == DISPLAY_MRTG) {
726 if ((argc - optind) != 2)
727 die("mrtg mode: must specify exactly two stats");
728 if (repeat_mode == REPEAT_FOREVER)
729 die("mrtg mode: cannot repeat display");
730 }
731
732 if (use_cpu_percent && repeat_mode == REPEAT_NONE)
733 die("CPU percentage usage display requires stat differences");
734
735 if (repeat_mode == REPEAT_NONE)
736 use_diffs = 0;
737 else
738 use_diffs = 1;
739
740 select_interesting(argc - optind, &argv[optind]);
741
742 /* We don't care if sg_init fails, because we can just display
743 the statistics that can be read as non-root. */
744 sg_init();
745 sg_snapshot();
746 if (sg_drop_privileges() != 0)
747 die("Failed to drop setuid/setgid privileges");
748
749 switch (repeat_mode) {
750 case REPEAT_NONE:
751 get_stats();
752 print_stats(argc, argv);
753 break;
754 case REPEAT_ONCE:
755 get_stats();
756 sleep(repeat_time);
757 sg_snapshot();
758 get_stats();
759 print_stats(argc, argv);
760 break;
761 case REPEAT_FOREVER:
762 while (1) {
763 get_stats();
764 print_stats(argc, argv);
765 printf("\n");
766 sleep(repeat_time);
767 sg_snapshot();
768 }
769 }
770
771 if (display_mode == DISPLAY_MRTG) {
772 printf("\n");
773 printf("statgrab\n");
774 }
775
776 sg_shutdown();
777
778 return 0;
779 }
780