From 2004f542b364535189083476f1395107e40c5f44 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Micha=C5=82=20Miros=C5=82aw?= Date: Wed, 11 Jun 2008 23:38:14 +0200 Subject: [PATCH] thermal plugin: Linux ACPI thermal zone plugin MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Hello Florian, On Wed, Jun 11, 2008 at 02:19:14PM +0200, Florian Forster wrote: > On Mon, Jun 09, 2008 at 08:33:23PM +0200, Michał Mirosław wrote: > > Here's temperature monitoring plugin using Linux ACPI thermal zone > > data from /sys/class/thermal/ or /proc/acpi/thermal_zone/. > thank you very much for your patch :) > I have a few (minor) requests for changes though ;) [cut] I updated the patch basing on your suggestions. You probably can use the 'walk_directory()' function in other plugins. And it would be useful to have some standard functions to use in config callback like boolean entry parser or common ignorelist entry names/parser. I could send a patch for some cleanups if nobody is working on that, yet. Best Regards, Michał Mirosław ACPI thermal zone plugin for collectd 4.4.1 Here's temperature monitoring plugin using Linux ACPI thermal zone data from /sys/class/thermal/ or /proc/acpi/thermal_zone/. Since this is the same source of data, only first directory found is used. Plugin configuration: ForceUseProcfs true # default is false (prefer new sysfs interface) Device "THRM" # default: no selection (include all devices found) IgnoreSelected true # default is false Signed-off-by: Michał Mirosław Signed-off-by: Florian Forster --- configure.in | 4 + src/Makefile.am | 8 ++ src/thermal.c | 305 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 src/thermal.c diff --git a/configure.in b/configure.in index b1b259bc..381cc4d8 100644 --- a/configure.in +++ b/configure.in @@ -2356,6 +2356,7 @@ plugin_serial="no" plugin_swap="no" plugin_tape="no" plugin_tcpconns="no" +plugin_thermal="no" plugin_users="no" plugin_vmem="no" plugin_vserver="no" @@ -2378,6 +2379,7 @@ then plugin_serial="yes" plugin_swap="yes" plugin_tcpconns="yes" + plugin_thermal="yes" plugin_vmem="yes" plugin_vserver="yes" plugin_wireless="yes" @@ -2554,6 +2556,7 @@ AC_PLUGIN([tail], [yes], [Parsing of logfiles]) AC_PLUGIN([tape], [$plugin_tape], [Tape drive statistics]) AC_PLUGIN([tcpconns], [$plugin_tcpconns], [TCP connection statistics]) AC_PLUGIN([teamspeak2], [yes], [TeamSpeak2 server statistics]) +AC_PLUGIN([thermal], [$plugin_thermal], [Linux ACPI thermal zone statistics]) AC_PLUGIN([unixsock], [yes], [Unixsock communication plugin]) AC_PLUGIN([users], [$plugin_users], [User statistics]) AC_PLUGIN([uuid], [yes], [UUID as hostname plugin]) @@ -2716,6 +2719,7 @@ Configuration: tape . . . . . . . . $enable_tape tcpconns . . . . . . $enable_tcpconns teamspeak2 . . . . . $enable_teamspeak2 + thermal . . . . . . . $enable_thermal unixsock . . . . . . $enable_unixsock users . . . . . . . . $enable_users uuid . . . . . . . . $enable_uuid diff --git a/src/Makefile.am b/src/Makefile.am index efbe0d73..5e127425 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -656,6 +656,14 @@ collectd_LDADD += "-dlopen" teamspeak2.la collectd_DEPENDENCIES += teamspeak2.la endif +if BUILD_PLUGIN_THERMAL +pkglib_LTLIBRARIES += thermal.la +thermal_la_SOURCES = thermal.c +thermal_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" thermal.la +collectd_DEPENDENCIES += thermal.la +endif + if BUILD_PLUGIN_UNIXSOCK pkglib_LTLIBRARIES += unixsock.la unixsock_la_SOURCES = unixsock.c \ diff --git a/src/thermal.c b/src/thermal.c new file mode 100644 index 00000000..a6ebe4eb --- /dev/null +++ b/src/thermal.c @@ -0,0 +1,305 @@ +/** + * collectd - src/thermal.c + * Copyright (C) 2008 Micha³ Miros³aw + * + * 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; only version 2 of the License is applicable. + * + * 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 + * + * Authors: + * Micha³ Miros³aw + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" +#include "utils_ignorelist.h" + +#if !KERNEL_LINUX +# error "This module is for Linux only." +#endif + +const char *const dirname_sysfs = "/sys/class/thermal"; +const char *const dirname_procfs = "/proc/acpi/thermal_zone"; + +static char force_procfs = 0; +static ignorelist_t *device_list; +static value_list_t vl_temp_template = VALUE_LIST_STATIC; +static value_list_t vl_state_template = VALUE_LIST_STATIC; + +enum dev_type { + TEMP = 0, + COOLING_DEV +}; + +static void thermal_submit (const char *plugin_instance, enum dev_type dt, double value) +{ + value_list_t vl = dt == TEMP ? vl_temp_template : vl_state_template; + value_t vt; + + vt.gauge = value; + + vl.values = &vt; + vl.time = time (NULL); + sstrncpy (vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); + + plugin_dispatch_values (dt == TEMP ? "temperature" : "gauge", &vl); +} + +static int read_file_contents (const char *filename, char *buf, int bufsize) +{ + FILE *fh; + int n; + + if ((fh = fopen (filename, "r")) == NULL) + return -1; + + n = fread(buf, 1, bufsize, fh); + fclose(fh); + + return n; +} + +static int thermal_sysfs_device_read (const char *name) +{ + char filename[256]; + char data[1024]; + int len; + int ok = 0; + + len = snprintf (filename, sizeof (filename), "%s/%s/temp", dirname_sysfs, name); + if ((len < 0) || ((unsigned int)len >= sizeof (filename))) + return -1; + + len = read_file_contents (filename, data, sizeof(data)); + if (len > 1 && data[--len] == '\n') { + char *endptr = NULL; + double temp; + + data[len] = 0; + errno = 0; + temp = strtod (data, &endptr) / 1000.0; + + if (endptr == data + len && errno == 0) { + thermal_submit(name, TEMP, temp); + ++ok; + } + } + + len = snprintf (filename, sizeof (filename), "%s/%s/cur_state", dirname_sysfs, name); + if ((len < 0) || ((unsigned int)len >= sizeof (filename))) + return -1; + + len = read_file_contents (filename, data, sizeof(data)); + if (len > 1 && data[--len] == '\n') { + char *endptr = NULL; + double state; + + data[len] = 0; + errno = 0; + state = strtod (data, &endptr); + + if (endptr == data + len && errno == 0) { + thermal_submit(name, COOLING_DEV, state); + ++ok; + } + } + + return ok ? 0 : -1; +} + +static int thermal_procfs_device_read (const char *name) +{ + const char str_temp[] = "temperature:"; + char filename[256]; + char data[1024]; + int len; + + /** + * rechot ~ # cat /proc/acpi/thermal_zone/THRM/temperature + * temperature: 55 C + */ + + len = snprintf (filename, sizeof (filename), "%s/%s/temperature", dirname_procfs, name); + if ((len < 0) || ((unsigned int)len >= sizeof (filename))) + return -1; + + len = read_file_contents (filename, data, sizeof(data)); + if (len > sizeof(str_temp) && data[--len] == '\n' && !strncmp(data, str_temp, sizeof(str_temp)-1)) { + char *endptr = NULL; + double temp; + double celsius, add; + + if (data[--len] == 'C') { + add = 0; + celsius = 1; + } else if (data[len] == 'F') { + add = -32; + celsius = 5/9; + } else if (data[len] == 'K') { + add = -273.15; + celsius = 1; + } else + return -1; + + while (len > 0 && data[--len] == ' ') + ; + data[len + 1] = 0; + + while (len > 0 && data[--len] != ' ') + ; + ++len; + + errno = 0; + temp = (strtod (data + len, &endptr) + add) * celsius; + + if (endptr != data + len && errno == 0) { + thermal_submit(name, TEMP, temp); + return 0; + } + } + + return -1; +} + +static const char *config_keys[] = { + "Device", + "IgnoreSelected", + "ForceUseProcfs" +}; + +static int thermal_config (const char *key, const char *value) +{ + if (device_list == NULL) + device_list = ignorelist_create (1); + + if (strcasecmp (key, "Device") == 0) + { + if (ignorelist_add (device_list, value)) + { + ERROR ("thermal plugin: " + "Cannot add value to ignorelist."); + return 1; + } + } + else if (strcasecmp (key, "IgnoreSelected") == 0) + { + ignorelist_set_invert (device_list, 1); + if ((strcasecmp (value, "True") == 0) + || (strcasecmp (value, "Yes") == 0) + || (strcasecmp (value, "On") == 0)) + ignorelist_set_invert (device_list, 0); + } + else if (strcasecmp (key, "ForceUseProcfs") == 0) + { + force_procfs = 0; + if ((strcasecmp (value, "True") == 0) + || (strcasecmp (value, "Yes") == 0) + || (strcasecmp (value, "On") == 0)) + force_procfs = 1; + } + else + { + return -1; + } + + return 0; +} + +static int walk_directory (const char *dir, int (*callback)(const char *dev)) +{ + struct dirent *ent; + DIR *dh; + int ok = 0; + + if ((dh = opendir (dir)) == NULL) + { + char errbuf[1024]; + ERROR ("Cannot open '%s': %s", dir, + sstrerror (errno, errbuf, sizeof (errbuf))); + return -1; + } + + while ((ent = readdir (dh)) != NULL) + { + if (ent->d_name[0] == '.') + continue; + + if (device_list) { + DEBUG ("thermal plugin: Checking ignorelist for '%s'", ent->d_name); + if (ignorelist_match (device_list, ent->d_name)) + continue; + } + + if (!callback(ent->d_name)) + ++ok; + } + + closedir (dh); + + return ok ? 0 : -1; +} + +static int thermal_sysfs_read (void) +{ + return walk_directory (dirname_sysfs, thermal_sysfs_device_read); +} + +static int thermal_procfs_read (void) +{ + return walk_directory (dirname_procfs, thermal_procfs_device_read); +} + +static int thermal_init (void) +{ + int ret = -1; + + if (!force_procfs && access (dirname_sysfs, R_OK | X_OK) == 0) { + ret = plugin_register_read ("thermal", thermal_sysfs_read); + } else if (access (dirname_procfs, R_OK | X_OK) == 0) { + ret = plugin_register_read ("thermal", thermal_procfs_read); + } + + if (!ret) { + vl_temp_template.values_len = 1; + vl_temp_template.interval = interval_g; + sstrncpy (vl_temp_template.host, hostname_g, + sizeof(vl_temp_template.host)); + sstrncpy (vl_temp_template.plugin, "thermal", + sizeof(vl_temp_template.plugin)); + sstrncpy (vl_temp_template.type_instance, "temperature", + sizeof(vl_temp_template.type_instance)); + + vl_state_template = vl_temp_template; + sstrncpy (vl_state_template.type_instance, "cooling_state", + sizeof(vl_state_template.type_instance)); + } + + return ret; +} + +static int thermal_shutdown (void) +{ + ignorelist_free (device_list); + + return 0; +} + +void module_register (void) +{ + plugin_register_config ("thermal", thermal_config, + config_keys, STATIC_ARRAY_SIZE(config_keys)); + plugin_register_init ("thermal", thermal_init); + plugin_register_shutdown ("thermal", thermal_shutdown); +} + -- 2.30.2