netstar pushed a commit to branch master.

http://git.enlightenment.org/apps/evisum.git/commit/?id=17ce819dc6e74c1e15cd9d7ab63fff7ed5b92f35

commit 17ce819dc6e74c1e15cd9d7ab63fff7ed5b92f35
Author: Alastair Poole <nets...@gmail.com>
Date:   Sun May 16 12:25:14 2021 +0100

    next: ...
---
 src/bin/next/machine.c         |  72 +++++
 src/bin/next/machine.h         | 149 ++++++++-
 src/bin/next/machine/cpu.x     | 678 +++++++++++++++++++++++++++++++++++++++++
 src/bin/next/machine/machine.x |  86 ++++++
 src/bin/next/machine/memory.x  | 280 +++++++++++++++++
 src/bin/next/machine/network.x | 170 +++++++++++
 src/bin/next/machine/sensors.x | 563 ++++++++++++++++++++++++++++++++++
 src/bin/next/macros.h          |  15 +
 8 files changed, 2011 insertions(+), 2 deletions(-)

diff --git a/src/bin/next/machine.c b/src/bin/next/machine.c
index a1a4e5f..a0de59c 100644
--- a/src/bin/next/machine.c
+++ b/src/bin/next/machine.c
@@ -1 +1,73 @@
+/*
+ * Copyright (c) 2018 Alastair Roy Poole <nets...@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define _DEFAULT_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#if !defined(__linux__)
+# include <sys/sysctl.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <pthread.h>
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define __MacOS__
+# include <mach/mach.h>
+# include <mach/vm_statistics.h>
+# include <mach/mach_types.h>
+# include <mach/mach_init.h>
+# include <mach/mach_host.h>
+# include <net/if_mib.h>
+#endif
+
+#if defined(__OpenBSD__)
+# include <sys/sched.h>
+# include <sys/swap.h>
+# include <sys/mount.h>
+# include <sys/sensors.h>
+# include <net/if_types.h>
+# include <ifaddrs.h>
+#endif
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+# include <net/if_mib.h>
+# include <vm/vm_param.h>
+# include <dev/acpica/acpiio.h>
+# include <sys/resource.h>
+#endif
+
+#include "macros.h"
 #include "machine.h"
+#include "machine/machine.x"
+#include "machine/cpu.x"
+#include "machine/memory.x"
+#include "machine/sensors.x"
+#include "machine/network.x"
+
diff --git a/src/bin/next/machine.h b/src/bin/next/machine.h
index 1763df9..aae6045 100644
--- a/src/bin/next/machine.h
+++ b/src/bin/next/machine.h
@@ -1,5 +1,150 @@
-#ifndef MACHINE_H
-#define MACHINE_H
+#ifndef __MACHINE_H__
+#define __MACHINE_H__
 
+/* All functions and data types implementing these APIs have no additional
+ * system dependencies deliberately.
+ *
+ * See machine.c and the files includes in machine/ sub directory.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef struct
+{
+   unsigned long total;
+   unsigned long idle;
+   float         percent;
+} cpu_core_t;
+
+// Will anyone have more than 8 vdrm/video card devices?
+#define MEM_VIDEO_CARD_MAX 8
+
+typedef struct
+{
+   uint64_t total;
+   uint64_t used;
+} meminfo_video_t;
+
+typedef struct
+{
+   uint64_t total;
+   uint64_t used;
+   uint64_t cached;
+   uint64_t buffered;
+   uint64_t shared;
+   uint64_t swap_total;
+   uint64_t swap_used;
+
+   uint64_t zfs_arc_used;
+
+   uint64_t        video_count;
+   meminfo_video_t video[MEM_VIDEO_CARD_MAX];
+} meminfo_t;
+
+typedef struct
+{
+   char   *name;
+   char   *child_name;
+#if defined(__linux__)
+   char   *path;
+#elif defined(__OpenBSD__)
+   int     mibs[5];
+#endif
+   double  value;
+   bool    invalid;
+} sensor_t;
+
+typedef struct
+{
+   char   *name;
+   char   *vendor;
+   char   *model;
+   double  charge_full;
+   double  charge_current;
+   uint8_t percent;
+   bool    present;
+#if defined(__OpenBSD__)
+   int     mibs[5];
+#endif
+} bat_t;
+
+typedef struct
+{
+   bool     have_ac;
+   int      battery_count;
+
+   bat_t  **batteries;
+#if defined(__OpenBSD__)
+   int      mibs[5];
+#endif
+} power_t;
+
+typedef struct
+{
+   char name[255];
+   struct
+   {
+      uint64_t in;
+      uint64_t out;
+   } xfer;
+} net_iface_t;
+
+int
+system_cpu_online_count_get(void);
+
+int
+system_cpu_count_get(void);
+
+cpu_core_t **
+system_cpu_usage_get(int *ncpu);
+
+cpu_core_t **
+system_cpu_usage_delayed_get(int *ncpu, int usecs);
+
+cpu_core_t **
+system_cpu_state_get(int *ncpu);
+
+int
+system_cpu_frequency_get(void);
+
+int
+system_cpu_n_frequency_get(int n);
+
+int
+system_cpu_n_temperature_get(int n);
+
+int
+system_cpu_temperature_min_max_get(int *min, int *max);
+
+int
+system_cpu_frequency_min_max_get(int *min, int *max);
+
+void
+system_cpu_topology_get(int *ids, int ncpus);
+
+void
+system_memory_usage_get(meminfo_t *memory);
+
+sensor_t **
+system_sensors_thermal_get(int *count);
+
+void
+system_sensors_thermal_free(sensor_t **sensors, int count);
+
+int
+system_sensor_thermal_get(sensor_t *sensor);
+
+void
+system_sensor_thermal_free(sensor_t *sensor);
+
+void
+system_power_state_get(power_t *power);
+
+void
+system_power_state_free(power_t *power);
+
+net_iface_t **
+system_network_ifaces_get(int *n);
 
 #endif
diff --git a/src/bin/next/machine/cpu.x b/src/bin/next/machine/cpu.x
new file mode 100644
index 0000000..8478168
--- /dev/null
+++ b/src/bin/next/machine/cpu.x
@@ -0,0 +1,678 @@
+/*
+ * Copyright (c) 2018 Alastair Roy Poole <nets...@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(__OpenBSD__)
+# define CPU_STATES      6
+#else
+# define CPU_STATES      5
+#endif
+
+static int
+cpu_count(void)
+{
+   static int cores = 0;
+
+   if (cores != 0)
+     return cores;
+
+#if defined(__linux__)
+   char buf[4096];
+   FILE *f;
+   int line = 0;
+
+   f = fopen("/proc/stat", "r");
+   if (!f) return 0;
+
+   while (fgets(buf, sizeof(buf), f))
+     {
+        if (line)
+          {
+             if (!strncmp(buf, "cpu", 3))
+               cores++;
+             else
+               break;
+          }
+        line++;
+     }
+
+   fclose(f);
+#elif defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) || 
defined(__OpenBSD__)
+   size_t len;
+   int mib[2] = { CTL_HW, HW_NCPU };
+
+   len = sizeof(cores);
+   if (sysctl(mib, 2, &cores, &len, NULL, 0) < 0)
+     return 0;
+#endif
+   return cores;
+}
+
+int
+system_cpu_count_get(void)
+{
+   return cpu_count();
+}
+
+int
+system_cpu_online_count_get(void)
+{
+#if defined(__OpenBSD__)
+   static int cores = 0;
+
+   if (cores != 0) return cores;
+
+   size_t len;
+   int mib[2] = { CTL_HW, HW_NCPUONLINE };
+
+   len = sizeof(cores);
+   if (sysctl(mib, 2, &cores, &len, NULL, 0) < 0)
+     return cpu_count();
+
+   return cores;
+#else
+   return cpu_count();
+#endif
+}
+
+static void
+_cpu_state_get(cpu_core_t **cores, int ncpu)
+{
+   int diff_total, diff_idle;
+   double ratio, percent;
+   unsigned long total, idle, used;
+   cpu_core_t *core;
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+   size_t size;
+   int i, j;
+
+   if (!ncpu)
+     return;
+   size = sizeof(unsigned long) * (CPU_STATES * ncpu);
+   unsigned long cpu_times[ncpu][CPU_STATES];
+
+   if (sysctlbyname("kern.cp_times", cpu_times, &size, NULL, 0) < 0)
+     return;
+
+   for (i = 0; i < ncpu; i++) {
+        core = cores[i];
+        unsigned long *cpu = cpu_times[i];
+
+        total = 0;
+        for (j = CP_USER; j <= CP_IDLE; j++)
+          total += cpu[j];
+
+        idle = cpu[CP_IDLE];
+
+        diff_total = total - core->total;
+        diff_idle = idle - core->idle;
+        if (diff_total == 0) diff_total = 1;
+
+        ratio = diff_total / 100.0;
+        used = diff_total - diff_idle;
+        percent = used / ratio;
+
+        if (percent > 100) percent = 100;
+        else if (percent < 0)
+          percent = 0;
+
+        core->percent = percent;
+        core->total = total;
+        core->idle = idle;
+     }
+#elif defined(__OpenBSD__)
+   static struct cpustats cpu_times[CPU_STATES];
+   static int cpu_time_mib[] = { CTL_KERN, KERN_CPUSTATS, 0 };
+   size_t size;
+   int i, j;
+
+   memset(&cpu_times, 0, CPU_STATES * sizeof(struct cpustats));
+   if (!ncpu)
+     return;
+
+   for (i = 0; i < ncpu; i++) {
+        core = cores[i];
+        size = sizeof(struct cpustats);
+        cpu_time_mib[2] = i;
+        if (sysctl(cpu_time_mib, 3, &cpu_times[i], &size, NULL, 0) < 0)
+          return;
+
+        total = 0;
+        for (j = 0; j < CPU_STATES; j++)
+          total += cpu_times[i].cs_time[j];
+
+        idle = cpu_times[i].cs_time[CP_IDLE];
+
+        diff_total = total - core->total;
+        if (diff_total == 0) diff_total = 1;
+
+        diff_idle = idle - core->idle;
+        ratio = diff_total / 100.0;
+        used = diff_total - diff_idle;
+        percent = used / ratio;
+
+        if (percent > 100) percent = 100;
+        else if (percent < 0)
+          percent = 0;
+
+        core->percent = percent;
+        core->total = total;
+        core->idle = idle;
+     }
+#elif defined(__linux__)
+   char *buf, name[128];
+   int i;
+
+   buf = file_contents("/proc/stat");
+   if (!buf) return;
+
+   for (i = 0; i < ncpu; i++) {
+        core = cores[i];
+        snprintf(name, sizeof(name), "cpu%d", i);
+        char *line = strstr(buf, name);
+        if (line)
+          {
+             line = strchr(line, ' ') + 1;
+             unsigned long cpu_times[4] = { 0 };
+
+             if (4 != sscanf(line, "%lu %lu %lu %lu", &cpu_times[0],
+                   &cpu_times[1], &cpu_times[2], &cpu_times[3]))
+               return;
+
+             total = cpu_times[0] + cpu_times[1] + cpu_times[2] + cpu_times[3];
+             idle = cpu_times[3];
+             diff_total = total - core->total;
+             if (diff_total == 0) diff_total = 1;
+
+             diff_idle = idle - core->idle;
+             ratio = diff_total / 100.0;
+             used = diff_total - diff_idle;
+             percent = used / ratio;
+
+             if (percent > 100) percent = 100;
+             else if (percent < 0)
+               percent = 0;
+
+             core->percent = percent;
+             core->total = total;
+             core->idle = idle;
+          }
+     }
+   free(buf);
+#elif defined(__MacOS__)
+   mach_msg_type_number_t count;
+   processor_cpu_load_info_t load;
+   mach_port_t mach_port;
+   unsigned int cpu_count;
+   int i;
+
+   cpu_count = ncpu;
+
+   count = HOST_CPU_LOAD_INFO_COUNT;
+   mach_port = mach_host_self();
+   if (host_processor_info(mach_port, PROCESSOR_CPU_LOAD_INFO, &cpu_count,
+                (processor_info_array_t *)&load, &count) != KERN_SUCCESS)
+     exit(-1);
+
+   for (i = 0; i < ncpu; i++) {
+        core = cores[i];
+
+        total = load[i].cpu_ticks[CPU_STATE_USER] +
+           load[i].cpu_ticks[CPU_STATE_SYSTEM] +
+           load[i].cpu_ticks[CPU_STATE_IDLE] +
+           load[i].cpu_ticks[CPU_STATE_NICE];
+        idle = load[i].cpu_ticks[CPU_STATE_IDLE];
+
+        diff_total = total - core->total;
+        if (diff_total == 0) diff_total = 1;
+        diff_idle = idle - core->idle;
+        ratio = diff_total / 100.0;
+        used = diff_total - diff_idle;
+        percent = used / ratio;
+
+        if (percent > 100) percent = 100;
+        else if (percent < 0)
+          percent = 0;
+
+        core->percent = percent;
+        core->total = total;
+        core->idle = idle;
+     }
+#endif
+}
+
+cpu_core_t **
+system_cpu_state_get(int *ncpu)
+{
+   cpu_core_t **cores;
+   int i;
+
+   *ncpu = cpu_count();
+   cores = malloc((*ncpu) * sizeof(cpu_core_t *));
+   for (i = 0; i < *ncpu; i++)
+      cores[i] = calloc(1, sizeof(cpu_core_t));
+
+   _cpu_state_get(cores, *ncpu);
+
+   return cores;
+}
+
+cpu_core_t **
+system_cpu_usage_delayed_get(int *ncpu, int usecs)
+{
+   cpu_core_t **cores;
+   int i;
+
+   *ncpu = cpu_count();
+
+   cores = malloc((*ncpu) * sizeof(cpu_core_t *));
+
+   for (i = 0; i < *ncpu; i++)
+     cores[i] = calloc(1, sizeof(cpu_core_t));
+
+   _cpu_state_get(cores, *ncpu);
+   usleep(usecs);
+   _cpu_state_get(cores, *ncpu);
+
+   return cores;
+}
+
+cpu_core_t **
+system_cpu_usage_get(int *ncpu)
+{
+   return system_cpu_usage_delayed_get(ncpu, 1000000);
+}
+
+static int  _cpu_temp_min = 0;
+static int  _cpu_temp_max = 100;
+static char _core_temps[256][512];
+static char _hwmon_path[256];
+
+int
+_cpu_n_temperature_read(int n)
+{
+   int temp = -1;
+#if defined(__linux__)
+
+   char *b = file_contents(_core_temps[n]);
+   if (b)
+     {
+        temp = atoi(b) / 1000;
+        free(b);
+     }
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+    int value;
+    size_t len = sizeof(value);
+
+    if (!_core_temps[n][0])
+      snprintf(_core_temps[n], sizeof(_core_temps[n]), 
"dev.cpu.%d.temperature", n);
+
+    if ((sysctlbyname(_core_temps[n], &value, &len, NULL, 0)) != -1)
+      {
+         temp = (value - 2732) / 10;
+      }
+#endif
+
+   return temp;
+}
+
+#if defined(__linux__)
+
+typedef struct _thermal_drv {
+   const char *name;
+   void (*init)(void);
+   char min;
+   char max;
+} thermal_drv;
+
+static void
+_coretemp_init(void)
+{
+   char buf[4096];
+   int cpu_count = system_cpu_count_get();
+
+   for (int j = 0; j < cpu_count; j++)
+     {
+        snprintf(buf, sizeof(buf), 
"/sys/devices/system/cpu/cpu%i/topology/core_id", j);
+        char *b = file_contents(buf);
+        if (b)
+          {
+             int core_id = atoi(b);
+             snprintf(_core_temps[j], sizeof(_core_temps[j]), 
"%s/temp%d_input", _hwmon_path, 2 + core_id);
+             free(b);
+          }
+     }
+}
+
+static void
+_generic_init(void)
+{
+   int i, cpu_count = system_cpu_count_get();
+
+   for (i = 0; i < cpu_count; i++)
+     snprintf(_core_temps[i], sizeof(_core_temps[i]), "%s/temp1_input", 
_hwmon_path);
+}
+
+#endif
+
+int
+system_cpu_n_temperature_get(int n)
+{
+#if defined(__linux__)
+   static int init = 0;
+
+   // This list is not exhastive by any means, if you have the
+   // hardware and can provide a better init, please do. WIP.
+   // Min max (where applicable)
+   thermal_drv drivers[] = {
+      { "coretemp", _coretemp_init, 0, 90 },    /* Intel Coretemp */
+      { "k10temp", _generic_init, 0, 90 },       /* AMD K10 */
+      { "cpu_thermal", _generic_init, 0, 90 },   /* BCM2835/BCM2711 (RPI3/4) */
+      { "cup", _generic_init, 0, 100 },          /* RK3399 */
+      { "soc_thermal", _generic_init, 0, 100 },  /* RK3326 */
+      { "cpu0_thermal", _generic_init, 0, 100 }, /* AllWinner A64 */
+      { "soc_dts0", _generic_init, 0, 90 },      /* Intel Baytrail */
+   };
+
+   if (!init)
+     {
+        char buf[4096];
+        struct dirent *dh;
+        DIR *dir;
+
+        memset(&_core_temps, 0, sizeof(_core_temps));
+        memset(&_hwmon_path, 0, sizeof(_hwmon_path));
+
+        dir = opendir("/sys/class/hwmon");
+        if (!dir)
+          {
+             init = 1;
+             return -1;
+          }
+
+        while ((dh = readdir(dir)) != NULL)
+          {
+             if (dh->d_name[0] == '.') continue;
+             snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s", dh->d_name);
+             char *link = realpath(buf, NULL);
+             if (!link) continue;
+
+             snprintf(buf, sizeof(buf), "%s/name", link);
+             char *b = file_contents(buf);
+             if (b)
+               {
+                  for (int i = 0; i < sizeof(drivers) / sizeof(thermal_drv); 
i++)
+                    {
+                       if (!strncmp(b, drivers[i].name, 
strlen(drivers[i].name)))
+                         {
+                            snprintf(_hwmon_path, sizeof(_hwmon_path), "%s", 
link);
+                            drivers[i].init();
+                         }
+                    }
+                  free(b);
+               }
+             free(link);
+             if (_hwmon_path[0]) break;
+          }
+
+        closedir(dir);
+        init = 1;
+     }
+
+   if (!_hwmon_path[0]) return -1;
+
+   return _cpu_n_temperature_read(n);
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+   static int init = 0;
+
+   if (!init)
+     {
+        memset(&_core_temps, 0, sizeof(_core_temps));
+        init = 1;
+     }
+
+    return _cpu_n_temperature_read(n);
+#endif
+   return -1;
+}
+
+int
+system_cpu_temperature_min_max_get(int *min, int *max)
+{
+
+   *min = _cpu_temp_min;
+   *max = _cpu_temp_max;
+
+   return 0;
+}
+
+int
+system_cpu_n_frequency_get(int n)
+{
+#if defined(__linux__)
+   int freq = -1;
+   FILE *f;
+   char buf[4096];
+   int tmp;
+
+   snprintf(buf, sizeof(buf), 
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", n);
+   f = fopen(buf, "r");
+   if (f)
+     {
+        if (fgets(buf, sizeof(buf), f))
+          {
+             tmp = strtol(buf, NULL, 10);
+             if (!((tmp == LONG_MIN || tmp == LONG_MAX) && errno == ERANGE))
+               freq = tmp;
+          }
+        fclose(f);
+        if (freq != -1) return freq;
+     }
+
+   return freq;
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
+   return system_cpu_frequency_get();
+#endif
+
+   return -1;
+}
+
+int
+system_cpu_frequency_min_max_get(int *min, int *max)
+{
+   int freq_min = 0x7fffffff, freq_max = 0;
+#if defined(__linux__)
+   char *s;
+
+   s = file_contents("/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq");
+   if (s)
+     {
+        freq_min = atoi(s);
+        free(s);
+        s = 
file_contents("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
+        if (s)
+          {
+             freq_max = atoi(s);
+             free(s);
+             *min = freq_min;
+             *max = freq_max;
+             if (freq_min < freq_max)
+               return 0;
+          }
+     }
+
+   s = 
file_contents("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies");
+   if (!s) return -1;
+
+   char *t = strtok(s, " ");
+   while (t)
+     {
+        int freq = atoi(t);
+        if (freq != 0)
+          {
+             if (freq > freq_max) freq_max = freq;
+             if (freq < freq_min) freq_min = freq;
+          }
+        t = strtok(NULL, " ");
+     }
+
+   free(s);
+
+   if (freq_min == 0x7fffffff || freq_max == 0) return -1;
+
+   *min = freq_min;
+   *max = freq_max;
+
+   return 0;
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+   char buf[4096];
+   size_t len = sizeof(buf);
+   char *t, *s;
+
+   if (sysctlbyname("dev.cpu.0.freq_levels", buf, &len, NULL, 0) != -1)
+     {
+        s = buf;
+        while (s)
+          {
+             t = strchr(s, '/');
+             if (!t) break;
+             *t = '\0';
+             int freq = atoi(s) * 1000;
+             if (freq > freq_max) freq_max = freq;
+             if (freq < freq_min) freq_min = freq;
+
+             s = strchr(t + 1, ' ');
+          }
+        if (freq_min == 0x7fffffff || freq_max == 0) return -1;
+
+        *min = freq_min;
+        *max = freq_max;
+
+        return 0;
+     }
+
+#elif defined(__OpenBSD__)
+   *min = 0;
+   *max = 100;
+
+   return 0;
+#endif
+   (void) freq_min; (void) freq_max;
+   return -1;
+}
+
+int
+system_cpu_frequency_get(void)
+{
+   int freq = -1;
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+   size_t len = sizeof(freq);
+   if (sysctlbyname("dev.cpu.0.freq", &freq, &len, NULL, 0) != -1)
+     freq *= 1000;
+#elif defined(__OpenBSD__)
+   int mib[2] = { CTL_HW, HW_CPUSPEED };
+   size_t len = sizeof(freq);
+   if (sysctl(mib, 2, &freq, &len, NULL, 0) != -1)
+     freq *= 1000;
+#elif defined(__linux__)
+   FILE *f;
+   char buf[4096];
+   int tmp;
+
+   f = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", "r");
+   if (f)
+     {
+        if (fgets(buf, sizeof(buf), f))
+          {
+             tmp = strtol(buf, NULL, 10);
+             if (!((tmp == LONG_MIN || tmp == LONG_MAX) && errno == ERANGE))
+               freq = tmp;
+          }
+        fclose(f);
+        if (freq != -1) return freq;
+     }
+
+   f = fopen("/proc/cpuinfo", "r");
+   if (!f) return freq;
+
+   while (fgets(buf, sizeof(buf), f))
+     {
+        if (!strncasecmp(buf, "cpu MHz", 7))
+          {
+             char *s = strchr(buf, ':') + 1;
+             tmp = strtol(s, NULL, 10);
+             if (!((tmp == LONG_MIN || tmp == LONG_MAX) && errno == ERANGE))
+               freq = tmp * 1000;
+             break;
+          }
+     }
+
+   fclose(f);
+#else
+
+#endif
+   return freq;
+}
+
+#if defined(__linux__)
+
+typedef struct {
+   short id;
+   short core_id;
+} core_top_t;
+
+static int
+_cmp(const void *a, const void *b)
+{
+   core_top_t *aa = (core_top_t *) a;
+   core_top_t *bb = (core_top_t *) b;
+
+   if (aa->core_id == bb->core_id) return 0;
+   else if (aa->core_id < bb->core_id) return -1;
+   else return 1;
+}
+
+#endif
+
+void
+system_cpu_topology_get(int *ids, int ncpu)
+{
+#if defined(__linux__)
+   char buf[4096];
+   core_top_t *cores = malloc(ncpu * sizeof(core_top_t));
+
+   for (int i = 0; i < ncpu; i++)
+     {
+        cores[i].id = i;
+        cores[i].core_id = i;
+        snprintf(buf, sizeof(buf), 
"/sys/devices/system/cpu/cpu%i/topology/core_id", i);
+        char *b = file_contents(buf);
+        if (b)
+          {
+             cores[i].core_id = atoi(b);
+             free(b);
+          }
+     }
+
+   qsort(cores, ncpu, sizeof(core_top_t), _cmp);
+
+   for (int i = 0; i < ncpu; i++)
+     {
+        ids[i] = cores[i].id;
+     }
+   free(cores);
+#endif
+}
diff --git a/src/bin/next/machine/machine.x b/src/bin/next/machine/machine.x
new file mode 100644
index 0000000..a68acf4
--- /dev/null
+++ b/src/bin/next/machine/machine.x
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018 Alastair Roy Poole <nets...@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdarg.h>
+
+char *
+file_contents(const char *path)
+{
+   FILE *f;
+   char *buf, *tmp;
+   size_t n = 1, len = 0;
+   const size_t block = 4096;
+
+   f = fopen(path, "r");
+   if (!f) return NULL;
+
+   buf = NULL;
+
+   while ((!feof(f)) && (!ferror(f)))
+     {
+        tmp = realloc(buf, ++n * (sizeof(char) * block) + 1);
+        if (!tmp) return NULL;
+        buf = tmp;
+        len += fread(buf + len, sizeof(char), block, f);
+     }
+
+   if (ferror(f))
+     {
+        free(buf);
+        fclose(f);
+        return NULL;
+     }
+   fclose(f);
+
+   buf[len] = 0;
+
+   return buf;
+}
+
+void
+strimmer(char *s)
+{
+   char *cp = s;
+
+   while (*cp)
+     {
+        if ((*cp == '\r') || (*cp == '\n'))
+          {
+             *cp = '\0';
+             return;
+          }
+        cp++;
+     }
+}
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+static long int
+_sysctlfromname(const char *name, void *mib, int depth, size_t *len)
+{
+   long int result;
+
+   if (sysctlnametomib(name, mib, len) < 0)
+     return -1;
+
+   *len = sizeof(result);
+   if (sysctl(mib, depth, &result, len, NULL, 0) < 0)
+     return -1;
+
+   return result;
+}
+
+#endif
+
diff --git a/src/bin/next/machine/memory.x b/src/bin/next/machine/memory.x
new file mode 100644
index 0000000..90fa1e2
--- /dev/null
+++ b/src/bin/next/machine/memory.x
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2018 Alastair Roy Poole <nets...@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(__linux__)
+static unsigned long
+_meminfo_parse_line(const char *line)
+{
+   char *p, *tok;
+
+   p = strchr(line, ':') + 1;
+   while (isspace(*p))
+     p++;
+   tok = strtok(p, " ");
+
+   return atoll(tok);
+}
+
+#endif
+
+void
+system_memory_usage_get(meminfo_t *memory)
+{
+   memset(memory, 0, sizeof(meminfo_t));
+#if defined(__linux__)
+   FILE *f;
+   unsigned long swap_free = 0, tmp_free = 0, tmp_slab = 0;
+   char line[256];
+   int i, fields = 0;
+
+   f = fopen("/proc/meminfo", "r");
+   if (!f) return;
+
+   while (fgets(line, sizeof(line), f) != NULL)
+     {
+        if (!strncmp("MemTotal:", line, 9))
+          {
+             memory->total = _meminfo_parse_line(line);
+             fields++;
+          }
+        else if (!strncmp("MemFree:", line, 8))
+          {
+             tmp_free = _meminfo_parse_line(line);
+             fields++;
+          }
+        else if (!strncmp("Cached:", line, 7))
+          {
+             memory->cached = _meminfo_parse_line(line);
+             fields++;
+          }
+        else if (!strncmp("Slab:", line, 5))
+          {
+             tmp_slab = _meminfo_parse_line(line);
+             fields++;
+          }
+        else if (!strncmp("Buffers:", line, 8))
+          {
+             memory->buffered = _meminfo_parse_line(line);
+             fields++;
+          }
+        else if (!strncmp("Shmem:", line, 6))
+          {
+             memory->shared = _meminfo_parse_line(line);
+             fields++;
+          }
+        else if (!strncmp("SwapTotal:", line, 10))
+          {
+             memory->swap_total = _meminfo_parse_line(line);
+             fields++;
+          }
+        else if (!strncmp("SwapFree:", line, 9))
+          {
+             swap_free = _meminfo_parse_line(line);
+             fields++;
+          }
+
+        if (fields >= 8)
+          break;
+     }
+
+   memory->cached += tmp_slab;
+   memory->used = memory->total - tmp_free - memory->cached - memory->buffered;
+   memory->swap_used = memory->swap_total - swap_free;
+
+   memory->total *= 1024;
+   memory->used *= 1024;
+   memory->buffered *= 1024;
+   memory->cached *= 1024;
+   memory->shared *= 1024;
+   memory->swap_total *= 1024;
+   memory->swap_used *= 1024;
+
+   fclose(f);
+   for (i = 0; i < MEM_VIDEO_CARD_MAX; i++)
+     {
+        struct stat st;
+        char buf[256];
+
+        // if no more drm devices exist - end of card list
+        snprintf(buf, sizeof(buf),
+                 "/sys/class/drm/card%i/device", i);
+        if (stat(buf, &st) != 0) break;
+        // not all drivers expose this, so video devices with no exposed video
+        // ram info will appear as 0 sized... much like swap.
+        snprintf(buf, sizeof(buf),
+                 "/sys/class/drm/card%i/device/mem_info_vram_total", i);
+        f = fopen(buf, "r");
+        if (f)
+          {
+             if (fgets(buf, sizeof(buf), f))
+               memory->video[i].total = atoll(buf);
+             fclose(f);
+          }
+        snprintf(buf, sizeof(buf),
+                 "/sys/class/drm/card%i/device/mem_info_vram_used", i);
+        f = fopen(buf, "r");
+        if (f)
+          {
+             if (fgets(buf, sizeof(buf), f))
+               memory->video[i].used = atoll(buf);
+             fclose(f);
+          }
+     }
+   memory->video_count = i;
+
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+   unsigned int free = 0, active = 0, inactive = 0, wired = 0;
+   unsigned int cached = 0, buffered = 0, zfs_arc = 0;
+   long int result = 0;
+   int page_size = getpagesize();
+   int mib[5] = { CTL_HW, HW_PHYSMEM, 0, 0, 0 };
+   size_t miblen, len = 0;
+
+   len = sizeof(memory->total);
+   if (sysctl(mib, 2, &memory->total, &len, NULL, 0) == -1)
+     return;
+
+   if ((active = _sysctlfromname("vm.stats.vm.v_active_count", mib, 4, &len)) 
== -1)
+     return;
+
+   if ((inactive = _sysctlfromname("vm.stats.vm.v_inactive_count", mib, 4, 
&len)) == -1)
+     return;
+
+   if ((wired = _sysctlfromname("vm.stats.vm.v_wire_count", mib, 4, &len)) == 
-1)
+     return;
+
+   if ((cached = _sysctlfromname("vm.stats.vm.v_cache_count", mib, 4, &len)) 
== -1)
+     return;
+
+   if ((free = _sysctlfromname("vm.stats.vm.v_free_count", mib, 4, &len)) == 
-1)
+     return;
+
+   if ((buffered = _sysctlfromname("vfs.bufspace", mib, 2, &len)) == -1)
+     return;
+
+   if ((zfs_arc = _sysctlfromname("kstat.zfs.misc.arcstats.c", mib, 5, &len)) 
!= -1)
+     memory->zfs_arc_used = zfs_arc;
+
+   memory->used = ((active + wired + cached) * page_size);
+   memory->buffered = buffered;
+   memory->cached = (cached * page_size);
+
+   result = _sysctlfromname("vm.swap_total", mib, 2, &len);
+   if (result == -1)
+     return;
+
+   memory->swap_total = result;
+
+   miblen = 3;
+   if (sysctlnametomib("vm.swap_info", mib, &miblen) == -1) return;
+
+   struct xswdev xsw;
+
+   for (int i = 0; ; i++) {
+        mib[miblen] = i;
+        len = sizeof(xsw);
+        if (sysctl(mib, miblen + 1, &xsw, &len, NULL, 0) == -1)
+          break;
+
+        memory->swap_used += (unsigned long) xsw.xsw_used * page_size;
+     }
+#elif defined(__OpenBSD__)
+   static int mib[] = { CTL_HW, HW_PHYSMEM64 };
+   static int bcstats_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT };
+   struct bcachestats bcstats;
+   static int uvmexp_mib[] = { CTL_VM, VM_UVMEXP };
+   struct uvmexp uvmexp;
+   int nswap, rnswap;
+   struct swapent *swdev = NULL;
+   size_t len;
+
+   len = sizeof(memory->total);
+   if (sysctl(mib, 2, &memory->total, &len, NULL, 0) == -1)
+     return;
+
+   len = sizeof(uvmexp);
+   if (sysctl(uvmexp_mib, 2, &uvmexp, &len, NULL, 0) == -1)
+     return;
+
+   len = sizeof(bcstats);
+   if (sysctl(bcstats_mib, 3, &bcstats, &len, NULL, 0) == -1)
+     return;
+
+   nswap = swapctl(SWAP_NSWAP, 0, 0);
+   if (nswap == 0)
+     goto swap_out;
+
+   swdev = calloc(nswap, sizeof(*swdev));
+   if (swdev == NULL)
+     goto swap_out;
+
+   rnswap = swapctl(SWAP_STATS, swdev, nswap);
+   if (rnswap == -1)
+     goto swap_out;
+
+   for (int i = 0; i < nswap; i++) {
+        if (swdev[i].se_flags & SWF_ENABLE)
+          {
+             memory->swap_used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
+             memory->swap_total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
+          }
+     }
+
+   memory->swap_total *= 1024;
+   memory->swap_used *= 1024;
+swap_out:
+   if (swdev)
+     free(swdev);
+   memory->cached = MEMSIZE(uvmexp.pagesize) * MEMSIZE(bcstats.numbufpages);
+   memory->used = MEMSIZE(uvmexp.pagesize) * MEMSIZE(uvmexp.active);
+   memory->buffered = MEMSIZE(uvmexp.pagesize) * (MEMSIZE(uvmexp.npages) - 
MEMSIZE(uvmexp.free));
+   memory->shared = MEMSIZE(uvmexp.pagesize) * MEMSIZE(uvmexp.wired);
+#elif defined(__MacOS__)
+   int mib[2] = { CTL_HW, HW_MEMSIZE };
+   size_t total;
+   vm_size_t page_size;
+   mach_port_t mach_port;
+   mach_msg_type_number_t count;
+   vm_statistics64_data_t vm_stats;
+   struct xsw_usage xsu;
+
+   size_t len = sizeof(size_t);
+   if (sysctl(mib, 2, &total, &len, NULL, 0) == -1)
+     return;
+   mach_port = mach_host_self();
+   count = sizeof(vm_stats) / sizeof(natural_t);
+
+   memory->total = total;
+
+   if (host_page_size(mach_port, &page_size) == KERN_SUCCESS &&
+       host_statistics64(mach_port, HOST_VM_INFO,
+                 (host_info64_t)&vm_stats, &count) == KERN_SUCCESS)
+     {
+        memory->used = (vm_stats.active_count + vm_stats.wire_count) * 
page_size;
+        memory->cached = vm_stats.active_count * page_size;
+        memory->shared = vm_stats.wire_count * page_size;
+        memory->buffered = vm_stats.inactive_count * page_size;
+     }
+
+   total = sizeof(xsu);
+   if (sysctlbyname("vm.swapusage", &xsu, &total, NULL, 0) != -1)
+     {
+        memory->swap_total = xsu.xsu_total;
+        memory->swap_used = xsu.xsu_used;
+     }
+#endif
+}
+
diff --git a/src/bin/next/machine/network.x b/src/bin/next/machine/network.x
new file mode 100644
index 0000000..dc4bd83
--- /dev/null
+++ b/src/bin/next/machine/network.x
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2018 Alastair Roy Poole <nets...@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__)
+static net_iface_t **
+_freebsd_generic_network_status(int *n)
+{
+   struct ifmibdata *ifmd;
+   size_t len;
+   int i, count;
+   len = sizeof(count);
+
+   if (sysctlbyname("net.link.generic.system.ifcount", &count, &len, NULL, 0) 
== -1)
+     return NULL;
+
+   ifmd = malloc(sizeof(struct ifmibdata));
+   if (!ifmd)
+     return NULL;
+
+   net_iface_t **ifaces = NULL;
+
+   for (i = 1; i <= count; i++) {
+        int mib[] = {
+           CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, i, IFDATA_GENERAL
+        };
+        len = sizeof(*ifmd);
+        if (sysctl(mib, 6, ifmd, &len, NULL, 0) < 0) continue;
+
+        net_iface_t *iface = malloc(sizeof(net_iface_t));
+        if (iface)
+          {
+             snprintf(iface->name, sizeof(iface->name), "%s",
+                      ifmd->ifmd_name);
+             iface->xfer.in = ifmd->ifmd_data.ifi_ibytes;
+             iface->xfer.out = ifmd->ifmd_data.ifi_obytes;
+             void *t = realloc(ifaces, (1 + 1 + *n) * sizeof(net_iface_t *));
+             ifaces = t;
+             ifaces[(*n)++] = iface;
+           }
+     }
+   free(ifmd);
+
+   return ifaces;
+}
+
+#endif
+
+#if defined(__OpenBSD__)
+static net_iface_t **
+_openbsd_generic_network_status(int *n)
+{
+   struct ifaddrs *interfaces, *ifa;
+
+   if (getifaddrs(&interfaces) < 0)
+     return NULL;
+
+   int sock = socket(AF_INET, SOCK_STREAM, 0);
+   if (sock < 0)
+     return NULL;
+
+   net_iface_t **ifaces = NULL;
+
+   for (ifa = interfaces; ifa; ifa = ifa->ifa_next) {
+        struct ifreq ifreq;
+        struct if_data if_data;
+
+        // TBC: Interfaces can have multiple addresses.
+        // Using this netmask check we should be obtaining
+        // the overall interface data across addresses.
+
+        if (ifa->ifa_netmask != 0) continue;
+
+        ifreq.ifr_data = (void *)&if_data;
+        strncpy(ifreq.ifr_name, ifa->ifa_name, IFNAMSIZ - 1);
+        if (ioctl(sock, SIOCGIFDATA, &ifreq) < 0)
+          return NULL;
+
+        const struct if_data *ifi = &if_data;
+        if (!LINK_STATE_IS_UP(ifi->ifi_link_state))
+          continue;
+
+        net_iface_t *iface = malloc(sizeof(net_iface_t));
+        if (iface)
+          {
+             snprintf(iface->name, sizeof(iface->name), "%s",
+                      ifa->ifa_name);
+             iface->xfer.in = ifi->ifi_ibytes;
+             iface->xfer.out = ifi->ifi_obytes;
+             void *t = realloc(ifaces, (1 + 1 + *n) * sizeof(net_iface_t *));
+             ifaces = t;
+             ifaces[(*n)++] = iface;
+          }
+     }
+   close(sock);
+
+   return ifaces;
+}
+
+#endif
+
+#if defined(__linux__)
+static net_iface_t **
+_linux_generic_network_status(int *n)
+{
+   FILE *f;
+   char buf[4096], name[128];
+   unsigned long int tmp_in, tmp_out, dummy;
+
+   f = fopen("/proc/net/dev", "r");
+   if (!f) return NULL;
+
+   net_iface_t **ifaces = NULL;
+
+   while (fgets(buf, sizeof(buf), f))
+     {
+        if (17 == sscanf(buf, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu "
+                         "%lu %lu %lu %lu %lu\n", name, &tmp_in, &dummy,
+                         &dummy, &dummy, &dummy, &dummy, &dummy, &dummy,
+                         &tmp_out, &dummy, &dummy, &dummy, &dummy, &dummy,
+                         &dummy, &dummy))
+          {
+             net_iface_t *iface = malloc(sizeof(net_iface_t));
+             if (iface)
+               {
+                  name[strlen(name)-1] = '\0';
+                  snprintf(iface->name, sizeof(iface->name), "%s", name);
+                  iface->xfer.in = tmp_in;
+                  iface->xfer.out = tmp_out;
+                  void *t = realloc(ifaces, (1 + 1 + *n) * sizeof(net_iface_t 
*));
+                  ifaces = t;
+                  ifaces[(*n)++] = iface;
+               }
+          }
+     }
+
+   fclose(f);
+
+   return ifaces;
+}
+
+#endif
+
+net_iface_t **
+system_network_ifaces_get(int *n)
+{
+   *n = 0;
+#if defined(__linux__)
+   return _linux_generic_network_status(n);
+#elif defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__)
+   return _freebsd_generic_network_status(n);
+#elif defined(__OpenBSD__)
+   return _openbsd_generic_network_status(n);
+#else
+   return NULL;
+#endif
+}
+
diff --git a/src/bin/next/machine/sensors.x b/src/bin/next/machine/sensors.x
new file mode 100644
index 0000000..e561475
--- /dev/null
+++ b/src/bin/next/machine/sensors.x
@@ -0,0 +1,563 @@
+/*
+ * Copyright (c) 2018 Alastair Roy Poole <nets...@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+void
+system_sensor_thermal_free(sensor_t *sensor)
+{
+   if (sensor->name)
+     free(sensor->name);
+   if (sensor->child_name)
+     free(sensor->child_name);
+#if defined(__linux__)
+   if (sensor->path)
+     free(sensor->path);
+#endif
+   free(sensor);
+}
+
+void
+system_sensors_thermal_free(sensor_t **sensors, int count)
+{
+   for (int i = 0; i < count; i++)
+     {
+        sensor_t *sensor = sensors[i];
+        system_sensor_thermal_free(sensor);
+     }
+   if (sensors)
+     free(sensors);
+}
+
+int
+system_sensor_thermal_get(sensor_t *sensor)
+{
+#if defined(__linux__)
+   char *d = file_contents(sensor->path);
+   if (d)
+     {
+        double val = atof(d);
+        if (val)
+          sensor->value = val /= 1000;
+        free(d);
+        return 1;
+     }
+   return 0;
+#elif defined(__OpenBSD__)
+   struct sensor snsr;
+   size_t slen = sizeof(struct sensor);
+
+   if (sysctl(sensor->mibs, 5, &snsr, &slen, NULL, 0) == -1) return 0;
+
+   sensor->value = (snsr.value - 273150000) / 1000000.0;
+
+   return 1;
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+   int value;
+   size_t len = sizeof(value);
+   if ((sysctlbyname(sensor->name, &value, &len, NULL, 0)) != -1)
+     {
+        sensor->value = (float) (value - 2732) / 10;
+        return 1;
+     }
+#endif
+   return 0;
+}
+
+sensor_t **
+system_sensors_thermal_get(int *sensor_count)
+{
+   sensor_t **sensors = NULL;
+   *sensor_count = 0;
+#if defined(__OpenBSD__)
+   sensor_t *sensor;
+   int mibs[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 };
+   int devn, n;
+   struct sensor snsr;
+   struct sensordev snsrdev;
+   size_t slen = sizeof(struct sensor);
+   size_t sdlen = sizeof(struct sensordev);
+
+   for (devn = 0;; devn++)
+      {
+        mibs[2] = devn;
+
+        if (sysctl(mibs, 3, &snsrdev, &sdlen, NULL, 0) == -1)
+          {
+             if (errno == ENOENT) break;
+             continue;
+          }
+
+        for (n = 0; n < snsrdev.maxnumt[SENSOR_TEMP]; n++) {
+             mibs[4] = n;
+
+             if (sysctl(mibs, 5, &snsr, &slen, NULL, 0) == -1)
+               continue;
+
+             if (slen > 0 && (snsr.flags & SENSOR_FINVALID) == 0)
+               break;
+          }
+
+        if (sysctl(mibs, 5, &snsr, &slen, NULL, 0) == -1)
+          continue;
+        if (snsr.type != SENSOR_TEMP)
+          continue;
+
+        void *t = realloc(sensors, (1 + *sensor_count) * sizeof(sensor_t *));
+        sensors = t;
+        sensors[(*sensor_count)++] = sensor = calloc(1, sizeof(sensor_t));
+        sensor->name = strdup(snsrdev.xname);
+        sensor->value = (snsr.value - 273150000) / 1000000.0; // (uK -> C)
+        memcpy(sensor->mibs, &mibs, sizeof(mibs));
+     }
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+   sensor_t *sensor;
+   int value;
+   char buf[256];
+   size_t len = sizeof(value);
+
+   if ((sysctlbyname("hw.acpi.thermal.tz0.temperature", &value, &len, NULL, 
0)) != -1)
+     {
+        void *t = realloc(sensors, (1 + *sensor_count) * sizeof(sensor_t *));
+        sensors = t;
+        sensors[(*sensor_count)++] = sensor = calloc(1, sizeof(sensor_t));
+        sensor->name = strdup("hw.acpi.thermal.tz0.temperature");
+        sensor->value = (float) (value -  2732) / 10;
+     }
+
+   int n = system_cpu_count_get();
+
+   for (int i = 0; i < n; i++)
+     {
+        len = sizeof(value);
+        snprintf(buf, sizeof(buf), "dev.cpu.%i.temperature", i);
+        if ((sysctlbyname(buf, &value, &len, NULL, 0)) != -1)
+          {
+             void *t = realloc(sensors, (1 + *sensor_count) * sizeof(sensor_t 
*));
+             sensors = t;
+             sensors[(*sensor_count)++] = sensor = calloc(1, sizeof(sensor_t));
+             sensor->name = strdup(buf);
+             sensor->value = (float) (value - 2732) / 10;
+          }
+     }
+
+#elif defined(__linux__)
+   sensor_t *sensor;
+   DIR *dir;
+   struct dirent *dh;
+   char buf[4096];
+   int seen[128];
+
+   dir = opendir("/sys/class/hwmon");
+   if (!dir) return NULL;
+
+   while ((dh = readdir(dir)) != NULL)
+     {
+        struct dirent **names = NULL;
+
+        snprintf(buf, sizeof(buf), "/sys/class/hwmon/%s", dh->d_name);
+        char *link = realpath(buf, NULL);
+        if (!link) continue;
+
+        int n = scandir(link, &names, 0, alphasort);
+        if (n < 0) continue;
+
+        int idx = 0;
+        memset(&seen, 0, sizeof(seen));
+
+        for (int i = 0; i < n; i++)
+          {
+             if (!strncmp(names[i]->d_name, "temp", 4))
+               {
+                  int id = atoi(names[i]->d_name + 4);
+                  if ((!id) || (id > sizeof(seen)))
+                    {
+                       free(names[i]);
+                       continue;
+                    }
+
+                  int found = 0;
+
+                  for (int j = 0; seen[j] != 0 && j < sizeof(seen); j++)
+                     if (seen[j] == id) found = 1;
+
+                  if (found)
+                    {
+                       free(names[i]);
+                       continue;
+                    }
+
+                  void *t = realloc(sensors, (1 + (*sensor_count)) * 
sizeof(sensor_t *));
+                  sensors = t;
+                  sensors[(*sensor_count)++] = sensor = calloc(1, 
sizeof(sensor_t));
+
+                  snprintf(buf, sizeof(buf), "%s/name", link);
+                  sensor->name = file_contents(buf);
+                  if (sensor->name)
+                    strimmer(sensor->name);
+
+                  snprintf(buf, sizeof(buf), "%s/temp%d_label", link, id);
+                  sensor->child_name = file_contents(buf);
+                  if (sensor->child_name)
+                    strimmer(sensor->child_name);
+
+                  snprintf(buf, sizeof(buf), "%s/temp%d_input", link, id);
+                  sensor->path = strdup(buf);
+                  char *d = file_contents(buf);
+                  if (d)
+                    {
+                       sensor->value = atoi(d);
+                       if (sensor->value) sensor->value /= 1000;
+                       free(d);
+                    }
+                  seen[idx++] = id;
+               }
+             free(names[i]);
+          }
+
+        free(names);
+        free(link);
+     }
+
+   closedir(dir);
+
+#elif defined(__MacOS__)
+#endif
+   return sensors;
+}
+
+static int
+_power_battery_count_get(power_t *power)
+{
+#if defined(__OpenBSD__)
+   struct sensordev snsrdev;
+   size_t sdlen = sizeof(struct sensordev);
+   int mibs[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 };
+   int i, devn;
+
+   for (devn = 0;; devn++) {
+        mibs[2] = devn;
+        if (sysctl(mibs, 3, &snsrdev, &sdlen, NULL, 0) == -1)
+          {
+             if (errno == ENXIO)
+               continue;
+             if (errno == ENOENT)
+               break;
+          }
+
+        if (!strncmp(snsrdev.xname, "acpibat", 7))
+          {
+             i = power->battery_count;
+
+             void *t = realloc(power->batteries, 1 +
+                               power->battery_count++ * sizeof(bat_t **));
+             power->batteries = t;
+             power->batteries[i] = calloc(1, sizeof(bat_t));
+             power->batteries[i]->name = strdup(snsrdev.xname);
+             power->batteries[i]->model = strdup("Unknown");
+             power->batteries[i]->vendor = strdup("Unknown");
+             power->batteries[i]->present = true;
+             power->batteries[i]->mibs[0] = mibs[0];
+             power->batteries[i]->mibs[1] = mibs[1];
+             power->batteries[i]->mibs[2] = mibs[2];
+          }
+        if (!strcmp("acpiac0", snsrdev.xname))
+          {
+             power->mibs[0] = mibs[0];
+             power->mibs[1] = mibs[1];
+             power->mibs[2] = mibs[2];
+          }
+     }
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+   int n_units, fd;
+   char name[256];
+
+   fd = open("/dev/acpi", O_RDONLY);
+   if (fd != -1)
+     {
+        if (ioctl(fd, ACPIIO_BATT_GET_UNITS, &n_units) != -1)
+          power->battery_count = n_units;
+        close(fd);
+     }
+
+   power->batteries = malloc(power->battery_count * sizeof(bat_t **));
+   for (int i = 0; i < power->battery_count; i++) {
+        power->batteries[i] = calloc(1, sizeof(bat_t));
+        snprintf(name, sizeof(name), "hw.acpi.battery.%i", i);
+        power->batteries[i]->name = strdup(name);
+        power->batteries[i]->present = true;
+     }
+#elif defined(__linux__)
+   char *type;
+   struct dirent **names;
+   char *buf;
+   char path[PATH_MAX];
+   int i, n, id;
+
+   n = scandir("/sys/class/power_supply", &names, 0, alphasort);
+   if (n < 0) return power->battery_count;
+
+   for (i = 0; i < n; i++) {
+        snprintf(path, sizeof(path), "/sys/class/power_supply/%s/type", 
names[i]->d_name);
+        type = file_contents(path);
+        if (type)
+          {
+             if (!strncmp(type, "Battery", 7))
+               {
+                  id = power->battery_count;
+                  void *t = realloc(power->batteries, (1 +
+                                    power->battery_count) * sizeof(bat_t **));
+                  power->batteries = t;
+                  power->batteries[id] = calloc(1, sizeof(bat_t));
+                  power->batteries[id]->name = strdup(names[i]->d_name);
+                  snprintf(path, sizeof(path), 
"/sys/class/power_supply/%s/manufacturer", names[i]->d_name);
+                  power->batteries[id]->vendor = file_contents(path);
+                  snprintf(path, sizeof(path), 
"/sys/class/power_supply/%s/model_name", names[i]->d_name);
+                  power->batteries[id]->model = file_contents(path);
+                  snprintf(path, sizeof(path), 
"/sys/class/power_supply/%s/present", names[i]->d_name);
+                  power->batteries[id]->present = 1;
+                  buf = file_contents(path);
+                  if (buf)
+                    {
+                       power->batteries[id]->present = atoi(buf);
+                       free(buf);
+                    }
+
+                  power->battery_count++;
+               }
+             free(type);
+          }
+
+        free(names[i]);
+     }
+
+   free(names);
+#endif
+
+   return power->battery_count;
+}
+
+static void
+_battery_state_get(power_t *power)
+{
+#if defined(__OpenBSD__)
+   int *mib;
+   double charge_full, charge_current;
+   size_t slen = sizeof(struct sensor);
+   struct sensor snsr;
+
+   for (int i = 0; i < power->battery_count; i++) {
+        charge_full = charge_current = 0;
+
+        mib = power->batteries[i]->mibs;
+        mib[3] = SENSOR_WATTHOUR;
+        mib[4] = 0;
+
+        if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1)
+          charge_full = (double)snsr.value;
+
+        mib[3] = SENSOR_WATTHOUR;
+        mib[4] = 3;
+
+        if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1)
+          charge_current = (double)snsr.value;
+
+        if (charge_current == 0 || charge_full == 0)
+          {
+             mib[3] = SENSOR_AMPHOUR;
+             mib[4] = 0;
+
+             if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1)
+               charge_full = (double)snsr.value;
+
+             mib[3] = SENSOR_AMPHOUR;
+             mib[4] = 3;
+
+             if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1)
+               charge_current = (double)snsr.value;
+          }
+
+        power->batteries[i]->charge_full = charge_full;
+        power->batteries[i]->charge_current = charge_current;
+     }
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+   int fd, i;
+   union acpi_battery_ioctl_arg battio;
+
+   if ((fd = open("/dev/acpi", O_RDONLY)) == -1) return;
+
+   for (i = 0; i < power->battery_count; i++) {
+        battio.unit = i;
+        if (ioctl(fd, ACPIIO_BATT_GET_BIX, &battio) != -1)
+          {
+             if (battio.bif.lfcap == 0)
+               power->batteries[i]->charge_full = battio.bif.dcap;
+             else
+               power->batteries[i]->charge_full = battio.bif.lfcap;
+          }
+        power->batteries[i]->vendor = strdup(battio.bix.oeminfo);
+        power->batteries[i]->model = strdup(battio.bix.model);
+        battio.unit = i;
+        if (ioctl(fd, ACPIIO_BATT_GET_BST, &battio) != -1)
+          power->batteries[i]->charge_current = battio.bst.cap;
+
+        if (battio.bst.state == ACPI_BATT_STAT_NOT_PRESENT)
+          power->batteries[i]->present = false;
+     }
+   close(fd);
+
+#elif defined(__linux__)
+   char path[PATH_MAX];
+   struct dirent *dh;
+   struct stat st;
+   DIR *dir;
+   char *buf, *link = NULL, *naming = NULL;
+
+
+   for (int i = 0; i < power->battery_count; i++) {
+        naming = NULL;
+
+        snprintf(path, sizeof(path), "/sys/class/power_supply/%s", 
power->batteries[i]->name);
+
+        if ((stat(path, &st) < 0) || (!S_ISDIR(st.st_mode)))
+          continue;
+
+        link = realpath(path, NULL);
+        if (!link) return;
+
+        dir = opendir(path);
+        if (!dir)
+          goto done;
+
+        while ((dh = readdir(dir)) != NULL)
+          {
+             char *e;
+             if (dh->d_name[0] == '.') continue;
+
+             if ((e = strstr(dh->d_name, "_full\0")))
+              {
+                 naming = strndup(dh->d_name, e - dh->d_name);
+                 break;
+              }
+          }
+        closedir(dir);
+
+        if (naming)
+          {
+             snprintf(path, sizeof(path), "%s/%s_full", link, naming);
+             buf = file_contents(path);
+             if (buf)
+               {
+                  power->batteries[i]->charge_full = atol(buf);
+                  free(buf);
+               }
+             snprintf(path, sizeof(path), "%s/%s_now", link, naming);
+             buf = file_contents(path);
+             if (buf)
+               {
+                  power->batteries[i]->charge_current = atol(buf);
+                  free(buf);
+               }
+             free(naming);
+          }
+        else
+          {
+             // Fallback to "coarse" representation.
+             snprintf(path, sizeof(path), "%s/capacity_level", link);
+             buf = file_contents(path);
+             if (buf)
+               {
+                  power->batteries[i]->charge_full = 100;
+                  if (buf[0] == 'F')
+                    power->batteries[i]->charge_current = 100;
+                  else if (buf[0] == 'H')
+                    power->batteries[i]->charge_current = 75;
+                  else if (buf[0] == 'N')
+                    power->batteries[i]->charge_current = 50;
+                  else if (buf[0] == 'L')
+                    power->batteries[i]->charge_current = 25;
+                  else if (buf[0] == 'C')
+                    power->batteries[i]->charge_current = 5;
+                  free(buf);
+               }
+          }
+     }
+done:
+   if (link) free(link);
+#endif
+}
+
+void
+system_power_state_get(power_t *power)
+{
+   int i;
+   memset(power, 0, sizeof(power_t));
+#if defined(__OpenBSD__)
+   struct sensor snsr;
+   size_t slen = sizeof(struct sensor);
+#endif
+
+   if (!_power_battery_count_get(power))
+     return;
+
+#if defined(__OpenBSD__)
+   power->mibs[3] = 9;
+   power->mibs[4] = 0;
+
+   if (sysctl(power->mibs, 5, &snsr, &slen, NULL, 0) != -1)
+     power->have_ac = (int)snsr.value;
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+   int val, fd;
+
+   fd = open("/dev/acpi", O_RDONLY);
+   if (fd != -1)
+     {
+        if (ioctl(fd, ACPIIO_ACAD_GET_STATUS, &val) != -1)
+          power->have_ac = val;
+        close(fd);
+     }
+#elif defined(__linux__)
+   char *buf = file_contents("/sys/class/power_supply/AC/online");
+   if (buf)
+     {
+        power->have_ac = atoi(buf);
+        free(buf);
+     }
+#endif
+
+   _battery_state_get(power);
+
+   for (i = 0; i < power->battery_count; i++) {
+        power->batteries[i]->percent = 100 *
+           (power->batteries[i]->charge_current /
+                                    power->batteries[i]->charge_full);
+     }
+}
+
+void
+system_power_state_free(power_t *power)
+{
+   for (int i = 0; i < power->battery_count; i++)
+     {
+        if (power->batteries[i]->name)
+          free(power->batteries[i]->name);
+        if (power->batteries[i]->model)
+          free(power->batteries[i]->model);
+        if (power->batteries[i]->vendor)
+          free(power->batteries[i]->vendor);
+        free(power->batteries[i]);
+     }
+   if (power->batteries)
+     free(power->batteries);
+}
diff --git a/src/bin/next/macros.h b/src/bin/next/macros.h
new file mode 100644
index 0000000..5e35042
--- /dev/null
+++ b/src/bin/next/macros.h
@@ -0,0 +1,15 @@
+#ifndef __MACROS_H__
+#define __MACROS_H__
+
+#define U64(n) (uint64_t) n
+#define I64(n) (int64_t)  n
+#define U32(n) (uint32_t) n
+#define I32(n) (int32_t)  n
+#define U16(n) (uint16_t) n
+#define I16(n) (int16_t)  n
+#define U8(n)  (uint8_t)  n
+#define I8(n)  (int8_t)   n
+
+#define MEMSIZE U64
+
+#endif

-- 


Reply via email to