Created
September 16, 2024 04:34
-
-
Save mischief/e999ecccff5d3aaa2dada1512b76134f to your computer and use it in GitHub Desktop.
hwmon
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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