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

# 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.36 2005/09/24 13:29:25 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
125 /* Replace spaces and dots with underscores. */
126 while (partlen-- > 0) {
127 if (*p == ' ' || *p == '.')
128 *p = '_';
129 p++;
130 }
131
132 *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 /* Compare two stats by name for qsort and bsearch. */
152 int stats_compare(const void *a, const void *b) {
153 return strcmp(((stat *)a)->name, ((stat *)b)->name);
154 }
155
156 /* 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 void populate_const() {
165 static int zero = 0;
166
167 /* Constants, for use with MRTG mode. */
168 add_stat(INT, &zero, "const", "0", NULL);
169 }
170
171 void populate_cpu() {
172 if (use_cpu_percent) {
173 sg_cpu_percents *cpu_p = sg_get_cpu_percents();
174
175 if (cpu_p != NULL) {
176 add_stat(FLOAT, &cpu_p->user,
177 "cpu", "user", NULL);
178 add_stat(FLOAT, &cpu_p->kernel,
179 "cpu", "kernel", NULL);
180 add_stat(FLOAT, &cpu_p->idle,
181 "cpu", "idle", NULL);
182 add_stat(FLOAT, &cpu_p->iowait,
183 "cpu", "iowait", NULL);
184 add_stat(FLOAT, &cpu_p->swap,
185 "cpu", "swap", NULL);
186 add_stat(FLOAT, &cpu_p->nice,
187 "cpu", "nice", NULL);
188 add_stat(TIME_T, &cpu_p->time_taken,
189 "cpu", "time_taken", NULL);
190 }
191 } else {
192 sg_cpu_stats *cpu_s;
193
194 cpu_s = use_diffs ? sg_get_cpu_stats_diff()
195 : sg_get_cpu_stats();
196 if (cpu_s != NULL) {
197 add_stat(LONG_LONG, &cpu_s->user,
198 "cpu", "user", NULL);
199 add_stat(LONG_LONG, &cpu_s->kernel,
200 "cpu", "kernel", NULL);
201 add_stat(LONG_LONG, &cpu_s->idle,
202 "cpu", "idle", NULL);
203 add_stat(LONG_LONG, &cpu_s->iowait,
204 "cpu", "iowait", NULL);
205 add_stat(LONG_LONG, &cpu_s->swap,
206 "cpu", "swap", NULL);
207 add_stat(LONG_LONG, &cpu_s->nice,
208 "cpu", "nice", NULL);
209 add_stat(LONG_LONG, &cpu_s->total,
210 "cpu", "total", NULL);
211 add_stat(TIME_T, &cpu_s->systime,
212 "cpu", "systime", NULL);
213 }
214 }
215 }
216
217 void populate_mem() {
218 sg_mem_stats *mem = sg_get_mem_stats();
219
220 if (mem != NULL) {
221 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 }
226 }
227
228 void populate_load() {
229 sg_load_stats *load = sg_get_load_stats();
230
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 }
237
238 void populate_user() {
239 sg_user_stats *user = sg_get_user_stats();
240
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 }
246
247 void populate_swap() {
248 sg_swap_stats *swap = sg_get_swap_stats();
249
250 if (swap != NULL) {
251 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 }
255 }
256
257 void populate_general() {
258 /* FIXME this should be renamed to host. */
259 sg_host_info *host = sg_get_host_info();
260
261 if (host != NULL) {
262 add_stat(STRING, &host->os_name,
263 "general", "os_name", NULL);
264 add_stat(STRING, &host->os_release,
265 "general", "os_release", NULL);
266 add_stat(STRING, &host->os_version,
267 "general", "os_version", NULL);
268 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 }
272 }
273
274 void populate_fs() {
275 int n, i;
276 sg_fs_stats *disk = sg_get_fs_stats(&n);
277
278 if (disk != NULL) {
279 for (i = 0; i < n; i++) {
280 /* FIXME it'd be nicer if libstatgrab did this */
281 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 if (strlen(name) == 2 && name[1] == ':')
293 name[1] = '\0';
294 if (strncmp(name, "/dev/", 5) == 0)
295 name += 5;
296 while ((p = strchr(name, '/')) != NULL)
297 *p = '_';
298
299 add_stat(STRING, &disk[i].device_name,
300 "fs", name, "device_name", NULL);
301 add_stat(STRING, &disk[i].fs_type,
302 "fs", name, "fs_type", NULL);
303 add_stat(STRING, &disk[i].mnt_point,
304 "fs", name, "mnt_point", NULL);
305 add_stat(BYTES, &disk[i].size,
306 "fs", name, "size", NULL);
307 add_stat(BYTES, &disk[i].used,
308 "fs", name, "used", NULL);
309 add_stat(BYTES, &disk[i].avail,
310 "fs", name, "avail", NULL);
311 add_stat(LONG_LONG, &disk[i].total_inodes,
312 "fs", name, "total_inodes", NULL);
313 add_stat(LONG_LONG, &disk[i].used_inodes,
314 "fs", name, "used_inodes", NULL);
315 add_stat(LONG_LONG, &disk[i].free_inodes,
316 "fs", name, "free_inodes", NULL);
317 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
332 free(buf);
333 }
334 }
335 }
336
337 void populate_disk() {
338 int n, i;
339 sg_disk_io_stats *diskio;
340
341 diskio = use_diffs ? sg_get_disk_io_stats_diff(&n)
342 : sg_get_disk_io_stats(&n);
343 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 "disk", name, "disk_name", NULL);
349 add_stat(BYTES, &diskio[i].read_bytes,
350 "disk", name, "read_bytes", NULL);
351 add_stat(BYTES, &diskio[i].write_bytes,
352 "disk", name, "write_bytes", NULL);
353 add_stat(TIME_T, &diskio[i].systime,
354 "disk", name, "systime", NULL);
355 }
356 }
357 }
358
359 void populate_proc() {
360 /* FIXME expose individual process info too */
361 sg_process_count *proc = sg_get_process_count();
362
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 }
371
372 void populate_net() {
373 int num_io, num_iface, i;
374 sg_network_io_stats *io;
375 sg_network_iface_stats *iface;
376
377 io = use_diffs ? sg_get_network_io_stats_diff(&num_io)
378 : sg_get_network_io_stats(&num_io);
379 if (io != NULL) {
380 for (i = 0; i < num_io; i++) {
381 const char *name = io[i].interface_name;
382
383 add_stat(STRING, &io[i].interface_name,
384 "net", name, "interface_name", NULL);
385 add_stat(BYTES, &io[i].tx,
386 "net", name, "tx", NULL);
387 add_stat(BYTES, &io[i].rx,
388 "net", name, "rx", NULL);
389 add_stat(LONG_LONG, &io[i].ipackets,
390 "net", name, "ipackets", NULL);
391 add_stat(LONG_LONG, &io[i].opackets,
392 "net", name, "opackets", NULL);
393 add_stat(LONG_LONG, &io[i].ierrors,
394 "net", name, "ierrors", NULL);
395 add_stat(LONG_LONG, &io[i].oerrors,
396 "net", name, "oerrors", NULL);
397 add_stat(LONG_LONG, &io[i].collisions,
398 "net", name, "collisions", NULL);
399 add_stat(TIME_T, &io[i].systime,
400 "net", name, "systime", NULL);
401 }
402 }
403
404 iface = sg_get_network_iface_stats(&num_iface);
405 if (iface != NULL) {
406 for (i = 0; i < num_iface; i++) {
407 const char *name = iface[i].interface_name;
408 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
426 add_stat(INT, &iface[i].speed,
427 "net", name, "speed", NULL);
428 add_stat(BOOL, &iface[i].up,
429 "net", name, "up", NULL);
430 add_stat(DUPLEX, &iface[i].duplex,
431 "net", name, "duplex", NULL);
432 }
433 }
434 }
435
436 void populate_page() {
437 sg_page_stats *page;
438
439 page = use_diffs ? sg_get_page_stats_diff() : sg_get_page_stats();
440 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 add_stat(TIME_T, &page->systime, "page", "systime", NULL);
444 }
445 }
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 strlen(t->name)) == 0) {
483 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
502 if (stats != NULL)
503 qsort(stats, num_stats, sizeof *stats, stats_compare);
504 }
505
506 /* Print the value of a stat. */
507 void print_stat_value(const stat *s) {
508 void *v = s->stat;
509 double fv;
510 long lv;
511 long long llv;
512
513 switch (s->type) {
514 case LONG_LONG:
515 #ifdef WIN32 /* Windows printf does not understand %lld, so use %I64d instead */
516 printf("%I64d", *(long long *)v);
517 #else
518 printf("%lld", *(long long *)v);
519 #endif
520 break;
521 case BYTES:
522 llv = *(long long *)v;
523 if (bytes_scale_factor != 0) {
524 llv /= bytes_scale_factor;
525 }
526 #ifdef WIN32
527 printf("%I64d", llv);
528 #else
529 printf("%lld", llv);
530 #endif
531 break;
532 case TIME_T:
533 /* FIXME option for formatted time? */
534 lv = *(time_t *)v;
535 printf("%ld", lv);
536 break;
537 case FLOAT:
538 case DOUBLE:
539 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 break;
550 case STRING:
551 /* FIXME escaping? */
552 printf("%s", *(char **)v);
553 break;
554 case INT:
555 printf("%d", *(int *)v);
556 break;
557 case BOOL:
558 printf("%s", *(int *)v ? "true" : "false");
559 break;
560 case DUPLEX:
561 switch (*(sg_iface_duplex *) v) {
562 case SG_IFACE_DUPLEX_FULL:
563 printf("full");
564 break;
565 case SG_IFACE_DUPLEX_HALF:
566 printf("half");
567 break;
568 default:
569 printf("unknown");
570 break;
571 }
572 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 char *name = argv[i];
605 stat key;
606 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
615 if (stats == NULL) {
616 s = NULL;
617 } else {
618 s = (const stat *)bsearch(&key, stats,
619 num_stats,
620 sizeof *stats,
621 compare);
622 }
623
624 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 print_stat(s);
643 }
644 }
645 }
646 }
647
648 void usage() {
649 printf("Usage: statgrab [OPTION]... [STAT]...\n"
650 "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 "\n");
655 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 " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
663 " -p Display CPU usage differences as percentages rather than\n"
664 " absolute values\n"
665 " -f FACTOR Display floating-point values as integers scaled by FACTOR\n"
666 " -K Display byte counts in kibibytes\n"
667 " -M Display byte counts in mebibytes\n"
668 " -G Display byte counts in gibibytes\n"
669 "\n");
670 printf("Version %s - report bugs to <%s>.\n",
671 PACKAGE_VERSION, PACKAGE_BUGREPORT);
672 exit(1);
673 }
674
675 int main(int argc, char **argv) {
676 opterr = 0;
677 while (1) {
678 int c = getopt(argc, argv, "lbmunsot:pf:KMG");
679 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 break;
709 case 'f':
710 float_scale_factor = atol(optarg);
711 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 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 if (repeat_mode == REPEAT_FOREVER)
730 die("mrtg mode: cannot repeat display");
731 }
732
733 if (use_cpu_percent && repeat_mode == REPEAT_NONE)
734 die("CPU percentage usage display requires stat differences");
735
736 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 /* We don't care if sg_init fails, because we can just display
744 the statistics that can be read as non-root. */
745 sg_init();
746 sg_snapshot();
747 if (sg_drop_privileges() != 0)
748 die("Failed to drop setuid/setgid privileges");
749
750 switch (repeat_mode) {
751 case REPEAT_NONE:
752 get_stats();
753 print_stats(argc, argv);
754 break;
755 case REPEAT_ONCE:
756 get_stats();
757 sleep(repeat_time);
758 sg_snapshot();
759 get_stats();
760 print_stats(argc, argv);
761 break;
762 case REPEAT_FOREVER:
763 while (1) {
764 get_stats();
765 print_stats(argc, argv);
766 printf("\n");
767 sleep(repeat_time);
768 sg_snapshot();
769 }
770 }
771
772 if (display_mode == DISPLAY_MRTG) {
773 printf("\n");
774 printf("statgrab\n");
775 }
776
777 sg_shutdown();
778
779 return 0;
780 }
781