Merge branch 'collectd-4.2' into collectd-4.3
[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  **/
23
24 #include "collectd.h"
25 #include "common.h"
26 #include "plugin.h"
27 #include "configfile.h"
28
29 #include <netdb.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <netinet/tcp.h>
33
34 #define MBMON_DEF_HOST "127.0.0.1"
35 #define MBMON_DEF_PORT "411" /* the default for Debian */
36
37 static const char *config_keys[] =
38 {
39         "Host",
40         "Port",
41         NULL
42 };
43 static int config_keys_num = 2;
44
45 static char *mbmon_host = NULL;
46 static char *mbmon_port = NULL;
47
48 /*
49  * NAME
50  *  mbmon_query_daemon
51  *
52  * DESCRIPTION
53  * Connect to the mbmon daemon and receive data.
54  *
55  * ARGUMENTS:
56  *  `buffer'            The buffer where we put the received ascii string.
57  *  `buffer_size'       Size of the buffer
58  *
59  * RETURN VALUE:
60  *   >= 0 if ok, < 0 otherwise.
61  *
62  * NOTES:
63  *  Example of possible strings, as received from daemon:
64  *    TEMP0 : 27.0
65  *    TEMP1 : 31.0
66  *    TEMP2 : 29.5
67  *    FAN0  : 4411
68  *    FAN1  : 4470
69  *    FAN2  : 4963
70  *    VC0   :  +1.68
71  *    VC1   :  +1.73
72  *
73  * FIXME:
74  *  we need to create a new socket each time. Is there another way?
75  *  Hm, maybe we can re-use the `sockaddr' structure? -octo
76  */
77 static int mbmon_query_daemon (char *buffer, int buffer_size)
78 {
79         int fd;
80         ssize_t status;
81         int buffer_fill;
82
83         const char *host;
84         const char *port;
85
86         struct addrinfo  ai_hints;
87         struct addrinfo *ai_list, *ai_ptr;
88         int              ai_return;
89
90         memset (&ai_hints, '\0', sizeof (ai_hints));
91         ai_hints.ai_flags    = 0;
92 #ifdef AI_ADDRCONFIG
93         ai_hints.ai_flags   |= AI_ADDRCONFIG;
94 #endif
95         ai_hints.ai_family   = PF_UNSPEC;
96         ai_hints.ai_socktype = SOCK_STREAM;
97         ai_hints.ai_protocol = IPPROTO_TCP;
98
99         host = mbmon_host;
100         if (host == NULL)
101                 host = MBMON_DEF_HOST;
102
103         port = mbmon_port;
104         if (port == NULL)
105                 port = MBMON_DEF_PORT;
106
107         if ((ai_return = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0)
108         {
109                 char errbuf[1024];
110                 ERROR ("mbmon: getaddrinfo (%s, %s): %s",
111                                 host, port,
112                                 (ai_return == EAI_SYSTEM)
113                                 ? sstrerror (errno, errbuf, sizeof (errbuf))
114                                 : gai_strerror (ai_return));
115                 return (-1);
116         }
117
118         fd = -1;
119         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
120         {
121                 /* create our socket descriptor */
122                 if ((fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol)) < 0)
123                 {
124                         char errbuf[1024];
125                         ERROR ("mbmon: socket: %s",
126                                         sstrerror (errno, errbuf,
127                                                 sizeof (errbuf)));
128                         continue;
129                 }
130
131                 /* connect to the mbmon daemon */
132                 if (connect (fd, (struct sockaddr *) ai_ptr->ai_addr, ai_ptr->ai_addrlen))
133                 {
134                         char errbuf[1024];
135                         INFO ("mbmon: connect (%s, %s): %s", host, port,
136                                         sstrerror (errno, errbuf,
137                                                 sizeof (errbuf)));
138                         close (fd);
139                         fd = -1;
140                         continue;
141                 }
142
143                 /* A socket could be opened and connecting succeeded. We're
144                  * done. */
145                 break;
146         }
147
148         freeaddrinfo (ai_list);
149
150         if (fd < 0)
151         {
152                 ERROR ("mbmon: Could not connect to daemon.");
153                 return (-1);
154         }
155
156         /* receive data from the mbmon daemon */
157         memset (buffer, '\0', buffer_size);
158
159         buffer_fill = 0;
160         while ((status = read (fd, buffer + buffer_fill, buffer_size - buffer_fill)) != 0)
161         {
162                 if (status == -1)
163                 {
164                         char errbuf[1024];
165
166                         if ((errno == EAGAIN) || (errno == EINTR))
167                                 continue;
168
169                         ERROR ("mbmon: Error reading from socket: %s",
170                                         sstrerror (errno, errbuf,
171                                                 sizeof (errbuf)));
172                         close (fd);
173                         return (-1);
174                 }
175                 buffer_fill += status;
176
177                 if (buffer_fill >= buffer_size)
178                         break;
179         }
180
181         if (buffer_fill >= buffer_size)
182         {
183                 buffer[buffer_size - 1] = '\0';
184                 WARNING ("mbmon: Message from mbmon has been truncated.");
185         }
186         else if (buffer_fill == 0)
187         {
188                 WARNING ("mbmon: Peer has unexpectedly shut down the socket. "
189                                 "Buffer: `%s'", buffer);
190                 close (fd);
191                 return (-1);
192         }
193
194         close (fd);
195         return (0);
196 }
197
198 static int mbmon_config (const char *key, const char *value)
199 {
200         if (strcasecmp (key, "host") == 0)
201         {
202                 if (mbmon_host != NULL)
203                         free (mbmon_host);
204                 mbmon_host = strdup (value);
205         }
206         else if (strcasecmp (key, "port") == 0)
207         {
208                 if (mbmon_port != NULL)
209                         free (mbmon_port);
210                 mbmon_port = strdup (value);
211         }
212         else
213         {
214                 return (-1);
215         }
216
217         return (0);
218 }
219
220 static void mbmon_submit (const char *type, const char *type_instance,
221                 double value)
222 {
223         value_t values[1];
224         value_list_t vl = VALUE_LIST_INIT;
225
226         values[0].gauge = value;
227
228         vl.values = values;
229         vl.values_len = 1;
230         vl.time = time (NULL);
231         strcpy (vl.host, hostname_g);
232         strcpy (vl.plugin, "mbmon");
233         strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
234
235         plugin_dispatch_values (type, &vl);
236 } /* void mbmon_submit */
237
238 /* Trim trailing whitespace from a string. */
239 static void trim_spaces (char *s)
240 {
241         size_t l;
242
243         for (l = strlen (s) - 1; (l > 0) && isspace ((int) s[l]); l--)
244                 s[l] = '\0';
245 }
246
247 static int mbmon_read (void)
248 {
249         char buf[1024];
250         char *s, *t;
251
252         /* get data from daemon */
253         if (mbmon_query_daemon (buf, sizeof (buf)) < 0)
254                 return (-1);
255
256         s = buf;
257         while ((t = strchr (s, ':')) != NULL)
258         {
259                 double value;
260                 char *nextc;
261
262                 char *type;
263                 char *inst;
264
265                 *t++ = '\0';
266                 trim_spaces (s);
267
268                 value = strtod (t, &nextc);
269                 if ((*nextc != '\n') && (*nextc != '\0'))
270                 {
271                         ERROR ("mbmon: value for `%s' contains invalid characters: `%s'", s, t);
272                         break;
273                 }
274
275                 if (strncmp (s, "TEMP", 4) == 0)
276                 {
277                         inst = s + 4;
278                         type = "temperature";
279                 }
280                 else if (strncmp (s, "FAN", 3) == 0)
281                 {
282                         inst = s + 3;
283                         type = "fanspeed";
284                 }
285                 else if (strncmp (s, "V", 1) == 0)
286                 {
287                         inst = s + 1;
288                         type = "voltage";
289                 }
290                 else
291                 {
292                         continue;
293                 }
294
295                 mbmon_submit (type, inst, value);
296
297                 if (*nextc == '\0')
298                         break;
299
300                 s = nextc + 1;
301         }
302
303         return (0);
304 } /* void mbmon_read */
305
306 /* module_register
307    Register collectd plugin. */
308 void module_register (void)
309 {
310         plugin_register_config ("mbmon", mbmon_config, config_keys, config_keys_num);
311         plugin_register_read ("mbmon", mbmon_read);
312 } /* void module_register */