"Fossies" - the Fresh Open Source Software Archive

Member "xosview-1.23/bsd/kernel.cc" (11 Jul 2020, 53518 Bytes) of package /linux/misc/xosview-1.23.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.22_vs_1.23.

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