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