3d50be539b0141e8a3d75f6bc8bd49283d7574fe
[collectd.git] / src / write_http.c
1 /**
2  * collectd - src/write_http.c
3  * Copyright (C) 2009       Paul Sadauskas
4  * Copyright (C) 2009       Doug MacEachern
5  * Copyright (C) 2007-2014  Florian octo Forster
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; only version 2 of the License is applicable.
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  *   Florian octo Forster <octo at collectd.org>
22  *   Doug MacEachern <dougm@hyperic.com>
23  *   Paul Sadauskas <psadauskas@gmail.com>
24  **/
25
26 #include "collectd.h"
27
28 #include "plugin.h"
29 #include "common.h"
30 #include "utils_format_json.h"
31 #include "utils_format_kairosdb.h"
32
33 #include <curl/curl.h>
34
35 #ifndef WRITE_HTTP_DEFAULT_BUFFER_SIZE
36 # define WRITE_HTTP_DEFAULT_BUFFER_SIZE 4096
37 #endif
38
39 /*
40  * Private variables
41  */
42 struct wh_callback_s
43 {
44         char *name;
45
46         char *location;
47         char *user;
48         char *pass;
49         char *credentials;
50         _Bool verify_peer;
51         _Bool verify_host;
52         char *cacert;
53         char *capath;
54         char *clientkey;
55         char *clientcert;
56         char *clientkeypass;
57         long sslversion;
58         _Bool store_rates;
59         _Bool log_http_error;
60         int   low_speed_limit;
61         time_t low_speed_time;
62         int timeout;
63
64 #define WH_FORMAT_COMMAND  0
65 #define WH_FORMAT_JSON     1
66 #define WH_FORMAT_KAIROSDB 2
67         int format;
68
69         CURL *curl;
70         struct curl_slist *headers;
71         char curl_errbuf[CURL_ERROR_SIZE];
72
73         char  *send_buffer;
74         size_t send_buffer_size;
75         size_t send_buffer_free;
76         size_t send_buffer_fill;
77         cdtime_t send_buffer_init_time;
78
79         pthread_mutex_t send_lock;
80 };
81 typedef struct wh_callback_s wh_callback_t;
82
83 static void wh_log_http_error (wh_callback_t *cb)
84 {
85         if (!cb->log_http_error)
86                 return;
87
88         long http_code = 0;
89
90         curl_easy_getinfo (cb->curl, CURLINFO_RESPONSE_CODE, &http_code);
91
92         if (http_code != 200)
93                 INFO ("write_http plugin: HTTP Error code: %lu", http_code);
94 }
95
96 static void wh_reset_buffer (wh_callback_t *cb)  /* {{{ */
97 {
98         memset (cb->send_buffer, 0, cb->send_buffer_size);
99         cb->send_buffer_free = cb->send_buffer_size;
100         cb->send_buffer_fill = 0;
101         cb->send_buffer_init_time = cdtime ();
102
103         if (cb->format == WH_FORMAT_JSON || cb->format == WH_FORMAT_KAIROSDB)
104         {
105                 format_json_initialize (cb->send_buffer,
106                                 &cb->send_buffer_fill,
107                                 &cb->send_buffer_free);
108         }
109 } /* }}} wh_reset_buffer */
110
111 static int wh_send_buffer (wh_callback_t *cb) /* {{{ */
112 {
113         int status = 0;
114
115         curl_easy_setopt (cb->curl, CURLOPT_POSTFIELDS, cb->send_buffer);
116         status = curl_easy_perform (cb->curl);
117
118         wh_log_http_error (cb);
119
120         if (status != CURLE_OK)
121         {
122                 ERROR ("write_http plugin: curl_easy_perform failed with "
123                                 "status %i: %s",
124                                 status, cb->curl_errbuf);
125         }
126         return (status);
127 } /* }}} wh_send_buffer */
128
129 static int wh_callback_init (wh_callback_t *cb) /* {{{ */
130 {
131         if (cb->curl != NULL)
132                 return (0);
133
134         cb->curl = curl_easy_init ();
135         if (cb->curl == NULL)
136         {
137                 ERROR ("curl plugin: curl_easy_init failed.");
138                 return (-1);
139         }
140
141         if (cb->low_speed_limit > 0 && cb->low_speed_time > 0)
142         {
143                 curl_easy_setopt (cb->curl, CURLOPT_LOW_SPEED_LIMIT,
144                                   (long) (cb->low_speed_limit * cb->low_speed_time));
145                 curl_easy_setopt (cb->curl, CURLOPT_LOW_SPEED_TIME,
146                                   (long) cb->low_speed_time);
147         }
148
149 #ifdef HAVE_CURLOPT_TIMEOUT_MS
150         if (cb->timeout > 0)
151                 curl_easy_setopt (cb->curl, CURLOPT_TIMEOUT_MS, (long) cb->timeout);
152 #endif
153
154         curl_easy_setopt (cb->curl, CURLOPT_NOSIGNAL, 1L);
155         curl_easy_setopt (cb->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT);
156
157         cb->headers = curl_slist_append (cb->headers, "Accept:  */*");
158         if (cb->format == WH_FORMAT_JSON || cb->format == WH_FORMAT_KAIROSDB)
159                 cb->headers = curl_slist_append (cb->headers, "Content-Type: application/json");
160         else
161                 cb->headers = curl_slist_append (cb->headers, "Content-Type: text/plain");
162         cb->headers = curl_slist_append (cb->headers, "Expect:");
163         curl_easy_setopt (cb->curl, CURLOPT_HTTPHEADER, cb->headers);
164
165         curl_easy_setopt (cb->curl, CURLOPT_ERRORBUFFER, cb->curl_errbuf);
166         curl_easy_setopt (cb->curl, CURLOPT_URL, cb->location);
167         curl_easy_setopt (cb->curl, CURLOPT_FOLLOWLOCATION, 1L);
168         curl_easy_setopt (cb->curl, CURLOPT_MAXREDIRS, 50L);
169
170         if (cb->user != NULL)
171         {
172 #ifdef HAVE_CURLOPT_USERNAME
173                 curl_easy_setopt (cb->curl, CURLOPT_USERNAME, cb->user);
174                 curl_easy_setopt (cb->curl, CURLOPT_PASSWORD,
175                         (cb->pass == NULL) ? "" : cb->pass);
176 #else
177                 size_t credentials_size;
178
179                 credentials_size = strlen (cb->user) + 2;
180                 if (cb->pass != NULL)
181                         credentials_size += strlen (cb->pass);
182
183                 cb->credentials = malloc (credentials_size);
184                 if (cb->credentials == NULL)
185                 {
186                         ERROR ("curl plugin: malloc failed.");
187                         return (-1);
188                 }
189
190                 ssnprintf (cb->credentials, credentials_size, "%s:%s",
191                                 cb->user, (cb->pass == NULL) ? "" : cb->pass);
192                 curl_easy_setopt (cb->curl, CURLOPT_USERPWD, cb->credentials);
193 #endif
194                 curl_easy_setopt (cb->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
195         }
196
197         curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYPEER, (long) cb->verify_peer);
198         curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYHOST,
199                         cb->verify_host ? 2L : 0L);
200         curl_easy_setopt (cb->curl, CURLOPT_SSLVERSION, cb->sslversion);
201         if (cb->cacert != NULL)
202                 curl_easy_setopt (cb->curl, CURLOPT_CAINFO, cb->cacert);
203         if (cb->capath != NULL)
204                 curl_easy_setopt (cb->curl, CURLOPT_CAPATH, cb->capath);
205
206         if (cb->clientkey != NULL && cb->clientcert != NULL)
207         {
208             curl_easy_setopt (cb->curl, CURLOPT_SSLKEY, cb->clientkey);
209             curl_easy_setopt (cb->curl, CURLOPT_SSLCERT, cb->clientcert);
210
211             if (cb->clientkeypass != NULL)
212                 curl_easy_setopt (cb->curl, CURLOPT_SSLKEYPASSWD, cb->clientkeypass);
213         }
214
215         wh_reset_buffer (cb);
216
217         return (0);
218 } /* }}} int wh_callback_init */
219
220 static int wh_flush_nolock (cdtime_t timeout, wh_callback_t *cb) /* {{{ */
221 {
222         int status;
223
224         DEBUG ("write_http plugin: wh_flush_nolock: timeout = %.3f; "
225                         "send_buffer_fill = %zu;",
226                         CDTIME_T_TO_DOUBLE (timeout),
227                         cb->send_buffer_fill);
228
229         /* timeout == 0  => flush unconditionally */
230         if (timeout > 0)
231         {
232                 cdtime_t now;
233
234                 now = cdtime ();
235                 if ((cb->send_buffer_init_time + timeout) > now)
236                         return (0);
237         }
238
239         if (cb->format == WH_FORMAT_COMMAND)
240         {
241                 if (cb->send_buffer_fill == 0)
242                 {
243                         cb->send_buffer_init_time = cdtime ();
244                         return (0);
245                 }
246
247                 status = wh_send_buffer (cb);
248                 wh_reset_buffer (cb);
249         }
250         else if (cb->format == WH_FORMAT_JSON || cb->format == WH_FORMAT_KAIROSDB)
251         {
252                 if (cb->send_buffer_fill <= 2)
253                 {
254                         cb->send_buffer_init_time = cdtime ();
255                         return (0);
256                 }
257
258                 status = format_json_finalize (cb->send_buffer,
259                                 &cb->send_buffer_fill,
260                                 &cb->send_buffer_free);
261                 if (status != 0)
262                 {
263                         ERROR ("write_http: wh_flush_nolock: "
264                                         "format_json_finalize failed.");
265                         wh_reset_buffer (cb);
266                         return (status);
267                 }
268
269                 status = wh_send_buffer (cb);
270                 wh_reset_buffer (cb);
271         }
272         else
273         {
274                 ERROR ("write_http: wh_flush_nolock: "
275                                 "Unknown format: %i",
276                                 cb->format);
277                 return (-1);
278         }
279
280         return (status);
281 } /* }}} wh_flush_nolock */
282
283 static int wh_flush (cdtime_t timeout, /* {{{ */
284                 const char *identifier __attribute__((unused)),
285                 user_data_t *user_data)
286 {
287         wh_callback_t *cb;
288         int status;
289
290         if (user_data == NULL)
291                 return (-EINVAL);
292
293         cb = user_data->data;
294
295         pthread_mutex_lock (&cb->send_lock);
296
297         if (cb->curl == NULL)
298         {
299                 status = wh_callback_init (cb);
300                 if (status != 0)
301                 {
302                         ERROR ("write_http plugin: wh_callback_init failed.");
303                         pthread_mutex_unlock (&cb->send_lock);
304                         return (-1);
305                 }
306         }
307
308         status = wh_flush_nolock (timeout, cb);
309         pthread_mutex_unlock (&cb->send_lock);
310
311         return (status);
312 } /* }}} int wh_flush */
313
314 static void wh_callback_free (void *data) /* {{{ */
315 {
316         wh_callback_t *cb;
317
318         if (data == NULL)
319                 return;
320
321         cb = data;
322
323         wh_flush_nolock (/* timeout = */ 0, cb);
324
325         if (cb->curl != NULL)
326         {
327                 curl_easy_cleanup (cb->curl);
328                 cb->curl = NULL;
329         }
330
331         if (cb->headers != NULL)
332         {
333                 curl_slist_free_all (cb->headers);
334                 cb->headers = NULL;
335         }
336
337         sfree (cb->name);
338         sfree (cb->location);
339         sfree (cb->user);
340         sfree (cb->pass);
341         sfree (cb->credentials);
342         sfree (cb->cacert);
343         sfree (cb->capath);
344         sfree (cb->clientkey);
345         sfree (cb->clientcert);
346         sfree (cb->clientkeypass);
347         sfree (cb->send_buffer);
348
349         sfree (cb);
350 } /* }}} void wh_callback_free */
351
352 static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{{ */
353                 wh_callback_t *cb)
354 {
355         char key[10*DATA_MAX_NAME_LEN];
356         char values[512];
357         char command[1024];
358         size_t command_len;
359
360         int status;
361
362         if (0 != strcmp (ds->type, vl->type)) {
363                 ERROR ("write_http plugin: DS type does not match "
364                                 "value list type");
365                 return -1;
366         }
367
368         /* Copy the identifier to `key' and escape it. */
369         status = FORMAT_VL (key, sizeof (key), vl);
370         if (status != 0) {
371                 ERROR ("write_http plugin: error with format_name");
372                 return (status);
373         }
374         escape_string (key, sizeof (key));
375
376         /* Convert the values to an ASCII representation and put that into
377          * `values'. */
378         status = format_values (values, sizeof (values), ds, vl, cb->store_rates);
379         if (status != 0) {
380                 ERROR ("write_http plugin: error with "
381                                 "wh_value_list_to_string");
382                 return (status);
383         }
384
385         command_len = (size_t) ssnprintf (command, sizeof (command),
386                         "PUTVAL %s interval=%.3f %s\r\n",
387                         key,
388                         CDTIME_T_TO_DOUBLE (vl->interval),
389                         values);
390         if (command_len >= sizeof (command)) {
391                 ERROR ("write_http plugin: Command buffer too small: "
392                                 "Need %zu bytes.", command_len + 1);
393                 return (-1);
394         }
395
396         pthread_mutex_lock (&cb->send_lock);
397
398         if (cb->curl == NULL)
399         {
400                 status = wh_callback_init (cb);
401                 if (status != 0)
402                 {
403                         ERROR ("write_http plugin: wh_callback_init failed.");
404                         pthread_mutex_unlock (&cb->send_lock);
405                         return (-1);
406                 }
407         }
408
409         if (command_len >= cb->send_buffer_free)
410         {
411                 status = wh_flush_nolock (/* timeout = */ 0, cb);
412                 if (status != 0)
413                 {
414                         pthread_mutex_unlock (&cb->send_lock);
415                         return (status);
416                 }
417         }
418         assert (command_len < cb->send_buffer_free);
419
420         /* `command_len + 1' because `command_len' does not include the
421          * trailing null byte. Neither does `send_buffer_fill'. */
422         memcpy (cb->send_buffer + cb->send_buffer_fill,
423                         command, command_len + 1);
424         cb->send_buffer_fill += command_len;
425         cb->send_buffer_free -= command_len;
426
427         DEBUG ("write_http plugin: <%s> buffer %zu/%zu (%g%%) \"%s\"",
428                         cb->location,
429                         cb->send_buffer_fill, cb->send_buffer_size,
430                         100.0 * ((double) cb->send_buffer_fill) / ((double) cb->send_buffer_size),
431                         command);
432
433         /* Check if we have enough space for this command. */
434         pthread_mutex_unlock (&cb->send_lock);
435
436         return (0);
437 } /* }}} int wh_write_command */
438
439 static int wh_write_json (const data_set_t *ds, const value_list_t *vl, /* {{{ */
440                 wh_callback_t *cb)
441 {
442         int status;
443
444         pthread_mutex_lock (&cb->send_lock);
445
446         if (cb->curl == NULL)
447         {
448                 status = wh_callback_init (cb);
449                 if (status != 0)
450                 {
451                         ERROR ("write_http plugin: wh_callback_init failed.");
452                         pthread_mutex_unlock (&cb->send_lock);
453                         return (-1);
454                 }
455         }
456
457         status = format_json_value_list (cb->send_buffer,
458                         &cb->send_buffer_fill,
459                         &cb->send_buffer_free,
460                         ds, vl, cb->store_rates);
461         if (status == -ENOMEM)
462         {
463                 status = wh_flush_nolock (/* timeout = */ 0, cb);
464                 if (status != 0)
465                 {
466                         wh_reset_buffer (cb);
467                         pthread_mutex_unlock (&cb->send_lock);
468                         return (status);
469                 }
470
471                 status = format_json_value_list (cb->send_buffer,
472                                 &cb->send_buffer_fill,
473                                 &cb->send_buffer_free,
474                                 ds, vl, cb->store_rates);
475         }
476         if (status != 0)
477         {
478                 pthread_mutex_unlock (&cb->send_lock);
479                 return (status);
480         }
481
482         DEBUG ("write_http plugin: <%s> buffer %zu/%zu (%g%%)",
483                         cb->location,
484                         cb->send_buffer_fill, cb->send_buffer_size,
485                         100.0 * ((double) cb->send_buffer_fill) / ((double) cb->send_buffer_size));
486
487         /* Check if we have enough space for this command. */
488         pthread_mutex_unlock (&cb->send_lock);
489
490         return (0);
491 } /* }}} int wh_write_json */
492
493 static int wh_write_kairosdb (const data_set_t *ds, const value_list_t *vl, /* {{{ */
494                 wh_callback_t *cb)
495 {
496         int status;
497
498         pthread_mutex_lock (&cb->send_lock);
499
500         if (cb->curl == NULL)
501         {
502                 status = wh_callback_init (cb);
503                 if (status != 0)
504                 {
505                         ERROR ("write_http plugin: wh_callback_init failed.");
506                         pthread_mutex_unlock (&cb->send_lock);
507                         return (-1);
508                 }
509         }
510
511         status = format_kairosdb_value_list (cb->send_buffer,
512                         &cb->send_buffer_fill,
513                         &cb->send_buffer_free,
514                         ds, vl, cb->store_rates);
515         if (status == -ENOMEM)
516         {
517                 status = wh_flush_nolock (/* timeout = */ 0, cb);
518                 if (status != 0)
519                 {
520                         wh_reset_buffer (cb);
521                         pthread_mutex_unlock (&cb->send_lock);
522                         return (status);
523                 }
524
525                 status = format_kairosdb_value_list (cb->send_buffer,
526                                 &cb->send_buffer_fill,
527                                 &cb->send_buffer_free,
528                                 ds, vl, cb->store_rates);
529         }
530         if (status != 0)
531         {
532                 pthread_mutex_unlock (&cb->send_lock);
533                 return (status);
534         }
535
536         DEBUG ("write_http plugin: <%s> buffer %zu/%zu (%g%%)",
537                         cb->location,
538                         cb->send_buffer_fill, cb->send_buffer_size,
539                         100.0 * ((double) cb->send_buffer_fill) / ((double) cb->send_buffer_size));
540
541         /* Check if we have enough space for this command. */
542         pthread_mutex_unlock (&cb->send_lock);
543
544         return (0);
545 } /* }}} int wh_write_kairosdb */
546
547 static int wh_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
548                 user_data_t *user_data)
549 {
550         wh_callback_t *cb;
551         int status;
552
553         if (user_data == NULL)
554                 return (-EINVAL);
555
556         cb = user_data->data;
557
558         switch(cb->format) {
559             case WH_FORMAT_JSON:
560                 status = wh_write_json (ds, vl, cb);
561                 break;
562             case WH_FORMAT_KAIROSDB:
563                 status = wh_write_kairosdb (ds, vl, cb);
564                 break;
565             default:
566                 status = wh_write_command (ds, vl, cb);
567                 break;
568         }
569         return (status);
570 } /* }}} int wh_write */
571
572 static int config_set_format (wh_callback_t *cb, /* {{{ */
573                 oconfig_item_t *ci)
574 {
575         char *string;
576
577         if ((ci->values_num != 1)
578                         || (ci->values[0].type != OCONFIG_TYPE_STRING))
579         {
580                 WARNING ("write_http plugin: The `%s' config option "
581                                 "needs exactly one string argument.", ci->key);
582                 return (-1);
583         }
584
585         string = ci->values[0].value.string;
586         if (strcasecmp ("Command", string) == 0)
587                 cb->format = WH_FORMAT_COMMAND;
588         else if (strcasecmp ("JSON", string) == 0)
589                 cb->format = WH_FORMAT_JSON;
590         else if (strcasecmp ("KAIROSDB", string) == 0)
591                 cb->format = WH_FORMAT_KAIROSDB;
592         else
593         {
594                 ERROR ("write_http plugin: Invalid format string: %s",
595                                 string);
596                 return (-1);
597         }
598
599         return (0);
600 } /* }}} int config_set_format */
601
602 static int wh_config_append_string (const char *name, struct curl_slist **dest, /* {{{ */
603     oconfig_item_t *ci)
604 {
605   struct curl_slist *temp = NULL;
606   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
607   {
608     WARNING ("write_http plugin: `%s' needs exactly one string argument.", name);
609     return (-1);
610   }
611
612   temp = curl_slist_append(*dest, ci->values[0].value.string);
613   if (temp == NULL)
614     return (-1);
615
616   *dest = temp;
617
618   return (0);
619 } /* }}} int wh_config_append_string */
620
621 static int wh_config_node (oconfig_item_t *ci) /* {{{ */
622 {
623         wh_callback_t *cb;
624         int buffer_size = 0;
625         user_data_t user_data = { 0 };
626         char callback_name[DATA_MAX_NAME_LEN];
627         int status = 0;
628         int i;
629
630         cb = calloc (1, sizeof (*cb));
631         if (cb == NULL)
632         {
633                 ERROR ("write_http plugin: calloc failed.");
634                 return (-1);
635         }
636         cb->verify_peer = 1;
637         cb->verify_host = 1;
638         cb->format = WH_FORMAT_COMMAND;
639         cb->sslversion = CURL_SSLVERSION_DEFAULT;
640         cb->low_speed_limit = 0;
641         cb->timeout = 0;
642         cb->log_http_error = 0;
643         cb->headers = NULL;
644
645
646         pthread_mutex_init (&cb->send_lock, /* attr = */ NULL);
647
648         cf_util_get_string (ci, &cb->name);
649
650         /* FIXME: Remove this legacy mode in version 6. */
651         if (strcasecmp ("URL", ci->key) == 0)
652                 cf_util_get_string (ci, &cb->location);
653
654         for (i = 0; i < ci->children_num; i++)
655         {
656                 oconfig_item_t *child = ci->children + i;
657
658                 if (strcasecmp ("URL", child->key) == 0)
659                         status = cf_util_get_string (child, &cb->location);
660                 else if (strcasecmp ("User", child->key) == 0)
661                         status = cf_util_get_string (child, &cb->user);
662                 else if (strcasecmp ("Password", child->key) == 0)
663                         status = cf_util_get_string (child, &cb->pass);
664                 else if (strcasecmp ("VerifyPeer", child->key) == 0)
665                         status = cf_util_get_boolean (child, &cb->verify_peer);
666                 else if (strcasecmp ("VerifyHost", child->key) == 0)
667                         status = cf_util_get_boolean (child, &cb->verify_host);
668                 else if (strcasecmp ("CACert", child->key) == 0)
669                         status = cf_util_get_string (child, &cb->cacert);
670                 else if (strcasecmp ("CAPath", child->key) == 0)
671                         status = cf_util_get_string (child, &cb->capath);
672                 else if (strcasecmp ("ClientKey", child->key) == 0)
673                         status = cf_util_get_string (child, &cb->clientkey);
674                 else if (strcasecmp ("ClientCert", child->key) == 0)
675                         status = cf_util_get_string (child, &cb->clientcert);
676                 else if (strcasecmp ("ClientKeyPass", child->key) == 0)
677                         status = cf_util_get_string (child, &cb->clientkeypass);
678                 else if (strcasecmp ("SSLVersion", child->key) == 0)
679                 {
680                         char *value = NULL;
681
682                         status = cf_util_get_string (child, &value);
683                         if (status != 0)
684                                 break;
685
686                         if (value == NULL || strcasecmp ("default", value) == 0)
687                                 cb->sslversion = CURL_SSLVERSION_DEFAULT;
688                         else if (strcasecmp ("SSLv2", value) == 0)
689                                 cb->sslversion = CURL_SSLVERSION_SSLv2;
690                         else if (strcasecmp ("SSLv3", value) == 0)
691                                 cb->sslversion = CURL_SSLVERSION_SSLv3;
692                         else if (strcasecmp ("TLSv1", value) == 0)
693                                 cb->sslversion = CURL_SSLVERSION_TLSv1;
694 #if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 34)
695                         else if (strcasecmp ("TLSv1_0", value) == 0)
696                                 cb->sslversion = CURL_SSLVERSION_TLSv1_0;
697                         else if (strcasecmp ("TLSv1_1", value) == 0)
698                                 cb->sslversion = CURL_SSLVERSION_TLSv1_1;
699                         else if (strcasecmp ("TLSv1_2", value) == 0)
700                                 cb->sslversion = CURL_SSLVERSION_TLSv1_2;
701 #endif
702                         else
703                         {
704                                 ERROR ("write_http plugin: Invalid SSLVersion "
705                                                 "option: %s.", value);
706                                 status = EINVAL;
707                         }
708
709                         sfree(value);
710                 }
711                 else if (strcasecmp ("Format", child->key) == 0)
712                         status = config_set_format (cb, child);
713                 else if (strcasecmp ("StoreRates", child->key) == 0)
714                         status = cf_util_get_boolean (child, &cb->store_rates);
715                 else if (strcasecmp ("BufferSize", child->key) == 0)
716                         status = cf_util_get_int (child, &buffer_size);
717                 else if (strcasecmp ("LowSpeedLimit", child->key) == 0)
718                         status = cf_util_get_int (child, &cb->low_speed_limit);
719                 else if (strcasecmp ("Timeout", child->key) == 0)
720                         status = cf_util_get_int (child, &cb->timeout);
721                 else if (strcasecmp ("LogHttpError", child->key) == 0)
722                         status = cf_util_get_boolean (child, &cb->log_http_error);
723                 else if (strcasecmp ("Header", child->key) == 0)
724                         status = wh_config_append_string ("Header", &cb->headers, child);
725                 else
726                 {
727                         ERROR ("write_http plugin: Invalid configuration "
728                                         "option: %s.", child->key);
729                         status = EINVAL;
730                 }
731
732                 if (status != 0)
733                         break;
734         }
735
736         if (status != 0)
737         {
738                 wh_callback_free (cb);
739                 return (status);
740         }
741
742         if (cb->location == NULL)
743         {
744                 ERROR ("write_http plugin: no URL defined for instance '%s'",
745                         cb->name);
746                 wh_callback_free (cb);
747                 return (-1);
748         }
749
750         if (cb->low_speed_limit > 0)
751                 cb->low_speed_time = CDTIME_T_TO_TIME_T(plugin_get_interval());
752
753         /* Determine send_buffer_size. */
754         cb->send_buffer_size = WRITE_HTTP_DEFAULT_BUFFER_SIZE;
755         if (buffer_size >= 1024)
756                 cb->send_buffer_size = (size_t) buffer_size;
757         else if (buffer_size != 0)
758                 ERROR ("write_http plugin: Ignoring invalid BufferSize setting (%d).",
759                                 buffer_size);
760
761         /* Allocate the buffer. */
762         cb->send_buffer = malloc (cb->send_buffer_size);
763         if (cb->send_buffer == NULL)
764         {
765                 ERROR ("write_http plugin: malloc(%zu) failed.", cb->send_buffer_size);
766                 wh_callback_free (cb);
767                 return (-1);
768         }
769         /* Nulls the buffer and sets ..._free and ..._fill. */
770         wh_reset_buffer (cb);
771
772         ssnprintf (callback_name, sizeof (callback_name), "write_http/%s",
773                         cb->name);
774         DEBUG ("write_http: Registering write callback '%s' with URL '%s'",
775                         callback_name, cb->location);
776
777         user_data.data = cb;
778         plugin_register_flush (callback_name, wh_flush, &user_data);
779
780         user_data.free_func = wh_callback_free;
781         plugin_register_write (callback_name, wh_write, &user_data);
782
783         return (0);
784 } /* }}} int wh_config_node */
785
786 static int wh_config (oconfig_item_t *ci) /* {{{ */
787 {
788         int i;
789
790         for (i = 0; i < ci->children_num; i++)
791         {
792                 oconfig_item_t *child = ci->children + i;
793
794                 if (strcasecmp ("Node", child->key) == 0)
795                         wh_config_node (child);
796                 /* FIXME: Remove this legacy mode in version 6. */
797                 else if (strcasecmp ("URL", child->key) == 0) {
798                         WARNING ("write_http plugin: Legacy <URL> block found. "
799                                 "Please use <Node> instead.");
800                         wh_config_node (child);
801                 }
802                 else
803                 {
804                         ERROR ("write_http plugin: Invalid configuration "
805                                         "option: %s.", child->key);
806                 }
807         }
808
809         return (0);
810 } /* }}} int wh_config */
811
812 static int wh_init (void) /* {{{ */
813 {
814         /* Call this while collectd is still single-threaded to avoid
815          * initialization issues in libgcrypt. */
816         curl_global_init (CURL_GLOBAL_SSL);
817         return (0);
818 } /* }}} int wh_init */
819
820 void module_register (void) /* {{{ */
821 {
822         plugin_register_complex_config ("write_http", wh_config);
823         plugin_register_init ("write_http", wh_init);
824 } /* }}} void module_register */
825
826 /* vim: set fdm=marker sw=8 ts=8 tw=78 et : */