"Fossies" - the Fresh Open Source Software Archive

Member "xosview-1.22/bsd/kernel.cc" (22 Feb 2020, 53522 Bytes) of package /linux/misc/xosview-1.22.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "kernel.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.21_vs_1.22.

    1 //
    2 //  NetBSD port:
    3 //  Copyright (c) 1995, 1996, 1997-2002 by Brian Grayson (bgrayson@netbsd.org)
    4 //
    5 //  This file was written by Brian Grayson for the NetBSD and xosview
    6 //    projects.
    7 //  This file contains code from the NetBSD project, which is covered
    8 //    by the standard BSD license.
    9 //  Dummy device ignore code by : David Cuka (dcuka@intgp1.ih.att.com)
   10 //  The OpenBSD interrupt meter code was written by Oleg Safiullin
   11 //    (form@vs.itam.nsc.ru).
   12 //  This file may be distributed under terms of the GPL or of the BSD
   13 //    license, whichever you choose.  The full license notices are
   14 //    contained in the files COPYING.GPL and COPYING.BSD, which you
   15 //    should have received.  If not, contact one of the xosview
   16 //    authors for a copy.
   17 //
   18 
   19 #include "kernel.h"
   20 
   21 #include <stdio.h>
   22 #include <stdlib.h>
   23 #include <unistd.h>
   24 #include <fcntl.h>
   25 #include <kvm.h>
   26 #include <nlist.h>
   27 #include <limits.h>
   28 #include <string.h>
   29 #include <err.h>
   30 #include <errno.h>
   31 #include <ifaddrs.h>
   32 #include <sysexits.h>
   33 #include <sys/types.h>
   34 #include <sys/queue.h>
   35 #include <sys/socket.h>
   36 #include <sys/sysctl.h>
   37 #include <net/if.h>
   38 
   39 #if defined(XOSVIEW_DFBSD)
   40 #define _KERNEL_STRUCTURES
   41 #include <kinfo.h>
   42 #endif
   43 
   44 #if defined(XOSVIEW_FREEBSD) || defined(XOSVIEW_DFBSD)
   45 static const char ACPIDEV[] = "/dev/acpi";
   46 static const char APMDEV[] = "/dev/apm";
   47 static int maxcpus = 1;
   48 #include <sys/ioctl.h>
   49 #include <sys/resource.h>
   50 #include <dev/acpica/acpiio.h>
   51 #include <machine/apm_bios.h>
   52 #endif
   53 
   54 #if defined(XOSVIEW_NETBSD)
   55 #include <sys/sched.h>
   56 #include <sys/iostat.h>
   57 #include <sys/envsys.h>
   58 #include <prop/proplib.h>
   59 #include <paths.h>
   60 static int mib_cpt[2] = { CTL_KERN, KERN_CP_TIME };
   61 static int mib_dsk[3] = { CTL_HW, HW_IOSTATS, sizeof(struct io_sysctl) };
   62 #endif
   63 
   64 #if defined(XOSVIEW_OPENBSD)
   65 #include <sys/sched.h>
   66 #include <sys/disk.h>
   67 #include <sys/mount.h>
   68 #include <net/route.h>
   69 #include <net/if_dl.h>
   70 static int mib_spd[2] = { CTL_HW, HW_CPUSPEED };
   71 static int mib_cpt[2] = { CTL_KERN, KERN_CPTIME };
   72 static int mib_cpt2[3] = { CTL_KERN, KERN_CPTIME2, 0 };
   73 static int mib_ifl[6] = { CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
   74 #endif
   75 
   76 #if defined(XOSVIEW_OPENBSD) || defined(XOSVIEW_DFBSD)
   77 #include <sys/sensors.h>
   78 static int mib_sen[5] = { CTL_HW, HW_SENSORS };
   79 #endif
   80 
   81 #if defined(HAVE_DEVSTAT)
   82 #include <devstat.h>
   83 #endif
   84 
   85 #if defined(HAVE_UVM)
   86 #include <string.h>
   87 #include <sys/malloc.h>
   88 #include <sys/device.h>
   89 #include <uvm/uvm_extern.h>
   90 #ifdef VM_UVMEXP2
   91 static int mib_uvm[2] = { CTL_VM, VM_UVMEXP2 };
   92 #else
   93 static int mib_uvm[2] = { CTL_VM, VM_UVMEXP };
   94 #endif
   95 #else
   96 #if defined(XOSVIEW_FREEBSD)
   97 #define _WANT_VMMETER
   98 #endif
   99 #include <sys/vmmeter.h>
  100 #endif
  101 
  102 #if defined(HAVE_SWAPCTL)
  103 #include <sys/swap.h>
  104 #endif
  105 
  106 
  107 // ------------------------  local variables  ----------------------------------
  108 
  109 //  This single kvm_t is shared by all of the kvm routines.
  110 kvm_t* kd = NULL;
  111 
  112 //  This struct has the list of all the symbols we want from the kernel.
  113 static struct nlist nlst[] =
  114 {
  115 // We put a dummy symbol for a don't care, and ignore warnings about
  116 // this later on.  This keeps the indices within the nlist constant.
  117 #define DUMMY_SYM "dummy_sym"
  118 
  119 #if defined(XOSVIEW_OPENBSD)
  120 { "_disklist" },
  121 #define DISKLIST_SYM_INDEX   0
  122 #else
  123 { DUMMY_SYM },
  124 #define DUMMY_0
  125 #endif
  126 #if defined(XOSVIEW_NETBSD)
  127 { "_allevents" },
  128 #define ALLEVENTS_SYM_INDEX  1
  129 { "_bufmem" },
  130 #define BUFMEM_SYM_INDEX     2
  131 #else
  132 { DUMMY_SYM },
  133 #define DUMMY_1
  134 { DUMMY_SYM },
  135 #define DUMMY_2
  136 #endif
  137 #if defined(XOSVIEW_FREEBSD)
  138 { "_intrnames" },
  139 #define INTRNAMES_SYM_INDEX  3
  140 # if __FreeBSD_version >= 900040
  141 { "_sintrnames" },
  142 # else
  143 { "_eintrnames" },
  144 # endif
  145 #define EINTRNAMES_SYM_INDEX 4
  146 { "_intrcnt" },
  147 #define INTRCNT_SYM_INDEX    5
  148 # if __FreeBSD_version >= 900040
  149 { "_sintrcnt" },
  150 # else
  151 { "_eintrcnt" },
  152 # endif
  153 #define EINTRCNT_SYM_INDEX   6
  154 #endif
  155 { NULL }
  156 };
  157 
  158 static char kernelFileName[_POSIX2_LINE_MAX];
  159 
  160 
  161 // ------------------------  utility functions  --------------------------------
  162 //  The following is an error-checking form of kvm_read.  In addition
  163 //  it uses kd as the implicit kernel-file to read.  Saves typing.
  164 //  Since this is C++, it's an inline function rather than a macro.
  165 
  166 static inline void
  167 safe_kvm_read(unsigned long kernel_addr, void* user_addr, size_t nbytes) {
  168     /*  Check for obvious bad symbols (i.e., from /netbsd when we
  169      *  booted off of /netbsd.old), such as symbols that reference
  170      *  0x00000000 (or anywhere in the first 256 bytes of memory).  */
  171     int retval = 0;
  172     if ( (kernel_addr & 0xffffff00) == 0 )
  173         errx(EX_SOFTWARE, "safe_kvm_read() was attempted on EA %#lx.", kernel_addr);
  174     if ( (retval = kvm_read(kd, kernel_addr, user_addr, nbytes)) == -1 )
  175         err(EX_SOFTWARE, "kvm_read() of kernel address %#lx", kernel_addr);
  176     if (retval != (int)nbytes)
  177         warn("safe_kvm_read(%#lx) returned %d bytes, not %d", kernel_addr, retval, (int)nbytes);
  178 }
  179 
  180 //  This version uses the symbol offset in the nlst variable, to make it
  181 //  a little more convenient.  BCG
  182 static inline void
  183 safe_kvm_read_symbol(int nlstOffset, void* user_addr, size_t nbytes) {
  184     safe_kvm_read(nlst[nlstOffset].n_value, user_addr, nbytes);
  185 }
  186 
  187 int
  188 ValidSymbol(int index) {
  189     return ( (nlst[index].n_value & 0xffffff00) != 0 );
  190 }
  191 
  192 int
  193 SymbolValue(int index) {
  194     return nlst[index].n_value;
  195 }
  196 
  197 void
  198 BSDInit() {
  199     kernelFileName[0] = '\0';
  200 }
  201 
  202 void
  203 SetKernelName(const char* kernelName) {
  204     if (strlen(kernelName) >= _POSIX2_LINE_MAX)
  205         errx(EX_OSFILE, "Kernel file name of '%s' is too long.", kernelName);
  206 
  207     strncpy(kernelFileName, kernelName, _POSIX2_LINE_MAX);
  208 }
  209 
  210 void
  211 OpenKDIfNeeded() {
  212     char errstring[_POSIX2_LINE_MAX];
  213 
  214     if (kd)
  215         return; //  kd is non-NULL, so it has been initialized.  BCG
  216 
  217     /*  Open it read-only, for a little added safety.  */
  218     /*  If the first character of kernelFileName is not '\0', then use
  219      *  that kernel file.  Otherwise, use the default kernel, by
  220      *  specifying NULL.  */
  221     if ((kd = kvm_openfiles((kernelFileName[0] ? kernelFileName : NULL),
  222                             NULL, NULL, O_RDONLY, errstring)) == NULL)
  223         err(EX_OSFILE, "OpenKDIfNeeded(): %s", errstring);
  224 
  225     // Parenthetical note:  FreeBSD kvm_openfiles() uses getbootfile() to get
  226     // the correct kernel file if the 1st arg is NULL.  As far as I can see,
  227     // one should always use NULL in FreeBSD, but I suppose control is never a
  228     // bad thing... (pavel 21-Jan-1998)
  229 
  230     /*  Now grab the symbol offsets for the symbols that we want.  */
  231     if (kvm_nlist(kd, nlst) < 0)
  232         err(EX_OSERR, "Could not get kvm symbols");
  233 
  234     //  Look at all of the returned symbols, and check for bad lookups.
  235     //  (This may be unnecessary, but better to check than not to...  )
  236     struct nlist *nlp = nlst;
  237     while (nlp && nlp->n_name) {
  238         if ( strncmp(nlp->n_name, DUMMY_SYM, strlen(DUMMY_SYM))) {
  239             if ( nlp->n_type == 0 || nlp->n_value == 0 )
  240 #if defined(XOSVIEW_FREEBSD) && defined(__alpha__)
  241                 /* XXX: this should be properly fixed. */
  242                 ;
  243 #else
  244                 warnx("kvm_nlist() lookup failed for symbol '%s'.", nlp->n_name);
  245 #endif
  246         }
  247         nlp++;
  248     }
  249 }
  250 
  251 int
  252 BSDGetCPUSpeed() {
  253     size_t size;
  254     int cpu_speed = 0;
  255 
  256 #if defined(XOSVIEW_FREEBSD)
  257     char name[25];
  258     int speed = 0, cpus = BSDCountCpus(), avail_cpus = 0;
  259     size = sizeof(speed);
  260     for (int i = 0; i < cpus; i++) {
  261         snprintf(name, 25, "dev.cpu.%d.freq", i);
  262         if ( sysctlbyname(name, &speed, &size, NULL, 0) == 0 ) {
  263             // count only cpus with individual freq available
  264             cpu_speed += speed;
  265             avail_cpus++;
  266         }
  267     }
  268     if (avail_cpus > 1)
  269         cpu_speed /= avail_cpus;
  270 #elif defined(XOSVIEW_OPENBSD)
  271     size = sizeof(cpu_speed);
  272     if ( sysctl(mib_spd, 2, &cpu_speed, &size, NULL, 0) < 0 )
  273         err(EX_OSERR, "syscl hw.cpuspeed failed");
  274 #else  /* XOSVIEW_NETBSD || XOSVIEW_DFBSD */
  275     uint64_t speed = 0;
  276     size = sizeof(speed);
  277 #if defined(XOSVIEW_NETBSD)
  278     if ( sysctlbyname("machdep.tsc_freq", &speed, &size, NULL, 0) < 0 )
  279         err(EX_OSERR, "sysctl machdep.tsc_freq failed");
  280 #else  /* XOSVIEW_DFBSD */
  281     if ( sysctlbyname("hw.tsc_frequency", &speed, &size, NULL, 0) < 0 )
  282         err(EX_OSERR, "sysctl hw.tsc_frequency failed");
  283 #endif
  284     cpu_speed = speed / 1000000;
  285 #endif
  286     return cpu_speed;
  287 }
  288 
  289 
  290 // --------------------  PageMeter & MemMeter functions  -----------------------
  291 void
  292 BSDPageInit() {
  293     OpenKDIfNeeded();
  294 }
  295 
  296 /* meminfo[5]  = { active, inactive, wired, cached, free } */
  297 /* pageinfo[2] = { pages_in, pages_out }                   */
  298 void
  299 BSDGetPageStats(uint64_t *meminfo, uint64_t *pageinfo) {
  300 #if defined(HAVE_UVM)
  301 #ifdef VM_UVMEXP2
  302     struct uvmexp_sysctl uvm;
  303 #else
  304     struct uvmexp uvm;
  305 #endif
  306     size_t size = sizeof(uvm);
  307     if ( sysctl(mib_uvm, 2, &uvm, &size, NULL, 0) < 0 )
  308         err(EX_OSERR, "sysctl vm.uvmexp failed");
  309 
  310     if (meminfo) {
  311         // UVM excludes kernel memory -> assume it is active mem
  312         meminfo[0] = (uint64_t)(uvm.npages - uvm.inactive - uvm.wired - uvm.free) * uvm.pagesize;
  313         meminfo[1] = (uint64_t)uvm.inactive * uvm.pagesize;
  314         meminfo[2] = (uint64_t)uvm.wired * uvm.pagesize;
  315 
  316         // cache is already included in active and inactive memory and
  317         // there's no way to know how much is in which -> disable cache
  318         meminfo[3] = 0;
  319         meminfo[4] = (uint64_t)uvm.free * uvm.pagesize;
  320     }
  321     if (pageinfo) {
  322         pageinfo[0] = (uint64_t)uvm.pgswapin;
  323         pageinfo[1] = (uint64_t)uvm.pgswapout;
  324     }
  325 #else  /* HAVE_UVM */
  326     struct vmmeter_fbsd {
  327         u_int v_active_count;
  328         u_int v_inactive_count;
  329         u_int v_wire_count;
  330         u_int v_cache_count;
  331         u_int v_free_count;
  332         u_int v_page_size;
  333         u_int v_vnodepgsin;
  334         u_int v_vnodepgsout;
  335         u_int v_swappgsin;
  336         u_int v_swappgsout;
  337     } vm;
  338 #if defined(XOSVIEW_FREEBSD)
  339     size_t size = sizeof(unsigned int);
  340 #define GET_VM_STATS(name) \
  341     sysctlbyname("vm.stats.vm." #name, &vm.name, &size, NULL, 0)
  342     GET_VM_STATS(v_active_count);
  343     GET_VM_STATS(v_inactive_count);
  344     GET_VM_STATS(v_wire_count);
  345 #if __FreeBSD_version < 1200017
  346     GET_VM_STATS(v_cache_count);
  347 #endif
  348     GET_VM_STATS(v_free_count);
  349     GET_VM_STATS(v_page_size);
  350     GET_VM_STATS(v_vnodepgsin);
  351     GET_VM_STATS(v_vnodepgsout);
  352     GET_VM_STATS(v_swappgsin);
  353     GET_VM_STATS(v_swappgsout);
  354 #undef GET_VM_STATS
  355 #else  /* XOSVIEW_DFBSD */
  356     struct vmstats vms;
  357     size_t size = sizeof(vms);
  358     if ( sysctlbyname("vm.vmstats", &vms, &size, NULL, 0) < 0 )
  359         err(EX_OSERR, "sysctl vm.vmstats failed");
  360     size = sizeof(vm);
  361     if ( sysctlbyname("vm.vmmeter", &vm, &size, NULL, 0) < 0 )
  362         err(EX_OSERR, "sysctl vm.vmmeter failed");
  363 #endif
  364     if (meminfo) {
  365 #if defined(XOSVIEW_FREEBSD)
  366         meminfo[0] = (uint64_t)vm.v_active_count * vm.v_page_size;
  367         meminfo[1] = (uint64_t)vm.v_inactive_count * vm.v_page_size;
  368         meminfo[2] = (uint64_t)vm.v_wire_count * vm.v_page_size;
  369 #if __FreeBSD_version < 1200017
  370         meminfo[3] = (uint64_t)vm.v_cache_count * vm.v_page_size;
  371 #endif
  372         meminfo[4] = (uint64_t)vm.v_free_count * vm.v_page_size;
  373 #else  /* XOSVIEW_DFBSD */
  374         meminfo[0] = (uint64_t)vms.v_active_count * vms.v_page_size;
  375         meminfo[1] = (uint64_t)vms.v_inactive_count * vms.v_page_size;
  376         meminfo[2] = (uint64_t)vms.v_wire_count * vms.v_page_size;
  377         meminfo[3] = (uint64_t)vms.v_cache_count * vms.v_page_size;
  378         meminfo[4] = (uint64_t)vms.v_free_count * vms.v_page_size;
  379 #endif
  380     }
  381     if (pageinfo) {
  382         pageinfo[0] = (uint64_t)vm.v_vnodepgsin + (uint64_t)vm.v_swappgsin;
  383         pageinfo[1] = (uint64_t)vm.v_vnodepgsout + (uint64_t)vm.v_swappgsout;
  384     }
  385 #endif
  386 }
  387 
  388 
  389 // ------------------------  CPUMeter functions  -------------------------------
  390 
  391 void
  392 BSDCPUInit() {
  393     OpenKDIfNeeded();
  394 #if defined(XOSVIEW_FREEBSD)
  395     size_t size = sizeof(maxcpus);
  396     if ( sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0 )
  397         err(EX_OSERR, "sysctl kern.smp.maxcpus failed");
  398 #elif defined(XOSVIEW_DFBSD)
  399     if ( kinfo_get_cpus(&maxcpus) )
  400         err(EX_OSERR, "kinfo_get_cpus() failed");
  401 #endif
  402 }
  403 
  404 void
  405 BSDGetCPUTimes(uint64_t *timeArray, unsigned int cpu) {
  406     // timeArray is CPUSTATES long.
  407     // cpu is the number of CPU to return, starting from 1. If cpu == 0,
  408     // return aggregate times for all CPUs.
  409     // All BSDs have separate calls for aggregate and separate times. Only
  410     // OpenBSD returns one CPU per call, others return all at once.
  411     if (!timeArray)
  412         err(EX_SOFTWARE, "BSDGetCPUTimes(): passed pointer was null.");
  413     size_t size;
  414 #if defined(XOSVIEW_DFBSD)
  415     size = sizeof(struct kinfo_cputime);
  416     struct kinfo_cputime *times = (struct kinfo_cputime *)calloc(maxcpus + 1, size);
  417 #elif defined(XOSVIEW_NETBSD)
  418     size = CPUSTATES * sizeof(uint64_t);
  419     uint64_t *times = (uint64_t*)calloc(BSDCountCpus() + 1, size);
  420 #elif defined(XOSVIEW_FREEBSD)
  421     size = CPUSTATES * sizeof(long);
  422     long *times = (long*)calloc(maxcpus + 1, size);
  423 #else // XOSVIEW_OPENBSD
  424     uint64_t *times = (uint64_t*)calloc(CPUSTATES, sizeof(uint64_t));
  425 #endif
  426     // this array will have aggregate values at 0, then each CPU (except on
  427     // OpenBSD), so that cpu can be used as index
  428     if (!times)
  429         err(EX_OSERR, "BSDGetCPUTimes(): malloc failed");
  430 
  431 #if defined(XOSVIEW_DFBSD)
  432     if (cpu == 0) {
  433         if (kinfo_get_sched_cputime(times))
  434             err(EX_OSERR, "kinfo_get_sched_cputime() failed");
  435     }
  436     else {
  437         size = maxcpus * sizeof(times[0]);
  438         if ( sysctlbyname("kern.cputime", times + 1, &size, NULL, 0) < 0 )
  439             err(EX_OSERR, "sysctl kern.cputime failed");
  440     }
  441     timeArray[0] = times[cpu].cp_user;
  442     timeArray[1] = times[cpu].cp_nice;
  443     timeArray[2] = times[cpu].cp_sys;
  444     timeArray[3] = times[cpu].cp_intr;
  445     timeArray[4] = times[cpu].cp_idle;
  446 #else  // !XOSVIEW_DFBSD
  447     size = CPUSTATES * sizeof(times[0]);
  448     if (cpu == 0) {  // aggregate times
  449 #if defined(XOSVIEW_FREEBSD)
  450         if ( sysctlbyname("kern.cp_time", times, &size, NULL, 0) < 0 )
  451 #else  // XOSVIEW_NETBSD || XOSVIEW_OPENBSD
  452         if ( sysctl(mib_cpt, 2, times, &size, NULL, 0) < 0 )
  453 #endif
  454             err(EX_OSERR, "sysctl kern.cp_time failed");
  455     }
  456     else {  // separate times
  457 #if defined(XOSVIEW_FREEBSD)
  458         size *= maxcpus;
  459         if ( sysctlbyname("kern.cp_times", times + CPUSTATES, &size, NULL, 0) < 0 )
  460             err(EX_OSERR, "sysctl kern.cp_times failed");
  461 #elif defined(XOSVIEW_NETBSD)
  462         size *= BSDCountCpus();
  463         if ( sysctl(mib_cpt, 2, times + CPUSTATES, &size, NULL, 0) < 0 )
  464             err(EX_OSERR, "sysctl kern.cp_time failed");
  465 #else  // XOSVIEW_OPENBSD
  466         mib_cpt2[2] = cpu - 1;
  467         if ( sysctl(mib_cpt2, 3, times, &size, NULL, 0) < 0 )
  468             err(EX_OSERR, "sysctl kern.cp_time2 failed");
  469 #endif
  470     }
  471     for (int i = 0; i < CPUSTATES; i++)
  472 #if defined(XOSVIEW_OPENBSD) // aggregates are long, singles uint64_t
  473         timeArray[i] = ( cpu ? times[i] : ((long*)(times))[i] );
  474 #else  // XOSVIEW_FREEBSD || XOSVIEW_NETBSD
  475         timeArray[i] = times[cpu * CPUSTATES + i];
  476 #endif
  477 #endif
  478     free(times);
  479 }
  480 
  481 
  482 // ------------------------  NetMeter functions  -------------------------------
  483 int
  484 BSDNetInit() {
  485     OpenKDIfNeeded();
  486     return 1;
  487 }
  488 
  489 void
  490 BSDGetNetInOut(uint64_t *inbytes, uint64_t *outbytes, const char *netIface, bool ignored) {
  491     struct ifaddrs *ifap, *ifa;
  492     *inbytes = 0;
  493     *outbytes = 0;
  494 
  495     if (getifaddrs(&ifap) != 0)
  496         return;
  497 
  498     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
  499         bool skipif = false;
  500 
  501         if (ifa->ifa_addr->sa_family != AF_LINK)
  502             continue;
  503 
  504         if ( strncmp(netIface, "False", 5) != 0 ) {
  505             if ( (!ignored && strncmp(ifa->ifa_name, netIface, 256) != 0) ||
  506                  ( ignored && strncmp(ifa->ifa_name, netIface, 256) == 0) )
  507                 skipif = true;
  508         }
  509 #define IFA_STAT(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s)
  510         if (!skipif) {
  511             *inbytes  += IFA_STAT(ibytes);
  512             *outbytes += IFA_STAT(obytes);
  513         }
  514 #undef IFA_STAT
  515     }
  516     freeifaddrs(ifap);
  517 }
  518 
  519 
  520 //  ---------------------- Swap Meter stuff  -----------------------------------
  521 
  522 int
  523 BSDSwapInit() {
  524     OpenKDIfNeeded();
  525     return 1;
  526 }
  527 
  528 void
  529 BSDGetSwapInfo(uint64_t *total, uint64_t *used) {
  530 #if defined(HAVE_SWAPCTL)
  531     //  This code is based on a patch sent in by Scott Stevens
  532     //  (s.k.stevens@ic.ac.uk, at the time).
  533     struct swapent *sep, *swapiter;
  534     int bsize, rnswap, nswap = swapctl(SWAP_NSWAP, 0, 0);
  535     *total = *used = 0;
  536 
  537     if (nswap < 1)  // no swap devices on
  538         return;
  539 
  540     if ( (sep = (struct swapent *)malloc(nswap* sizeof(struct swapent))) == NULL )
  541         err(EX_OSERR, "BSDGetSwapInfo(): malloc failed");
  542     rnswap = swapctl(SWAP_STATS, (void *)sep, nswap);
  543     if (rnswap < 0)
  544         err(EX_OSERR, "BSDGetSwapInfo(): getting SWAP_STATS failed");
  545     if (nswap != rnswap)
  546         warnx("SWAP_STATS gave different value than SWAP_NSWAP "
  547               "(nswap=%d versus rnswap=%d).", nswap, rnswap);
  548 
  549     swapiter = sep;
  550     bsize = 512;  // block size is that of underlying device, *usually* 512 bytes
  551     for ( ; rnswap-- > 0; swapiter++) {
  552         *total += (uint64_t)swapiter->se_nblks * bsize;
  553         *used += (uint64_t)swapiter->se_inuse * bsize;
  554     }
  555     free(sep);
  556 #else
  557     struct kvm_swap kswap;
  558     OpenKDIfNeeded();
  559     int pgsize = getpagesize();
  560     if ( kvm_getswapinfo(kd, &kswap, 1, 0) )
  561         err(EX_OSERR, "BSDGetSwapInfo(): kvm_getswapinfo failed");
  562 
  563     *total = (uint64_t)kswap.ksw_total * pgsize;
  564     *used = (uint64_t)kswap.ksw_used * pgsize;
  565 #endif
  566 }
  567 
  568 
  569 // ----------------------- Disk Meter stuff  -----------------------------------
  570 
  571 #ifdef HAVE_DEVSTAT
  572 /*
  573  * Make use of the new FreeBSD kernel device statistics library using
  574  * code shamelessly borrowed from xsysinfo, which borrowed shamelessly
  575  * from FreeBSD's iostat(8).
  576  */
  577 long generation;
  578 devstat_select_mode select_mode;
  579 struct devstat_match *matches;
  580 int num_matches = 0;
  581 int num_selected, num_selections;
  582 long select_generation;
  583 static struct statinfo cur, last;
  584 int num_devices;
  585 struct device_selection *dev_select;
  586 int nodisk = 0;
  587 
  588 void
  589 DevStat_Init(void) {
  590     /*
  591      * Make sure that the userland devstat version matches the kernel
  592      * devstat version.
  593      */
  594 #if defined(XOSVIEW_FREEBSD)
  595     if (devstat_checkversion(NULL) < 0) {
  596 #else
  597     if (checkversion() < 0) {
  598 #endif
  599         nodisk++;
  600         warn("%s\n", devstat_errbuf);
  601         return;
  602     }
  603 
  604     /* find out how many devices we have */
  605 #if defined(XOSVIEW_FREEBSD)
  606     if ( (num_devices = devstat_getnumdevs(NULL)) < 0 ) {
  607 #else
  608     if ( (num_devices = getnumdevs()) < 0 ) {
  609 #endif
  610         nodisk++;
  611         warn("%s\n", devstat_errbuf);
  612         return;
  613     }
  614 
  615     cur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
  616     last.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
  617 
  618     /*
  619      * Grab all the devices.  We don't look to see if the list has
  620      * changed here, since it almost certainly has.  We only look for
  621      * errors.
  622      */
  623 #if defined(XOSVIEW_FREEBSD)
  624     if (devstat_getdevs(NULL, &cur) == -1) {
  625 #else
  626     if (getdevs(&cur) == -1) {
  627 #endif
  628         nodisk++;
  629         warn("%s\n", devstat_errbuf);
  630         return;
  631     }
  632 
  633     num_devices = cur.dinfo->numdevs;
  634     generation = cur.dinfo->generation;
  635     dev_select = NULL;
  636 
  637     /* only interested in disks */
  638     matches = NULL;
  639     char da[3] = "da";
  640 #if defined(XOSVIEW_FREEBSD)
  641     if (devstat_buildmatch(da, &matches, &num_matches) != 0) {
  642 #else
  643     if (buildmatch(da, &matches, &num_matches) != 0) {
  644 #endif
  645         nodisk++;
  646         warn("%s\n", devstat_errbuf);
  647         return;
  648     }
  649 
  650     if (num_matches == 0)
  651         select_mode = DS_SELECT_ADD;
  652     else
  653         select_mode = DS_SELECT_ONLY;
  654 
  655     /*
  656      * At this point, selectdevs will almost surely indicate that the
  657      * device list has changed, so we don't look for return values of 0
  658      * or 1.  If we get back -1, though, there is an error.
  659      */
  660 #if defined(XOSVIEW_FREEBSD)
  661     if (devstat_selectdevs(&dev_select, &num_selected,
  662 #else
  663     if (selectdevs(&dev_select, &num_selected,
  664 #endif
  665                    &num_selections, &select_generation,
  666                    generation, cur.dinfo->devices, num_devices,
  667                    matches, num_matches, NULL, 0, select_mode, 10, 0) == -1) {
  668         nodisk++;
  669         warn("%s\n", devstat_errbuf);
  670     }
  671 }
  672 
  673 uint64_t
  674 DevStat_Get(uint64_t *read_bytes, uint64_t *write_bytes) {
  675     int dn;
  676     long double busy_seconds;
  677     uint64_t reads, writes, total_bytes = 0;
  678     struct devinfo *tmp_dinfo;
  679 
  680     if (nodisk > 0)
  681         /* Diskless system or some error happened. */
  682         return 0;
  683 
  684     /*
  685      * Here what we want to do is refresh our device stats.
  686      * getdevs() returns 1 when the device list has changed.
  687      * If the device list has changed, we want to go through
  688      * the selection process again, in case a device that we
  689      * were previously displaying has gone away.
  690      */
  691 #if defined(XOSVIEW_FREEBSD)
  692     switch (devstat_getdevs(NULL, &cur)) {
  693 #else
  694     switch (getdevs(&cur)) {
  695 #endif
  696     case -1:
  697         return (0);
  698     case 1:
  699         int retval;
  700         num_devices = cur.dinfo->numdevs;
  701         generation = cur.dinfo->generation;
  702 #if defined(XOSVIEW_FREEBSD)
  703         retval = devstat_selectdevs(&dev_select, &num_selected,
  704 #else
  705         retval = selectdevs(&dev_select, &num_selected,
  706 #endif
  707                             &num_selections, &select_generation,
  708                             generation, cur.dinfo->devices,
  709                             num_devices, matches, num_matches,
  710                             NULL, 0, select_mode, 10, 0);
  711         switch(retval) {
  712         case -1:
  713             return (0);
  714         case 1:
  715             break;
  716         default:
  717             break;
  718         break;
  719         }
  720     default:
  721         break;
  722     }
  723 
  724     /*
  725      * Calculate elapsed time up front, since it's the same for all
  726      * devices.
  727      */
  728 #if defined(XOSVIEW_FREEBSD)
  729     busy_seconds = cur.snap_time - last.snap_time;
  730 #else
  731     busy_seconds = compute_etime(cur.busy_time, last.busy_time);
  732 #endif
  733     /* this is the first time thru so just copy cur to last */
  734     if (last.dinfo->numdevs == 0) {
  735         tmp_dinfo = last.dinfo;
  736         last.dinfo = cur.dinfo;
  737         cur.dinfo = tmp_dinfo;
  738 #if defined(XOSVIEW_FREEBSD)
  739         last.snap_time = cur.snap_time;
  740 #else
  741         last.busy_time = cur.busy_time;
  742 #endif
  743         return (0);
  744     }
  745 
  746     for (dn = 0; dn < num_devices; dn++) {
  747         int di;
  748         if ( (dev_select[dn].selected == 0) || (dev_select[dn].selected > 10) )
  749             continue;
  750 
  751         di = dev_select[dn].position;
  752 #if defined(XOSVIEW_FREEBSD)
  753         if (devstat_compute_statistics(&cur.dinfo->devices[di],
  754                                        &last.dinfo->devices[di], busy_seconds,
  755                                        DSM_TOTAL_BYTES_READ, &reads,
  756                                        DSM_TOTAL_BYTES_WRITE, &writes,
  757                                        DSM_NONE) != 0) {
  758 #else
  759         if (compute_stats_read(&cur.dinfo->devices[di],
  760                                &last.dinfo->devices[di], busy_seconds,
  761                                &reads, NULL,
  762                                NULL, NULL, NULL, NULL, NULL, NULL) != 0) {
  763             warn("%s\n", devstat_errbuf);
  764             break;
  765         }
  766         if (compute_stats_write(&cur.dinfo->devices[di],
  767                                 &last.dinfo->devices[di], busy_seconds,
  768                                 &writes, NULL,
  769                                 NULL, NULL, NULL, NULL, NULL, NULL) != 0) {
  770 #endif
  771             warn("%s\n", devstat_errbuf);
  772             break;
  773         }
  774         *read_bytes += reads;
  775         *write_bytes += writes;
  776         total_bytes += reads + writes;
  777     }
  778 
  779     tmp_dinfo = last.dinfo;
  780     last.dinfo = cur.dinfo;
  781     cur.dinfo = tmp_dinfo;
  782 #if defined(XOSVIEW_FREEBSD)
  783     last.snap_time = cur.snap_time;
  784 #else
  785     last.busy_time = cur.busy_time;
  786 #endif
  787 
  788     return total_bytes;
  789 }
  790 #endif
  791 
  792 int
  793 BSDDiskInit() {
  794     OpenKDIfNeeded();
  795 #if defined(HAVE_DEVSTAT)
  796     DevStat_Init();
  797 #endif
  798     return 1;
  799 }
  800 
  801 uint64_t
  802 BSDGetDiskXFerBytes(uint64_t *read_bytes, uint64_t *write_bytes) {
  803 #if defined(HAVE_DEVSTAT)
  804     return DevStat_Get(read_bytes, write_bytes);
  805 #else
  806     *read_bytes = *write_bytes = 0;
  807 # if defined(XOSVIEW_NETBSD)
  808     size_t size;
  809     // Do a sysctl with a NULL data pointer to get the size that would
  810     // have been returned, and use that to figure out # drives.
  811     if ( sysctl(mib_dsk, 3, NULL, &size, NULL, 0) < 0 )
  812         err(EX_OSERR, "BSDGetDiskXFerBytes(): sysctl hw.iostats #1 failed");
  813     unsigned int ndrives = size / mib_dsk[2];
  814     struct io_sysctl drive_stats[ndrives];
  815 
  816     // Get the stats.
  817     if ( sysctl(mib_dsk, 3, drive_stats, &size, NULL, 0) < 0 )
  818         err(EX_OSERR, "BSDGetDiskXFerBytes(): sysctl hw.iostats #2 failed");
  819 
  820     // Now accumulate the total.
  821     for (uint i = 0; i < ndrives; i++) {
  822         *read_bytes += drive_stats[i].rbytes;
  823         *write_bytes += drive_stats[i].wbytes;
  824     }
  825 # else  /* XOSVIEW_OPENBSD */
  826   /*  This function is a little tricky -- we have to iterate over a
  827    *  list in kernel land.  To make things simpler, data structures
  828    *  and pointers for objects in kernel-land have kvm tacked on front
  829    *  of their names.  Thus, kvmdiskptr points to a disk struct in
  830    *  kernel memory.  kvmcurrdisk is a copy of the kernel's struct,
  831    *  and it has pointers in it to other structs, so it also is
  832    *  prefixed with kvm.  */
  833     struct disklist_head kvmdisklist;
  834     struct disk *kvmdiskptr;
  835     struct disk kvmcurrdisk;
  836     safe_kvm_read_symbol(DISKLIST_SYM_INDEX, &kvmdisklist, sizeof(kvmdisklist));
  837     kvmdiskptr = TAILQ_FIRST(&kvmdisklist);
  838     while (kvmdiskptr != NULL) {
  839         safe_kvm_read((unsigned long)kvmdiskptr, &kvmcurrdisk, sizeof(kvmcurrdisk));
  840         *read_bytes += kvmcurrdisk.dk_rbytes;
  841         *write_bytes += kvmcurrdisk.dk_wbytes;
  842         kvmdiskptr = TAILQ_NEXT(&kvmcurrdisk, dk_link);
  843     }
  844 # endif
  845 #endif
  846     return (*read_bytes + *write_bytes);
  847 }
  848 
  849 
  850 //  ---------------------- Interrupt Meter stuff  ------------------------------
  851 
  852 int
  853 BSDIntrInit() {
  854     OpenKDIfNeeded();
  855     // Make sure the intr counter array is nonzero in size.
  856 #if defined(XOSVIEW_FREEBSD)
  857 # if __FreeBSD_version >= 900040
  858     size_t nintr;
  859     safe_kvm_read(nlst[EINTRCNT_SYM_INDEX].n_value, &nintr, sizeof(nintr));
  860     return ValidSymbol(INTRCNT_SYM_INDEX) && ValidSymbol(EINTRCNT_SYM_INDEX) && (nintr > 0);
  861 # else
  862     return ValidSymbol(INTRCNT_SYM_INDEX) && ValidSymbol(EINTRCNT_SYM_INDEX) && ((SymbolValue(EINTRCNT_SYM_INDEX) - SymbolValue(INTRCNT_SYM_INDEX)) > 0);
  863 # endif
  864 #elif defined(XOSVIEW_NETBSD)
  865     return ValidSymbol(ALLEVENTS_SYM_INDEX);
  866 #endif
  867     return 1;
  868 }
  869 
  870 int
  871 BSDNumInts() {
  872     /* This code is stolen from vmstat. */
  873     int count = 0, nbr = 0;
  874 #if defined(XOSVIEW_FREEBSD)
  875     size_t inamlen, nintr;
  876     char *intrnames, *intrs;
  877 
  878 # if __FreeBSD_version >= 900040
  879     safe_kvm_read(nlst[EINTRCNT_SYM_INDEX].n_value, &nintr, sizeof(nintr));
  880     safe_kvm_read(nlst[EINTRNAMES_SYM_INDEX].n_value, &inamlen, sizeof(inamlen));
  881 # else
  882     nintr = nlst[EINTRCNT_SYM_INDEX].n_value - nlst[INTRCNT_SYM_INDEX].n_value;
  883     inamlen = nlst[EINTRNAMES_SYM_INDEX].n_value - nlst[INTRNAMES_SYM_INDEX].n_value;
  884 #  endif
  885     if (nintr == 0 || inamlen == 0) {
  886         warnx("Could not get interrupt numbers.");
  887         return 0;
  888     }
  889 
  890     intrnames = intrs = (char *)malloc(inamlen);
  891     if (!intrs)
  892         err(EX_OSERR, "BSDNumInts(): malloc failed");
  893     safe_kvm_read(nlst[INTRNAMES_SYM_INDEX].n_value, intrs, inamlen);
  894     nintr /= sizeof(long);
  895     for (uint i = 0; i < nintr; i++) {
  896         if ( intrnames[0] && sscanf(intrnames, "irq%d", &nbr) == 1 && nbr > count )
  897             count = nbr;
  898         intrnames += strlen(intrnames) + 1;
  899     }
  900     free(intrs);
  901 #elif defined(XOSVIEW_NETBSD)
  902     struct evcntlist events;
  903     struct evcnt evcnt, *evptr;
  904     char dummy[30];
  905     char *name;
  906 
  907     safe_kvm_read(nlst[ALLEVENTS_SYM_INDEX].n_value, &events, sizeof(events));
  908     evptr = TAILQ_FIRST(&events);
  909     while (evptr) {
  910         safe_kvm_read((unsigned long)evptr, &evcnt, sizeof(evcnt));
  911         if (evcnt.ev_type == EVCNT_TYPE_INTR) {
  912             if ( !(name = (char *)malloc(evcnt.ev_namelen + 1)) )
  913                 err(EX_OSERR, "BSDNumInts(): malloc failed");
  914             safe_kvm_read((unsigned long)evcnt.ev_name, name, evcnt.ev_namelen + 1);
  915             if ( sscanf(name, "%s%d", dummy, &nbr) == 2 && nbr > count )
  916                 count = nbr;
  917             free(name);
  918         }
  919         evptr = TAILQ_NEXT(&evcnt, ev_list);
  920     }
  921 #elif defined(XOSVIEW_OPENBSD)
  922     int nintr = 0;
  923     int mib_int[4] = { CTL_KERN, KERN_INTRCNT, KERN_INTRCNT_NUM };
  924     size_t size = sizeof(nintr);
  925     if ( sysctl(mib_int, 3, &nintr, &size, NULL, 0) < 0 ) {
  926         warn("Could not get interrupt count");
  927         return 0;
  928     }
  929     for (int i = 0; i < nintr; i++) {
  930         mib_int[2] = KERN_INTRCNT_VECTOR;
  931         mib_int[3] = i;
  932         size = sizeof(nbr);
  933         if ( sysctl(mib_int, 4, &nbr, &size, NULL, 0) < 0 )
  934             warn("Could not get name of interrupt %d", i);
  935         else
  936             if ( nbr > count )
  937                 count = nbr;
  938     }
  939 #else  // XOSVIEW_DFBSD
  940     int nintr = 0;
  941     size_t inamlen;
  942     char *intrnames, *intrs;
  943 
  944     if ( sysctlbyname("hw.intrnames", NULL, &inamlen, NULL, 0) != 0 ) {
  945         warn("sysctl hw.intrnames failed");
  946         return 0;
  947     }
  948     intrnames = intrs = (char *)malloc(inamlen);
  949     if (!intrs)
  950         err(EX_OSERR, "BSDNumInts(): malloc failed");
  951 
  952     if ( sysctlbyname("hw.intrnames", intrs, &inamlen, NULL, 0) < 0 ) {
  953         warn("sysctl hw.intrnames failed");
  954         free(intrs);
  955         return 0;
  956     }
  957     for (uint i = 0; i < inamlen; i++) {
  958         if (intrs[i] == '\0')  // count end-of-strings
  959             nintr++;
  960     }
  961     for (int i = 0; i < nintr; i++) {
  962         if ( sscanf(intrnames, "irq%d", &nbr) == 0 ) {
  963             if ( ++nbr > count )  // unused ints are named irqn where
  964                 count = nbr;      // 0<=n<=255, used ones have device name
  965         }
  966         intrnames += strlen(intrnames) + 1;
  967     }
  968     free(intrs);
  969 #endif
  970     return count;  // this is the highest numbered interrupt
  971 }
  972 
  973 void
  974 BSDGetIntrStats(uint64_t *intrCount, unsigned int *intrNbrs) {
  975     /* This code is stolen from vmstat */
  976     int nbr = 0;
  977 #if defined(XOSVIEW_FREEBSD)
  978     unsigned long *kvm_intrcnt, *intrcnt;
  979     char *kvm_intrnames, *intrnames;
  980     size_t inamlen, nintr;
  981 
  982 # if __FreeBSD_version >= 900040
  983     safe_kvm_read(nlst[EINTRCNT_SYM_INDEX].n_value, &nintr, sizeof(nintr));
  984     safe_kvm_read(nlst[EINTRNAMES_SYM_INDEX].n_value, &inamlen, sizeof(inamlen));
  985 # else
  986     nintr = nlst[EINTRCNT_SYM_INDEX].n_value - nlst[INTRCNT_SYM_INDEX].n_value;
  987     inamlen = nlst[EINTRNAMES_SYM_INDEX].n_value - nlst[INTRNAMES_SYM_INDEX].n_value;
  988 # endif
  989     if (nintr == 0 || inamlen == 0) {
  990         warnx("Could not get interrupt numbers.");
  991         return;
  992     }
  993     if ( ((kvm_intrcnt = (unsigned long *)malloc(nintr)) == NULL) ||
  994          ((kvm_intrnames = (char *)malloc(inamlen)) == NULL) )
  995         err(EX_OSERR, "BSDGetIntrStats(): malloc failed");
  996 
  997     // keep track of the mem we're given:
  998     intrcnt = kvm_intrcnt;
  999     intrnames = kvm_intrnames;
 1000 
 1001     safe_kvm_read(nlst[INTRCNT_SYM_INDEX].n_value, kvm_intrcnt, nintr);
 1002     safe_kvm_read(nlst[INTRNAMES_SYM_INDEX].n_value, kvm_intrnames, inamlen);
 1003 
 1004     nintr /= sizeof(long);
 1005     /* kvm_intrname has the ASCII names of the IRQs, every null-terminated
 1006      * string corresponds to a value in the kvm_intrcnt array
 1007      * e.g. irq1: atkbd0   */
 1008     for (uint i = 0; i < nintr; i++) {
 1009         /* Figure out which irq we have here */
 1010         if ( kvm_intrnames[0] && sscanf(kvm_intrnames, "irq%d", &nbr) == 1 ) {
 1011             intrCount[nbr] = *kvm_intrcnt;
 1012             if (intrNbrs)
 1013                 intrNbrs[nbr] = 1;
 1014         }
 1015         kvm_intrcnt++;
 1016         kvm_intrnames += strlen(kvm_intrnames) + 1;
 1017     }
 1018     free(intrcnt);
 1019     free(intrnames);
 1020 #elif defined(XOSVIEW_NETBSD)
 1021     struct evcntlist events;
 1022     struct evcnt evcnt, *evptr;
 1023     char dummy[30];
 1024     char *name;
 1025 
 1026     safe_kvm_read(nlst[ALLEVENTS_SYM_INDEX].n_value, &events, sizeof(events));
 1027     evptr = TAILQ_FIRST(&events);
 1028     while (evptr) {
 1029         safe_kvm_read((unsigned long)evptr, &evcnt, sizeof(evcnt));
 1030         if (evcnt.ev_type == EVCNT_TYPE_INTR) {
 1031             if ( !(name = (char *)malloc(evcnt.ev_namelen + 1)) )
 1032                 err(EX_OSERR, "BSDGetIntrStats(): malloc failed");
 1033             safe_kvm_read((unsigned long)evcnt.ev_name, name, evcnt.ev_namelen + 1);
 1034             if ( sscanf(name, "%s%d", dummy, &nbr) == 2 ) {
 1035                 intrCount[nbr] = evcnt.ev_count;
 1036                 if (intrNbrs)
 1037                     intrNbrs[nbr] = 1;
 1038             }
 1039             free(name);
 1040         }
 1041         evptr = TAILQ_NEXT(&evcnt, ev_list);
 1042     }
 1043 #elif defined(XOSVIEW_OPENBSD)
 1044     int nintr = 0;
 1045     uint64_t count = 0;
 1046     size_t size = sizeof(nintr);
 1047     int mib_int[4] = { CTL_KERN, KERN_INTRCNT, KERN_INTRCNT_NUM };
 1048     if ( sysctl(mib_int, 3, &nintr, &size, NULL, 0) < 0 ) {
 1049         warn("Could not get interrupt count");
 1050         return;
 1051     }
 1052     for (int i = 0; i < nintr; i++) {
 1053         mib_int[2] = KERN_INTRCNT_VECTOR;
 1054         mib_int[3] = i;
 1055         size = sizeof(nbr);
 1056         if ( sysctl(mib_int, 4, &nbr, &size, NULL, 0) < 0 )
 1057             continue;  // not active
 1058         mib_int[2] = KERN_INTRCNT_CNT;
 1059         size = sizeof(count);
 1060         if ( sysctl(mib_int, 4, &count, &size, NULL, 0) < 0 ) {
 1061             warn("sysctl kern.intrcnt.cnt.%d failed", i);
 1062             count = 0;
 1063         }
 1064         intrCount[nbr] += count;  // += because ints can share number
 1065         if (intrNbrs)
 1066             intrNbrs[nbr] = 1;
 1067     }
 1068 #else  // XOSVIEW_DFBSD
 1069     int nintr = 0;
 1070     size_t inamlen;
 1071     unsigned long *intrcnt;
 1072     char *dummy, *intrs, **intrnames;
 1073 
 1074     if ( sysctlbyname("hw.intrnames", NULL, &inamlen, NULL, 0) != 0 ) {
 1075         warn("sysctl hw.intrnames failed");
 1076         return;
 1077     }
 1078 
 1079     dummy = intrs = (char *)malloc(inamlen);
 1080     if (!intrs)
 1081         err(EX_OSERR, "BSDGetIntrStats(): malloc failed");
 1082     if ( sysctlbyname("hw.intrnames", intrs, &inamlen, NULL, 0) < 0 ) {
 1083         warn("sysctl hw.intrnames failed");
 1084         free(intrs);
 1085         return;
 1086     }
 1087     for (uint i = 0; i < inamlen; i++) {
 1088         if (intrs[i] == '\0')  // count end-of-strings
 1089             nintr++;
 1090     }
 1091     if ( !(intrnames = (char **)malloc(nintr * sizeof(char *))) )
 1092         err(EX_OSERR, "BSDGetIntrStats(): malloc failed");
 1093 
 1094     for (int i = 0; i < nintr; i++) {
 1095         intrnames[i] = intrs;
 1096         intrs += strlen(intrs) + 1;
 1097     }
 1098     if ( !(intrcnt = (unsigned long *)calloc(nintr, sizeof(long))) )
 1099         err(EX_OSERR, "BSDGetIntrStats(): malloc failed");
 1100 
 1101     inamlen = nintr * sizeof(long);
 1102     if ( sysctlbyname("hw.intrcnt", intrcnt, &inamlen, NULL, 0) < 0 )
 1103         err(EX_OSERR, "sysctl hw.intrcnt failed");
 1104 
 1105     for (int i = 0; i < nintr; i++) {
 1106         if ( sscanf(intrnames[i], "irq%d", &nbr) == 0 ) {
 1107             nbr++;
 1108             intrCount[nbr] += intrcnt[i];
 1109             if (intrNbrs)
 1110                 intrNbrs[nbr] = 1;
 1111         }
 1112     }
 1113     free(dummy);
 1114     free(intrnames);
 1115     free(intrcnt);
 1116 #endif
 1117 }
 1118 
 1119 
 1120 //  ---------------------- Sensor Meter stuff  ---------------------------------
 1121 
 1122 static int mib_cpu[2] = { CTL_HW, HW_NCPU };
 1123 
 1124 int
 1125 BSDCountCpus(void) {
 1126     int cpus = 0;
 1127     size_t size = sizeof(cpus);
 1128     if ( sysctl(mib_cpu, 2, &cpus, &size, NULL, 0) < 0 )
 1129         warn("sysctl hw.ncpu failed.");
 1130     return cpus;
 1131 }
 1132 
 1133 #if defined(__i386__) || defined(__x86_64__)
 1134 unsigned int
 1135 BSDGetCPUTemperature(float *temps, float *tjmax) {
 1136     unsigned int nbr = 0;
 1137 #if defined(XOSVIEW_NETBSD)
 1138     // All kinds of sensors are read with libprop. We have to go through them
 1139     // to find either Intel Core 2 or AMD ones. Actual temperature is in
 1140     // cur-value and TjMax, if present, in critical-max.
 1141     // Values are in microdegrees Kelvin.
 1142     int fd;
 1143     const char *name = NULL;
 1144     char dummy[20];
 1145     prop_dictionary_t pdict;
 1146     prop_object_t pobj, pobj1, pobj2;
 1147     prop_object_iterator_t piter, piter2;
 1148     prop_array_t parray;
 1149 
 1150     if ( (fd = open(_PATH_SYSMON, O_RDONLY)) == -1 ) {
 1151         warn("Could not open %s", _PATH_SYSMON);
 1152         return 0;  // this seems to happen occasionally, so only warn
 1153     }
 1154     if (prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &pdict))
 1155         err(EX_OSERR, "Could not get sensor dictionary");
 1156     if (close(fd) == -1)
 1157         err(EX_OSERR, "Could not close %s", _PATH_SYSMON);
 1158 
 1159     if (prop_dictionary_count(pdict) == 0) {
 1160         warn("No sensors found");
 1161         return 0;
 1162     }
 1163     if ( !(piter = prop_dictionary_iterator(pdict)) )
 1164         err(EX_OSERR, "Could not get sensor iterator");
 1165 
 1166     while ( (pobj = prop_object_iterator_next(piter)) ) {
 1167         parray = (prop_array_t)prop_dictionary_get_keysym(pdict, (prop_dictionary_keysym_t)pobj);
 1168         if (prop_object_type(parray) != PROP_TYPE_ARRAY)
 1169             continue;
 1170         name = prop_dictionary_keysym_cstring_nocopy((prop_dictionary_keysym_t)pobj);
 1171         if ( strncmp(name, "coretemp", 8) && strncmp(name, "amdtemp", 7) )
 1172             continue;
 1173         if ( !(piter2 = prop_array_iterator(parray)) )
 1174             err(EX_OSERR, "Could not get sensor iterator");
 1175 
 1176         int i = 0;
 1177         sscanf(name, "%[^0-9]%d", dummy, &i);
 1178         while ( (pobj = prop_object_iterator_next(piter2)) ) {
 1179             if ( !(pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "type")) )
 1180                 continue;
 1181             if ( (pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "cur-value")) ) {
 1182                 if (temps)
 1183                     temps[i] = (prop_number_integer_value((prop_number_t)pobj1) / 1000000.0) - 273.15;
 1184                 nbr++;
 1185             }
 1186             if ( (pobj2 = prop_dictionary_get((prop_dictionary_t)pobj, "critical-max")) && tjmax )
 1187                 tjmax[i] = (prop_number_integer_value((prop_number_t)pobj2) / 1000000.0) - 273.15;
 1188         }
 1189         prop_object_iterator_release(piter2);
 1190     }
 1191     prop_object_iterator_release(piter);
 1192     prop_object_release(pdict);
 1193 #else  /* XOSVIEW_NETBSD */
 1194     int val = 0;
 1195     size_t size = sizeof(val);
 1196 
 1197 #if defined(XOSVIEW_OPENBSD) || defined(XOSVIEW_DFBSD)
 1198     // All kinds of sensors are read with sysctl. We have to go through them
 1199     // to find either Intel Core 2 or AMD ones.
 1200     // Values are in microdegrees Kelvin.
 1201     struct sensordev sd;
 1202     struct sensor s;
 1203     int cpu = 0;
 1204     char dummy[10];
 1205 
 1206     for (int dev = 0; dev < 1024; dev++) {  // go through all sensor devices
 1207         mib_sen[2] = dev;
 1208         size = sizeof(sd);
 1209         if ( sysctl(mib_sen, 3, &sd, &size, NULL, 0) < 0 ) {
 1210             if (errno == ENOENT)
 1211                 break;  // no more sensors
 1212             if (errno == ENXIO)
 1213                 continue;  // no sensor with this mib
 1214             err(EX_OSERR, "sysctl hw.sensors.%d failed", dev);
 1215         }
 1216         if ( strncmp(sd.xname, "cpu", 3) )
 1217             continue;  // not CPU sensor
 1218         sscanf(sd.xname, "%[^0-9]%d", dummy, &cpu);
 1219 
 1220         mib_sen[3] = SENSOR_TEMP;  // for each device, get temperature sensors
 1221         for (int i = 0; i < sd.maxnumt[SENSOR_TEMP]; i++) {
 1222             mib_sen[4] = i;
 1223             size = sizeof(s);
 1224             if ( sysctl(mib_sen, 5, &s, &size, NULL, 0) < 0 )
 1225                 continue;  // no sensor on this core?
 1226             if (s.flags & SENSOR_FINVALID)
 1227                 continue;
 1228             if (temps)
 1229                 temps[cpu] = (float)(s.value - 273150000) / 1000000.0;
 1230             nbr++;
 1231         }
 1232     }
 1233 #else  /* XOSVIEW_FREEBSD */
 1234     // Temperatures can be read with sysctl dev.cpu.%d.temperature on both
 1235     // Intel Core 2 and AMD K8+ processors.
 1236     // Values are in degrees Celsius (FreeBSD < 7.2) or in
 1237     // 10*degrees Kelvin (FreeBSD >= 7.3).
 1238     char name[25];
 1239     int cpus = BSDCountCpus();
 1240     for (int i = 0; i < cpus; i++) {
 1241         snprintf(name, 25, "dev.cpu.%d.temperature", i);
 1242         if ( sysctlbyname(name, &val, &size, NULL, 0) == 0) {
 1243             nbr++;
 1244             if (temps)
 1245 #if __FreeBSD_version >= 702106
 1246                 temps[i] = ((float)val - 2732.0) / 10.0;
 1247 #else
 1248                 temps[i] = (float)val;
 1249 #endif
 1250         }
 1251         else
 1252             warn("sysctl %s failed", name);
 1253 
 1254         if (tjmax) {
 1255             snprintf(name, 25, "dev.cpu.%d.coretemp.tjmax", i);
 1256             if ( sysctlbyname(name, &val, &size, NULL, 0) == 0 )
 1257 #if __FreeBSD_version >= 702106
 1258                 tjmax[i] = ((float)val - 2732.0) / 10.0;
 1259 #else
 1260                 tjmax[i] = (float)val;
 1261 #endif
 1262             else
 1263                 warn("sysctl %s failed", name);
 1264         }
 1265     }
 1266 #endif
 1267 #endif
 1268     return nbr;
 1269 }
 1270 #endif
 1271 
 1272 void
 1273 BSDGetSensor(const char *name, const char *valname, float *value, char *unit) {
 1274     if (!name || !valname || !value)
 1275         errx(EX_SOFTWARE, "NULL pointer passed to BSDGetSensor().");
 1276 #if defined(XOSVIEW_NETBSD)
 1277     /* Adapted from envstat. */
 1278     // All kinds of sensors are read with libprop. Specific device and value
 1279     // can be asked for. Values are transformed to suitable units.
 1280     int fd, val = 0;
 1281     char type[20];
 1282     prop_dictionary_t pdict;
 1283     prop_object_t pobj, pobj1;
 1284     prop_object_iterator_t piter;
 1285 
 1286     if ( (fd = open(_PATH_SYSMON, O_RDONLY)) == -1 ) {
 1287         warn("Could not open %s", _PATH_SYSMON);
 1288         return;  // this seems to happen occasionally, so only warn
 1289     }
 1290     if (prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &pdict))
 1291         err(EX_OSERR, "Could not get sensor dictionary");
 1292     if (close(fd) == -1)
 1293         err(EX_OSERR, "Could not close %s", _PATH_SYSMON);
 1294 
 1295     if (prop_dictionary_count(pdict) == 0) {
 1296         warn("No sensors found");
 1297         return;
 1298     }
 1299     pobj = prop_dictionary_get(pdict, name);
 1300     if (prop_object_type(pobj) != PROP_TYPE_ARRAY)
 1301         err(EX_USAGE, "Device %s does not exist", name);
 1302 
 1303     if ( !(piter = prop_array_iterator((prop_array_t)pobj)) )
 1304         err(EX_OSERR, "Could not get sensor iterator");
 1305 
 1306     while ( (pobj = prop_object_iterator_next(piter)) ) {
 1307         if ( !(pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "type")) )
 1308             continue;
 1309         strlcpy(type, prop_string_cstring_nocopy((prop_string_t)pobj1), 20);
 1310         if ( strncmp(type, "Indicator", 3) == 0 ||
 1311              strncmp(type, "Battery", 3) == 0   ||
 1312              strncmp(type, "Drive", 3) == 0 )
 1313             continue;  // these are string values
 1314         if ( (pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, valname)) )
 1315             val = prop_number_integer_value((prop_number_t)pobj1);
 1316         else
 1317             err(EX_USAGE, "Value %s does not exist", valname);
 1318         if ( strncmp(type, "Temperature", 4) == 0 ) {
 1319             *value = (val / 1000000.0) - 273.15;  // temperatures are in microkelvins
 1320             if (unit)
 1321                 strcpy(unit, "\260C");
 1322         }
 1323         else if ( strncmp(type, "Fan", 3) == 0 ) {
 1324             *value = (float)val;                  // plain integer value
 1325             if (unit)
 1326                 strcpy(unit, "RPM");
 1327         }
 1328         else if ( strncmp(type, "Integer", 3) == 0 )
 1329             *value = (float)val;                  // plain integer value
 1330         else if ( strncmp(type, "Voltage", 4) == 0 ) {
 1331             *value = (float)val / 1000000.0;      // electrical units are in micro{V,A,W,Ohm}
 1332             if (unit)
 1333                 strcpy(unit, "V");
 1334         }
 1335         else if ( strncmp(type, "Ampere hour", 7) == 0 ) {
 1336             *value = (float)val / 1000000.0;      // electrical units are in micro{V,A,W,Ohm}
 1337             if (unit)
 1338                 strcpy(unit, "Ah");
 1339         }
 1340         else if ( strncmp(type, "Ampere", 7) == 0 ) {
 1341             *value = (float)val / 1000000.0;      // electrical units are in micro{V,A,W,Ohm}
 1342             if (unit)
 1343                 strcpy(unit, "A");
 1344         }
 1345         else if ( strncmp(type, "Watt hour", 5) == 0 ) {
 1346             *value = (float)val / 1000000.0;      // electrical units are in micro{V,A,W,Ohm}
 1347             if (unit)
 1348                 strcpy(unit, "Wh");
 1349         }
 1350         else if ( strncmp(type, "Watts", 5) == 0 ) {
 1351             *value = (float)val / 1000000.0;      // electrical units are in micro{V,A,W,Ohm}
 1352             if (unit)
 1353                 strcpy(unit, "W");
 1354         }
 1355         else if ( strncmp(type, "Ohms", 4) == 0 ) {
 1356             *value = (float)val / 1000000.0;      // electrical units are in micro{V,A,W,Ohm}
 1357             if (unit)
 1358                 strcpy(unit, "Ohm");
 1359         }
 1360     }
 1361     prop_object_iterator_release(piter);
 1362     prop_object_release(pdict);
 1363 #else  /* XOSVIEW_NETBSD */
 1364     size_t size;
 1365     char dummy[50];
 1366 #if defined(XOSVIEW_FREEBSD) || defined(XOSVIEW_DFBSD)
 1367     // FreeBSD has no sensor framework, but ACPI thermal zones might work.
 1368     // They are readable through sysctl (also works in Dragonfly).
 1369     // Values are in 10 * degrees Kelvin.
 1370     if ( strncmp(name, "tz", 2) == 0 ) {
 1371         int val = 0;
 1372         size = sizeof(val);
 1373         snprintf(dummy, 50, "hw.acpi.thermal.%s.%s", name, valname);
 1374         if ( sysctlbyname(dummy, &val, &size, NULL, 0) < 0 )
 1375             err(EX_OSERR, "sysctl %s failed", dummy);
 1376         *value = ((float)val - 2732.0) / 10.0;
 1377         if (unit)
 1378             strcpy(unit, "\260C");
 1379         return;
 1380     }
 1381     // If Dragonfly and tzN specified, return. Otherwise, fall through.
 1382 #endif
 1383 #if defined(XOSVIEW_OPENBSD) || defined(XOSVIEW_DFBSD)
 1384     /* Adapted from systat. */
 1385     // All kinds of sensors are read with sysctl. We have to go through them
 1386     // to find the required device and value. Parameter 'name' is the device
 1387     // name and 'valname' consists of type and sensor index (e.g. it0.temp1).
 1388     //  Values are transformed to suitable units.
 1389     int index = -1;
 1390     struct sensordev sd;
 1391     struct sensor s;
 1392 
 1393     for (int dev = 0; dev < 1024; dev++) {  // go through all sensor devices
 1394         mib_sen[2] = dev;
 1395         size = sizeof(sd);
 1396         if ( sysctl(mib_sen, 3, &sd, &size, NULL, 0) < 0 ) {
 1397             if (errno == ENOENT)
 1398                 break;  // no more devices
 1399             if (errno == ENXIO)
 1400                 continue;  // no device with this mib
 1401             err(EX_OSERR, "sysctl hw.sensors.%d failed", dev);
 1402         }
 1403         if ( strncmp(sd.xname, name, sizeof(name)) )
 1404             continue;  // sensor name does not match
 1405 
 1406         for (int t = 0; t < SENSOR_MAX_TYPES; t++) {
 1407             if ( strncmp(sensor_type_s[t], valname, strlen(sensor_type_s[t])) )
 1408                 continue;  // wrong type
 1409             mib_sen[3] = t;
 1410             sscanf(valname, "%[^0-9]%d", dummy, &index);
 1411             if (index < sd.maxnumt[t]) {
 1412                 mib_sen[4] = index;
 1413                 size = sizeof(s);
 1414                 if ( sysctl(mib_sen, 5, &s, &size, NULL, 0) < 0 ) {
 1415                     if (errno != ENOENT)
 1416                         err(EX_OSERR, "sysctl hw.sensors.%d.%d.%d failed", dev, t, index);
 1417                     continue;  // no more sensors
 1418                 }
 1419                 if (s.flags & SENSOR_FINVALID)
 1420                     continue;
 1421                 switch (t) {
 1422                 case SENSOR_TEMP:
 1423                     *value = (float)(s.value - 273150000) / 1000000.0;
 1424                     if (unit)
 1425                         strcpy(unit, "\260C");
 1426                     break;
 1427                 case SENSOR_FANRPM:
 1428                     *value = (float)s.value;
 1429                     if (unit)
 1430                         strcpy(unit, "RPM");
 1431                     break;
 1432                 case SENSOR_VOLTS_DC:
 1433                 case SENSOR_VOLTS_AC:
 1434                     *value = (float)s.value / 1000000.0;
 1435                     if (unit)
 1436                         strcpy(unit, "V");
 1437                     break;
 1438                 case SENSOR_OHMS:
 1439                     *value = (float)s.value;
 1440                     if (unit)
 1441                         strcpy(unit, "Ohm");
 1442                     break;
 1443                 case SENSOR_WATTS:
 1444                     *value = (float)s.value / 1000000.0;
 1445                     if (unit)
 1446                         strcpy(unit, "W");
 1447                     break;
 1448                 case SENSOR_AMPS:
 1449                     *value = (float)s.value / 1000000.0;
 1450                     if (unit)
 1451                         strcpy(unit, "A");
 1452                     break;
 1453                 case SENSOR_WATTHOUR:
 1454                     *value = (float)s.value / 1000000.0;
 1455                     if (unit)
 1456                         strcpy(unit, "Wh");
 1457                     break;
 1458                 case SENSOR_AMPHOUR:
 1459                     *value = (float)s.value / 1000000.0;
 1460                     if (unit)
 1461                         strcpy(unit, "Ah");
 1462                     break;
 1463                 case SENSOR_PERCENT:
 1464                     *value = (float)s.value / 1000.0;
 1465                     if (unit)
 1466                         strcpy(unit, "%");
 1467                     break;
 1468                 case SENSOR_LUX:
 1469                     *value = (float)s.value / 1000000.0;
 1470                     if (unit)
 1471                         strcpy(unit, "lx");
 1472                     break;
 1473                 case SENSOR_TIMEDELTA:
 1474                     *value = (float)s.value / 1000000000.0;
 1475                     if (unit)
 1476                         strcpy(unit, "s");
 1477                     break;
 1478 #if defined(XOSVIEW_OPENBSD)
 1479                 case SENSOR_HUMIDITY:
 1480                     *value = (float)s.value / 1000.0;
 1481                     if (unit)
 1482                         strcpy(unit, "%");
 1483                     break;
 1484                 case SENSOR_FREQ:
 1485                     *value = (float)s.value / 1000000.0;
 1486                     if (unit)
 1487                         strcpy(unit, "Hz");
 1488                     break;
 1489                 case SENSOR_ANGLE:
 1490                     *value = (float)s.value / 1000000.0;
 1491                     if (unit)
 1492                         strcpy(unit, "\260");
 1493                     break;
 1494 #if OpenBSD > 201211
 1495                 case SENSOR_DISTANCE:
 1496                     *value = (float)s.value / 1000000.0;
 1497                     if (unit)
 1498                         strcpy(unit, "m");
 1499                     break;
 1500                 case SENSOR_PRESSURE:
 1501                     *value = (float)s.value / 1000.0;
 1502                     if (unit)
 1503                         strcpy(unit, "Pa");
 1504                     break;
 1505                 case SENSOR_ACCEL:
 1506                     *value = (float)s.value / 1000000.0;
 1507                     if (unit)
 1508                         strcpy(unit, "m\\/s\262"); // m/s²
 1509                     break;
 1510 #endif
 1511 #endif
 1512                 case SENSOR_INDICATOR:
 1513                 case SENSOR_INTEGER:
 1514                 case SENSOR_DRIVE:
 1515                 default:
 1516                     *value = (float)s.value;
 1517                     break;
 1518                 }
 1519             }
 1520         }
 1521     }
 1522 #endif
 1523 #endif
 1524 }
 1525 
 1526 
 1527 //  ---------------------- Battery Meter stuff ---------------------------------
 1528 
 1529 bool
 1530 BSDHasBattery() {
 1531 #if defined(XOSVIEW_NETBSD)
 1532     int fd;
 1533     prop_dictionary_t pdict;
 1534     prop_object_t pobj;
 1535 
 1536     if ( (fd = open(_PATH_SYSMON, O_RDONLY)) == -1 )
 1537         return false;
 1538     if ( prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &pdict) )
 1539         err(EX_OSERR, "Could not get sensor dictionary");
 1540     if ( close(fd) == -1 )
 1541         err(EX_OSERR, "Could not close %s", _PATH_SYSMON);
 1542 
 1543     if ( prop_dictionary_count(pdict) == 0 )
 1544         return false;
 1545     pobj = prop_dictionary_get(pdict, "acpibat0"); // just check for 1st battery
 1546     if ( prop_object_type(pobj) != PROP_TYPE_ARRAY )
 1547         return false;
 1548     return true;
 1549 #elif defined(XOSVIEW_OPENBSD)
 1550     // check if we can get full capacity of the 1st battery
 1551     float val = -1.0;
 1552     BSDGetSensor("acpibat0", "amphour0", &val);
 1553     if (val < 0)
 1554         return false;
 1555     return true;
 1556 #else // XOSVIEW_FREEBSD || XOSVIEW_DFBSD
 1557     int fd;
 1558     if ( (fd = open(ACPIDEV, O_RDONLY)) == -1 ) {
 1559         // No ACPI -> try APM
 1560         if ( (fd = open(APMDEV, O_RDONLY)) == -1 )
 1561             return false;
 1562         struct apm_info aip;
 1563         if ( ioctl(fd, APMIO_GETINFO, &aip) == -1 )
 1564             return false;
 1565         if ( close(fd) == -1 )
 1566             err(EX_OSERR, "Could not close %s", APMDEV);
 1567         if (aip.ai_batt_stat == 0xff || aip.ai_batt_life == 0xff)
 1568             return false;
 1569         return true;
 1570     }
 1571 
 1572     union acpi_battery_ioctl_arg battio;
 1573     battio.unit = ACPI_BATTERY_ALL_UNITS;
 1574     if ( ioctl(fd, ACPIIO_BATT_GET_BATTINFO, &battio) == -1 )
 1575         return false;
 1576     if ( close(fd) == -1 )
 1577         err(EX_OSERR, "Could not close %s", ACPIDEV);
 1578     return ( battio.battinfo.state != ACPI_BATT_STAT_NOT_PRESENT );
 1579 #endif
 1580 }
 1581 
 1582 void
 1583 BSDGetBatteryInfo(int *remaining, unsigned int *state) {
 1584     *state = XOSVIEW_BATT_NONE;
 1585 #if defined(XOSVIEW_NETBSD) || defined(XOSVIEW_OPENBSD)
 1586     int batteries = 0;
 1587 #if defined(XOSVIEW_NETBSD)
 1588     /* Again adapted from envstat. */
 1589     // All kinds of sensors are read with libprop. We have to go through them
 1590     // to find the batteries. We need capacity, charge, presence, charging
 1591     // status and discharge rate for each battery for the calculations.
 1592     // For simplicity, assume all batteries have the same
 1593     // charge/discharge status.
 1594     int fd;
 1595     int total_capacity = 0, total_charge = 0, total_low = 0, total_crit = 0;
 1596     const char *name = NULL;
 1597     prop_dictionary_t pdict;
 1598     prop_object_t pobj, pobj1;
 1599     prop_object_iterator_t piter, piter2;
 1600     prop_array_t parray;
 1601 
 1602     if ( (fd = open(_PATH_SYSMON, O_RDONLY)) == -1 ) {
 1603         warn("Could not open %s", _PATH_SYSMON);
 1604         return;  // this seems to happen occasionally, so only warn
 1605     }
 1606     if ( prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &pdict) )
 1607         err(EX_OSERR, "Could not get sensor dictionary");
 1608     if ( close(fd) == -1 )
 1609         err(EX_OSERR, "Could not close %s", _PATH_SYSMON);
 1610 
 1611     if ( prop_dictionary_count(pdict) == 0 ) {
 1612         warn("No sensors found");
 1613         return;
 1614     }
 1615     if ( !(piter = prop_dictionary_iterator(pdict)) )
 1616         err(EX_OSERR, "Could not get sensor iterator");
 1617 
 1618     while ( (pobj = prop_object_iterator_next(piter)) ) {
 1619         int present = 0, capacity = 0, charge = 0, low = 0, crit = 0;
 1620         name = prop_dictionary_keysym_cstring_nocopy((prop_dictionary_keysym_t)pobj);
 1621         if ( strncmp(name, "acpibat", 7) )
 1622             continue;
 1623         parray = (prop_array_t)prop_dictionary_get_keysym(pdict, (prop_dictionary_keysym_t)pobj);
 1624         if ( prop_object_type(parray) != PROP_TYPE_ARRAY )
 1625             continue;
 1626         if ( !(piter2 = prop_array_iterator(parray)) )
 1627             err(EX_OSERR, "Could not get sensor iterator");
 1628 
 1629         while ( (pobj = prop_object_iterator_next(piter2)) ) {
 1630             if ( !(pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "state")) )
 1631                 continue;
 1632             if ( prop_string_equals_cstring((prop_string_t)pobj1, "invalid") ||
 1633                  prop_string_equals_cstring((prop_string_t)pobj1, "unknown") )
 1634                 continue; // skip sensors without valid data
 1635             if ( !(pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "description")) )
 1636                 continue;
 1637             name = prop_string_cstring_nocopy((prop_string_t)pobj1);
 1638             if ( strncmp(name, "present", 7) == 0 ) { // is battery present
 1639                 if ( (pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "cur-value")) )
 1640                     present = prop_number_integer_value((prop_number_t)pobj1);
 1641             }
 1642             else if ( strncmp(name, "design cap", 10) == 0 ) { // get full capacity
 1643                 if ( (pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "cur-value")) )
 1644                     capacity = prop_number_integer_value((prop_number_t)pobj1);
 1645             }
 1646             else if ( strncmp(name, "charge", 7) == 0 ) { // get present charge, low and critical levels
 1647                 if ( (pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "cur-value")) )
 1648                     charge = prop_number_integer_value((prop_number_t)pobj1);
 1649                 if ( (pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "warning-capacity")) )
 1650                     low = prop_number_integer_value((prop_number_t)pobj1);
 1651                 if ( (pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "critical-capacity")) )
 1652                     crit = prop_number_integer_value((prop_number_t)pobj1);
 1653             }
 1654             else if ( strncmp(name, "charging", 8) == 0 ) { // charging or not?
 1655                 if ( (pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "cur-value")) )
 1656                     if ( prop_number_integer_value((prop_number_t)pobj1) )
 1657                         *state |= XOSVIEW_BATT_CHARGING;
 1658             }
 1659             else if ( strncmp(name, "discharge rate", 14) == 0 ) { // discharging or not?
 1660                 if ( (pobj1 = prop_dictionary_get((prop_dictionary_t)pobj, "cur-value")) )
 1661                     if ( prop_number_integer_value((prop_number_t)pobj1) )
 1662                         *state |= XOSVIEW_BATT_DISCHARGING;
 1663             }
 1664         }
 1665         if (present) {
 1666             total_capacity += capacity;
 1667             total_charge += charge;
 1668             total_low += low;
 1669             total_crit += crit;
 1670             batteries++;
 1671         }
 1672         prop_object_iterator_release(piter2);
 1673     }
 1674     prop_object_iterator_release(piter);
 1675     prop_object_release(pdict);
 1676 #else // XOSVIEW_OPENBSD
 1677     float total_capacity = 0, total_charge = 0, total_low = 0, total_crit = 0;
 1678     char battery[16];
 1679     while (batteries < 1024) {
 1680         float val = -1.0;
 1681         snprintf(battery, 15, "acpibat%d", batteries);
 1682         BSDGetSensor(battery, "amphour0", &val); // full capacity
 1683         if (val < 0) // no more batteries
 1684             break;
 1685         batteries++;
 1686         total_capacity += val;
 1687         BSDGetSensor(battery, "amphour1", &val); // warning capacity
 1688         total_low += val;
 1689         BSDGetSensor(battery, "amphour2", &val); // low capacity
 1690         total_crit += val;
 1691         BSDGetSensor(battery, "amphour3", &val); // remaining
 1692         total_charge += val;
 1693         BSDGetSensor(battery, "raw0", &val); // state
 1694         if ((int)val == 1)
 1695             *state |= XOSVIEW_BATT_DISCHARGING;
 1696         else if ((int)val == 2)
 1697             *state |= XOSVIEW_BATT_CHARGING;
 1698         // there's also 0 state for idle/full
 1699     }
 1700 #endif
 1701     if (batteries == 0) { // all batteries are off
 1702         *state = XOSVIEW_BATT_NONE;
 1703         *remaining = 0;
 1704         return;
 1705     }
 1706     *remaining = 100 * total_charge / total_capacity;
 1707     if ( !(*state & XOSVIEW_BATT_CHARGING) &&
 1708          !(*state & XOSVIEW_BATT_DISCHARGING) )
 1709         *state |= XOSVIEW_BATT_FULL;  // it's full when not charging nor discharging
 1710     if (total_capacity < total_low)
 1711         *state |= XOSVIEW_BATT_LOW;
 1712     if (total_capacity < total_crit)
 1713         *state |= XOSVIEW_BATT_CRITICAL;
 1714 #else // XOSVIEW_FREEBSD || XOSVIEW_DFBSD
 1715     /* Adapted from acpiconf and apm. */
 1716     int fd;
 1717     if ( (fd = open(ACPIDEV, O_RDONLY)) == -1 ) {
 1718         // No ACPI -> try APM
 1719         if ( (fd = open(APMDEV, O_RDONLY)) == -1 )
 1720             err(EX_OSFILE, "could not open %s or %s", ACPIDEV, APMDEV);
 1721         struct apm_info aip;
 1722         if ( ioctl(fd, APMIO_GETINFO, &aip) == -1 )
 1723             err(EX_IOERR, "failed to get APM battery info");
 1724         if ( close(fd) == -1 )
 1725             err(EX_OSERR, "Could not close %s", APMDEV);
 1726         if (aip.ai_batt_life <= 100)
 1727             *remaining = aip.ai_batt_life; // only 0-100 are valid values
 1728         else
 1729             *remaining = 0;
 1730         if (aip.ai_batt_stat == 0)
 1731             *state |= XOSVIEW_BATT_FULL;
 1732         else if (aip.ai_batt_stat == 1)
 1733             *state |= XOSVIEW_BATT_LOW;
 1734         else if (aip.ai_batt_stat == 2)
 1735             *state |= XOSVIEW_BATT_CRITICAL;
 1736         else if (aip.ai_batt_stat == 3)
 1737             *state |= XOSVIEW_BATT_CHARGING;
 1738         else
 1739             *state = XOSVIEW_BATT_NONE;
 1740         return;
 1741     }
 1742     // ACPI
 1743     union acpi_battery_ioctl_arg battio;
 1744     battio.unit = ACPI_BATTERY_ALL_UNITS;
 1745     if ( ioctl(fd, ACPIIO_BATT_GET_BATTINFO, &battio) == -1 )
 1746         err(EX_IOERR, "failed to get ACPI battery info");
 1747     if ( close(fd) == -1 )
 1748         err(EX_OSERR, "Could not close %s", ACPIDEV);
 1749     *remaining = battio.battinfo.cap;
 1750     if (battio.battinfo.state != ACPI_BATT_STAT_NOT_PRESENT) {
 1751         if (battio.battinfo.state == 0)
 1752             *state |= XOSVIEW_BATT_FULL;
 1753         if (battio.battinfo.state & ACPI_BATT_STAT_CRITICAL)
 1754             *state |= XOSVIEW_BATT_CRITICAL;
 1755         if (battio.battinfo.state & ACPI_BATT_STAT_DISCHARG)
 1756             *state |= XOSVIEW_BATT_DISCHARGING;
 1757         if (battio.battinfo.state & ACPI_BATT_STAT_CHARGING)
 1758             *state |= XOSVIEW_BATT_CHARGING;
 1759     }
 1760 #endif
 1761 }