5a37083a0aa6e680c26852b4cd6e1307386315f1
[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         SPEC (appconnect_time,         "AppconnectTime",         dispatch_gauge, "duration", CURLINFO_APPCONNECT_TIME),
140
141 #undef SPEC
142 };
143
144 static void enable_field (curl_stats_t *s, size_t offset)
145 {
146         *(bool *)((char *)s + offset) = true;
147 } /* enable_field */
148
149 static bool field_enabled (curl_stats_t *s, size_t offset)
150 {
151         return *(bool *)((char *)s + offset);
152 } /* field_enabled */
153
154 /*
155  * Public API
156  */
157 curl_stats_t *curl_stats_from_config (oconfig_item_t *ci)
158 {
159         curl_stats_t *s;
160         int i;
161
162         if (ci == NULL)
163                 return NULL;
164
165         s = calloc (sizeof (*s), 1);
166         if (s == NULL)
167                 return NULL;
168
169         for (i = 0; i < ci->children_num; ++i)
170         {
171                 oconfig_item_t *c = ci->children + i;
172                 size_t field;
173
174                 for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field) {
175                         if (! strcasecmp (c->key, field_specs[field].config_key))
176                                 break;
177                         if (! strcasecmp (c->key, field_specs[field].name))
178                                 break;
179                 }
180                 if (field >= STATIC_ARRAY_SIZE (field_specs))
181                 {
182                         ERROR ("curl stats: Unknown field name %s", c->key);
183                         free (s);
184                         return NULL;
185                 }
186
187                 if ((c->values_num != 1)
188                                 || ((c->values[0].type != OCONFIG_TYPE_STRING)
189                                         && (c->values[0].type != OCONFIG_TYPE_BOOLEAN))) {
190                         ERROR ("curl stats: `%s' expects a single boolean argument", c->key);
191                         free (s);
192                         return NULL;
193                 }
194
195                 if (((c->values[0].type == OCONFIG_TYPE_STRING)
196                                         && IS_TRUE (c->values[0].value.string))
197                                 || ((c->values[0].type == OCONFIG_TYPE_BOOLEAN)
198                                         && c->values[0].value.boolean))
199                         enable_field (s, field_specs[field].offset);
200         }
201
202         return s;
203 } /* curl_stats_from_config */
204
205 void curl_stats_destroy (curl_stats_t *s)
206 {
207         if (s != NULL)
208                 free (s);
209 } /* curl_stats_destroy */
210
211 int curl_stats_dispatch (curl_stats_t *s, CURL *curl,
212                 const char *hostname, const char *plugin, const char *plugin_instance,
213                 const char *instance_prefix)
214 {
215         value_list_t vl = VALUE_LIST_INIT;
216         size_t field;
217
218         if (s == NULL)
219                 return 0;
220         if (curl == NULL)
221                 return -1;
222
223         if (hostname != NULL)
224                 sstrncpy (vl.host, hostname, sizeof (vl.host));
225         if (plugin != NULL)
226                 sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
227         if (plugin_instance != NULL)
228                 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
229
230         for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field)
231         {
232                 int status;
233
234                 if (! field_enabled (s, field_specs[field].offset))
235                         continue;
236
237                 sstrncpy (vl.type, field_specs[field].type, sizeof (vl.type));
238                 ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
239                                 instance_prefix ? instance_prefix : "", field_specs[field].name);
240
241                 vl.values = NULL;
242                 vl.values_len = 0;
243                 status = field_specs[field].dispatcher (curl, field_specs[field].info, &vl);
244                 if (status < 0)
245                         return status;
246         }
247
248         return 0;
249 } /* curl_stats_dispatch */