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