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, 1 month ago) by tdb
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.37: +30 -26 lines
Error occurred while calculating annotation data.
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

# 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.37 2006/03/17 13:23:05 ats 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_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
69 stat_item *stat_items = NULL;
70 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 int use_diffs = 0;
79 long float_scale_factor = 0;
80 long long bytes_scale_factor = 0;
81
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 free(stat_items[i].name);
94 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
129 /* Replace spaces and dots with underscores. */
130 while (partlen-- > 0) {
131 if (*p == ' ' || *p == '.')
132 *p = '_';
133 p++;
134 }
135
136 *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 stat_items = realloc(stat_items, alloc_stats * sizeof *stat_items);
145 if (stat_items == NULL)
146 die("out of memory");
147 }
148
149 stat_items[num_stats].name = name;
150 stat_items[num_stats].type = type;
151 stat_items[num_stats].stat = stat;
152 ++num_stats;
153 }
154
155 /* Compare two stats by name for qsort and bsearch. */
156 int stats_compare(const void *a, const void *b) {
157 return strcmp(((stat_item *)a)->name, ((stat_item *)b)->name);
158 }
159
160 /* Compare up to the length of the key for bsearch. */
161 int stats_compare_prefix(const void *key, const void *item) {
162 const char *kn = ((stat_item *)key)->name;
163 const char *in = ((stat_item *)item)->name;
164
165 return strncmp(kn, in, strlen(kn));
166 }
167
168 void populate_const() {
169 static int zero = 0;
170
171 /* Constants, for use with MRTG mode. */
172 add_stat(INT, &zero, "const", "0", NULL);
173 }
174
175 void populate_cpu() {
176 if (use_cpu_percent) {
177 sg_cpu_percents *cpu_p = sg_get_cpu_percents();
178
179 if (cpu_p != NULL) {
180 add_stat(FLOAT, &cpu_p->user,
181 "cpu", "user", NULL);
182 add_stat(FLOAT, &cpu_p->kernel,
183 "cpu", "kernel", NULL);
184 add_stat(FLOAT, &cpu_p->idle,
185 "cpu", "idle", NULL);
186 add_stat(FLOAT, &cpu_p->iowait,
187 "cpu", "iowait", NULL);
188 add_stat(FLOAT, &cpu_p->swap,
189 "cpu", "swap", NULL);
190 add_stat(FLOAT, &cpu_p->nice,
191 "cpu", "nice", NULL);
192 add_stat(TIME_T, &cpu_p->time_taken,
193 "cpu", "time_taken", NULL);
194 }
195 } else {
196 sg_cpu_stats *cpu_s;
197
198 cpu_s = use_diffs ? sg_get_cpu_stats_diff()
199 : sg_get_cpu_stats();
200 if (cpu_s != NULL) {
201 add_stat(LONG_LONG, &cpu_s->user,
202 "cpu", "user", NULL);
203 add_stat(LONG_LONG, &cpu_s->kernel,
204 "cpu", "kernel", NULL);
205 add_stat(LONG_LONG, &cpu_s->idle,
206 "cpu", "idle", NULL);
207 add_stat(LONG_LONG, &cpu_s->iowait,
208 "cpu", "iowait", NULL);
209 add_stat(LONG_LONG, &cpu_s->swap,
210 "cpu", "swap", NULL);
211 add_stat(LONG_LONG, &cpu_s->nice,
212 "cpu", "nice", NULL);
213 add_stat(LONG_LONG, &cpu_s->total,
214 "cpu", "total", NULL);
215 add_stat(TIME_T, &cpu_s->systime,
216 "cpu", "systime", NULL);
217 }
218 }
219 }
220
221 void populate_mem() {
222 sg_mem_stats *mem = sg_get_mem_stats();
223
224 if (mem != NULL) {
225 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 }
230 }
231
232 void populate_load() {
233 sg_load_stats *load = sg_get_load_stats();
234
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 }
241
242 void populate_user() {
243 sg_user_stats *user = sg_get_user_stats();
244
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 }
250
251 void populate_swap() {
252 sg_swap_stats *swap = sg_get_swap_stats();
253
254 if (swap != NULL) {
255 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 }
259 }
260
261 void populate_general() {
262 /* FIXME this should be renamed to host. */
263 sg_host_info *host = sg_get_host_info();
264
265 if (host != NULL) {
266 add_stat(STRING, &host->os_name,
267 "general", "os_name", NULL);
268 add_stat(STRING, &host->os_release,
269 "general", "os_release", NULL);
270 add_stat(STRING, &host->os_version,
271 "general", "os_version", NULL);
272 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 }
276 }
277
278 void populate_fs() {
279 int n, i;
280 sg_fs_stats *disk = sg_get_fs_stats(&n);
281
282 if (disk != NULL) {
283 for (i = 0; i < n; i++) {
284 /* FIXME it'd be nicer if libstatgrab did this */
285 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 if (strlen(name) == 2 && name[1] == ':')
297 name[1] = '\0';
298 if (strncmp(name, "/dev/", 5) == 0)
299 name += 5;
300 while ((p = strchr(name, '/')) != NULL)
301 *p = '_';
302
303 add_stat(STRING, &disk[i].device_name,
304 "fs", name, "device_name", NULL);
305 add_stat(STRING, &disk[i].fs_type,
306 "fs", name, "fs_type", NULL);
307 add_stat(STRING, &disk[i].mnt_point,
308 "fs", name, "mnt_point", NULL);
309 add_stat(BYTES, &disk[i].size,
310 "fs", name, "size", NULL);
311 add_stat(BYTES, &disk[i].used,
312 "fs", name, "used", NULL);
313 add_stat(BYTES, &disk[i].avail,
314 "fs", name, "avail", NULL);
315 add_stat(LONG_LONG, &disk[i].total_inodes,
316 "fs", name, "total_inodes", NULL);
317 add_stat(LONG_LONG, &disk[i].used_inodes,
318 "fs", name, "used_inodes", NULL);
319 add_stat(LONG_LONG, &disk[i].free_inodes,
320 "fs", name, "free_inodes", NULL);
321 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
336 free(buf);
337 }
338 }
339 }
340
341 void populate_disk() {
342 int n, i;
343 sg_disk_io_stats *diskio;
344
345 diskio = use_diffs ? sg_get_disk_io_stats_diff(&n)
346 : sg_get_disk_io_stats(&n);
347 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 "disk", name, "disk_name", NULL);
353 add_stat(BYTES, &diskio[i].read_bytes,
354 "disk", name, "read_bytes", NULL);
355 add_stat(BYTES, &diskio[i].write_bytes,
356 "disk", name, "write_bytes", NULL);
357 add_stat(TIME_T, &diskio[i].systime,
358 "disk", name, "systime", NULL);
359 }
360 }
361 }
362
363 void populate_proc() {
364 /* FIXME expose individual process info too */
365 sg_process_count *proc = sg_get_process_count();
366
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 }
375
376 void populate_net() {
377 int num_io, num_iface, i;
378 sg_network_io_stats *io;
379 sg_network_iface_stats *iface;
380
381 io = use_diffs ? sg_get_network_io_stats_diff(&num_io)
382 : sg_get_network_io_stats(&num_io);
383 if (io != NULL) {
384 for (i = 0; i < num_io; i++) {
385 const char *name = io[i].interface_name;
386
387 add_stat(STRING, &io[i].interface_name,
388 "net", name, "interface_name", NULL);
389 add_stat(BYTES, &io[i].tx,
390 "net", name, "tx", NULL);
391 add_stat(BYTES, &io[i].rx,
392 "net", name, "rx", NULL);
393 add_stat(LONG_LONG, &io[i].ipackets,
394 "net", name, "ipackets", NULL);
395 add_stat(LONG_LONG, &io[i].opackets,
396 "net", name, "opackets", NULL);
397 add_stat(LONG_LONG, &io[i].ierrors,
398 "net", name, "ierrors", NULL);
399 add_stat(LONG_LONG, &io[i].oerrors,
400 "net", name, "oerrors", NULL);
401 add_stat(LONG_LONG, &io[i].collisions,
402 "net", name, "collisions", NULL);
403 add_stat(TIME_T, &io[i].systime,
404 "net", name, "systime", NULL);
405 }
406 }
407
408 iface = sg_get_network_iface_stats(&num_iface);
409 if (iface != NULL) {
410 for (i = 0; i < num_iface; i++) {
411 const char *name = iface[i].interface_name;
412 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
430 add_stat(INT, &iface[i].speed,
431 "net", name, "speed", NULL);
432 add_stat(BOOL, &iface[i].up,
433 "net", name, "up", NULL);
434 add_stat(DUPLEX, &iface[i].duplex,
435 "net", name, "duplex", NULL);
436 }
437 }
438 }
439
440 void populate_page() {
441 sg_page_stats *page;
442
443 page = use_diffs ? sg_get_page_stats_diff() : sg_get_page_stats();
444 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 add_stat(TIME_T, &page->systime, "page", "systime", NULL);
448 }
449 }
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 strlen(t->name)) == 0) {
487 t->interesting = 1;
488 break;
489 }
490 }
491 }
492 }
493 }
494
495 /* Clear and rebuild the stat_items array. */
496 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
506 if (stat_items != NULL)
507 qsort(stat_items, num_stats, sizeof *stat_items, stats_compare);
508 }
509
510 /* Print the value of a stat_item. */
511 void print_stat_value(const stat_item *s) {
512 void *v = s->stat;
513 double fv;
514 long lv;
515 long long llv;
516
517 switch (s->type) {
518 case LONG_LONG:
519 #ifdef WIN32 /* Windows printf does not understand %lld, so use %I64d instead */
520 printf("%I64d", *(long long *)v);
521 #else
522 printf("%lld", *(long long *)v);
523 #endif
524 break;
525 case BYTES:
526 llv = *(long long *)v;
527 if (bytes_scale_factor != 0) {
528 llv /= bytes_scale_factor;
529 }
530 #ifdef WIN32
531 printf("%I64d", llv);
532 #else
533 printf("%lld", llv);
534 #endif
535 break;
536 case TIME_T:
537 /* FIXME option for formatted time? */
538 lv = *(time_t *)v;
539 printf("%ld", lv);
540 break;
541 case FLOAT:
542 case DOUBLE:
543 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 break;
554 case STRING:
555 /* FIXME escaping? */
556 printf("%s", *(char **)v);
557 break;
558 case INT:
559 printf("%d", *(int *)v);
560 break;
561 case BOOL:
562 printf("%s", *(int *)v ? "true" : "false");
563 break;
564 case DUPLEX:
565 switch (*(sg_iface_duplex *) v) {
566 case SG_IFACE_DUPLEX_FULL:
567 printf("full");
568 break;
569 case SG_IFACE_DUPLEX_HALF:
570 printf("half");
571 break;
572 default:
573 printf("unknown");
574 break;
575 }
576 break;
577 }
578 }
579
580 /* Print the name and value of a stat. */
581 void print_stat(const stat_item *s) {
582 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 print_stat(&stat_items[i]);
605 } else {
606 /* Print selected stats. */
607 for (i = optind; i < argc; i++) {
608 char *name = argv[i];
609 stat_item key;
610 const stat_item *s, *end;
611 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
619 if (stat_items == NULL) {
620 s = NULL;
621 } else {
622 s = (const stat_item *)bsearch(&key, stat_items,
623 num_stats,
624 sizeof *stat_items,
625 compare);
626 }
627
628 if (s == NULL) {
629 printf("Unknown stat %s\n", name);
630 continue;
631 }
632
633 /* Find the range of stats the user wanted. */
634 for (; s >= stat_items; s--) {
635 if (compare(&key, s) != 0)
636 break;
637 }
638 s++;
639 for (end = s; end < &stat_items[num_stats]; end++) {
640 if (compare(&key, end) != 0)
641 break;
642 }
643
644 /* And print them. */
645 for (; s < end; s++) {
646 print_stat(s);
647 }
648 }
649 }
650 }
651
652 void usage() {
653 printf("Usage: statgrab [OPTION]... [STAT]...\n"
654 "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 "\n");
659 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 " -t DELAY When repeating, wait DELAY seconds between updates (default 1)\n"
667 " -p Display CPU usage differences as percentages rather than\n"
668 " absolute values\n"
669 " -f FACTOR Display floating-point values as integers scaled by FACTOR\n"
670 " -K Display byte counts in kibibytes\n"
671 " -M Display byte counts in mebibytes\n"
672 " -G Display byte counts in gibibytes\n"
673 "\n");
674 printf("Version %s - report bugs to <%s>.\n",
675 PACKAGE_VERSION, PACKAGE_BUGREPORT);
676 exit(1);
677 }
678
679 int main(int argc, char **argv) {
680 opterr = 0;
681 while (1) {
682 int c = getopt(argc, argv, "lbmunsot:pf:KMG");
683 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 break;
713 case 'f':
714 float_scale_factor = atol(optarg);
715 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 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 if (repeat_mode == REPEAT_FOREVER)
734 die("mrtg mode: cannot repeat display");
735 }
736
737 if (use_cpu_percent && repeat_mode == REPEAT_NONE)
738 die("CPU percentage usage display requires stat differences");
739
740 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 /* We don't care if sg_init fails, because we can just display
748 the statistics that can be read as non-root. */
749 sg_init();
750 sg_snapshot();
751 if (sg_drop_privileges() != 0)
752 die("Failed to drop setuid/setgid privileges");
753
754 switch (repeat_mode) {
755 case REPEAT_NONE:
756 get_stats();
757 print_stats(argc, argv);
758 break;
759 case REPEAT_ONCE:
760 get_stats();
761 sleep(repeat_time);
762 sg_snapshot();
763 get_stats();
764 print_stats(argc, argv);
765 break;
766 case REPEAT_FOREVER:
767 while (1) {
768 get_stats();
769 print_stats(argc, argv);
770 printf("\n");
771 sleep(repeat_time);
772 sg_snapshot();
773 }
774 }
775
776 if (display_mode == DISPLAY_MRTG) {
777 printf("\n");
778 printf("statgrab\n");
779 }
780
781 sg_shutdown();
782
783 return 0;
784 }
785