From c8e76316f549f75149d4787fd2e2390bbaee5531 Mon Sep 17 00:00:00 2001 From: Flavio Stanchina Date: Wed, 20 Dec 2006 15:45:58 +0100 Subject: [PATCH] New plugin "mbmon" to collect motherboard status information: temperatures, voltages and cooling fan speeds. Signed-off-by: Flavio Stanchina --- configure.in | 2 + debian/collectd.conf | 6 + debian/control | 16 ++- debian/copyright | 3 + debian/rules | 2 +- src/Makefile.am | 11 ++ src/collectd.conf.in | 6 + src/collectd.conf.pod | 14 +++ src/collectd.pod | 19 +++ src/mbmon.c | 332 ++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 409 insertions(+), 2 deletions(-) create mode 100644 src/mbmon.c diff --git a/configure.in b/configure.in index 69ac2cfc..1aa25eb2 100644 --- a/configure.in +++ b/configure.in @@ -990,6 +990,7 @@ AC_COLLECTD([email], [disable], [module], [email statistics]) AC_COLLECTD([quota], [enable], [module], [quota statistics (experimental)]) AC_COLLECTD([hddtemp], [disable], [module], [hdd temperature statistics]) AC_COLLECTD([load], [disable], [module], [system load statistics]) +AC_COLLECTD([mbmon], [disable], [module], [motherboard monitor statistics]) AC_COLLECTD([memory], [disable], [module], [memory statistics]) AC_COLLECTD([multimeter],[disable], [module], [multimeter statistics]) AC_COLLECTD([mysql], [disable], [module], [mysql statistics]) @@ -1042,6 +1043,7 @@ Configuration: email . . . . . . . $enable_email hddtemp . . . . . . $enable_hddtemp load . . . . . . . $enable_load + mbmon . . . . . . . $enable_mbmon memory . . . . . . $enable_memory multimeter . . . . $enable_multimeter mysql . . . . . . . $enable_mysql diff --git a/debian/collectd.conf b/debian/collectd.conf index e9e246dc..91eef477 100644 --- a/debian/collectd.conf +++ b/debian/collectd.conf @@ -27,6 +27,7 @@ LoadPlugin df LoadPlugin disk #LoadPlugin hddtemp LoadPlugin load +#LoadPlugin mbmon LoadPlugin memory #LoadPlugin mysql #LoadPlugin nfs @@ -59,6 +60,11 @@ LoadPlugin users # Port 7634 # +# +# Host 127.0.0.1 +# Port 411 +# + # # Host localhost # Port 123 diff --git a/debian/control b/debian/control index f9f8f3c2..56e8463e 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ Package: collectd Architecture: any Depends: ${shlibs:Depends} Suggests: collectd-apache, collectd-mysql, collectd-sensors, collectd-dev, - collectd-hddtemp, collectd-ping, librrds-perl + collectd-hddtemp, collectd-mbmon, collectd-ping, librrds-perl Description: statistics collection daemon collectd is a small daemon written in C for performance. It reads various system statistics and updates RRD files, creating them if necessary. Since @@ -82,6 +82,20 @@ Description: statistics collection daemon (hddtemp plugin) . This package contains the hddtemp plugin which collects harddisk temperatures. +Package: collectd-mbmon +Architecture: any +Depends: collectd (= ${Source-Version}), ${shlibs:Depends} +Recommends: mbmon (>= 2.01) +Description: statistics collection daemon (mbmon plugin) + collectd is a small daemon written in C for performance. It reads various + system statistics and updates RRD files, creating them if necessary. Since + the daemon doesn't need to startup every time it wants to update the files + it's very fast and easy on the system. Also, the statistics are very fine + grained since the files are updated every 10 seconds. + . + This package contains the mbmon plugin which collects motherboard status + information: temperatures, voltages and cooling fan speeds. + Package: collectd-mysql Architecture: any Depends: collectd (= ${Source-Version}), ${shlibs:Depends} diff --git a/debian/copyright b/debian/copyright index 2d287969..c75c8ee6 100644 --- a/debian/copyright +++ b/debian/copyright @@ -46,6 +46,9 @@ License: File src/hddtemp.c Copyright (C) 2005, 2006 Vincent Stehlé + File src/mbmon.c + Copyright (C) 2006 Flavio Stanchina + File src/nfs.c Copyright (C) 2005, 2006 Jason Pepas diff --git a/debian/rules b/debian/rules index 6a4cdf3c..602efd54 100755 --- a/debian/rules +++ b/debian/rules @@ -68,7 +68,7 @@ install-arch: build install -D -m 0644 $(CURDIR)/debian/collectd.conf \ $(CURDIR)/debian/collectd/etc/collectd/collectd.conf - for PLUGIN in apache hddtemp mysql ping sensors; do \ + for PLUGIN in apache hddtemp mbmon mysql ping sensors; do \ plugin_dir=$(CURDIR)/debian/collectd-$$PLUGIN/usr/lib/collectd/; \ mkdir -p $$plugin_dir; \ mv $(CURDIR)/debian/collectd/usr/lib/collectd/$$PLUGIN.so \ diff --git a/src/Makefile.am b/src/Makefile.am index 6e7e3395..d9b3b9ca 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -220,6 +220,17 @@ load_la_LDFLAGS += -lstatgrab endif endif +if BUILD_MODULE_MBMON +pkglib_LTLIBRARIES += mbmon.la +mbmon_la_SOURCES = mbmon.c +mbmon_la_LDFLAGS = -module -avoid-version +if BUILD_WITH_LIBSOCKET +mbmon_la_LDFLAGS += -lsocket +endif +collectd_LDADD += "-dlopen" mbmon.la +collectd_DEPENDENCIES += mbmon.la +endif + if BUILD_MODULE_MEMORY pkglib_LTLIBRARIES += memory.la memory_la_SOURCES = memory.c diff --git a/src/collectd.conf.in b/src/collectd.conf.in index b73278fc..977dde2f 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -31,6 +31,7 @@ @BUILD_MODULE_DNS_TRUE@LoadPlugin dns @BUILD_MODULE_HDDTEMP_TRUE@LoadPlugin hddtemp @BUILD_MODULE_LOAD_TRUE@LoadPlugin load +@BUILD_MODULE_MBMON_TRUE@LoadPlugin mbmon @BUILD_MODULE_MEMORY_TRUE@LoadPlugin memory @BUILD_MODULE_MULTIMETER_TRUE@LoadPlugin multimeter @BUILD_MODULE_MYSQL_TRUE@LoadPlugin mysql @@ -69,6 +70,11 @@ # Port 7634 # +# +# Host 127.0.0.1 +# Port 411 +# + # # Host localhost # Port 123 diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index aa4799a4..cffb7ad3 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -240,6 +240,20 @@ TCP-Port to connect to. Defaults to B<7634>. =back +=head2 Plugin C + +=over 4 + +=item B I + +Hostname to connect to. Defaults to B<127.0.0.1>. + +=item B I + +TCP-Port to connect to. Defaults to B<411>. + +=back + =head2 Plugin C =over 4 diff --git a/src/collectd.pod b/src/collectd.pod index f5ed782c..bf76055f 100644 --- a/src/collectd.pod +++ b/src/collectd.pod @@ -56,6 +56,10 @@ System load averages (I) =item +Motherboard monitor (I) + +=item + Memory usage (I) =item @@ -233,6 +237,20 @@ L for details. The B homepage can be found at L. +=head2 mbmon + +The B module uses mbmon to retrieve temperature, voltage, etc. + +collectd connects to B (127.0.0.1), port B<411/tcp>. +The B and B options can be used to change these +default values. See L for details. C has to be +running to work correctly. If C is not running timeouts may appear +which may interfere with other statistics.. + +C must be run with the -r option ("print TAG and Value format"); +Debian's /etc/init.d/mbmon script already does this, other people +will need to ensure that this is the case. + =head2 hddtemp To get values from B collectd connects to B (127.0.0.1), @@ -561,6 +579,7 @@ The DS'es depend on the module creating the RRD files: =head1 SEE ALSO L, L, L, L, +L, L =head1 AUTHOR diff --git a/src/mbmon.c b/src/mbmon.c new file mode 100644 index 00000000..0df65cca --- /dev/null +++ b/src/mbmon.c @@ -0,0 +1,332 @@ +/** + * collectd - src/mbmon.c + * Copyright (C) 2006 Flavio Stanchina + * Based on the hddtemp plugin. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Flavio Stanchina + * + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" +#include "utils_debug.h" + +#define MODULE_NAME "mbmon" + +#include +#include +#include +#include + +#define MBMON_DEF_HOST "127.0.0.1" +#define MBMON_DEF_PORT "411" /* the default for Debian */ + +/* BUFFER_SIZE + Size of the buffer we use to receive from the mbmon daemon. */ +#define BUFFER_SIZE 1024 + +static char *filename_format = "mbmon-%s.rrd"; + +static char *ds_def[] = +{ + "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U", + NULL +}; +static int ds_num = 1; + +static char *config_keys[] = +{ + "Host", + "Port", + NULL +}; +static int config_keys_num = 2; + +static char *mbmon_host = NULL; +static char *mbmon_port = NULL; + +/* + * NAME + * mbmon_query_daemon + * + * DESCRIPTION + * Connect to the mbmon daemon and receive data. + * + * ARGUMENTS: + * `buffer' The buffer where we put the received ascii string. + * `buffer_size' Size of the buffer + * + * RETURN VALUE: + * >= 0 if ok, < 0 otherwise. + * + * NOTES: + * Example of possible strings, as received from daemon: + * TEMP0 : 27.0 + * TEMP1 : 31.0 + * TEMP2 : 29.5 + * FAN0 : 4411 + * FAN1 : 4470 + * FAN2 : 4963 + * VC0 : +1.68 + * VC1 : +1.73 + * + * FIXME: + * we need to create a new socket each time. Is there another way? + * Hm, maybe we can re-use the `sockaddr' structure? -octo + */ +static int mbmon_query_daemon (char *buffer, int buffer_size) +{ + int fd; + ssize_t status; + int buffer_fill; + + const char *host; + const char *port; + + struct addrinfo ai_hints; + struct addrinfo *ai_list, *ai_ptr; + int ai_return; + + memset (&ai_hints, '\0', sizeof (ai_hints)); + ai_hints.ai_flags = AI_ADDRCONFIG; + ai_hints.ai_family = PF_UNSPEC; + ai_hints.ai_socktype = SOCK_STREAM; + ai_hints.ai_protocol = IPPROTO_TCP; + + host = mbmon_host; + if (host == NULL) + host = MBMON_DEF_HOST; + + port = mbmon_port; + if (port == NULL) + port = MBMON_DEF_PORT; + + if ((ai_return = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0) + { + syslog (LOG_ERR, "mbmon: getaddrinfo (%s, %s): %s", + host, port, + ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return)); + return (-1); + } + + fd = -1; + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) + { + /* create our socket descriptor */ + if ((fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol)) < 0) + { + syslog (LOG_ERR, "mbmon: socket: %s", + strerror (errno)); + continue; + } + + /* connect to the mbmon daemon */ + if (connect (fd, (struct sockaddr *) ai_ptr->ai_addr, ai_ptr->ai_addrlen)) + { + DBG ("mbmon: connect (%s, %s): %s", host, port, + strerror (errno)); + close (fd); + fd = -1; + continue; + } + + /* A socket could be opened and connecting succeeded. We're + * done. */ + break; + } + + freeaddrinfo (ai_list); + + if (fd < 0) + { + syslog (LOG_ERR, "mbmon: Could not connect to daemon."); + return (-1); + } + + /* receive data from the mbmon daemon */ + memset (buffer, '\0', buffer_size); + + buffer_fill = 0; + while ((status = read (fd, buffer + buffer_fill, buffer_size - buffer_fill)) != 0) + { + if (status == -1) + { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + + syslog (LOG_ERR, "mbmon: Error reading from socket: %s", + strerror (errno)); + close (fd); + return (-1); + } + buffer_fill += status; + + if (buffer_fill >= buffer_size) + break; + } + + if (buffer_fill >= buffer_size) + { + buffer[buffer_size - 1] = '\0'; + syslog (LOG_WARNING, "mbmon: Message from mbmon has been truncated."); + } + else if (buffer_fill == 0) + { + syslog (LOG_WARNING, "mbmon: Peer has unexpectedly shut down the socket. " + "Buffer: `%s'", buffer); + close (fd); + return (-1); + } + + close (fd); + return (0); +} + +static int mbmon_config (char *key, char *value) +{ + if (strcasecmp (key, "host") == 0) + { + if (mbmon_host != NULL) + free (mbmon_host); + mbmon_host = strdup (value); + } + else if (strcasecmp (key, "port") == 0) + { + if (mbmon_port != NULL) + free (mbmon_port); + mbmon_port = strdup (value); + } + else + { + return (-1); + } + + return (0); +} + +static void mbmon_init (void) +{ + return; +} + +static void mbmon_write (char *host, char *inst, char *val) +{ + char filename[BUFFER_SIZE]; + int status; + + /* construct filename */ + status = snprintf (filename, BUFFER_SIZE, filename_format, inst); + if (status < 1) + return; + else if (status >= BUFFER_SIZE) + return; + + rrd_update_file (host, filename, val, ds_def, ds_num); +} + +static void mbmon_submit (char *inst, double value) +{ + char buf[BUFFER_SIZE]; + + if (snprintf (buf, BUFFER_SIZE, "%u:%.3f", (unsigned int) curtime, value) + >= BUFFER_SIZE) + return; + + plugin_submit (MODULE_NAME, inst, buf); +} + +/* Trim trailing whitespace from a string. */ +static void trim_spaces (char *s) +{ + size_t l; + + for (l = strlen (s) - 1; (l > 0) && isspace (s[l]); l--) + s[l] = '\0'; +} + +static void mbmon_read (void) +{ + char buf[BUFFER_SIZE]; + char *s, *t; + + static int wait_time = 1; + static int wait_left = 0; + + if (wait_left >= 10) + { + wait_left -= 10; + return; + } + + /* get data from daemon */ + if (mbmon_query_daemon (buf, BUFFER_SIZE) < 0) + { + /* This limit is reached in log2(86400) =~ 17 steps. Since + * there is a 2^n seconds wait between each step it will need + * roughly one day to reach this limit. -octo */ + + wait_time *= 2; + if (wait_time > 86400) + wait_time = 86400; + + wait_left = wait_time; + + return; + } + else + { + wait_time = 1; + wait_left = 0; + } + + s = buf; + while ((t = strchr (s, ':')) != NULL) + { + double value; + char *nextc; + + *t++ = '\0'; + trim_spaces (s); + + value = strtod (t, &nextc); + if ((*nextc != '\n') && (*nextc != '\0')) + { + syslog (LOG_ERR, "mbmon: value for `%s' contains invalid characters: `%s'", s, t); + break; + } + + mbmon_submit (s, value); + + if (*nextc == '\0') + break; + + s = nextc + 1; + } +} + +/* module_register + Register collectd plugin. */ +void module_register (void) +{ + plugin_register (MODULE_NAME, mbmon_init, mbmon_read, mbmon_write); + cf_register (MODULE_NAME, mbmon_config, config_keys, config_keys_num); +} + +#undef MODULE_NAME -- 2.11.0