Add files from parent branch HEAD:
[pkgsrc.git] / parallel / ganglia-monitor-core / files / dfly-metrics.c
1 /*
2  *  First stab at support for metrics in FreeBSD 
3  *  by Preston Smith <psmith@physics.purdue.edu>
4  *  Wed Feb 27 14:55:33 EST 2002
5  *  Improved by Brooks Davis <brooks@one-eyed-alien.net>,
6  *  Fixed libkvm code.
7  *  Tue Jul 15 16:42:22 EST 2003
8  *
9  * $Id: dfly-metrics.c,v 1.1 2006/04/30 22:28:44 joerg Exp $
10  */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include <kvm.h>
16
17 #include <sys/param.h> 
18 #include <sys/mount.h>
19 #include <sys/sysctl.h>
20 #include <sys/time.h>
21 #include <sys/user.h>
22 #include <kinfo.h>
23 #include <sys/stat.h>
24 #include <vm/vm_param.h>
25
26 #include <sys/socket.h>
27 #include <net/if.h>
28 #include <net/if_dl.h>
29 #include <net/route.h>
30
31 #include <unistd.h>
32 #include <err.h>
33 #include <fcntl.h>
34 #include <limits.h>
35 #include <paths.h>
36
37 #include "interface.h"
38 #include "libmetrics.h"
39
40 #define MIB_SWAPINFO_SIZE 3
41
42 #ifndef MIN_NET_POLL_INTERVAL
43 #define MIN_NET_POLL_INTERVAL 0.5
44 #endif
45
46 #ifndef MIN_CPU_POLL_INTERVAL
47 #define MIN_CPU_POLL_INTERVAL 0.5
48 #endif
49
50 #ifndef UINT64_MAX
51 #define UINT64_MAX      ULLONG_MAX
52 #endif
53
54 #define VFCF_NONLOCAL   (VFCF_NETWORK|VFCF_SYNTHETIC|VFCF_LOOPBACK)
55
56 #define timertod(tvp) \
57     ((double)(tvp)->tv_sec + (double)(tvp)->tv_usec/(1000*1000))
58
59 #ifndef XSWDEV_VERSION
60 #define XSWDEV_VERSION  1
61 struct xswdev {
62         u_int   xsw_version;
63         udev_t  xsw_dev;
64         int     xsw_flags;
65         int     xsw_nblks;
66         int     xsw_used;
67 };
68 #endif
69
70 struct traffic {
71         uint64_t in_bytes;
72         uint64_t out_bytes;
73         uint64_t in_pkts;
74         uint64_t out_pkts;
75 };
76
77 static void get_netbw(double *, double *, double *, double *);
78 static uint64_t counterdiff(uint64_t, uint64_t, uint64_t, uint64_t);
79
80
81 static char      *makenetvfslist(void);
82 static size_t     regetmntinfo(struct statfs **, long, const char **);
83 static int        checkvfsname(const char *, const char **);
84 static const char **makevfslist(char *);
85 static float      find_disk_space(double *, double *);
86
87 static int use_vm_swap_info = 0;
88 static int mibswap[MIB_SWAPINFO_SIZE];
89 static size_t mibswap_size;
90 static kvm_t *kd = NULL;
91 static int pagesize;
92 static int        skipvfs;
93
94 /*
95  * This function is called only once by the gmond.  Use to 
96  * initialize data structures, etc or just return SYNAPSE_SUCCESS;
97  */
98 g_val_t
99 metric_init(void)
100 {
101    g_val_t val;
102
103    /*
104     * Try to use the vm.swap_info sysctl to gather swap data.  If it
105     * isn't implemented, fall back to trying to old kvm based interface.
106     */
107    mibswap_size = MIB_SWAPINFO_SIZE;
108    if (sysctlnametomib("vm.swap_info", mibswap, &mibswap_size) == -1) {
109       kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "metric_init()");
110    } else {
111       /*
112        * RELEASE versions of FreeBSD with the swap mib have a version
113        * of libkvm that doesn't need root for simple proc access so we
114        * just open /dev/null to give us a working handle here.
115        */
116       kd = kvm_open(_PATH_DEVNULL, NULL, NULL, O_RDONLY, "metric_init()");
117       use_vm_swap_info = 1;
118    }
119    pagesize = getpagesize();
120
121    /* Initalize some counters */
122    get_netbw(NULL, NULL, NULL, NULL);
123    cpu_state(-1);
124
125    val.int32 = SYNAPSE_SUCCESS;
126    return val;
127 }
128
129 g_val_t
130 cpu_num_func ( void )
131 {
132    g_val_t val;
133    int ncpu;
134
135    if (kinfo_get_cpus(&ncpu))
136         ncpu = 1;
137
138    val.uint16 = ncpu;
139    return val;
140 }
141
142 g_val_t
143 cpu_speed_func ( void )
144 {
145    g_val_t val;
146    int cpu_speed;
147    size_t len = sizeof(cpu_speed);
148
149    /*
150     * machdep.tsc_freq is an i386/amd64 only feature, but it's the best
151     * we've got at the moment.
152     */
153    if (sysctlbyname("machdep.tsc_freq", &cpu_speed, &len, NULL, 0) == -1)
154      cpu_speed = 0;
155    val.uint16 = cpu_speed /= 1000000;
156
157    return val;
158 }
159
160 g_val_t
161 mem_total_func ( void )
162 {
163    g_val_t val;
164    size_t len;
165    long total;
166
167    len = sizeof(total);
168
169    if (sysctlbyname("hw.physmem", &total, &len, NULL, 0) == -1)
170         total = 0;
171    val.uint32 = total / 1024;
172
173    return val;
174 }
175
176 g_val_t
177 swap_total_func ( void )
178 {
179    g_val_t val;
180    struct kvm_swap swap[1];
181    struct xswdev xsw;
182    size_t size;
183    int totswap, n;
184    val.uint32 = 0;
185    totswap = 0;
186
187    if (use_vm_swap_info) {
188       for (n = 0; ; ++n) {
189         mibswap[mibswap_size] = n;
190         size = sizeof(xsw);
191         if (sysctl(mibswap, mibswap_size + 1, &xsw, &size, NULL, 0) == -1)
192            break;
193         if (xsw.xsw_version != XSWDEV_VERSION)
194            return val;
195          totswap += xsw.xsw_nblks;
196        }
197    } else if(kd != NULL) {
198       n = kvm_getswapinfo(kd, swap, 1, 0);
199       if (n < 0 || swap[0].ksw_total == 0) {
200          val.uint32 = 0;
201       }
202       totswap = swap[0].ksw_total;
203     }
204    
205    val.uint32 = totswap * (pagesize / 1024);
206    return val;
207 }
208
209 g_val_t
210 boottime_func ( void )
211 {
212    g_val_t val;
213    struct timeval boottime;
214    size_t size;
215
216    size = sizeof(boottime);
217    if (sysctlbyname("kern.boottime", &boottime, &size, NULL, 0) == -1)
218        boottime.tv_sec = 0;
219
220    val.uint32 = (uint32_t) boottime.tv_sec;
221
222    return val;
223 }
224
225 g_val_t
226 sys_clock_func ( void )
227 {
228    g_val_t val;
229
230    val.uint32 = time(NULL);
231    return val;
232 }
233
234 g_val_t
235 machine_type_func ( void )
236 {
237         g_val_t val;
238         size_t len = sizeof(val.str);
239
240         if (sysctlbyname("hw.machine", val.str, &len, NULL, 0) == -1 ||
241             (len == 0))
242                 strlcpy(val.str, "unknown", sizeof(val.str));
243
244         return val;
245 }
246
247 g_val_t
248 os_name_func ( void )
249 {
250         g_val_t val;
251         size_t len = sizeof(val.str);
252
253         if (sysctlbyname("kern.ostype", val.str, &len, NULL, 0) == -1 ||
254             (len == 0))
255                 strlcpy(val.str, "DragonFly (unknown)", sizeof(val.str));
256
257         return val;
258 }        
259
260 g_val_t
261 os_release_func ( void )
262 {
263         g_val_t val;
264         size_t len = sizeof(val.str);
265
266         if (sysctlbyname("kern.osrelease", val.str, &len, NULL, 0) == -1 ||
267             (len == 0))
268                 strlcpy(val.str, "unknown", sizeof(val.str));
269
270         return val;
271 }        
272
273 /* Get the CPU state given by index, from kern.cp_time
274  * Use the constants in <sys/dkstat.h>
275  * CP_USER=0, CP_NICE=1, CP_SYS=2, CP_INTR=3, CP_IDLE=4
276  */
277
278 int cpu_state(int which) {
279    static struct kinfo_cputime cp_old, cp_diff;
280    static uint64_t total_change, half_change;
281    static struct timeval this_time, last_time;
282
283    struct kinfo_cputime cp_time;
284    struct timeval time_diff;
285    int i;
286
287    if (which == -1) {
288       bzero(&cp_old, sizeof(cp_old));
289       bzero(&cp_diff, sizeof(cp_diff));
290       total_change = 1;
291       half_change = 0;
292       bzero(&last_time, sizeof(last_time));
293       return 0.0;
294    }
295
296    gettimeofday(&this_time, NULL);
297    timersub(&this_time, &last_time, &time_diff);
298    if (timertod(&time_diff) < MIN_CPU_POLL_INTERVAL) {
299       goto output;
300    }
301    last_time = this_time;
302
303    if (kinfo_get_sched_cputime(&cp_time)) {
304       warn("kinfo_get_sched_cputime");
305       return 0.0;
306    }
307    cp_diff.cp_user = cp_time.cp_user - cp_old.cp_user;
308    cp_diff.cp_nice = cp_time.cp_nice - cp_old.cp_nice;
309    cp_diff.cp_sys = cp_time.cp_sys - cp_old.cp_sys;
310    cp_diff.cp_intr = cp_time.cp_intr - cp_old.cp_intr;
311    cp_diff.cp_idle = cp_time.cp_idle - cp_old.cp_idle;
312    total_change = cp_diff.cp_user + cp_diff.cp_nice + cp_diff.cp_sys
313      + cp_diff.cp_sys + cp_diff.cp_intr + cp_diff.cp_idle;
314    if (total_change == 0)
315      total_change = 1;
316    half_change = total_change >> 1;
317
318 output:
319    switch (which) {
320    case 0:
321       return (cp_diff.cp_user * 100LL + half_change) / total_change;
322    case 1:
323       return (cp_diff.cp_nice * 100LL + half_change) / total_change;
324    case 2:
325       return (cp_diff.cp_sys * 100LL + half_change) / total_change;
326    case 3:
327       return (cp_diff.cp_intr * 100LL + half_change) / total_change;
328    case 4:
329       return (cp_diff.cp_idle * 100LL + half_change) / total_change;
330    default:
331       return 0;
332    }
333 }
334
335 g_val_t
336 cpu_user_func ( void )
337 {
338    g_val_t val;
339
340    val.f = (float) cpu_state(0);
341
342    return val;
343 }
344
345 g_val_t
346 cpu_nice_func ( void )
347 {
348    g_val_t val;
349
350    val.f = (float) cpu_state(1);
351
352    return val;
353 }
354
355 g_val_t 
356 cpu_system_func ( void )
357 {
358    g_val_t val;
359
360    val.f = (float) cpu_state(2);
361
362    return val;
363 }
364
365 g_val_t 
366 cpu_idle_func ( void )
367 {
368    g_val_t val;
369
370    val.f = (float) cpu_state(4);
371
372    return val;
373 }
374
375 /*
376 ** FIXME - This metric is not valid on FreeBSD.
377 */
378 g_val_t 
379 cpu_wio_func ( void )
380 {
381    g_val_t val;
382    
383    val.f = 0.0;
384    return val;
385 }
386
387 /*
388 ** FIXME - Idle time since startup.  The scheduler apparently knows
389 ** this, but we it's fairly pointless so it's not exported.
390 */
391 g_val_t 
392 cpu_aidle_func ( void )
393 {
394    g_val_t val;
395    val.f = 0.0;
396    return val;
397 }
398
399 g_val_t 
400 cpu_intr_func ( void )
401 {
402    g_val_t val;
403
404    val.f = (float) cpu_state(3);
405
406    return val;
407 }
408
409 /*
410 ** FIXME - This metric is not valid on FreeBSD.
411 */
412 g_val_t 
413 cpu_sintr_func ( void )
414 {
415    g_val_t val;
416    val.f = 0.0;
417    return val;
418 }
419
420 g_val_t
421 load_one_func ( void )
422 {
423    g_val_t val;
424    double load[3];
425
426    getloadavg(load, 3);
427    val.f = load[0];
428
429    return val;
430 }
431
432 g_val_t
433 load_five_func ( void )
434 {
435    g_val_t val;
436    double load[3];
437
438    getloadavg(load, 3);
439    val.f = load[1];
440
441    return val;
442 }
443
444 g_val_t
445 load_fifteen_func ( void )
446 {
447    g_val_t val;
448    double load[3];
449
450    getloadavg(load, 3);
451    val.f = load[2];
452
453    return val;
454 }
455
456 g_val_t
457 proc_total_func ( void )
458 {
459    g_val_t val;
460    size_t len = 0;
461
462    sysctlbyname("kern.proc.all", NULL, &len, NULL, 0);
463
464    val.uint32 = (len / sizeof (struct kinfo_proc)); 
465
466    return val;
467 }
468
469
470 g_val_t
471 proc_run_func( void )
472 {
473    struct kinfo_proc *kp;
474    int i;
475    int state;
476    int nentries;
477    int what = KERN_PROC_ALL;
478    g_val_t val;
479
480    val.uint32 = 0;
481
482    if (kd == NULL)
483       goto output;
484 #ifdef KERN_PROC_NOTHREADS
485    what |= KERN_PROC_NOTHREADS
486 #endif
487    if ((kp = kvm_getprocs(kd, what, 0, &nentries)) == 0 || nentries < 0)
488       goto output;
489
490    for (i = 0; i < nentries; kp++, i++) {
491 #ifdef KINFO_PROC_SIZE
492       state = kp->ki_stat;
493 #else
494       state = kp->kp_proc.p_stat;
495 #endif
496       switch(state) {
497          case SRUN:
498          case SIDL:
499                 val.uint32++;
500                 break;
501       }
502    }
503
504    if (val.uint32 > 0)
505         val.uint32--;
506
507 output:
508    return val;
509 }
510
511 /*
512 ** FIXME - The whole ganglia model of memory is bogus.  Free memory is
513 ** generally a bad idea with a modern VM and so is reporting it.  There
514 ** is simply no way to report a value for "free" memory that makes any
515 ** kind of sense.  Free+inactive might be a decent value for "free".
516 */
517 g_val_t
518 mem_free_func ( void )
519 {
520    g_val_t val;
521    size_t len; 
522    int free_pages;
523
524    len = sizeof (free_pages);
525    if((sysctlbyname("vm.stats.vm.v_free_count", &free_pages, &len, NULL, 0) 
526         == -1) || !len) free_pages = 0; 
527
528    val.uint32 = free_pages * (pagesize / 1024);
529    return val;
530 }
531
532 /*
533 ** FreeBSD don't seem to report this anywhere.  It's actually quite
534 ** complicated as there is SysV shared memory, POSIX shared memory,
535 ** and mmap shared memory at a minimum.
536 */
537 g_val_t
538 mem_shared_func ( void )
539 {
540    g_val_t val;
541
542    val.uint32 = 0;
543
544    return val;
545 }
546
547 /*
548 ** FIXME - this isn't really valid.  It lists some VFS buffer space,
549 ** but the real picture is much more complex.
550 */
551 g_val_t
552 mem_buffers_func ( void )
553 {
554    g_val_t val;
555    size_t len;
556    int buffers;
557
558    len = sizeof (buffers);
559    if((sysctlbyname("vfs.bufspace", &buffers, &len, NULL, 0) == -1) || !len)
560                 buffers = 0; 
561    buffers /= 1024;
562
563    val.uint32 = buffers;
564    return val;
565 }
566
567 /*
568 ** FIXME - this isn't really valid.  It lists some VM cache space,
569 ** but the real picture is more complex.
570 */
571 g_val_t
572 mem_cached_func ( void )
573 {
574    g_val_t val;
575    size_t len;
576    int cache;
577
578    len = sizeof (cache);
579    if((sysctlbyname("vm.stats.vm.v_cache_count", &cache, &len, NULL, 0) == -1) 
580         || !len)
581       cache = 0; 
582
583    val.uint32 = cache * (pagesize / 1024);
584    return val;
585 }
586
587 g_val_t
588 swap_free_func ( void )
589 {
590    g_val_t val;
591
592    struct kvm_swap swap[1];
593    struct xswdev xsw;
594    size_t size;
595    int totswap, usedswap, freeswap, n;
596    val.uint32 = 0;
597    totswap = 0;
598    usedswap = 0;
599    if (use_vm_swap_info) {
600       for (n = 0; ; ++n) {
601         mibswap[mibswap_size] = n;
602         size = sizeof(xsw);
603         if (sysctl(mibswap, mibswap_size + 1, &xsw, &size, NULL, 0) == -1)
604            break;
605         if (xsw.xsw_version != XSWDEV_VERSION)
606            return val;
607          totswap += xsw.xsw_nblks;
608          usedswap += xsw.xsw_used;
609        }
610    } else if(kd != NULL) {
611       n = kvm_getswapinfo(kd, swap, 1, 0);
612       totswap = swap[0].ksw_total;
613       usedswap = swap[0].ksw_used;
614    }
615    freeswap = totswap - usedswap;
616    val.uint32 = freeswap * (pagesize / 1024);
617    return val;
618 }
619
620
621 g_val_t
622 mtu_func ( void )
623 {
624    /* We want to find the minimum MTU (Max packet size) over all UP interfaces.
625 */
626    g_val_t val;
627    val.uint32 = get_min_mtu();
628    /* A val of 0 means there are no UP interfaces. Shouldn't happen. */
629    return val;
630 }
631
632 g_val_t
633 pkts_in_func ( void )
634 {
635    double in_pkts;
636    g_val_t val;
637
638    get_netbw(NULL, NULL, &in_pkts, NULL);
639
640    val.f = (float)in_pkts;
641    return val;
642 }
643
644 g_val_t
645 pkts_out_func ( void )
646 {
647    double out_pkts;
648    g_val_t val;
649
650    get_netbw(NULL, NULL, NULL, &out_pkts);
651
652    val.f = (float)out_pkts;
653    return val;
654 }
655
656 g_val_t
657 bytes_out_func ( void )
658 {
659    double out_bytes;
660    g_val_t val;
661
662    get_netbw(NULL, &out_bytes, NULL, NULL);
663
664    val.f = (float)out_bytes;
665    return val;
666 }
667
668 g_val_t
669 bytes_in_func ( void )
670 {
671    double in_bytes;
672    g_val_t val;
673
674    get_netbw(&in_bytes, NULL, NULL, NULL);
675
676    val.f = (float)in_bytes;
677    return val;
678 }
679
680 /*
681  * Disk space reporting functions from Linux code.  find_disk_space()
682  * body derived from FreeBSD df and mount code.
683  */
684
685 g_val_t
686 disk_free_func( void )
687 {
688    double total_free=0.0;
689    double total_size=0.0;
690    g_val_t val;
691
692    find_disk_space(&total_size, &total_free);
693
694    val.d = total_free;
695    return val;
696 }
697
698 g_val_t
699 disk_total_func( void )
700 {
701    double total_free=0.0;
702    double total_size=0.0;
703    g_val_t val;
704
705    find_disk_space(&total_size, &total_free);
706
707    val.d = total_size;
708    return val;
709 }
710
711 g_val_t
712 part_max_used_func( void )
713 {
714    double total_free=0.0;
715    double total_size=0.0;
716    float most_full;
717    g_val_t val;
718
719    most_full = find_disk_space(&total_size, &total_free);
720
721    val.f = most_full;
722    return val;
723 }
724
725
726 /*
727  * Copyright (c) 1980, 1983, 1990, 1993, 1994, 1995
728  *      The Regents of the University of California.  All rights reserved.
729  * (c) UNIX System Laboratories, Inc.
730  * All or some portions of this file are derived from material licensed
731  * to the University of California by American Telephone and Telegraph
732  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
733  * the permission of UNIX System Laboratories, Inc.
734  *
735  * Redistribution and use in source and binary forms, with or without
736  * modification, are permitted provided that the following conditions
737  * are met:
738  * 1. Redistributions of source code must retain the above copyright
739  *    notice, this list of conditions and the following disclaimer.
740  * 2. Redistributions in binary form must reproduce the above copyright
741  *    notice, this list of conditions and the following disclaimer in the
742  *    documentation and/or other materials provided with the distribution.
743  * 3. All advertising materials mentioning features or use of this software
744  *    must display the following acknowledgement:
745  *      This product includes software developed by the University of
746  *      California, Berkeley and its contributors.
747  * 4. Neither the name of the University nor the names of its contributors
748  *    may be used to endorse or promote products derived from this software
749  *    without specific prior written permission.
750  *
751  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
752  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
753  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
754  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
755  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
756  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
757  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
758  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
759  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
760  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
761  * SUCH DAMAGE.
762  *
763  *
764  * NOTE: The copyright of UC Berkeley's Berkeley Software Distribution
765  * ("BSD") source has been updated.  The copyright addendum may be found
766  * at ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change.
767  */
768
769 static float
770 find_disk_space(double *total, double *tot_avail)
771 {
772         struct statfs *mntbuf;
773         const char *fstype;
774         const char **vfslist;
775         char *netvfslist;
776         size_t i, mntsize;
777         size_t used, availblks;
778         const double reported_units = 1e9;
779         double toru;
780         float pct;
781         float most_full = 0.0;
782
783         *total = 0.0;
784         *tot_avail = 0.0;
785
786         fstype = "ufs";
787
788         netvfslist = makenetvfslist();
789         vfslist = makevfslist(netvfslist);
790         free(netvfslist);
791
792         mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
793         mntsize = regetmntinfo(&mntbuf, mntsize, vfslist);
794         for (i = 0; i < mntsize; i++) {
795                 if ((mntbuf[i].f_flags & MNT_IGNORE) == 0) {
796                         used = mntbuf[i].f_blocks - mntbuf[i].f_bfree;
797                         availblks = mntbuf[i].f_bavail + used;
798                         pct = (availblks == 0 ? 100.0 :
799                             (double)used / (double)availblks * 100.0);
800                         if (pct > most_full)
801                                 most_full = pct;
802
803                         toru = reported_units/mntbuf[i].f_bsize;
804                         *total += mntbuf[i].f_blocks / toru;
805                         *tot_avail += mntbuf[i].f_bavail / toru;
806                 }
807         }
808
809         return most_full;
810 }
811
812 /*
813  * Make a pass over the file system info in ``mntbuf'' filtering out
814  * file system types not in vfslist and possibly re-stating to get
815  * current (not cached) info.  Returns the new count of valid statfs bufs.
816  */
817 static size_t
818 regetmntinfo(struct statfs **mntbufp, long mntsize, const char **vfslist)
819 {
820         int i, j;
821         struct statfs *mntbuf;
822
823         if (vfslist == NULL)
824                 return (getmntinfo(mntbufp, MNT_WAIT));
825
826         mntbuf = *mntbufp;
827         for (j = 0, i = 0; i < mntsize; i++) {
828                 if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
829                         continue;
830                 (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]);
831                 j++;
832         }
833         return (j);
834 }
835
836 static int
837 checkvfsname(vfsname, vfslist)
838         const char *vfsname;
839         const char **vfslist;
840 {
841
842         if (vfslist == NULL)
843                 return (0);
844         while (*vfslist != NULL) {
845                 if (strcmp(vfsname, *vfslist) == 0)
846                         return (skipvfs);
847                 ++vfslist;
848         }
849         return (!skipvfs);
850 }
851
852 static const char **
853 makevfslist(fslist)
854         char *fslist;
855 {
856         const char **av;
857         int i;
858         char *nextcp;
859
860         if (fslist == NULL)
861                 return (NULL);
862         if (fslist[0] == 'n' && fslist[1] == 'o') {
863                 fslist += 2;
864                 skipvfs = 1;
865         }
866         for (i = 0, nextcp = fslist; *nextcp; nextcp++)
867                 if (*nextcp == ',')
868                         i++;
869         if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) {
870                 warnx("malloc failed");
871                 return (NULL);
872         }
873         nextcp = fslist;
874         i = 0;
875         av[i++] = nextcp;
876         while ((nextcp = strchr(nextcp, ',')) != NULL) {
877                 *nextcp++ = '\0';
878                 av[i++] = nextcp;
879         }
880         av[i++] = NULL;
881         return (av);
882 }
883
884 static char *
885 makenetvfslist(void)
886 {
887         char *str = NULL, *strptr, **listptr = NULL;
888         size_t slen;
889         int cnt, i;
890
891         int mib[3], maxvfsconf;
892         size_t miblen;
893         struct ovfsconf *ptr;
894
895         mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM;
896         miblen=sizeof(maxvfsconf);
897         if (sysctl(mib, (unsigned int)(sizeof(mib) / sizeof(mib[0])),
898             &maxvfsconf, &miblen, NULL, 0)) {
899                 warnx("sysctl failed");
900                 goto done;
901         }
902
903         if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) {
904                 warnx("malloc failed");
905                 goto done;
906         }
907
908         cnt = 0;
909         while ((ptr = getvfsent()) != NULL && cnt < maxvfsconf) {
910                 if (ptr->vfc_flags & VFCF_NONLOCAL)
911                         continue;
912
913                 listptr[cnt] = strdup(ptr->vfc_name);
914                 if (listptr[cnt] == NULL) {
915                         warnx("malloc failed");
916                         goto done;
917                 }
918                 cnt++;
919         }
920
921         if (cnt == 0)
922                 goto done;
923
924         /*
925          * Count up the string lengths, we need a extra byte to hold
926          * the between entries ',' or the NUL at the end.
927          */
928         for (i = 0; i < cnt; i++)
929                 slen = strlen(listptr[i]) + 1;
930         /* Add 2 for initial "no". */
931         slen += 2;
932
933         if ((str = malloc(slen)) == NULL) {
934                 warnx("malloc failed");
935                 goto done;
936         }
937
938         str[0] = 'n';
939         str[1] = 'o';
940         for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) {
941                 strcpy(strptr, listptr[i]);
942                 strptr += strlen(listptr[i]);
943                 *strptr = ',';
944         }
945         *strptr = '\0';
946
947 done:
948         if (listptr != NULL) {
949                 for(i = 0; i < cnt && listptr[i] != NULL; i++)
950                         free(listptr[i]);
951                 free(listptr);
952         }
953         return (str);
954
955 }
956
957 static void
958 get_netbw(double *in_bytes, double *out_bytes,
959     double *in_pkts, double *out_pkts)
960 {
961 #ifdef NETBW_DEBUG
962         char            name[IFNAMSIZ];
963 #endif
964         struct          if_msghdr *ifm, *nextifm;
965         struct          sockaddr_dl *sdl;
966         char            *buf, *lim, *next;
967         size_t          needed;
968         int             mib[6];
969         int             i;
970         int             index;
971         static double   ibytes, obytes, ipkts, opkts;
972         struct timeval  this_time;
973         struct timeval  time_diff;
974         struct traffic  traffic;
975         static struct timeval last_time = {0,0};
976         static int      indexes = 0;
977         static int      *seen = NULL;
978         static struct traffic *lastcount = NULL;
979         static double   o_ibytes, o_obytes, o_ipkts, o_opkts;
980
981         ibytes = obytes = ipkts = opkts = 0.0;
982
983         mib[0] = CTL_NET;
984         mib[1] = PF_ROUTE;
985         mib[2] = 0;
986         mib[3] = 0;                     /* address family */
987         mib[4] = NET_RT_IFLIST;
988         mib[5] = 0;             /* interface index */
989
990         gettimeofday(&this_time, NULL);
991         timersub(&this_time, &last_time, &time_diff);
992         if (timertod(&time_diff) < MIN_NET_POLL_INTERVAL) {
993                 goto output;
994         }
995
996         if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
997                 errx(1, "iflist-sysctl-estimate");
998         if ((buf = malloc(needed)) == NULL)
999                 errx(1, "malloc");
1000         if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
1001                 errx(1, "actual retrieval of interface table");
1002         lim = buf + needed;
1003
1004         next = buf;
1005         while (next < lim) {
1006
1007                 ifm = (struct if_msghdr *)next;
1008                 
1009                 if (ifm->ifm_type == RTM_IFINFO) {
1010                         sdl = (struct sockaddr_dl *)(ifm + 1);
1011                 } else {
1012                         fprintf(stderr, "out of sync parsing NET_RT_IFLIST\n");
1013                         fprintf(stderr, "expected %d, got %d\n", RTM_IFINFO,
1014                                 ifm->ifm_type);
1015                         fprintf(stderr, "msglen = %d\n", ifm->ifm_msglen);
1016                         fprintf(stderr, "buf:%p, next:%p, lim:%p\n", buf, next,
1017                                 lim);
1018                         exit (1);
1019                 }
1020
1021                 next += ifm->ifm_msglen;
1022                 while (next < lim) {
1023                         nextifm = (struct if_msghdr *)next;
1024
1025                         if (nextifm->ifm_type != RTM_NEWADDR)
1026                                 break;
1027
1028                         next += nextifm->ifm_msglen;
1029                 }
1030
1031                 if ((ifm->ifm_flags & IFF_LOOPBACK) || 
1032                     !(ifm->ifm_flags & IFF_UP))
1033                         continue;
1034
1035                 index = ifm->ifm_index;
1036
1037                 /* If we don't have a previous value yet, make a slot. */
1038                 if (index >= indexes) {
1039                         seen = realloc(seen, sizeof(*seen)*(index+1));
1040                         lastcount = realloc(lastcount,
1041                             sizeof(*lastcount)*(index+1));
1042
1043                         /* Initalize the new slots */
1044                         for (i = indexes; i <= index; i++) {
1045                                 seen[i] = 0;
1046                         }
1047                         indexes = index+1;
1048                 }
1049
1050                 /*
1051                  * If this is the first time we've seen this interface,
1052                  * set the last values to the current ones.  That causes
1053                  * us to see no bandwidth on the interface the first
1054                  * time, but that's OK.
1055                  */
1056                 if (!seen[index]) {
1057                         seen[index] = 1;
1058                         lastcount[index].in_bytes = ifm->ifm_data.ifi_ibytes;
1059                         lastcount[index].out_bytes = ifm->ifm_data.ifi_obytes;
1060                         lastcount[index].in_pkts = ifm->ifm_data.ifi_ipackets;
1061                         lastcount[index].out_pkts = ifm->ifm_data.ifi_opackets;
1062                 }
1063
1064                 traffic.in_bytes = counterdiff(lastcount[index].in_bytes,
1065                     ifm->ifm_data.ifi_ibytes, ULONG_MAX, 0);
1066                 traffic.out_bytes = counterdiff(lastcount[index].out_bytes,
1067                     ifm->ifm_data.ifi_obytes, ULONG_MAX, 0);
1068                 traffic.in_pkts = counterdiff(lastcount[index].in_pkts,
1069                     ifm->ifm_data.ifi_ipackets, ULONG_MAX, 0);
1070                 traffic.out_pkts = counterdiff(lastcount[index].out_pkts,
1071                     ifm->ifm_data.ifi_opackets, ULONG_MAX, 0);
1072
1073                 lastcount[index].in_bytes = ifm->ifm_data.ifi_ibytes;
1074                 lastcount[index].out_bytes = ifm->ifm_data.ifi_obytes;
1075                 lastcount[index].in_pkts = ifm->ifm_data.ifi_ipackets;
1076                 lastcount[index].out_pkts = ifm->ifm_data.ifi_opackets;
1077
1078 #ifdef NETBW_DEBUG
1079                 if_indextoname(index, name);
1080                 printf("%s: \n", name);
1081                 printf("\topackets=%llu ipackets=%llu\n",
1082                     traffic.out_pkts, traffic.in_pkts);
1083                 printf("\tobytes=%llu ibytes=%llu\n",
1084                     traffic.out_bytes, traffic.in_bytes);
1085 #endif
1086
1087                 if (timerisset(&last_time)) {
1088                         ibytes += (double)traffic.in_bytes / timertod(&time_diff);
1089                         obytes += (double)traffic.out_bytes / timertod(&time_diff);
1090                         ipkts += (double)traffic.in_pkts / timertod(&time_diff);
1091                         opkts += (double)traffic.out_pkts / timertod(&time_diff);
1092                 }
1093         }
1094         free(buf);
1095
1096         /* Save the values from this time */
1097         last_time = this_time;
1098         o_ibytes = ibytes;
1099         o_obytes = obytes;
1100         o_ipkts = ipkts;
1101         o_opkts = opkts;
1102
1103 output:
1104         if (in_bytes != NULL)
1105                 *in_bytes = o_ibytes;
1106         if (out_bytes != NULL)
1107                 *out_bytes = o_obytes;
1108         if (in_pkts != NULL)
1109                 *in_pkts = o_ipkts;
1110         if (out_pkts != NULL)
1111                 *out_pkts = o_opkts;
1112 }
1113
1114 static uint64_t
1115 counterdiff(uint64_t oldval, uint64_t newval, uint64_t maxval, uint64_t maxdiff)
1116 {
1117         uint64_t diff;
1118
1119         if (maxdiff == 0)
1120                 maxdiff = maxval;
1121
1122         /* Paranoia */
1123         if (oldval > maxval || newval > maxval)
1124                 return 0;
1125
1126         /*
1127          * Tackle the easy case.  Don't worry about maxdiff here because
1128          * we're SOL if it happens (i.e. assuming a reset just makes
1129          * matters worse).
1130          */
1131         if (oldval <= newval)
1132                 return (newval - oldval);
1133
1134         /*
1135          * Now the tricky part.  If we assume counters never get reset,
1136          * this is easy.  Unfortunaly, they do get reset on some
1137          * systems, so we need to try and deal with that.  Our huristic
1138          * is that if out difference is greater then maxdiff and newval
1139          * is less or equal to maxdiff, then we've probably been reset
1140          * rather then actually wrapping.  Obviously, you need to be
1141          * careful to poll often enough that you won't exceed maxdiff or
1142          * you will get undersized numbers when you do wrap.
1143          */
1144         diff = maxval - oldval + newval;
1145         if (diff > maxdiff && newval <= maxdiff)
1146                 return newval;
1147
1148         return diff;
1149 }