configure / curl stats: Check for CURLINFO_APPCONNECT_TIME.
[collectd.git] / src / utils_curl_stats.c
1 /**
2  * collectd - src/utils_curl_stats.c
3  * Copyright (C) 2015       Sebastian 'tokkee' Harl
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Sebastian Harl <sh@tokkee.org>
25  **/
26
27 #include "collectd.h"
28 #include "common.h"
29 #include "utils_curl_stats.h"
30
31 #include <stdbool.h>
32 #include <stddef.h>
33
34 struct curl_stats_s
35 {
36         bool total_time;
37         bool namelookup_time;
38         bool connect_time;
39         bool pretransfer_time;
40         bool size_upload;
41         bool size_download;
42         bool speed_download;
43         bool speed_upload;
44         bool header_size;
45         bool request_size;
46         bool content_length_download;
47         bool content_length_upload;
48         bool starttransfer_time;
49         bool redirect_time;
50         bool redirect_count;
51         bool num_connects;
52         bool appconnect_time;
53 };
54
55 /*
56  * Private functions
57  */
58
59 static int dispatch_gauge (CURL *curl, CURLINFO info, value_list_t *vl)
60 {
61         CURLcode code;
62         value_t v;
63
64         code = curl_easy_getinfo (curl, info, &v.gauge);
65         if (code != CURLE_OK)
66                 return -1;
67
68         vl->values = &v;
69         vl->values_len = 1;
70
71         return plugin_dispatch_values (vl);
72 } /* dispatch_gauge */
73
74 /* dispatch a speed, in bytes/second */
75 static int dispatch_speed (CURL *curl, CURLINFO info, value_list_t *vl)
76 {
77         CURLcode code;
78         value_t v;
79
80         code = curl_easy_getinfo (curl, info, &v.gauge);
81         if (code != CURLE_OK)
82                 return -1;
83
84         v.gauge *= 8;
85
86         vl->values = &v;
87         vl->values_len = 1;
88
89         return plugin_dispatch_values (vl);
90 } /* dispatch_speed */
91
92 /* dispatch a size/count, reported as a long value */
93 static int dispatch_size (CURL *curl, CURLINFO info, value_list_t *vl)
94 {
95         CURLcode code;
96         value_t v;
97         long raw;
98
99         code = curl_easy_getinfo (curl, info, &raw);
100         if (code != CURLE_OK)
101                 return -1;
102
103         v.gauge = (double)raw;
104
105         vl->values = &v;
106         vl->values_len = 1;
107
108         return plugin_dispatch_values (vl);
109 } /* dispatch_size */
110
111 static struct {
112         const char *name;
113         const char *config_key;
114         size_t offset;
115
116         int (*dispatcher)(CURL *, CURLINFO, value_list_t *);
117         const char *type;
118         CURLINFO info;
119 } field_specs[] = {
120 #define SPEC(name, config_key, dispatcher, type, info) \
121         { #name, config_key, offsetof (curl_stats_t, name), dispatcher, type, info }
122
123         SPEC (total_time,              "TotalTime",              dispatch_gauge, "duration", CURLINFO_TOTAL_TIME),
124         SPEC (namelookup_time,         "NamelookupTime",         dispatch_gauge, "duration", CURLINFO_NAMELOOKUP_TIME),
125         SPEC (connect_time,            "ConnectTime",            dispatch_gauge, "duration", CURLINFO_CONNECT_TIME),
126         SPEC (pretransfer_time,        "PretransferTime",        dispatch_gauge, "duration", CURLINFO_PRETRANSFER_TIME),
127         SPEC (size_upload,             "SizeUpload",             dispatch_gauge, "bytes",    CURLINFO_SIZE_UPLOAD),
128         SPEC (size_download,           "SizeDownload",           dispatch_gauge, "bytes",    CURLINFO_SIZE_DOWNLOAD),
129         SPEC (speed_download,          "SpeedDownload",          dispatch_speed, "bitrate",  CURLINFO_SPEED_DOWNLOAD),
130         SPEC (speed_upload,            "SpeedUpload",            dispatch_speed, "bitrate",  CURLINFO_SPEED_UPLOAD),
131         SPEC (header_size,             "HeaderSize",             dispatch_size,  "bytes",    CURLINFO_HEADER_SIZE),
132         SPEC (request_size,            "RequestSize",            dispatch_size,  "bytes",    CURLINFO_REQUEST_SIZE),
133         SPEC (content_length_download, "ContentLengthDownload",  dispatch_gauge, "bytes",    CURLINFO_CONTENT_LENGTH_DOWNLOAD),
134         SPEC (content_length_upload,   "ContentLengthUpload",    dispatch_gauge, "bytes",    CURLINFO_CONTENT_LENGTH_UPLOAD),
135         SPEC (starttransfer_time,      "StarttransferTime",      dispatch_gauge, "duration", CURLINFO_STARTTRANSFER_TIME),
136         SPEC (redirect_time,           "RedirectTime",           dispatch_gauge, "duration", CURLINFO_REDIRECT_TIME),
137         SPEC (redirect_count,          "RedirectCount",          dispatch_size,  "count",    CURLINFO_REDIRECT_COUNT),
138         SPEC (num_connects,            "NumConnects",            dispatch_size,  "count",    CURLINFO_NUM_CONNECTS),
139 #ifdef HAVE_CURLINFO_APPCONNECT_TIME
140         SPEC (appconnect_time,         "AppconnectTime",         dispatch_gauge, "duration", CURLINFO_APPCONNECT_TIME),
141 #endif
142
143 #undef SPEC
144 };
145
146 static void enable_field (curl_stats_t *s, size_t offset)
147 {
148         *(bool *)((char *)s + offset) = true;
149 } /* enable_field */
150
151 static bool field_enabled (curl_stats_t *s, size_t offset)
152 {
153         return *(bool *)((char *)s + offset);
154 } /* field_enabled */
155
156 /*
157  * Public API
158  */
159 curl_stats_t *curl_stats_from_config (oconfig_item_t *ci)
160 {
161         curl_stats_t *s;
162         int i;
163
164         if (ci == NULL)
165                 return NULL;
166
167         s = calloc (sizeof (*s), 1);
168         if (s == NULL)
169                 return NULL;
170
171         for (i = 0; i < ci->children_num; ++i)
172         {
173                 oconfig_item_t *c = ci->children + i;
174                 size_t field;
175
176                 for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field) {
177                         if (! strcasecmp (c->key, field_specs[field].config_key))
178                                 break;
179                         if (! strcasecmp (c->key, field_specs[field].name))
180                                 break;
181                 }
182                 if (field >= STATIC_ARRAY_SIZE (field_specs))
183                 {
184                         ERROR ("curl stats: Unknown field name %s", c->key);
185                         free (s);
186                         return NULL;
187                 }
188
189                 if ((c->values_num != 1)
190                                 || ((c->values[0].type != OCONFIG_TYPE_STRING)
191                                         && (c->values[0].type != OCONFIG_TYPE_BOOLEAN))) {
192                         ERROR ("curl stats: `%s' expects a single boolean argument", c->key);
193                         free (s);
194                         return NULL;
195                 }
196
197                 if (((c->values[0].type == OCONFIG_TYPE_STRING)
198                                         && IS_TRUE (c->values[0].value.string))
199                                 || ((c->values[0].type == OCONFIG_TYPE_BOOLEAN)
200                                         && c->values[0].value.boolean))
201                         enable_field (s, field_specs[field].offset);
202         }
203
204         return s;
205 } /* curl_stats_from_config */
206
207 void curl_stats_destroy (curl_stats_t *s)
208 {
209         if (s != NULL)
210                 free (s);
211 } /* curl_stats_destroy */
212
213 int curl_stats_dispatch (curl_stats_t *s, CURL *curl,
214                 const char *hostname, const char *plugin, const char *plugin_instance,
215                 const char *instance_prefix)
216 {
217         value_list_t vl = VALUE_LIST_INIT;
218         size_t field;
219
220         if (s == NULL)
221                 return 0;
222         if (curl == NULL)
223                 return -1;
224
225         if (hostname != NULL)
226                 sstrncpy (vl.host, hostname, sizeof (vl.host));
227         if (plugin != NULL)
228                 sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
229         if (plugin_instance != NULL)
230                 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
231
232         for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field)
233         {
234                 int status;
235
236                 if (! field_enabled (s, field_specs[field].offset))
237                         continue;
238
239                 sstrncpy (vl.type, field_specs[field].type, sizeof (vl.type));
240                 ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
241                                 instance_prefix ? instance_prefix : "", field_specs[field].name);
242
243                 vl.values = NULL;
244                 vl.values_len = 0;
245                 status = field_specs[field].dispatcher (curl, field_specs[field].info, &vl);
246                 if (status < 0)
247                         return status;
248         }
249
250         return 0;
251 } /* curl_stats_dispatch */