Merge remote-tracking branch 'github/pr/631'
[collectd.git] / src / hddtemp.c
1 /**
2  * collectd - src/hddtemp.c
3  * Copyright (C) 2005,2006  Vincent StehlĂ©
4  * Copyright (C) 2006-2010  Florian octo Forster
5  * Copyright (C) 2008       Sebastian Harl
6  * Copyright (C) 2014       Carnegie Mellon University
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2 of the License, or (at your
11  * option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
21  *
22  * Authors:
23  *   Vincent StehlĂ© <vincent.stehle at free.fr>
24  *   Florian octo Forster <octo at collectd.org>
25  *   Sebastian Harl <sh at tokkee.org>
26  *   Benjamin Gilbert <bgilbert at backtick.net>
27  *
28  * TODO:
29  *   Do a pass, some day, and spare some memory. We consume too much for now
30  *   in string buffers and the like.
31  *
32  **/
33
34 #include "collectd.h"
35
36 #include "common.h"
37 #include "plugin.h"
38
39 # include <netdb.h>
40 # include <netinet/in.h>
41 # include <netinet/tcp.h>
42 # include <libgen.h> /* for basename */
43 # include <assert.h>
44
45 #if HAVE_LINUX_MAJOR_H
46 # include <linux/major.h>
47 #endif
48
49 #define HDDTEMP_DEF_HOST "127.0.0.1"
50 #define HDDTEMP_DEF_PORT "7634"
51 #define HDDTEMP_MAX_RECV_BUF (1 << 20)
52
53 static const char *config_keys[] =
54 {
55         "Host",
56         "Port"
57 };
58 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
59
60 static char *hddtemp_host = NULL;
61 static char hddtemp_port[16];
62
63 /*
64  * NAME
65  *  hddtemp_query_daemon
66  *
67  * DESCRIPTION
68  * Connect to the hddtemp daemon and receive data.
69  *
70  * ARGUMENTS:
71  *  `buffer'            The buffer where we put the received ascii string.
72  *  `buffer_size'       Size of the buffer
73  *
74  * RETURN VALUE:
75  *   >= 0 if ok, < 0 otherwise.
76  *
77  * NOTES:
78  *  Example of possible strings, as received from daemon:
79  *    |/dev/hda|ST340014A|36|C|
80  *    |/dev/hda|ST380011A|46|C||/dev/hdd|ST340016A|SLP|*|
81  *
82  * FIXME:
83  *  we need to create a new socket each time. Is there another way?
84  *  Hm, maybe we can re-use the `sockaddr' structure? -octo
85  */
86 static char *hddtemp_query_daemon (void)
87 {
88         int fd;
89         ssize_t status;
90
91         char *buffer;
92         int buffer_size;
93         int buffer_fill;
94         char *new_buffer;
95
96         const char *host;
97         const char *port;
98
99         struct addrinfo *ai_list;
100         int              ai_return;
101
102         host = hddtemp_host;
103         if (host == NULL)
104                 host = HDDTEMP_DEF_HOST;
105
106         port = hddtemp_port;
107         if (strlen (port) == 0)
108                 port = HDDTEMP_DEF_PORT;
109
110         struct addrinfo ai_hints = {
111                 .ai_flags = AI_ADDRCONFIG,
112                 .ai_family = AF_UNSPEC,
113                 .ai_protocol = IPPROTO_TCP,
114                 .ai_socktype = SOCK_STREAM
115         };
116
117         if ((ai_return = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0)
118         {
119                 char errbuf[1024];
120                 ERROR ("hddtemp plugin: getaddrinfo (%s, %s): %s",
121                                 host, port,
122                                 (ai_return == EAI_SYSTEM)
123                                 ? sstrerror (errno, errbuf, sizeof (errbuf))
124                                 : gai_strerror (ai_return));
125                 return (NULL);
126         }
127
128         fd = -1;
129         for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
130         {
131                 /* create our socket descriptor */
132                 fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype,
133                                 ai_ptr->ai_protocol);
134                 if (fd < 0)
135                 {
136                         char errbuf[1024];
137                         ERROR ("hddtemp plugin: socket: %s",
138                                         sstrerror (errno, errbuf, sizeof (errbuf)));
139                         continue;
140                 }
141
142                 /* connect to the hddtemp daemon */
143                 if (connect (fd, (struct sockaddr *) ai_ptr->ai_addr,
144                                         ai_ptr->ai_addrlen))
145                 {
146                         char errbuf[1024];
147                         INFO ("hddtemp plugin: connect (%s, %s) failed: %s",
148                                         host, port,
149                                         sstrerror (errno, errbuf, sizeof (errbuf)));
150                         close (fd);
151                         fd = -1;
152                         continue;
153                 }
154
155                 /* A socket could be opened and connecting succeeded. We're
156                  * done. */
157                 break;
158         }
159
160         freeaddrinfo (ai_list);
161
162         if (fd < 0)
163         {
164                 ERROR ("hddtemp plugin: Could not connect to daemon.");
165                 return (NULL);
166         }
167
168         /* receive data from the hddtemp daemon */
169         buffer = NULL;
170         buffer_size = 0;
171         buffer_fill = 0;
172         while (1)
173         {
174                 if ((buffer_size == 0) || (buffer_fill >= buffer_size - 1))
175                 {
176                         if (buffer_size == 0)
177                                 buffer_size = 1024;
178                         else
179                                 buffer_size *= 2;
180                         if (buffer_size > HDDTEMP_MAX_RECV_BUF)
181                         {
182                                 WARNING ("hddtemp plugin: Message from hddtemp has been "
183                                                 "truncated.");
184                                 break;
185                         }
186                         new_buffer = realloc (buffer, buffer_size);
187                         if (new_buffer == NULL) {
188                                 close (fd);
189                                 free (buffer);
190                                 ERROR ("hddtemp plugin: Allocation failed.");
191                                 return (NULL);
192                         }
193                         buffer = new_buffer;
194                 }
195                 status = read (fd, buffer + buffer_fill, buffer_size - buffer_fill - 1);
196                 if (status == 0) {
197                         break;
198                 }
199                 else if (status == -1)
200                 {
201                         char errbuf[1024];
202
203                         if ((errno == EAGAIN) || (errno == EINTR))
204                                 continue;
205
206                         ERROR ("hddtemp plugin: Error reading from socket: %s",
207                                         sstrerror (errno, errbuf, sizeof (errbuf)));
208                         close (fd);
209                         free (buffer);
210                         return (NULL);
211                 }
212                 buffer_fill += status;
213         }
214
215         if (buffer_fill == 0)
216         {
217                 WARNING ("hddtemp plugin: Peer has unexpectedly shut down "
218                                 "the socket. Buffer: `%s'", buffer);
219                 close (fd);
220                 free (buffer);
221                 return (NULL);
222         }
223
224         assert (buffer_fill < buffer_size);
225         buffer[buffer_fill] = '\0';
226         close (fd);
227         return (buffer);
228 }
229
230 static int hddtemp_config (const char *key, const char *value)
231 {
232         if (strcasecmp (key, "Host") == 0)
233         {
234                 if (hddtemp_host != NULL)
235                         free (hddtemp_host);
236                 hddtemp_host = strdup (value);
237         }
238         else if (strcasecmp (key, "Port") == 0)
239         {
240                 int port = (int) (atof (value));
241                 if ((port > 0) && (port <= 65535))
242                         ssnprintf (hddtemp_port, sizeof (hddtemp_port),
243                                         "%i", port);
244                 else
245                         sstrncpy (hddtemp_port, value, sizeof (hddtemp_port));
246         }
247         else
248         {
249                 return (-1);
250         }
251
252         return (0);
253 }
254
255 static void hddtemp_submit (char *type_instance, double value)
256 {
257         value_list_t vl = VALUE_LIST_INIT;
258
259         vl.values = &(value_t) { .gauge = value };
260         vl.values_len = 1;
261         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
262         sstrncpy (vl.plugin, "hddtemp", sizeof (vl.plugin));
263         sstrncpy (vl.type, "temperature", sizeof (vl.type));
264         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
265
266         plugin_dispatch_values (&vl);
267 }
268
269 static int hddtemp_read (void)
270 {
271         char *buf;
272         char *ptr;
273         char *saveptr;
274         char *name;
275         char *model;
276         char *temperature;
277         char *mode;
278
279         /* get data from daemon */
280         buf = hddtemp_query_daemon ();
281         if (buf == NULL)
282                 return (-1);
283
284         /* NB: strtok_r will eat up "||" and leading "|"'s */
285         ptr = buf;
286         saveptr = NULL;
287         while ((name = strtok_r (ptr, "|", &saveptr)) != NULL &&
288                (model = strtok_r (NULL, "|", &saveptr)) != NULL &&
289                (temperature = strtok_r (NULL, "|", &saveptr)) != NULL &&
290                (mode = strtok_r (NULL, "|", &saveptr)) != NULL)
291         {
292                 double temperature_value;
293
294                 ptr = NULL;
295
296                 /* Skip non-temperature information */
297                 if (mode[0] != 'C' && mode[0] != 'F')
298                         continue;
299
300                 name = basename (name);
301                 temperature_value = atof (temperature);
302
303                 /* Convert farenheit to celsius */
304                 if (mode[0] == 'F')
305                         temperature_value = (temperature_value - 32.0) * 5.0 / 9.0;
306
307                 hddtemp_submit (name, temperature_value);
308         }
309         
310         free (buf);
311         return (0);
312 } /* int hddtemp_read */
313
314 /* module_register
315    Register collectd plugin. */
316 void module_register (void)
317 {
318         plugin_register_config ("hddtemp", hddtemp_config,
319                         config_keys, config_keys_num);
320         plugin_register_read ("hddtemp", hddtemp_read);
321 }