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 **/
27 #include "collectd.h"
29 #include "common.h"
30 #include "utils_curl_stats.h"
32 #include <stdbool.h>
33 #include <stddef.h>
35 struct curl_stats_s {
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 };
55 /*
56 * Private functions
57 */
59 static int dispatch_gauge(CURL *curl, CURLINFO info, value_list_t *vl) {
60 CURLcode code;
61 value_t v;
63 code = curl_easy_getinfo(curl, info, &v.gauge);
64 if (code != CURLE_OK)
65 return -1;
67 vl->values = &v;
68 vl->values_len = 1;
70 return plugin_dispatch_values(vl);
71 } /* dispatch_gauge */
73 /* dispatch a speed, in bytes/second */
74 static int dispatch_speed(CURL *curl, CURLINFO info, value_list_t *vl) {
75 CURLcode code;
76 value_t v;
78 code = curl_easy_getinfo(curl, info, &v.gauge);
79 if (code != CURLE_OK)
80 return -1;
82 v.gauge *= 8;
84 vl->values = &v;
85 vl->values_len = 1;
87 return plugin_dispatch_values(vl);
88 } /* dispatch_speed */
90 /* dispatch a size/count, reported as a long value */
91 static int dispatch_size(CURL *curl, CURLINFO info, value_list_t *vl) {
92 CURLcode code;
93 value_t v;
94 long raw;
96 code = curl_easy_getinfo(curl, info, &raw);
97 if (code != CURLE_OK)
98 return -1;
100 v.gauge = (double)raw;
102 vl->values = &v;
103 vl->values_len = 1;
105 return plugin_dispatch_values(vl);
106 } /* dispatch_size */
108 static struct {
109 const char *name;
110 const char *config_key;
111 size_t offset;
113 int (*dispatcher)(CURL *, CURLINFO, value_list_t *);
114 const char *type;
115 CURLINFO info;
116 } field_specs[] = {
117 #define SPEC(name, config_key, dispatcher, type, info) \
118 { #name, config_key, offsetof(curl_stats_t, name), dispatcher, type, info }
120 SPEC(total_time, "TotalTime", dispatch_gauge, "duration",
121 CURLINFO_TOTAL_TIME),
122 SPEC(namelookup_time, "NamelookupTime", dispatch_gauge, "duration",
123 CURLINFO_NAMELOOKUP_TIME),
124 SPEC(connect_time, "ConnectTime", dispatch_gauge, "duration",
125 CURLINFO_CONNECT_TIME),
126 SPEC(pretransfer_time, "PretransferTime", dispatch_gauge, "duration",
127 CURLINFO_PRETRANSFER_TIME),
128 SPEC(size_upload, "SizeUpload", dispatch_gauge, "bytes",
129 CURLINFO_SIZE_UPLOAD),
130 SPEC(size_download, "SizeDownload", dispatch_gauge, "bytes",
131 CURLINFO_SIZE_DOWNLOAD),
132 SPEC(speed_download, "SpeedDownload", dispatch_speed, "bitrate",
133 CURLINFO_SPEED_DOWNLOAD),
134 SPEC(speed_upload, "SpeedUpload", dispatch_speed, "bitrate",
135 CURLINFO_SPEED_UPLOAD),
136 SPEC(header_size, "HeaderSize", dispatch_size, "bytes",
137 CURLINFO_HEADER_SIZE),
138 SPEC(request_size, "RequestSize", dispatch_size, "bytes",
139 CURLINFO_REQUEST_SIZE),
140 SPEC(content_length_download, "ContentLengthDownload", dispatch_gauge,
141 "bytes", CURLINFO_CONTENT_LENGTH_DOWNLOAD),
142 SPEC(content_length_upload, "ContentLengthUpload", dispatch_gauge, "bytes",
143 CURLINFO_CONTENT_LENGTH_UPLOAD),
144 SPEC(starttransfer_time, "StarttransferTime", dispatch_gauge, "duration",
145 CURLINFO_STARTTRANSFER_TIME),
146 SPEC(redirect_time, "RedirectTime", dispatch_gauge, "duration",
147 CURLINFO_REDIRECT_TIME),
148 SPEC(redirect_count, "RedirectCount", dispatch_size, "count",
149 CURLINFO_REDIRECT_COUNT),
150 SPEC(num_connects, "NumConnects", dispatch_size, "count",
151 CURLINFO_NUM_CONNECTS),
152 #ifdef HAVE_CURLINFO_APPCONNECT_TIME
153 SPEC(appconnect_time, "AppconnectTime", dispatch_gauge, "duration",
154 CURLINFO_APPCONNECT_TIME),
155 #endif
157 #undef SPEC
158 };
160 static void enable_field(curl_stats_t *s, size_t offset) {
161 *(bool *)((char *)s + offset) = true;
162 } /* enable_field */
164 static bool field_enabled(curl_stats_t *s, size_t offset) {
165 return *(bool *)((char *)s + offset);
166 } /* field_enabled */
168 /*
169 * Public API
170 */
171 curl_stats_t *curl_stats_from_config(oconfig_item_t *ci) {
172 curl_stats_t *s;
174 if (ci == NULL)
175 return NULL;
177 s = calloc(1, sizeof(*s));
178 if (s == NULL)
179 return NULL;
181 for (int i = 0; i < ci->children_num; ++i) {
182 oconfig_item_t *c = ci->children + i;
183 size_t field;
185 _Bool enabled = 0;
187 for (field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) {
188 if (!strcasecmp(c->key, field_specs[field].config_key))
189 break;
190 if (!strcasecmp(c->key, field_specs[field].name))
191 break;
192 }
193 if (field >= STATIC_ARRAY_SIZE(field_specs)) {
194 ERROR("curl stats: Unknown field name %s", c->key);
195 free(s);
196 return NULL;
197 }
199 if (cf_util_get_boolean(c, &enabled) != 0) {
200 free(s);
201 return NULL;
202 }
203 if (enabled)
204 enable_field(s, field_specs[field].offset);
205 }
207 return s;
208 } /* curl_stats_from_config */
210 void curl_stats_destroy(curl_stats_t *s) {
211 if (s != NULL)
212 free(s);
213 } /* curl_stats_destroy */
215 int curl_stats_dispatch(curl_stats_t *s, CURL *curl, const char *hostname,
216 const char *plugin, const char *plugin_instance) {
217 value_list_t vl = VALUE_LIST_INIT;
219 if (s == NULL)
220 return 0;
221 if ((curl == NULL) || (hostname == NULL) || (plugin == NULL)) {
222 ERROR("curl stats: dispatch() called with missing arguments "
223 "(curl=%p; hostname=%s; plugin=%s)",
224 curl, hostname == NULL ? "<NULL>" : hostname,
225 plugin == NULL ? "<NULL>" : plugin);
226 return -1;
227 }
229 sstrncpy(vl.host, hostname, sizeof(vl.host));
230 sstrncpy(vl.plugin, plugin, sizeof(vl.plugin));
231 if (plugin_instance != NULL)
232 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
234 for (size_t field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) {
235 int status;
237 if (!field_enabled(s, field_specs[field].offset))
238 continue;
240 sstrncpy(vl.type, field_specs[field].type, sizeof(vl.type));
241 sstrncpy(vl.type_instance, field_specs[field].name,
242 sizeof(vl.type_instance));
244 vl.values = NULL;
245 vl.values_len = 0;
246 status = field_specs[field].dispatcher(curl, field_specs[field].info, &vl);
247 if (status < 0)
248 return status;
249 }
251 return 0;
252 } /* curl_stats_dispatch */