/* * Copyright (c) 2007 Daniel Borca All rights reserved. * * 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; either version 2 of the License, or * (at your option) any later version. * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "../list.h" #include "../sensors.h" static int spawn_sync_read (const char *filename, const char *const argv[], int max, char *out) { #define READ 0 #define WRITE 1 pid_t pid; int fd[2]; int status; int size; int eof; if (pipe(fd) != 0) { return -1; } pid = fork(); if (pid == -1) { return -1; } if (pid == 0) { close(fd[READ]); close(STDERR_FILENO); dup2(fd[WRITE], STDOUT_FILENO); exit(execvp(filename, (char *const *)argv)); } close(fd[WRITE]); for (--max, size = 0, eof = 0; !eof;) { char buf[512]; int chunk = read(fd[READ], buf, sizeof(buf)); if (chunk < 0) { break; } eof = (chunk < (int)sizeof(buf)); if (size + chunk > max) { chunk = max - size; } memcpy(&out[size], buf, chunk); size += chunk; } out[size] = '\0'; close(fd[READ]); wait(&status); return size; #undef WRITE #undef READ } int sensors_nvidia (const char *setting, int *val) { const char *argv[] = { "nvidia-settings", "-q" , NULL, NULL }; char out[BUFSIZ]; int dummy; argv[2] = (char *)setting; if (val == NULL) { val = &dummy; } if (spawn_sync_read(argv[0], argv, sizeof(out), out) < 0) { return -1; } if (sscanf(out, " Attribute %*s %*s %d", val) != 1) { return -1; } return 0; } int sensors_read_line (const char *filename, int max, char *out) { char *p; FILE *f; f = fopen(filename, "rt"); if (f == NULL) { return -1; } if (fgets(out, max, f) == NULL) { fclose(f); return -1; } fclose(f); p = strchr(out, '\n'); if (p != NULL) { *p = '\0'; return p - out; } return strlen(out); } static int add_sensor (SENSOR *list, const char *filename, const char *name, SENSOR_TYPE type) { SENSOR *s; char *p, buf[256]; s = malloc(sizeof(SENSOR)); if (s == NULL) { return -1; } s->type = type; s->idata = 0; switch (type) { case S_ACPI_THERMAL_ZONE: p = malloc(strlen(filename) + 32); if (p == NULL) { break; } strcat(strcpy(p, filename), "/trip_points"); s->idata = 100; if (sensors_read_line(p, sizeof(buf), buf) > 0) { sscanf(buf, "%*s %*s %d", &s->idata); } s->filename = strcat(strcpy(p, filename), "/temperature"); s->name = strdup(name); if (s->name == NULL) { free(p); break; } list_append(list, s); return 0; case S_ACPI_AC_ADAPTER: p = malloc(strlen(filename) + 32); if (p == NULL) { break; } s->filename = strcat(strcpy(p, filename), "/state"); s->name = strdup(name); if (s->name == NULL) { free(p); break; } list_append(list, s); return 0; case S_ACPI_BATTERY: p = malloc(strlen(filename) + 32); if (p == NULL) { break; } s->filename = strcat(strcpy(p, filename), "/"); s->name = strdup(name); if (s->name == NULL) { free(p); break; } list_append(list, s); return 0; case S_HWMON_CORETEMP: p = malloc(strlen(filename) + 32); if (p == NULL) { break; } strcat(strcpy(p, filename), "/device/name"); if (sensors_read_line(p, sizeof(buf), buf) <= 0) { free(p); break; } if (strcmp(buf, "coretemp")) { free(p); break; } strcat(strcpy(p, filename), "/device/temp1_crit"); s->idata = 100; if (sensors_read_line(p, sizeof(buf), buf) > 0) { sscanf(buf, "%d", &s->idata); } strcat(strcpy(p, filename), "/device/temp1_label"); if (sensors_read_line(p, sizeof(buf), buf) > 0) { s->name = strdup(buf); } else { s->name = strdup(name); } s->filename = strcat(strcpy(p, filename), "/device/temp1_input"); if (s->name == NULL) { free(s->filename); break; } list_append(list, s); return 0; case S_NVIDIA_SETTINGS_GPUCORETEMP: if (sensors_nvidia(name, NULL) != 0) { break; } p = strdup(filename); if (p == NULL) { break; } s->filename = p; s->name = strdup(name); if (s->name == NULL) { free(p); break; } list_append(list, s); return 0; } free(s); return -1; } static void scan_dirs (const char *dirname, SENSOR *list, SENSOR_TYPE type) { DIR *dir; int len = strlen(dirname); dir = opendir(dirname); if (dir != NULL) { struct dirent *ent; while ((ent = readdir(dir))) { if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) { char *fullname = malloc(len + 1 + strlen(ent->d_name) + 1); if (fullname != NULL) { struct stat buf; int l = len; strcpy(fullname, dirname); if (fullname[l - 1] != '/') { fullname[l++] = '/'; } strcpy(&fullname[l], ent->d_name); /* don't use d_type */ if (stat(fullname, &buf) == 0 && S_ISDIR(buf.st_mode)) { add_sensor(list, fullname, ent->d_name, type); } free(fullname); } } } closedir(dir); } } SENSOR * sensors_init (void) { SENSOR *list; list = malloc(sizeof(SENSOR)); if (list == NULL) { return NULL; } list->type = -1; list_create(list); scan_dirs("/proc/acpi/thermal_zone", list, S_ACPI_THERMAL_ZONE); scan_dirs("/proc/acpi/ac_adapter", list, S_ACPI_AC_ADAPTER); scan_dirs("/proc/acpi/battery", list, S_ACPI_BATTERY); scan_dirs("/sys/class/hwmon", list, S_HWMON_CORETEMP); add_sensor(list, "nvidia-settings", "GPUCoreTemp", S_NVIDIA_SETTINGS_GPUCORETEMP); return list; } void sensors_free (SENSOR *list) { SENSOR *s, *tmp; list_foreach_s (s, tmp, list) { list_remove(s); free(s->filename); free((char *)s->name); free(s); } free(list); }