diff options
Diffstat (limited to 'modules/devices/sensors.c')
-rw-r--r-- | modules/devices/sensors.c | 503 |
1 files changed, 410 insertions, 93 deletions
diff --git a/modules/devices/sensors.c b/modules/devices/sensors.c index c9d78ff7..095f0bc4 100644 --- a/modules/devices/sensors.c +++ b/modules/devices/sensors.c @@ -1,10 +1,10 @@ /* * HardInfo - Displays System Information - * Copyright (C) 2003-2006 Leandro A. F. Pereira <leandro@hardinfo.org> + * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2. + * the Free Software Foundation, version 2 or later. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -22,12 +22,21 @@ #include "expr.h" #include "hardinfo.h" #include "socket.h" +#include "udisks2_util.h" + +#if defined(HAS_LIBSENSORS) && HAS_LIBSENSORS +#include <sensors/sensors.h> +#endif gchar *sensors = NULL; +gchar *sensor_icons = NULL; GHashTable *sensor_compute = NULL; GHashTable *sensor_labels = NULL; +gboolean hwmon_first_run = TRUE; + +static gchar *last_group = NULL; -static void read_sensor_labels(gchar *driver) { +static void read_sensor_labels(gchar *devname) { FILE *conf; gchar buf[256], *line, *p; gboolean lock = FALSE; @@ -55,14 +64,14 @@ static void read_sensor_labels(gchar *driver) { continue; } else if (lock && strstr(line, "label")) { /* label lines */ gchar **names = g_strsplit(strstr(line, "label") + 5, " ", 0); - gchar *name = NULL, *value = NULL; + gchar *key = NULL, *value = NULL; for (i = 0; names[i]; i++) { if (names[i][0] == '\0') continue; - if (!name) - name = g_strdup(names[i]); + if (!key) + key = g_strdup_printf("%s/%s", devname, names[i]); else if (!value) value = g_strdup(names[i]); else @@ -70,7 +79,7 @@ static void read_sensor_labels(gchar *driver) { } remove_quotes(value); - g_hash_table_insert(sensor_labels, name, value); + g_hash_table_insert(sensor_labels, key, g_strstrip(value)); g_strfreev(names); } else if (lock && strstr(line, "ignore")) { /* ignore lines */ @@ -80,19 +89,18 @@ static void read_sensor_labels(gchar *driver) { while (*p == ' ') p++; - g_hash_table_insert(sensor_labels, g_strdup(p), "ignore"); + g_hash_table_insert(sensor_labels, g_strdup_printf("%s/%s", devname, p), "ignore"); } else if (lock && strstr(line, "compute")) { /* compute lines */ + strend(line, ','); gchar **formulas = g_strsplit(strstr(line, "compute") + 7, " ", 0); - gchar *name = NULL, *formula = NULL; + gchar *key = NULL, *formula = NULL; for (i = 0; formulas[i]; i++) { if (formulas[i][0] == '\0') continue; - if (formulas[i][0] == ',') - break; - if (!name) - name = g_strdup(formulas[i]); + if (!key) + key = g_strdup_printf("%s/%s", devname, formulas[i]); else if (!formula) formula = g_strdup(formulas[i]); else @@ -100,7 +108,7 @@ static void read_sensor_labels(gchar *driver) { } g_strfreev(formulas); - g_hash_table_insert(sensor_compute, name, + g_hash_table_insert(sensor_compute, key, math_string_to_postfix(formula)); } else if (g_str_has_prefix(line, "chip")) { /* chip lines (delimiter) */ @@ -110,7 +118,7 @@ static void read_sensor_labels(gchar *driver) { for (i = 1; chips[i]; i++) { strend(chips[i], '*'); - if (g_str_has_prefix(chips[i] + 1, driver)) { + if (g_str_has_prefix(chips[i] + 1, devname)) { lock = TRUE; break; } @@ -128,36 +136,59 @@ static void read_sensor_labels(gchar *driver) { static void add_sensor(const char *type, const char *sensor, - const char *driver, + const char *parent, double value, - const char *unit) { + const char *unit, + const char *icon) { char key[64]; - sensors = h_strdup_cprintf("%s/%s=%.2f%s|%s\n", sensors, - driver, sensor, value, unit, type); + snprintf(key, sizeof(key), "%s/%s", parent, sensor); + + if (SENSORS_GROUP_BY_TYPE) { + // group by type + if (g_strcmp0(last_group, type) != 0) { + sensors = h_strdup_cprintf("[%s]\n", sensors, type); + g_free(last_group); + last_group = g_strdup(type); + } + sensors = h_strdup_cprintf("$%s$%s=%.2f%s|%s\n", sensors, + key, sensor, value, unit, parent); + } + else { + // group by device source / driver + if (g_strcmp0(last_group, parent) != 0) { + sensors = h_strdup_cprintf("[%s]\n", sensors, parent); + g_free(last_group); + last_group = g_strdup(parent); + } + sensors = h_strdup_cprintf("$%s$%s=%.2f%s|%s\n", sensors, + key, sensor, value, unit, type); + } + + if (icon != NULL) { + sensor_icons = h_strdup_cprintf("Icon$%s$%s=%s.png\n", sensor_icons, + key, sensor, icon); + } - snprintf(key, sizeof(key), "%s/%s", driver, sensor); moreinfo_add_with_prefix("DEV", key, g_strdup_printf("%.2f%s", value, unit)); lginterval = h_strdup_cprintf("UpdateInterval$%s=1000\n", lginterval, key); } -static gchar *get_sensor_label(gchar *sensor) { +static gchar *get_sensor_label_from_conf(gchar *key) { gchar *ret; + ret = g_hash_table_lookup(sensor_labels, key); - ret = g_hash_table_lookup(sensor_labels, sensor); - if (!ret) - ret = g_strdup(sensor); - else - ret = g_strdup(ret); + if (ret) + return g_strdup(ret); - return ret; + return NULL; } -static float adjust_sensor(gchar *name, float value) { +static float adjust_sensor(gchar *key, float value) { GSList *postfix; - postfix = g_hash_table_lookup(sensor_compute, name); + postfix = g_hash_table_lookup(sensor_compute, key); if (!postfix) return value; @@ -168,79 +199,137 @@ static char *get_sensor_path(int number, const char *prefix) { return g_strdup_printf("/sys/class/hwmon/hwmon%d/%s", number, prefix); } -static char *determine_driver_for_hwmon_path(char *path) { - char *tmp, *driver; +static char *determine_devname_for_hwmon_path(char *path) { + char *tmp, *devname = NULL; + + // device name + tmp = g_strdup_printf("%s/name", path); + g_file_get_contents(tmp, &devname, NULL, NULL); + g_free(tmp); + if (devname) + return g_strstrip(devname); + // fallback: driver name (from driver link) tmp = g_strdup_printf("%s/device/driver", path); - driver = g_file_read_link(tmp, NULL); + devname = g_file_read_link(tmp, NULL); g_free(tmp); - if (driver) { - tmp = g_path_get_basename(driver); - g_free(driver); - driver = tmp; - } else { + if (!devname) { + // fallback: device folder name (from device link) tmp = g_strdup_printf("%s/device", path); - driver = g_file_read_link(tmp, NULL); + devname = g_file_read_link(tmp, NULL); g_free(tmp); } - if (!driver) { - tmp = g_strdup_printf("%s/name", path); - if (!g_file_get_contents(tmp, &driver, NULL, NULL)) { - driver = g_strdup("unknown"); - } else { - driver = g_strstrip(driver); - } - g_free(tmp); + if (devname) { + tmp = g_path_get_basename(devname); + g_free(devname); + return tmp; } - return driver; + return g_strdup("unknown"); } struct HwmonSensor { const char *friendly_name; - const char *path_format; + const char *value_file_regex; + const char *value_path_format; + const char *label_path_format; const char *key_format; const char *unit; const float adjust_ratio; - const int begin_at; + const char *icon; }; static const struct HwmonSensor hwmon_sensors[] = { { - "Fan", + "Fan Speed", + "^fan([0-9]+)_input$", "%s/fan%d_input", + "%s/fan%d_label", "fan%d", - "RPM", + " RPM", 1.0, - 1 + "fan" }, { "Temperature", + "^temp([0-9]+)_input$", "%s/temp%d_input", + "%s/temp%d_label", "temp%d", "\302\260C", 1000.0, - 1 + "therm" }, { "Voltage", + "^in([0-9]+)_input$", "%s/in%d_input", + "%s/in%d_label", "in%d", "V", 1000.0, - 0 + "bolt" + }, + { + "Current", + "^curr([0-9]+)_input$", + "%s/curr%d_input", + "%s/curr%d_label", + "curr%d", + " A", + 1000.0, + "bolt" + }, + { + "Power", + "^power([0-9]+)_input$", + "%s/power%d_input", + "%s/power%d_label", + "power%d", + " W", + 1000000.0, + "bolt" + }, + { + "CPU Voltage", + "^cpu([0-9]+)_vid$", + "%s/cpu%d_vid", + NULL, + "cpu%d_vid", + " V", + 1000.0, + "bolt" }, { } }; static const char *hwmon_prefix[] = {"device", "", NULL}; +static gboolean read_raw_hwmon_value(gchar *path_hwmon, const gchar *item_path_format, int item_id, gchar **result){ + gchar *full_path; + gboolean file_result; + + if (item_path_format == NULL) + return FALSE; + + full_path = g_strdup_printf(item_path_format, path_hwmon, item_id); + file_result = g_file_get_contents(full_path, result, NULL, NULL); + + g_free(full_path); + + return file_result; +} + static void read_sensors_hwmon(void) { - int hwmon, count; - gchar *path_hwmon, *path_sensor, *tmp, *driver, *name, *mon; - const char **prefix; + int hwmon, count, min, max; + gchar *path_hwmon, *tmp, *devname, *name, *mon, *key; + const char **prefix, *entry; + GDir *dir; + GRegex *regex; + GMatchInfo *match_info; + GError *err = NULL; for (prefix = hwmon_prefix; *prefix; prefix++) { hwmon = 0; @@ -248,56 +337,92 @@ static void read_sensors_hwmon(void) { while (path_hwmon && g_file_test(path_hwmon, G_FILE_TEST_EXISTS)) { const struct HwmonSensor *sensor; - driver = determine_driver_for_hwmon_path(path_hwmon); - DEBUG("hwmon%d has driver=%s", hwmon, driver); - - if (!sensor_labels) { - read_sensor_labels(driver); + devname = determine_devname_for_hwmon_path(path_hwmon); + DEBUG("hwmon%d has device=%s", hwmon, devname); + if (hwmon_first_run) { + read_sensor_labels(devname); } + dir = g_dir_open(path_hwmon, 0, NULL); + if (!dir) + continue; + for (sensor = hwmon_sensors; sensor->friendly_name; sensor++) { DEBUG("current sensor type=%s", sensor->friendly_name); + regex = g_regex_new (sensor->value_file_regex, 0, 0, &err); + if (err != NULL){ + g_free(err); + err = NULL; + continue; + } - for (count = sensor->begin_at;; count++) { - path_sensor = - g_strdup_printf(sensor->path_format, path_hwmon, count); - DEBUG("should be reading from %s", path_sensor); - if (!g_file_get_contents(path_sensor, &tmp, NULL, NULL)) { - g_free(path_sensor); - if (count < 256) - continue; // brute-force find all - else - break; + g_dir_rewind(dir); + min = 999; + max = -1; + + while ((entry = g_dir_read_name(dir))) { + g_regex_match(regex, entry, 0, &match_info); + if (g_match_info_matches(match_info)) { + tmp = g_match_info_fetch(match_info, 1); + count = atoi(tmp); + g_free (tmp); + + if (count < min){ + min = count; + } + if (count > max){ + max = count; + } + } + g_match_info_free(match_info); + } + g_regex_unref(regex); + + for (count = min; count <= max; count++) { + if (!read_raw_hwmon_value(path_hwmon, sensor->value_path_format, count, &tmp)) { + continue; } mon = g_strdup_printf(sensor->key_format, count); - name = get_sensor_label(mon); + key = g_strdup_printf("%s/%s", devname, mon); + name = get_sensor_label_from_conf(key); + if (name == NULL){ + if (read_raw_hwmon_value(path_hwmon, sensor->label_path_format, count, &name)){ + name = g_strchomp(name); + } + else{ + name = g_strdup(mon); + } + } + if (!g_str_equal(name, "ignore")) { - float adjusted = adjust_sensor(mon, + float adjusted = adjust_sensor(key, atof(tmp) / sensor->adjust_ratio); add_sensor(sensor->friendly_name, name, - driver, + devname, adjusted, - sensor->unit); + sensor->unit, + sensor->icon); } g_free(tmp); g_free(mon); + g_free(key); g_free(name); - g_free(path_sensor); } } + g_dir_close(dir); g_free(path_hwmon); - g_free(driver); + g_free(devname); path_hwmon = get_sensor_path(++hwmon, *prefix); } - g_free(path_hwmon); } + hwmon_first_run = FALSE; } static void read_sensors_acpi(void) { @@ -323,7 +448,8 @@ static void read_sensors_acpi(void) { entry, "ACPI Thermal Zone", temperature, - "\302\260C"); + "\302\260C", + "therm"); } } @@ -332,6 +458,78 @@ static void read_sensors_acpi(void) { } } +// Sensors for Apple PowerPC devices using Windfarm driver. +struct WindfarmSensorType { + const char *type; + const char *icon; + const char *file_regex; + const char *unit; + gboolean with_decimal_p; +}; +static const struct WindfarmSensorType windfarm_sensor_types[] = { + {"Fan", "fan", "^[a-z-]+-fan(-[0-9]+)?$", " RPM", FALSE}, + {"Temperature", "therm", "^[a-z-]+-temp(-[0-9]+)?$", "\302\260C", TRUE}, + {"Power", "bolt", "^[a-z-]+-power(-[0-9]+)?$", " W", TRUE}, + {"Current", "bolt", "^[a-z-]+-current(-[0-9]+)?$", " A", TRUE}, + {"Voltage", "bolt", "^[a-z-]+-voltage(-[0-9]+)?$", " V", TRUE}, + { } +}; +static void read_sensors_windfarm(void) +{ + const gchar *path_wf = "/sys/devices/platform/windfarm.0"; + GDir *wf; + gchar *tmp = NULL; + gint v1, v2; + double value; + + wf = g_dir_open(path_wf, 0, NULL); + if (wf) { + GRegex *regex; + GError *err = NULL; + const gchar *entry; + const struct WindfarmSensorType *sensor; + + for (sensor = windfarm_sensor_types; sensor->type; sensor++) { + DEBUG("current windfarm sensor type=%s", sensor->type); + regex = g_regex_new(sensor->file_regex, 0, 0, &err); + if (err != NULL) { + g_free(err); + err = NULL; + continue; + } + + g_dir_rewind(wf); + + while ((entry = g_dir_read_name(wf))) { + if (g_regex_match(regex, entry, 0, NULL)) { + gchar *path = g_strdup_printf("%s/%s", path_wf, entry); + if (g_file_get_contents(path, &tmp, NULL, NULL)) { + + if (sensor->with_decimal_p) { + // format source + // https://elixir.free-electrons.com/linux/v5.14/source/drivers/macintosh/windfarm_core.c#L301 + sscanf(tmp, "%d.%03d", &v1, &v2); + value = v1 + (v2 / 1000.0); + } else { + value = (double)atoi(tmp); + } + g_free(tmp); + + tmp = g_strdup(entry); + add_sensor(sensor->type, g_strdelimit(tmp, "-", ' '), + "windfarm", value, sensor->unit, + sensor->icon); + g_free(tmp); + } + g_free(path); + } + } + g_regex_unref(regex); + } + g_dir_close(wf); + } +} + static void read_sensors_sys_thermal(void) { const gchar *path_tz = "/sys/class/thermal"; @@ -355,7 +553,8 @@ static void read_sensors_sys_thermal(void) { entry, "thermal", temperature / 1000.0, - "\302\260C"); + "\302\260C", + "therm"); g_free(contents); } @@ -379,7 +578,8 @@ static void read_sensors_omnibook(void) { "CPU", "omnibook", temperature, - "\302\260C\n"); + "\302\260C", + "therm"); g_free(contents); } @@ -401,7 +601,7 @@ static void read_sensors_hddtemp(void) { gchar **disks; int i; - disks = g_strsplit(buffer, "\n", 0); + disks = g_strsplit(buffer, "||", 0); for (i = 0; disks[i]; i++) { gchar **fields = g_strsplit(disks[i] + 1, "|", 5); @@ -412,12 +612,13 @@ static void read_sensors_hddtemp(void) { * 3 -> C */ const gchar *unit = strcmp(fields[3], "C") - ? "\302\260C" : "\302\260F"; - add_sensor("Hard Drive", + ? "\302\260F" : "\302\260C"; + add_sensor("Drive Temperature", fields[1], "hddtemp", atoi(fields[2]), - unit); + unit, + "therm"); g_strfreev(fields); } @@ -426,28 +627,144 @@ static void read_sensors_hddtemp(void) { } } +static void read_sensors_udisks2(void) { + GSList *node; + GSList *temps; + udiskt *disk; + + temps = get_udisks2_temps(); + if (temps == NULL) + return; + + for (node = temps; node != NULL; node = node->next) { + disk = (udiskt *)node->data; + add_sensor("Drive Temperature", + disk->drive, + "udisks2", + disk->temperature, + "\302\260C", + "therm"); + udiskt_free(disk); + } + g_slist_free(temps); +} + +#if HAS_LIBSENSORS +static const struct libsensors_feature_type { + const char *type_name; + const char *icon; + const char *unit; + sensors_subfeature_type input; +} libsensors_feature_types[SENSORS_FEATURE_MAX] = { + [SENSORS_FEATURE_FAN] = {"Fan", "fan", "RPM", + SENSORS_SUBFEATURE_FAN_INPUT}, + [SENSORS_FEATURE_TEMP] = {"Temperature", "therm", "\302\260C", + SENSORS_SUBFEATURE_TEMP_INPUT}, + [SENSORS_FEATURE_POWER] = {"Power", "bolt", "W", + SENSORS_SUBFEATURE_POWER_INPUT}, + [SENSORS_FEATURE_CURR] = {"Current", "bolt", "A", + SENSORS_SUBFEATURE_CURR_INPUT}, + [SENSORS_FEATURE_IN] = {"Voltage", "bolt", "V", + SENSORS_SUBFEATURE_IN_INPUT}, + [SENSORS_FEATURE_VID] = {"CPU Voltage", "bolt", "V", + SENSORS_SUBFEATURE_VID}, +}; +static gboolean libsensors_initialized; + +static int read_sensors_libsensors(void) { + char chip_name_buf[512]; + const sensors_chip_name *name; + int chip_nr = 0; + int added_sensors = 0; + + if (!libsensors_initialized) + return 0; + + while ((name = sensors_get_detected_chips(NULL, &chip_nr))) { + const struct sensors_feature *feat; + int feat_nr = 0; + + sensors_snprintf_chip_name(chip_name_buf, 512, name); + + while ((feat = sensors_get_features(name, &feat_nr))) { + const struct libsensors_feature_type *feat_descr; + const struct sensors_subfeature *subfeat; + double value; + + feat_descr = &libsensors_feature_types[feat->type]; + if (!feat_descr->type_name) + continue; + + subfeat = sensors_get_subfeature(name, feat, feat_descr->input); + if (!subfeat) + continue; + + if (!sensors_get_value(name, subfeat->number, &value)) { + char *label = sensors_get_label(name, feat); + gchar *label_with_chip = g_strdup_printf("%s (%s)", label, chip_name_buf); + + add_sensor(feat_descr->type_name, + label_with_chip, + "libsensors", + value, + feat_descr->unit, + feat_descr->icon); + + free(label_with_chip); + free(label); + + added_sensors++; + } + } + } + + return added_sensors; +} +#else +static int read_sensors_libsensors(void) +{ + return 0; +} +#endif + void scan_sensors_do(void) { g_free(sensors); + g_free(sensor_icons); + g_free(last_group); + last_group = NULL; sensors = g_strdup(""); + sensor_icons = g_strdup(""); g_free(lginterval); lginterval = g_strdup(""); - read_sensors_hwmon(); - read_sensors_acpi(); - read_sensors_sys_thermal(); - read_sensors_omnibook(); + if (read_sensors_libsensors() == 0) { + read_sensors_hwmon(); + read_sensors_acpi(); + read_sensors_sys_thermal(); + read_sensors_omnibook(); + } + + read_sensors_windfarm(); read_sensors_hddtemp(); - /* FIXME: Add support for ibm acpi and more sensors */ + read_sensors_udisks2(); } -void sensors_init(void) { +void sensor_init(void) { +#if HAS_LIBSENSORS + libsensors_initialized = sensors_init(NULL) == 0; +#endif + sensor_labels = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); sensor_compute = g_hash_table_new(g_str_hash, g_str_equal); } -void sensors_shutdown(void) { +void sensor_shutdown(void) { +#if HAS_LIBSENSORS + sensors_cleanup(); +#endif + g_hash_table_destroy(sensor_labels); g_hash_table_destroy(sensor_compute); } |