http plugin: Make the output match the PUTVAL plain-text protocol
[collectd.git] / src / http.c
1 /**
2  * collectd - src/http.c
3  * Copyright (C) 2007-2009  Florian octo Forster
4  * Copyright (C) 2009       Doug MacEachern
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; only version 2 of the License is applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  *   Doug MacEachern <dougm@hyperic.com>
22  **/
23
24 #include "collectd.h"
25 #include "plugin.h"
26 #include "common.h"
27 #include "utils_cache.h"
28 #include "utils_parse_option.h"
29
30 #if HAVE_PTHREAD_H
31 # include <pthread.h>
32 #endif
33
34 #include <curl/curl.h>
35
36 /*
37  * Private variables
38  */
39 static const char *config_keys[] =
40 {
41         "Location", "User", "Password"
42 };
43 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
44
45 static char *location   = NULL;
46
47 char *user;
48 char *pass;
49 char *credentials;
50
51 CURL *curl;
52 char curl_errbuf[CURL_ERROR_SIZE];
53
54 #define SEND_BUFFER_SIZE 4096
55 static char   send_buffer[SEND_BUFFER_SIZE];
56 static char  *send_buffer_ptr;
57 static int    send_buffer_fill;
58
59 static pthread_mutex_t  send_lock = PTHREAD_MUTEX_INITIALIZER;
60
61 static int http_init(void) /* {{{ */
62 {
63
64         curl = curl_easy_init ();
65
66         if (curl == NULL)
67         {
68                 ERROR ("curl plugin: curl_easy_init failed.");
69                 return (-1);
70         }
71
72         struct curl_slist *headers=NULL;
73
74         curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
75
76         headers = curl_slist_append(headers, "Accept:  */*");
77         headers = curl_slist_append(headers, "Content-Type: text/plain");
78         curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headers);
79
80         curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, curl_errbuf);
81         curl_easy_setopt (curl, CURLOPT_URL, location);
82
83         if (user != NULL)
84         {
85                 size_t credentials_size;
86
87                 credentials_size = strlen (user) + 2;
88                 if (pass != NULL)
89                         credentials_size += strlen (pass);
90
91                 credentials = (char *) malloc (credentials_size);
92                 if (credentials == NULL)
93                 {
94                         ERROR ("curl plugin: malloc failed.");
95                         return (-1);
96                 }
97
98                 ssnprintf (credentials, credentials_size, "%s:%s",
99                                 user, (pass == NULL) ? "" : pass);
100                 curl_easy_setopt (curl, CURLOPT_USERPWD, credentials);
101                 curl_easy_setopt (curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
102         }
103
104         return (0);
105 } /* }}} */
106
107 static int http_value_list_to_string (char *buffer, int buffer_len, /* {{{ */
108                 const data_set_t *ds, const value_list_t *vl)
109 {
110         int offset = 0;
111         int status;
112         int i;
113         gauge_t *rates = NULL;
114
115         assert (0 == strcmp (ds->type, vl->type));
116
117         memset (buffer, '\0', buffer_len);
118
119         for (i = 0; i < ds->ds_num; i++)
120         {
121                 if ((ds->ds[i].type != DS_TYPE_COUNTER)
122                                 && (ds->ds[i].type != DS_TYPE_GAUGE)
123                                 && (ds->ds[i].type != DS_TYPE_DERIVE)
124                                 && (ds->ds[i].type != DS_TYPE_ABSOLUTE))
125                         return (-1);
126
127                 if (ds->ds[i].type == DS_TYPE_GAUGE) 
128                 {
129                         status = ssnprintf (buffer + offset, buffer_len - offset,
130                                         ":%lf", vl->values[i].gauge);
131                 } 
132                 else if (ds->ds[i].type == DS_TYPE_COUNTER)
133                 {
134                         if (rates == NULL)
135                                 rates = uc_get_rate (ds, vl);
136                         if (rates == NULL)
137                         {
138                                 WARNING ("csv plugin: "
139                                                 "uc_get_rate failed.");
140                                 return (-1);
141                         }
142                         status = ssnprintf (buffer + offset,
143                                         buffer_len - offset,
144                                         ":%lf", rates[i]);
145                 }
146                 else if (ds->ds[i].type == DS_TYPE_DERIVE)
147                 {
148                         status = ssnprintf (buffer + offset,
149                                         buffer_len - offset,
150                                         ":%"PRIi64,
151                                         vl->values[i].derive);
152                 }
153                 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
154                 {
155                         status = ssnprintf (buffer + offset,
156                                         buffer_len - offset,
157                                         ":%"PRIu64,
158                                         vl->values[i].absolute);
159                 }
160
161                 if ((status < 1) || (status >= (buffer_len - offset)))
162                 {
163                         sfree (rates);
164                         return (-1);
165                 }
166
167                 offset += status;
168         } /* for ds->ds_num */
169
170         sfree (rates);
171         return (0);
172 } /* }}} int http_value_list_to_string */
173
174 static int http_config (const char *key, const char *value) /* {{{ */
175 {
176         if (strcasecmp ("Location", key) == 0)
177         {
178                 if (location != NULL)
179                         free (location);
180                 location = strdup (value);
181                 if (location != NULL)
182                 {
183                         int len = strlen (location);
184                         while ((len > 0) && (location[len - 1] == '/'))
185                         {
186                                 len--;
187                                 location[len] = '\0';
188                         }
189                         if (len <= 0)
190                         {
191                                 free (location);
192                                 location = NULL;
193                         }
194                 }
195         }
196         else if (strcasecmp ("User", key) == 0)
197         {
198                 if (user != NULL)
199                         free (user);
200                 user = strdup (value);
201                 if (user != NULL)
202                 {
203                         int len = strlen (user);
204                         while ((len > 0) && (user[len - 1] == '/'))
205                         {
206                                 len--;
207                                 user[len] = '\0';
208                         }
209                         if (len <= 0)
210                         {
211                                 free (user);
212                                 user = NULL;
213                         }
214                 }
215         }
216         else if (strcasecmp ("Password", key) == 0)
217         {
218                 if (pass != NULL)
219                         free (pass);
220                 pass = strdup (value);
221                 if (pass != NULL)
222                 {
223                         int len = strlen (pass);
224                         while ((len > 0) && (pass[len - 1] == '/'))
225                         {
226                                 len--;
227                                 pass[len] = '\0';
228                         }
229                         if (len <= 0)
230                         {
231                                 free (pass);
232                                 pass = NULL;
233                         }
234                 }
235         }
236         else
237         {
238                 return (-1);
239         }
240         return (0);
241 } /* }}} int http_config */
242
243 static void http_init_buffer (void)  /* {{{ */
244 {
245         memset (send_buffer, 0, sizeof (send_buffer));
246         send_buffer_ptr = send_buffer;
247         send_buffer_fill = 0;
248 } /* }}} http_init_buffer */
249
250 static int http_send_buffer (char *buffer) /* {{{ */
251 {
252         int status = 0;
253         curl_easy_setopt (curl, CURLOPT_POSTFIELDS, buffer);
254         //status = curl_easy_perform (curl);
255         if (status != 0)
256         {
257                 ERROR ("http plugin: curl_easy_perform failed with staus %i: %s",
258                                 status, curl_errbuf);
259         }
260         return (status);
261 } /* }}} http_send_buffer */
262
263 static int http_flush_buffer (void) /* {{{ */
264 {
265         int status = 0;
266         DEBUG ("http plugin: flushing buffer:\n%s", send_buffer);
267
268         status = http_send_buffer (send_buffer);
269         http_init_buffer ();
270
271         return (status);
272 } /* }}} http_flush_buffer */
273
274 static int http_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
275                 user_data_t __attribute__((unused)) *user_data)
276 {
277
278         char key[1024];
279         char values[512];
280
281         int status;
282
283         if (0 != strcmp (ds->type, vl->type)) {
284                 ERROR ("http plugin: DS type does not match value list type");
285                 return -1;
286         }
287
288         status = format_name( key, sizeof(key), vl->host, vl->plugin,
289                         vl->plugin_instance, vl->type, vl->type_instance );
290
291         if (status != 0) {
292                 ERROR ("http plugin: error with format_name");
293                 return (status);
294         }
295
296         status = http_value_list_to_string (values, sizeof (values), ds, vl);
297
298         if (status != 0) {
299                 ERROR ("http plugin: error with http_value_list_to_string");
300                 return (status);
301         }
302
303
304         pthread_mutex_lock (&send_lock);
305
306         status = ssnprintf (send_buffer + send_buffer_fill, sizeof (send_buffer) - send_buffer_fill,
307                         "PUTVAL %s interval=%i %u%s\n",
308                         key, interval_g, vl->time, values);
309         send_buffer_fill += status;
310
311         if ((sizeof (send_buffer) - send_buffer_fill) < (sizeof(key) + sizeof(values)))
312         {
313                 status = http_flush_buffer();
314                 if (status != 0)
315                         return status;
316
317         }
318
319         pthread_mutex_unlock (&send_lock);
320
321
322         return (0);
323
324 } /* }}} int http_write */
325
326 static int http_shutdown (void) /* {{{ */
327 {
328         http_flush_buffer();
329         curl_easy_cleanup(curl);
330         return (0);
331 }
332
333 void module_register (void) /* {{{ */
334 {
335         plugin_register_init("http", http_init);
336         plugin_register_config ("http", http_config,
337                         config_keys, config_keys_num);
338         plugin_register_write ("http", http_write, /* user_data = */ NULL);
339         plugin_register_shutdown("http", http_shutdown);
340 } /* }}} void module_register */
341
342 /* vim: set fdm=marker sw=8 ts=8 tw=78 : */