next: ...

This commit is contained in:
Alastair Poole 2021-05-16 12:25:14 +01:00
parent 3138afa782
commit 17ce819dc6
8 changed files with 2011 additions and 2 deletions

View File

@ -1 +1,73 @@
/*
* Copyright (c) 2018 Alastair Roy Poole <netstar@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"

View File

@ -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

678
src/bin/next/machine/cpu.x Normal file
View File

@ -0,0 +1,678 @@
/*
* Copyright (c) 2018 Alastair Roy Poole <netstar@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
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2018 Alastair Roy Poole <netstar@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

View File

@ -0,0 +1,280 @@
/*
* Copyright (c) 2018 Alastair Roy Poole <netstar@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
}

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) 2018 Alastair Roy Poole <netstar@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
}

View File

@ -0,0 +1,563 @@
/*
* Copyright (c) 2018 Alastair Roy Poole <netstar@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);
}

15
src/bin/next/macros.h Normal file
View File

@ -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