summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 430b0b4)
raw | patch | inline | side by side (parent: 430b0b4)
author | Michał Mirosław <mirq-linux@rere.qmqm.pl> | |
Wed, 11 Jun 2008 21:38:14 +0000 (23:38 +0200) | ||
committer | Florian Forster <octo@huhu.verplant.org> | |
Thu, 12 Jun 2008 05:27:43 +0000 (07:27 +0200) |
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:
<Plugin thermal>
ForceUseProcfs true # default is false (prefer new sysfs interface)
Device "THRM" # default: no selection (include all devices found)
IgnoreSelected true # default is false
</Plugin>
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: Florian Forster <octo@huhu.verplant.org>
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:
<Plugin thermal>
ForceUseProcfs true # default is false (prefer new sysfs interface)
Device "THRM" # default: no selection (include all devices found)
IgnoreSelected true # default is false
</Plugin>
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: Florian Forster <octo@huhu.verplant.org>
configure.in | patch | blob | history | |
src/Makefile.am | patch | blob | history | |
src/thermal.c | [new file with mode: 0644] | patch | blob |
diff --git a/configure.in b/configure.in
index b1b259bc320dd1346f07c3a99793fb0bec57a7d1..381cc4d8b3f2c3a45902d6637ec418c96985e298 100644 (file)
--- a/configure.in
+++ b/configure.in
plugin_swap="no"
plugin_tape="no"
plugin_tcpconns="no"
+plugin_thermal="no"
plugin_users="no"
plugin_vmem="no"
plugin_vserver="no"
plugin_serial="yes"
plugin_swap="yes"
plugin_tcpconns="yes"
+ plugin_thermal="yes"
plugin_vmem="yes"
plugin_vserver="yes"
plugin_wireless="yes"
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])
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 efbe0d730a66e90db05d5d5af92cd6d375111c0d..5e1274257285caa5a7486d399da4ec22511f27e3 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
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
--- /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 <mirq-linux at rere.qmqm.pl>
+ **/
+
+#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);
+}
+