/* * HardInfo - Displays System Information * Copyright (C) 2003-2006 Leandro A. F. Pereira * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "devices.h" #include "expr.h" #include "hardinfo.h" #include "socket.h" gchar *sensors = NULL; GHashTable *sensor_compute = NULL; GHashTable *sensor_labels = NULL; static void read_sensor_labels(gchar *driver) { FILE *conf; gchar buf[256], *line, *p; gboolean lock = FALSE; gint i; /* Try to open lm-sensors config file sensors3.conf */ conf = fopen("/etc/sensors3.conf", "r"); /* If it fails, try to open sensors.conf */ if (!conf) conf = fopen("/etc/sensors.conf", "r"); if (!conf) { /* Cannot open config file. */ return; } while (fgets(buf, 256, conf)) { line = buf; remove_linefeed(line); strend(line, '#'); if (*line == '\0') { continue; } else if (lock && strstr(line, "label")) { /* label lines */ gchar **names = g_strsplit(strstr(line, "label") + 5, " ", 0); gchar *name = NULL, *value = NULL; for (i = 0; names[i]; i++) { if (names[i][0] == '\0') continue; if (!name) name = g_strdup(names[i]); else if (!value) value = g_strdup(names[i]); else value = g_strconcat(value, " ", names[i], NULL); } remove_quotes(value); g_hash_table_insert(sensor_labels, name, value); g_strfreev(names); } else if (lock && strstr(line, "ignore")) { /* ignore lines */ p = strstr(line, "ignore") + 6; if (!strchr(p, ' ')) continue; while (*p == ' ') p++; g_hash_table_insert(sensor_labels, g_strdup(p), "ignore"); } else if (lock && strstr(line, "compute")) { /* compute lines */ gchar **formulas = g_strsplit(strstr(line, "compute") + 7, " ", 0); gchar *name = 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]); else if (!formula) formula = g_strdup(formulas[i]); else formula = g_strconcat(formula, formulas[i], NULL); } g_strfreev(formulas); g_hash_table_insert(sensor_compute, name, math_string_to_postfix(formula)); } else if (g_str_has_prefix(line, "chip")) { /* chip lines (delimiter) */ if (lock == FALSE) { gchar **chips = g_strsplit(line, " ", 0); for (i = 1; chips[i]; i++) { strend(chips[i], '*'); if (g_str_has_prefix(chips[i] + 1, driver)) { lock = TRUE; break; } } g_strfreev(chips); } else { break; } } } fclose(conf); } static void add_sensor(const char *type, const char *sensor, const char *driver, double value, const char *unit) { 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", 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) { gchar *ret; ret = g_hash_table_lookup(sensor_labels, sensor); if (!ret) ret = g_strdup(sensor); else ret = g_strdup(ret); return ret; } static float adjust_sensor(gchar *name, float value) { GSList *postfix; postfix = g_hash_table_lookup(sensor_compute, name); if (!postfix) return value; return math_postfix_eval(postfix, value); } 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; tmp = g_strdup_printf("%s/device/driver", path); driver = g_file_read_link(tmp, NULL); g_free(tmp); if (driver) { tmp = g_path_get_basename(driver); g_free(driver); driver = tmp; } else { tmp = g_strdup_printf("%s/device", path); driver = 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); } return driver; } struct HwmonSensor { const char *friendly_name; const char *path_format; const char *key_format; const char *unit; const float adjust_ratio; const int begin_at; }; static const struct HwmonSensor hwmon_sensors[] = { { "Fan", "%s/fan%d_input", "fan%d", "RPM", 1.0, 1 }, { "Temperature", "%s/temp%d_input", "temp%d", "\302\260C", 1000.0, 1 }, { "Voltage", "%s/in%d_input", "in%d", "V", 1000.0, 0 }, { } }; static const char *hwmon_prefix[] = {"device", "", NULL}; static void read_sensors_hwmon(void) { int hwmon, count; gchar *path_hwmon, *path_sensor, *tmp, *driver, *name, *mon; const char **prefix; for (prefix = hwmon_prefix; *prefix; prefix++) { hwmon = 0; path_hwmon = get_sensor_path(hwmon, *prefix); 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); } for (sensor = hwmon_sensors; sensor->friendly_name; sensor++) { DEBUG("current sensor type=%s", sensor->friendly_name); 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; } mon = g_strdup_printf(sensor->key_format, count); name = get_sensor_label(mon); if (!g_str_equal(name, "ignore")) { float adjusted = adjust_sensor(mon, atof(tmp) / sensor->adjust_ratio); add_sensor(sensor->friendly_name, name, driver, adjusted, sensor->unit); } g_free(tmp); g_free(mon); g_free(name); g_free(path_sensor); } } g_free(path_hwmon); g_free(driver); path_hwmon = get_sensor_path(++hwmon, *prefix); } g_free(path_hwmon); } } static void read_sensors_acpi(void) { const gchar *path_tz = "/proc/acpi/thermal_zone"; if (g_file_test(path_tz, G_FILE_TEST_EXISTS)) { GDir *tz; if ((tz = g_dir_open(path_tz, 0, NULL))) { const gchar *entry; while ((entry = g_dir_read_name(tz))) { gchar *path = g_strdup_printf("%s/%s/temperature", path_tz, entry); gchar *contents; if (g_file_get_contents(path, &contents, NULL, NULL)) { int temperature; sscanf(contents, "temperature: %d C", &temperature); add_sensor("Temperature", entry, "ACPI Thermal Zone", temperature, "\302\260C"); } } g_dir_close(tz); } } } static void read_sensors_sys_thermal(void) { const gchar *path_tz = "/sys/class/thermal"; if (g_file_test(path_tz, G_FILE_TEST_EXISTS)) { GDir *tz; if ((tz = g_dir_open(path_tz, 0, NULL))) { const gchar *entry; gchar *temp = g_strdup(""); while ((entry = g_dir_read_name(tz))) { gchar *path = g_strdup_printf("%s/%s/temp", path_tz, entry); gchar *contents; if (g_file_get_contents(path, &contents, NULL, NULL)) { int temperature; sscanf(contents, "%d", &temperature); add_sensor("Temperature", entry, "thermal", temperature / 1000.0, "\302\260C"); g_free(contents); } } g_dir_close(tz); } } } static void read_sensors_omnibook(void) { const gchar *path_ob = "/proc/omnibook/temperature"; gchar *contents; if (g_file_get_contents(path_ob, &contents, NULL, NULL)) { int temperature; sscanf(contents, "CPU temperature: %d C", &temperature); add_sensor("Temperature", "CPU", "omnibook", temperature, "\302\260C\n"); g_free(contents); } } static void read_sensors_hddtemp(void) { Socket *s; gchar buffer[1024]; gint len = 0; if (!(s = sock_connect("127.0.0.1", 7634))) return; while (!len) len = sock_read(s, buffer, sizeof(buffer)); sock_close(s); if (len > 2 && buffer[0] == '|' && buffer[1] == '/') { gchar **disks; int i; disks = g_strsplit(buffer, "\n", 0); for (i = 0; disks[i]; i++) { gchar **fields = g_strsplit(disks[i] + 1, "|", 5); /* * 0 -> /dev/hda * 1 -> FUJITSU MHV2080AH * 2 -> 41 * 3 -> C */ const gchar *unit = strcmp(fields[3], "C") ? "\302\260C" : "\302\260F"; add_sensor("Hard Drive", fields[1], "hddtemp", atoi(fields[2]), unit); g_strfreev(fields); } g_strfreev(disks); } } void scan_sensors_do(void) { g_free(sensors); sensors = g_strdup(""); g_free(lginterval); lginterval = g_strdup(""); read_sensors_hwmon(); read_sensors_acpi(); read_sensors_sys_thermal(); read_sensors_omnibook(); read_sensors_hddtemp(); /* FIXME: Add support for ibm acpi and more sensors */ } void sensors_init(void) { 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) { g_hash_table_destroy(sensor_labels); g_hash_table_destroy(sensor_compute); }