Skip to content

Instantly share code, notes, and snippets.

@mischief
Created September 16, 2024 04:34
Show Gist options
  • Save mischief/e999ecccff5d3aaa2dada1512b76134f to your computer and use it in GitHub Desktop.
Save mischief/e999ecccff5d3aaa2dada1512b76134f to your computer and use it in GitHub Desktop.
hwmon
#include <err.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stddef.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <stdlib.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-event.h>
#include <systemd/sd-device.h>
const char busInterface[] = "com.mischief.hwmon1.Sensor";
static void
closep(int *fd)
{
if(fd && *fd >= 0)
close(*fd);
}
static void
freecharp(char **p)
{
if(p)
free(*p);
}
static size_t
snarf(int dirfd, const char *name, char **out)
{
__attribute__((cleanup(closep))) int fd = -1;
struct stat statubuf;
char buf[4096];
size_t n;
fd = openat(dirfd, name, O_RDONLY);
if(fd < 0)
return -errno;
if(fstat(fd, &statubuf) < 0)
return -errno;
n = read(fd, buf, sizeof(buf)-1);
if(n < 0)
return -errno;
buf[n] = 0;
*out = strndup(buf, n);
if(!*out)
return -errno;
return n;
}
enum {
SUBSYSTEM_THERMAL = 1,
SUBSYSTEM_HWMON = 2,
};
typedef struct thermal_sensor {
unsigned ref;
char *path;
char *buspath;
int dirfd;
int type;
char *name;
char *label;
int32_t temperature; // millic
sd_bus_slot *slot;
sd_device *dev;
sd_event_source *evt;
} thermal_sensor;
void
thermal_sensor_unref(thermal_sensor *sensor)
{
if(sensor && --sensor->ref == 0){
close(sensor->dirfd);
free(sensor->path);
free(sensor->buspath);
free(sensor->name);
free(sensor->label);
sd_bus_slot_unref(sensor->slot);
sd_device_unref(sensor->dev);
sd_event_source_unref(sensor->evt);
free(sensor);
}
}
void
thermal_sensor_unrefp(thermal_sensor **sensor)
{
if(*sensor)
thermal_sensor_unref(*sensor);
}
static int
thermal_sensor_new(sd_device *dev_ref, thermal_sensor **out)
{
__attribute__((cleanup(thermal_sensor_unrefp))) thermal_sensor *s = NULL;
const char *tmp;
int rv;
s = calloc(1, sizeof(*s));
if(!s)
return -ENOMEM;
s->ref = 1;
s->dev = sd_device_ref(dev_ref);
if((rv = sd_device_get_syspath(s->dev, &tmp)) < 0)
return rv;
if(!(s->path = strdup(tmp)))
return -errno;
if((rv = sd_device_get_sysname(s->dev, &tmp)) < 0)
return -rv;
if(!(s->name = strdup(tmp)))
return -errno;
if(asprintf(&s->buspath, "/com/github/mischief/hwmon1/%s", s->name) < 0)
return -errno;
if((rv = sd_device_get_subsystem(s->dev, &tmp)) < 0)
return rv;
if(strcmp(tmp, "thermal") == 0){
s->type = SUBSYSTEM_THERMAL;
} else if(strcmp(tmp, "hwmon") == 0){
s->type = SUBSYSTEM_HWMON;
}
s->dirfd = open(s->path, O_DIRECTORY|O_PATH);
if(s->dirfd < 0)
return -errno;
switch(s->type){
case SUBSYSTEM_THERMAL:
if((rv = snarf(s->dirfd, "type", &s->label)) < 0)
return rv;
break;
case SUBSYSTEM_HWMON:
if((rv = snarf(s->dirfd, "name", &s->label)) < 0)
return rv;
break;
default:
__builtin_unreachable();
}
*out = s;
s = NULL;
return 0;
}
static int
thermal_sensor_update(thermal_sensor *s){
__attribute__((cleanup(freecharp))) char *buf = NULL;
char *e;
int rv;
long v;
switch(s->type){
case SUBSYSTEM_THERMAL:
if((rv = snarf(s->dirfd, "temp", &buf)) < 0)
return -rv;
break;
case SUBSYSTEM_HWMON:
if((rv = snarf(s->dirfd, "temp1_input", &buf)) < 0)
return -rv;
break;
default:
__builtin_unreachable();
}
errno = 0;
v = strtol(buf, &e, 10);
if(errno == ERANGE || buf == e)
return -ERANGE;
if(v == s->temperature)
return 0;
warnx("timer for %s updated, %d -> %ld", s->name, s->temperature, v);
s->temperature = v;
rv = sd_bus_emit_properties_changed(sd_bus_slot_get_bus(s->slot), s->buspath, busInterface, "temperature", NULL);
if(rv < 0)
return -rv;
return 0;
}
static int
thermal_sensor_timer(sd_event_source *source, uint64_t usec, void *userdata)
{
int rv;
thermal_sensor *s = userdata;
if((rv = sd_event_source_set_time_relative(source, 10 * 1000000)) < 0)
return rv;
rv = thermal_sensor_update(s);
if(rv < 0){
warnx("failed to update sensor %s: %s", s->path, strerror(-rv));
return rv;
}
return 0;
}
static const sd_bus_vtable thermal_sensor_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("label", "s", NULL, offsetof(thermal_sensor, label), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("temperature", "i", NULL, offsetof(thermal_sensor, temperature), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_VTABLE_END,
};
static int
thermal_sensor_hookup(thermal_sensor *s, sd_bus *b, sd_event *e)
{
int rv;
__attribute__((cleanup(sd_event_source_unrefp))) sd_event_source *source = NULL;
if((rv = sd_bus_add_object_vtable(b, &s->slot, s->buspath, busInterface, thermal_sensor_vtable, s)) < 0)
return rv;
if((rv = sd_bus_emit_interfaces_added(b, s->buspath, busInterface, NULL)) < 0)
return rv;
if((rv = sd_event_add_time_relative(e, &source, CLOCK_MONOTONIC, 0, 0, thermal_sensor_timer, s)) < 0)
return rv;
if((rv = sd_event_source_set_enabled(source, SD_EVENT_ON)) < 0)
return rv;
s->evt = sd_event_source_ref(source);
return 0;
}
/*
typedef struct thermal_table {
int xxx;
} thermal_table;
*/
static void
prdev(sd_device *dev)
{
const char *tmp;
sd_device *parent = NULL;
int ishwmon = 0;
sd_device_get_syspath(dev, &tmp);
warnx("syspath: %s", tmp);
sd_device_get_subsystem(dev, &tmp);
warnx("subsystem: %s", tmp);
if(strncmp(tmp, "hwmon", 5) == 0 || strncmp(tmp, "thermal", 7) == 0)
ishwmon = 1;
sd_device_get_devtype(dev, &tmp);
warnx("devtype: %s", tmp);
sd_device_get_driver(dev, &tmp);
warnx("driver: %s", tmp);
sd_device_get_devname(dev, &tmp);
warnx("devname: %s", tmp);
if(ishwmon && sd_device_get_parent(dev, &parent) >= 0)
prdev(parent);
warnx("");
}
static int
monitor_handler(sd_device_monitor *m, sd_device *dev, void *v)
{
prdev(dev);
return 0;
}
static int
walk_enumerator(sd_device_enumerator *enumerator, sd_bus *b, sd_event *e)
{
sd_device *d;
const char *tmp;
int rv;
for(d = sd_device_enumerator_get_device_first(enumerator); d;
d = sd_device_enumerator_get_device_next(enumerator)){
if((rv = sd_device_get_sysname(d, &tmp)) < 0)
return -rv;
if(strstr(tmp, "cooling_device") != NULL)
continue;
prdev(d);
// XXX
thermal_sensor *x;
if((rv = thermal_sensor_new(d, &x)) < 0)
errx(EX_OSERR, "thermal_sensor_new: %s", strerror(-rv));
if((rv = thermal_sensor_hookup(x, b, e)) < 0)
errx(EX_OSERR, "thermal_sensor_hookup: %s", strerror(-rv));
}
return 0;
}
int
main(int argc, char *argv[])
{
int rv;
__attribute__((cleanup(sd_event_unrefp))) sd_event *event = NULL;
__attribute__((cleanup(sd_bus_flush_close_unrefp))) sd_bus *bus = NULL;
__attribute__((cleanup(sd_device_enumerator_unrefp))) sd_device_enumerator *enumerator = NULL;
__attribute__((cleanup(sd_device_monitor_unrefp))) sd_device_monitor *monitor = NULL;
(void) argc;
(void) argv;
if((rv = sd_event_default(&event)) < 0)
errx(EX_OSERR, "sd_event_default: %s", strerror(-rv));
if(sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL) < 0)
err(EX_OSERR, "sd_event_add_signal");
if(sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL) < 0)
err(EX_OSERR, "sd_event_add_signal");
if(sd_bus_default(&bus) < 0)
err(EX_OSERR, "sd_bus_default");
if(sd_bus_add_object_manager(bus, NULL, "/com/github/mischief/hwmon1") < 0)
err(EX_OSERR, "sd_bus_add_object_manager");
if(sd_bus_attach_event(bus, event, 0) < 0)
err(EX_OSERR, "sd_bus_attach_event");
if(sd_bus_request_name(bus, "com.github.mischief.hwmon1", 0) < 0)
err(EX_OSERR, "sd_bus_request_name");
if(sd_device_enumerator_new(&enumerator) < 0)
err(EX_OSERR, "sd_device_enumerator_new");
if(sd_device_enumerator_add_match_subsystem(enumerator, "hwmon", 1) < 0)
err(EX_OSERR, "sd_device_enumerator_add_match_subsystem");
if(sd_device_enumerator_add_match_subsystem(enumerator, "thermal", 1) < 0)
err(EX_OSERR, "sd_device_enumerator_add_match_subsystem");
if(sd_device_monitor_new(&monitor) < 0)
err(EX_OSERR, "sd_device_monitor_new");
if(sd_device_monitor_filter_add_match_subsystem_devtype(monitor, "hwmon", "hwmon") < 0)
err(EX_OSERR, "sd_device_monitor_filter_add_match_subsystem_devtype");
if(sd_device_monitor_attach_event(monitor, event) < 0)
err(EX_OSERR, "sd_device_monitor_attach_event");
if(sd_device_monitor_start(monitor, monitor_handler, NULL) < 0)
err(EX_OSERR, "sd_device_monitor_start");
walk_enumerator(enumerator, bus, event);
if(sd_event_loop(event) < 0)
err(EX_OSERR, "sd_event_loop");
return EX_OK;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment