995368c91114ed633b821b50503f370af14da478
[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: text/csv;q=0.8, */*;q=0.2");
77   headers = curl_slist_append(headers, "Content-Type: text/csv");
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, int index)
109 {
110   int offset = 0;
111   int status;
112   gauge_t *rates = NULL;
113
114   assert (0 == strcmp (ds->type, vl->type));
115
116   memset (buffer, '\0', buffer_len);
117
118   if ((ds->ds[index].type != DS_TYPE_COUNTER)
119       && (ds->ds[index].type != DS_TYPE_GAUGE))
120     return (-1);
121
122   if (ds->ds[index].type == DS_TYPE_COUNTER)
123   {
124     if (rates == NULL)
125       rates = uc_get_rate (ds, vl);
126     if (rates == NULL)
127     {
128       WARNING ("http plugin: "
129           "uc_get_rate failed.");
130       return (-1);
131     }
132     if (isnan(rates[index]))
133     {
134       /* dont output */
135       return (-1);
136     }
137     status = ssnprintf (buffer + offset,
138         buffer_len - offset,
139         "%lf", rates[index]);
140   }
141   else /* if (ds->ds[index].type == DS_TYPE_GAUGE) */
142   {
143     status = ssnprintf (buffer + offset, buffer_len - offset,
144         "%lf", vl->values[index].gauge);
145   }
146
147   if ((status < 1) || (status >= (buffer_len - offset)))
148   {
149     sfree (rates);
150     return (-1);
151   }
152
153   offset += status;
154
155   sfree (rates);
156   return (0);
157 } /* }}} int http_value_list_to_string */
158
159 static int http_value_list_to_timestamp (char *buffer, int buffer_len, /* {{{ */
160     const data_set_t *ds, const value_list_t *vl)
161 {
162   int offset = 0;
163   int status;
164
165   assert (0 == strcmp (ds->type, vl->type));
166
167   memset (buffer, '\0', buffer_len);
168
169   status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
170   if ((status < 1) || (status >= buffer_len))
171     return (-1);
172   offset = status;
173
174   return (0);
175 } /* }}} int http_value_list_to_timestamp */
176
177 static int http_value_list_to_metric_name (char *buffer, int buffer_len, /* {{{ */
178     const data_set_t *ds, const value_list_t *vl)
179 {
180   int offset = 0;
181   int status;
182
183   assert (0 == strcmp (ds->type, vl->type));
184
185   /* hostname */
186   status = ssnprintf (buffer + offset, buffer_len - offset,
187       "%s", vl->host);
188   if ((status < 1) || (status >= buffer_len - offset))
189     return (-1);
190   offset += status;
191
192   /* plugin */
193   status = ssnprintf (buffer + offset, buffer_len - offset,
194       ",%s", vl->plugin);
195   if ((status < 1) || (status >= buffer_len - offset))
196     return (-1);
197   offset += status;
198
199   /* plugin_instance */
200   if (strlen (vl->plugin_instance) > 0)
201   {
202     status = ssnprintf (buffer + offset, buffer_len - offset,
203         ",%s", vl->plugin_instance);
204     if ((status < 1) || (status >= buffer_len - offset))
205       return (-1);
206     offset += status;
207   }
208
209   /* type (if its the same as plugin, don't bother repeating it */
210   if (0 != strcmp (vl->type, vl->plugin)) 
211   {
212     status = ssnprintf (buffer + offset, buffer_len - offset,
213         ",%s", vl->type);
214     if ((status < 1) || (status >= buffer_len - offset))
215       return (-1);
216     offset += status;
217   }
218
219   /* type_instance */
220   if (strlen (vl->type_instance) > 0)
221   {
222     status = ssnprintf (buffer + offset, buffer_len - offset,
223         ",%s", vl->type_instance);
224     if ((status < 1) || (status >= buffer_len - offset))
225       return (-1);
226     offset += status;
227   }
228
229   return (offset);
230 } /* }}} int http_value_list_to_metric_name */
231
232 static int http_config (const char *key, const char *value) /* {{{ */
233 {
234   if (strcasecmp ("Location", key) == 0)
235   {
236     if (location != NULL)
237       free (location);
238     location = strdup (value);
239     if (location != NULL)
240     {
241       int len = strlen (location);
242       while ((len > 0) && (location[len - 1] == '/'))
243       {
244         len--;
245         location[len] = '\0';
246       }
247       if (len <= 0)
248       {
249         free (location);
250         location = NULL;
251       }
252     }
253   }
254   else if (strcasecmp ("User", key) == 0)
255   {
256     if (user != NULL)
257       free (user);
258     user = strdup (value);
259     if (user != NULL)
260     {
261       int len = strlen (user);
262       while ((len > 0) && (user[len - 1] == '/'))
263       {
264         len--;
265         user[len] = '\0';
266       }
267       if (len <= 0)
268       {
269         free (user);
270         user = NULL;
271       }
272     }
273   }
274   else if (strcasecmp ("Password", key) == 0)
275   {
276     if (pass != NULL)
277       free (pass);
278     pass = strdup (value);
279     if (pass != NULL)
280     {
281       int len = strlen (pass);
282       while ((len > 0) && (pass[len - 1] == '/'))
283       {
284         len--;
285         pass[len] = '\0';
286       }
287       if (len <= 0)
288       {
289         free (pass);
290         pass = NULL;
291       }
292     }
293   }
294   else
295   {
296     return (-1);
297   }
298   return (0);
299 } /* }}} int http_config */
300
301 static void http_init_buffer (void)  /* {{{ */
302 {
303         memset (send_buffer, 0, sizeof (send_buffer));
304         send_buffer_ptr = send_buffer;
305         send_buffer_fill = 0;
306 } /* }}} http_init_buffer */
307
308 static void http_send_buffer (char *buffer) /* {{{ */
309 {
310         printf("Sending: --------\n");
311         printf(buffer);
312         printf("---------------\n");
313
314         int status = 0;
315         curl_easy_setopt (curl, CURLOPT_POSTFIELDS, buffer);
316         status = curl_easy_perform (curl);
317         if (status != 0)
318         {
319                 ERROR ("http plugin: curl_easy_perform failed with staus %i: %s",
320                                 status, curl_errbuf);
321         }
322 } /* }}} http_send_buffer */
323
324 static void http_flush_buffer (void) /* {{{ */
325 {
326         DEBUG ("http plugin: flush_buffer: send_buffer_fill = %i",
327                         send_buffer_fill);
328
329         http_send_buffer (send_buffer);
330         http_init_buffer ();
331 } /* }}} http_flush_buffer */
332
333 static int http_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
334     user_data_t __attribute__((unused)) *user_data)
335 {
336
337   char         metric_name[512];
338   int          metric_prefix_len;
339   char         value[512];
340   char         timestamp[512];
341
342   int status;
343   int i;
344
345   if (0 != strcmp (ds->type, vl->type)) {
346     ERROR ("http plugin: DS type does not match value list type");
347     return -1;
348   }
349
350   metric_prefix_len = http_value_list_to_metric_name (metric_name, 
351       sizeof (metric_name), ds, vl);
352     
353   if (metric_prefix_len == -1)
354     return (-1);
355
356   DEBUG ("http plugin: http_write: metric_name = %s", metric_name);
357
358   if (http_value_list_to_timestamp (timestamp, sizeof (timestamp), ds, vl) != 0)
359     return (-1);
360
361   for (i = 0; i < ds->ds_num; i++) 
362   {
363
364     if (http_value_list_to_string (value, sizeof (value), ds, vl, i) != 0)
365       return (-1);
366
367     ssnprintf(metric_name + metric_prefix_len, sizeof (metric_name) - metric_prefix_len,
368         ",%s", ds->ds[i].name); 
369
370     escape_string (metric_name, sizeof (metric_name));
371
372     pthread_mutex_lock (&send_lock);
373
374     status = ssnprintf (send_buffer + send_buffer_fill, sizeof (send_buffer) - send_buffer_fill,
375         "\"%s\",%s,%s\n",
376         metric_name, timestamp, value);
377     send_buffer_fill += status;
378
379     printf(send_buffer);
380     printf("Fill: %i\n", send_buffer_fill);
381     printf("----\n");
382
383     if ((sizeof (send_buffer) - send_buffer_fill) < 128)
384     {
385             http_flush_buffer();
386     }
387
388     pthread_mutex_unlock (&send_lock);
389
390   } /* for */
391
392
393   return (0);
394
395 } /* }}} int http_write */
396
397 static int http_shutdown (void) /* {{{ */
398 {
399         curl_easy_cleanup(curl);
400         return (0);
401 }
402
403 void module_register (void) /* {{{ */
404 {
405   plugin_register_init("http", http_init);
406   plugin_register_config ("http", http_config,
407       config_keys, config_keys_num);
408   plugin_register_write ("http", http_write, /* user_data = */ NULL);
409   plugin_register_shutdown("http", http_shutdown);
410 } /* }}} void module_register */
411
412 /* vim: set fdm=marker sw=8 ts=8 tw=78 : */