ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/libstatgrab/src/statgrab/statgrab.c
(Generate patch)

Comparing projects/libstatgrab/src/statgrab/statgrab.c (file contents):
Revision 1.2 by tdb, Wed Aug 27 14:00:12 2003 UTC vs.
Revision 1.35 by tdb, Wed Jul 13 13:01:24 2005 UTC

# Line 1 | Line 1
1   /*
2 < * i-scream central monitoring system
2 > * i-scream libstatgrab
3   * http://www.i-scream.org
4 < * Copyright (C) 2000-2003 i-scream
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
# Line 16 | Line 16
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$
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>
# Line 27 | Line 33
33  
34   typedef enum {
35          LONG_LONG = 0,
36 +        BYTES,
37          TIME_T,
38          FLOAT,
39          DOUBLE,
40          STRING,
41 <        INT
41 >        INT,
42 >        BOOL,
43 >        DUPLEX
44   } stat_type;
45  
46   typedef enum {
# Line 62 | Line 71 | 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) {
# Line 75 | Line 87 | void clear_stats() {
87  
88          for (i = 0; i < num_stats; i++)
89                  free(stats[i].name);
78        free(stats);
79        stats = NULL;
90          num_stats = 0;
81        alloc_stats = 0;
91   }
92  
93   /* Add a stat. The varargs make up the name, joined with dots; the name is
# Line 138 | Line 147 | void add_stat(stat_type type, void *stat, ...) {
147          ++num_stats;
148   }
149  
150 < /* Compare two stats by name, for sorting purposes. */
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 < /* Clear and rebuild the stats array. */
156 < void get_stats(int use_diffs) {
157 <        cpu_states_t *cpu_s;
158 <        cpu_percent_t *cpu_p;
150 <        mem_stat_t *mem;
151 <        load_stat_t *load;
152 <        user_stat_t *user;
153 <        swap_stat_t *swap;
154 <        general_stat_t *gen;
155 <        disk_stat_t *disk;
156 <        diskio_stat_t *diskio;
157 <        process_stat_t *proc;
158 <        network_stat_t *net;
159 <        page_stat_t *page;
160 <        static int zero = 0;
161 <        int n, i;
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 <        clear_stats();
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 <        /* FIXME when only fetching some stats, it'd be more efficient to only
169 <           do the libstatgrab calls needed, rather than fetching everything. */
170 <
170 > void populate_cpu() {
171          if (use_cpu_percent) {
172 <                cpu_p = cpu_percent_usage();
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);
176 >                                 "cpu", "user", NULL);
177                          add_stat(FLOAT, &cpu_p->kernel,
178 <                                 "cpu", "kernel", NULL);
178 >                                 "cpu", "kernel", NULL);
179                          add_stat(FLOAT, &cpu_p->idle,
180 <                                 "cpu", "idle", NULL);
180 >                                 "cpu", "idle", NULL);
181                          add_stat(FLOAT, &cpu_p->iowait,
182 <                                 "cpu", "iowait", NULL);
182 >                                 "cpu", "iowait", NULL);
183                          add_stat(FLOAT, &cpu_p->swap,
184 <                                 "cpu", "swap", NULL);
184 >                                 "cpu", "swap", NULL);
185                          add_stat(FLOAT, &cpu_p->nice,
186 <                                 "cpu", "nice", NULL);
187 <                        add_stat(TIME_T, &cpu_s->systime,
188 <                                 "cpu", "time_taken", NULL);
186 >                                 "cpu", "nice", NULL);
187 >                        add_stat(TIME_T, &cpu_p->time_taken,
188 >                                 "cpu", "time_taken", NULL);
189                  }
190          } else {
191 <                cpu_s = use_diffs ? get_cpu_diff() : get_cpu_totals();
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);
197 >                                 "cpu", "user", NULL);
198                          add_stat(LONG_LONG, &cpu_s->kernel,
199 <                                 "cpu", "kernel", NULL);
199 >                                 "cpu", "kernel", NULL);
200                          add_stat(LONG_LONG, &cpu_s->idle,
201 <                                 "cpu", "idle", NULL);
201 >                                 "cpu", "idle", NULL);
202                          add_stat(LONG_LONG, &cpu_s->iowait,
203 <                                 "cpu", "iowait", NULL);
203 >                                 "cpu", "iowait", NULL);
204                          add_stat(LONG_LONG, &cpu_s->swap,
205 <                                 "cpu", "swap", NULL);
205 >                                 "cpu", "swap", NULL);
206                          add_stat(LONG_LONG, &cpu_s->nice,
207 <                                 "cpu", "nice", NULL);
207 >                                 "cpu", "nice", NULL);
208                          add_stat(LONG_LONG, &cpu_s->total,
209 <                                 "cpu", "total", NULL);
209 >                                 "cpu", "total", NULL);
210                          add_stat(TIME_T, &cpu_s->systime,
211 <                                 "cpu", "systime", NULL);
211 >                                 "cpu", "systime", NULL);
212                  }
213          }
214 + }
215  
216 <        mem = get_memory_stats();
216 > void populate_mem() {
217 >        sg_mem_stats *mem = sg_get_mem_stats();
218 >
219          if (mem != NULL) {
220 <                add_stat(LONG_LONG, &mem->total, "mem", "total", NULL);
221 <                add_stat(LONG_LONG, &mem->free, "mem", "free", NULL);
222 <                add_stat(LONG_LONG, &mem->used, "mem", "used", NULL);
223 <                add_stat(LONG_LONG, &mem->cache, "mem", "cache", 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 <        load = get_load_stats();
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 <        user = get_user_stats();
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 <        swap = get_swap_stats();
246 > void populate_swap() {
247 >        sg_swap_stats *swap = sg_get_swap_stats();
248 >
249          if (swap != NULL) {
250 <                add_stat(LONG_LONG, &swap->total, "swap", "total", NULL);
251 <                add_stat(LONG_LONG, &swap->used, "swap", "used", NULL);
252 <                add_stat(LONG_LONG, &swap->free, "swap", "free", 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 <        gen = get_general_stats();
257 <        if (gen != NULL) {
258 <                add_stat(STRING, &gen->os_name,
259 <                         "general", "os_name", NULL);
260 <                add_stat(STRING, &gen->os_release,
261 <                         "general", "os_release", NULL);
262 <                add_stat(STRING, &gen->os_version,
263 <                         "general", "os_version", NULL);
264 <                add_stat(STRING, &gen->platform, "general", "platform", NULL);
265 <                add_stat(STRING, &gen->hostname, "general", "hostname", NULL);
266 <                add_stat(TIME_T, &gen->uptime, "general", "uptime", NULL);
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 <        disk = get_disk_stats(&n);
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 <                        const char *name = disk[i].device_name,
281 <                                   *p = strrchr(name, '/');
282 <                        if (p != NULL)
283 <                                name = p + 1;
284 <                        if (*name == '\0')
285 <                                name = "root";
286 <        
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);
299 >                                 "fs", name, "device_name", NULL);
300                          add_stat(STRING, &disk[i].fs_type,
301 <                                 "fs", name, "fs_type", NULL);
301 >                                 "fs", name, "fs_type", NULL);
302                          add_stat(STRING, &disk[i].mnt_point,
303 <                                 "fs", name, "mnt_point", NULL);
304 <                        add_stat(LONG_LONG, &disk[i].size,
305 <                                 "fs", name, "size", NULL);
306 <                        add_stat(LONG_LONG, &disk[i].used,
307 <                                 "fs", name, "used", NULL);
308 <                        add_stat(LONG_LONG, &disk[i].avail,
309 <                                 "fs", name, "avail", NULL);
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);
311 >                                 "fs", name, "total_inodes", NULL);
312                          add_stat(LONG_LONG, &disk[i].used_inodes,
313 <                                 "fs", name, "used_inodes", NULL);
313 >                                 "fs", name, "used_inodes", NULL);
314                          add_stat(LONG_LONG, &disk[i].free_inodes,
315 <                                 "fs", name, "free_inodes", NULL);
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 <        diskio = use_diffs ? get_diskio_stats_diff(&n) : get_diskio_stats(&n);
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(LONG_LONG, &diskio[i].read_bytes,
349 <                                 "disk", name, "read_bytes", NULL);
350 <                        add_stat(LONG_LONG, &diskio[i].write_bytes,
351 <                                 "disk", name, "write_bytes", NULL);
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);
353 >                                 "disk", name, "systime", NULL);
354                  }
355          }
356 + }
357  
358 <        proc = get_process_stats();
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);
# Line 305 | Line 366 | void get_stats(int use_diffs) {
366                  add_stat(INT, &proc->stopped, "proc", "stopped", NULL);
367                  add_stat(INT, &proc->zombie, "proc", "zombie", NULL);
368          }
369 + }
370  
371 <        net = use_diffs ? get_network_stats_diff(&n) : get_network_stats(&n);
372 <        if (net != NULL) {
373 <                for (i = 0; i < n; i++) {
374 <                        const char *name = net[i].interface_name;
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, &net[i].interface_name,
383 <                                 "net", name, "interface_name", NULL);
384 <                        add_stat(LONG_LONG, &net[i].tx,
385 <                                 "net", name, "tx", NULL);
386 <                        add_stat(LONG_LONG, &net[i].rx,
387 <                                 "net", name, "rx", NULL);
388 <                        add_stat(TIME_T, &net[i].systime,
389 <                                 "net", name, "systime", NULL);
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 <        page = use_diffs ? get_page_stats_diff() : get_page_stats();
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(LONG_LONG, &page->systime, "page", "systime", NULL);
442 >                add_stat(TIME_T, &page->systime, "page", "systime", NULL);
443          }
444 + }
445  
446 <        qsort(stats, num_stats, sizeof *stats, stats_compare);
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                  printf("%lld", *(long long *)v);
515                  break;
516 +        case BYTES:
517 +                llv = *(long long *)v;
518 +                if (bytes_scale_factor != 0) {
519 +                        llv /= bytes_scale_factor;
520 +                }
521 +                printf("%lld", llv);
522 +                break;
523          case TIME_T:
524                  /* FIXME option for formatted time? */
525 <                printf("%ld", *(time_t *)v);
525 >                lv = *(time_t *)v;
526 >                printf("%ld", lv);
527                  break;
528          case FLOAT:
348                printf("%f", *(float *)v);
349                break;
529          case DOUBLE:
530 <                printf("%f", *(double *)v);
530 >                if (s->type == FLOAT) {
531 >                        fv = *(float *)v;
532 >                } else {
533 >                        fv = *(double *)v;
534 >                }
535 >                if (float_scale_factor != 0) {
536 >                        printf("%ld", (long)(float_scale_factor * fv));
537 >                } else {
538 >                        printf("%f", fv);
539 >                }
540                  break;
541          case STRING:
542                  /* FIXME escaping? */
# Line 357 | Line 545 | void print_stat_value(const stat *s) {
545          case INT:
546                  printf("%d", *(int *)v);
547                  break;
548 +        case BOOL:
549 +                printf("%s", *(int *)v ? "true" : "false");
550 +                break;
551 +        case DUPLEX:
552 +                switch (*(sg_iface_duplex *) v) {
553 +                case SG_IFACE_DUPLEX_FULL:
554 +                        printf("full");
555 +                        break;
556 +                case SG_IFACE_DUPLEX_HALF:
557 +                        printf("half");
558 +                        break;
559 +                default:
560 +                        printf("unknown");
561 +                        break;
562 +                }
563 +                break;
564          }
565   }
566  
# Line 388 | Line 592 | void print_stats(int argc, char **argv) {
592          } else {
593                  /* Print selected stats. */
594                  for (i = optind; i < argc; i++) {
595 +                        char *name = argv[i];
596                          stat key;
597 <                        const stat *s;
597 >                        const stat *s, *end;
598 >                        int (*compare)(const void *, const void *);
599  
600 <                        key.name = argv[i];
601 <                        s = (const stat *)bsearch(&key, stats, num_stats,
602 <                                                  sizeof *stats,
603 <                                                  stats_compare);
604 <                        if (s != NULL) {
600 >                        key.name = name;
601 >                        if (name[strlen(name) - 1] == '.')
602 >                                compare = stats_compare_prefix;
603 >                        else
604 >                                compare = stats_compare;
605 >
606 >                        if (stats == NULL) {
607 >                                s = NULL;
608 >                        } else {
609 >                                s = (const stat *)bsearch(&key, stats,
610 >                                                          num_stats,
611 >                                                          sizeof *stats,
612 >                                                          compare);
613 >                        }
614 >
615 >                        if (s == NULL) {
616 >                                printf("Unknown stat %s\n", name);
617 >                                continue;
618 >                        }
619 >
620 >                        /* Find the range of stats the user wanted. */
621 >                        for (; s >= stats; s--) {
622 >                                if (compare(&key, s) != 0)
623 >                                        break;
624 >                        }
625 >                        s++;
626 >                        for (end = s; end < &stats[num_stats]; end++) {
627 >                                if (compare(&key, end) != 0)
628 >                                        break;
629 >                        }
630 >
631 >                        /* And print them. */
632 >                        for (; s < end; s++) {
633                                  print_stat(s);
634                          }
635                  }
# Line 404 | Line 638 | void print_stats(int argc, char **argv) {
638  
639   void usage() {
640          printf("Usage: statgrab [OPTION]... [STAT]...\n"
641 <               "Display system statistics (all statistics by default).\n"
641 >               "Display system statistics.\n"
642 >               "\n"
643 >               "If no STATs are given, all will be displayed. Specify 'STAT.' to display all\n"
644 >               "statistics starting with that prefix.\n"
645                 "\n");
646          printf("  -l         Linux sysctl-style output (default)\n"
647                 "  -b         BSD sysctl-style output\n"
# Line 414 | Line 651 | void usage() {
651                 "  -s         Display stat differences repeatedly\n"
652                 "  -o         Display stat differences once\n"
653                 "  -t DELAY   When repeating, wait DELAY seconds between updates (default 1)\n"
654 <               "  -p         Display CPU usage as percentages rather than absolute values\n"
654 >               "  -p         Display CPU usage differences as percentages rather than\n"
655 >               "             absolute values\n"
656 >               "  -f FACTOR  Display floating-point values as integers scaled by FACTOR\n"
657 >               "  -K         Display byte counts in kibibytes\n"
658 >               "  -M         Display byte counts in mebibytes\n"
659 >               "  -G         Display byte counts in gibibytes\n"
660                 "\n");
661          printf("Version %s - report bugs to <%s>.\n",
662 <                PACKAGE_VERSION, PACKAGE_BUGREPORT);
662 >               PACKAGE_VERSION, PACKAGE_BUGREPORT);
663          exit(1);
664   }
665  
666   int main(int argc, char **argv) {
667          opterr = 0;
668          while (1) {
669 <                int c = getopt(argc, argv, "lbmunsot:p");
669 >                int c = getopt(argc, argv, "lbmunsot:pf:KMG");
670                  if (c == -1)
671                          break;
672                  switch (c) {
# Line 455 | Line 697 | int main(int argc, char **argv) {
697                  case 'p':
698                          use_cpu_percent = 1;
699                          break;
700 +                case 'f':
701 +                        float_scale_factor = atol(optarg);
702 +                        break;
703 +                case 'K':
704 +                        bytes_scale_factor = 1024;
705 +                        break;
706 +                case 'M':
707 +                        bytes_scale_factor = 1024 * 1024;
708 +                        break;
709 +                case 'G':
710 +                        bytes_scale_factor = 1024 * 1024 * 1024;
711 +                        break;
712                  default:
713                          usage();
714                  }
# Line 463 | Line 717 | int main(int argc, char **argv) {
717          if (display_mode == DISPLAY_MRTG) {
718                  if ((argc - optind) != 2)
719                          die("mrtg mode: must specify exactly two stats");
720 <                if (repeat_mode != REPEAT_NONE)
720 >                if (repeat_mode == REPEAT_FOREVER)
721                          die("mrtg mode: cannot repeat display");
722          }
723  
724 +        if (use_cpu_percent && repeat_mode == REPEAT_NONE)
725 +                die("CPU percentage usage display requires stat differences");
726 +
727 +        if (repeat_mode == REPEAT_NONE)
728 +                use_diffs = 0;
729 +        else
730 +                use_diffs = 1;
731 +
732 +        select_interesting(argc - optind, &argv[optind]);
733 +
734 +        /* We don't care if sg_init fails, because we can just display
735 +           the statistics that can be read as non-root. */
736 +        sg_init();
737 +        if (sg_drop_privileges() != 0)
738 +                die("Failed to drop setuid/setgid privileges");
739 +
740          switch (repeat_mode) {
741          case REPEAT_NONE:
742 <                get_stats(0);
742 >                get_stats();
743                  print_stats(argc, argv);
744                  break;
745          case REPEAT_ONCE:
746 <                get_stats(1);
746 >                get_stats();
747                  sleep(repeat_time);
748 <                get_stats(1);
748 >                get_stats();
749                  print_stats(argc, argv);
750                  break;
751          case REPEAT_FOREVER:
752                  while (1) {
753 <                        get_stats(1);
753 >                        get_stats();
754                          print_stats(argc, argv);
755                          printf("\n");
756                          sleep(repeat_time);

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines