Code

Implemented `LoadDS' which tells plugins to only register their DataSources.
[collectd.git] / src / mbmon.c
1 /**
2  * collectd - src/mbmon.c
3  * Copyright (C) 2006 Flavio Stanchina
4  * Based on the hddtemp plugin.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  *
20  * Authors:
21  *   Flavio Stanchina <flavio at stanchina.net>
22  **/
24 #include "collectd.h"
25 #include "common.h"
26 #include "plugin.h"
27 #include "configfile.h"
29 #if HAVE_NETDB_H && HAVE_SYS_SOCKET_H && HAVE_NETINET_IN_H && HAVE_NETINET_TCP_H
30 # include <netdb.h>
31 # include <sys/socket.h>
32 # include <netinet/in.h>
33 # include <netinet/tcp.h>
34 # define MBMON_HAVE_READ 1
35 #else
36 # define MBMON_HAVE_READ 0
37 #endif
39 #define MBMON_DEF_HOST "127.0.0.1"
40 #define MBMON_DEF_PORT "411" /* the default for Debian */
42 static data_source_t data_source_fanspeed[1] =
43 {
44         {"value", DS_TYPE_GAUGE, 0, NAN}
45 };
47 static data_set_t fanspeed_ds =
48 {
49         "fanspeed", 1, data_source_fanspeed
50 };
52 static data_source_t data_source_temperature[1] =
53 {
54         {"value", DS_TYPE_GAUGE, -273.15, NAN}
55 };
57 static data_set_t temperature_ds =
58 {
59         "temperature", 1, data_source_temperature
60 };
62 static data_source_t data_source_voltage[1] =
63 {
64         {"voltage", DS_TYPE_GAUGE, NAN, NAN}
65 };
67 static data_set_t voltage_ds =
68 {
69         "voltage", 1, data_source_voltage
70 };
72 static const char *config_keys[] =
73 {
74         "Host",
75         "Port",
76         NULL
77 };
78 static int config_keys_num = 2;
80 #if MBMON_HAVE_READ
81 static char *mbmon_host = NULL;
82 static char *mbmon_port = NULL;
84 /*
85  * NAME
86  *  mbmon_query_daemon
87  *
88  * DESCRIPTION
89  * Connect to the mbmon daemon and receive data.
90  *
91  * ARGUMENTS:
92  *  `buffer'            The buffer where we put the received ascii string.
93  *  `buffer_size'       Size of the buffer
94  *
95  * RETURN VALUE:
96  *   >= 0 if ok, < 0 otherwise.
97  *
98  * NOTES:
99  *  Example of possible strings, as received from daemon:
100  *    TEMP0 : 27.0
101  *    TEMP1 : 31.0
102  *    TEMP2 : 29.5
103  *    FAN0  : 4411
104  *    FAN1  : 4470
105  *    FAN2  : 4963
106  *    VC0   :  +1.68
107  *    VC1   :  +1.73
108  *
109  * FIXME:
110  *  we need to create a new socket each time. Is there another way?
111  *  Hm, maybe we can re-use the `sockaddr' structure? -octo
112  */
113 static int mbmon_query_daemon (char *buffer, int buffer_size)
115         int fd;
116         ssize_t status;
117         int buffer_fill;
119         const char *host;
120         const char *port;
122         struct addrinfo  ai_hints;
123         struct addrinfo *ai_list, *ai_ptr;
124         int              ai_return;
126         memset (&ai_hints, '\0', sizeof (ai_hints));
127         ai_hints.ai_flags    = AI_ADDRCONFIG;
128         ai_hints.ai_family   = PF_UNSPEC;
129         ai_hints.ai_socktype = SOCK_STREAM;
130         ai_hints.ai_protocol = IPPROTO_TCP;
132         host = mbmon_host;
133         if (host == NULL)
134                 host = MBMON_DEF_HOST;
136         port = mbmon_port;
137         if (port == NULL)
138                 port = MBMON_DEF_PORT;
140         if ((ai_return = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0)
141         {
142                 char errbuf[1024];
143                 ERROR ("mbmon: getaddrinfo (%s, %s): %s",
144                                 host, port,
145                                 (ai_return == EAI_SYSTEM)
146                                 ? sstrerror (errno, errbuf, sizeof (errbuf))
147                                 : gai_strerror (ai_return));
148                 return (-1);
149         }
151         fd = -1;
152         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
153         {
154                 /* create our socket descriptor */
155                 if ((fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol)) < 0)
156                 {
157                         char errbuf[1024];
158                         ERROR ("mbmon: socket: %s",
159                                         sstrerror (errno, errbuf,
160                                                 sizeof (errbuf)));
161                         continue;
162                 }
164                 /* connect to the mbmon daemon */
165                 if (connect (fd, (struct sockaddr *) ai_ptr->ai_addr, ai_ptr->ai_addrlen))
166                 {
167                         char errbuf[1024];
168                         DEBUG ("mbmon: connect (%s, %s): %s", host, port,
169                                         sstrerror (errno, errbuf,
170                                                 sizeof (errbuf)));
171                         close (fd);
172                         fd = -1;
173                         continue;
174                 }
176                 /* A socket could be opened and connecting succeeded. We're
177                  * done. */
178                 break;
179         }
181         freeaddrinfo (ai_list);
183         if (fd < 0)
184         {
185                 ERROR ("mbmon: Could not connect to daemon.");
186                 return (-1);
187         }
189         /* receive data from the mbmon daemon */
190         memset (buffer, '\0', buffer_size);
192         buffer_fill = 0;
193         while ((status = read (fd, buffer + buffer_fill, buffer_size - buffer_fill)) != 0)
194         {
195                 if (status == -1)
196                 {
197                         char errbuf[1024];
199                         if ((errno == EAGAIN) || (errno == EINTR))
200                                 continue;
202                         ERROR ("mbmon: Error reading from socket: %s",
203                                         sstrerror (errno, errbuf,
204                                                 sizeof (errbuf)));
205                         close (fd);
206                         return (-1);
207                 }
208                 buffer_fill += status;
210                 if (buffer_fill >= buffer_size)
211                         break;
212         }
214         if (buffer_fill >= buffer_size)
215         {
216                 buffer[buffer_size - 1] = '\0';
217                 WARNING ("mbmon: Message from mbmon has been truncated.");
218         }
219         else if (buffer_fill == 0)
220         {
221                 WARNING ("mbmon: Peer has unexpectedly shut down the socket. "
222                                 "Buffer: `%s'", buffer);
223                 close (fd);
224                 return (-1);
225         }
227         close (fd);
228         return (0);
231 static int mbmon_config (const char *key, const char *value)
233         if (strcasecmp (key, "host") == 0)
234         {
235                 if (mbmon_host != NULL)
236                         free (mbmon_host);
237                 mbmon_host = strdup (value);
238         }
239         else if (strcasecmp (key, "port") == 0)
240         {
241                 if (mbmon_port != NULL)
242                         free (mbmon_port);
243                 mbmon_port = strdup (value);
244         }
245         else
246         {
247                 return (-1);
248         }
250         return (0);
253 static void mbmon_submit (const char *type, const char *type_instance,
254                 double value)
256         value_t values[1];
257         value_list_t vl = VALUE_LIST_INIT;
259         values[0].gauge = value;
261         vl.values = values;
262         vl.values_len = 1;
263         vl.time = time (NULL);
264         strcpy (vl.host, hostname_g);
265         strcpy (vl.plugin, "mbmon");
266         strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
268         plugin_dispatch_values (type, &vl);
269 } /* void mbmon_submit */
271 /* Trim trailing whitespace from a string. */
272 static void trim_spaces (char *s)
274         size_t l;
276         for (l = strlen (s) - 1; (l > 0) && isspace (s[l]); l--)
277                 s[l] = '\0';
280 static int mbmon_read (void)
282         char buf[1024];
283         char *s, *t;
285         /* get data from daemon */
286         if (mbmon_query_daemon (buf, sizeof (buf)) < 0)
287                 return (-1);
289         s = buf;
290         while ((t = strchr (s, ':')) != NULL)
291         {
292                 double value;
293                 char *nextc;
295                 char *type;
296                 char *inst;
298                 *t++ = '\0';
299                 trim_spaces (s);
301                 value = strtod (t, &nextc);
302                 if ((*nextc != '\n') && (*nextc != '\0'))
303                 {
304                         ERROR ("mbmon: value for `%s' contains invalid characters: `%s'", s, t);
305                         break;
306                 }
308                 if (strncmp (s, "TEMP", 4) == 0)
309                 {
310                         inst = s + 4;
311                         type = "temperature";
312                 }
313                 else if (strncmp (s, "FAN", 3) == 0)
314                 {
315                         inst = s + 3;
316                         type = "fanspeed";
317                 }
318                 else if (strncmp (s, "V", 1) == 0)
319                 {
320                         inst = s + 1;
321                         type = "voltage";
322                 }
323                 else
324                 {
325                         continue;
326                 }
328                 mbmon_submit (type, inst, value);
330                 if (*nextc == '\0')
331                         break;
333                 s = nextc + 1;
334         }
336         return (0);
337 } /* void mbmon_read */
338 #endif /* MBMON_HAVE_READ */
340 /* module_register
341    Register collectd plugin. */
342 void module_register (modreg_e load)
344         if (load & MR_DATASETS)
345         {
346                 plugin_register_data_set (&fanspeed_ds);
347                 plugin_register_data_set (&temperature_ds);
348                 plugin_register_data_set (&voltage_ds);
349         }
351 #if MBMON_HAVE_READ
352         if (load & MR_READ)
353         {
354                 plugin_register_config ("mbmon", mbmon_config, config_keys, config_keys_num);
355                 plugin_register_read ("mbmon", mbmon_read);
356         }
357 #endif /* MBMON_HAVE_READ */
358 } /* void module_register */