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 {
37 bool total_time;
38 bool namelookup_time;
39 bool connect_time;
40 bool pretransfer_time;
41 bool size_upload;
42 bool size_download;
43 bool speed_download;
44 bool speed_upload;
45 bool header_size;
46 bool request_size;
47 bool content_length_download;
48 bool content_length_upload;
49 bool starttransfer_time;
50 bool redirect_time;
51 bool redirect_count;
52 bool num_connects;
53 bool appconnect_time;
54 };
56 /*
57 * Private functions
58 */
60 static int dispatch_gauge (CURL *curl, CURLINFO info, value_list_t *vl)
61 {
62 CURLcode code;
63 value_t v;
65 code = curl_easy_getinfo (curl, info, &v.gauge);
66 if (code != CURLE_OK)
67 return -1;
69 vl->values = &v;
70 vl->values_len = 1;
72 return plugin_dispatch_values (vl);
73 } /* dispatch_gauge */
75 /* dispatch a speed, in bytes/second */
76 static int dispatch_speed (CURL *curl, CURLINFO info, value_list_t *vl)
77 {
78 CURLcode code;
79 value_t v;
81 code = curl_easy_getinfo (curl, info, &v.gauge);
82 if (code != CURLE_OK)
83 return -1;
85 v.gauge *= 8;
87 vl->values = &v;
88 vl->values_len = 1;
90 return plugin_dispatch_values (vl);
91 } /* dispatch_speed */
93 /* dispatch a size/count, reported as a long value */
94 static int dispatch_size (CURL *curl, CURLINFO info, value_list_t *vl)
95 {
96 CURLcode code;
97 value_t v;
98 long raw;
100 code = curl_easy_getinfo (curl, info, &raw);
101 if (code != CURLE_OK)
102 return -1;
104 v.gauge = (double)raw;
106 vl->values = &v;
107 vl->values_len = 1;
109 return plugin_dispatch_values (vl);
110 } /* dispatch_size */
112 static struct {
113 const char *name;
114 const char *config_key;
115 size_t offset;
117 int (*dispatcher)(CURL *, CURLINFO, value_list_t *);
118 const char *type;
119 CURLINFO info;
120 } field_specs[] = {
121 #define SPEC(name, config_key, dispatcher, type, info) \
122 { #name, config_key, offsetof (curl_stats_t, name), dispatcher, type, info }
124 SPEC (total_time, "TotalTime", dispatch_gauge, "duration", CURLINFO_TOTAL_TIME),
125 SPEC (namelookup_time, "NamelookupTime", dispatch_gauge, "duration", CURLINFO_NAMELOOKUP_TIME),
126 SPEC (connect_time, "ConnectTime", dispatch_gauge, "duration", CURLINFO_CONNECT_TIME),
127 SPEC (pretransfer_time, "PretransferTime", dispatch_gauge, "duration", CURLINFO_PRETRANSFER_TIME),
128 SPEC (size_upload, "SizeUpload", dispatch_gauge, "bytes", CURLINFO_SIZE_UPLOAD),
129 SPEC (size_download, "SizeDownload", dispatch_gauge, "bytes", CURLINFO_SIZE_DOWNLOAD),
130 SPEC (speed_download, "SpeedDownload", dispatch_speed, "bitrate", CURLINFO_SPEED_DOWNLOAD),
131 SPEC (speed_upload, "SpeedUpload", dispatch_speed, "bitrate", CURLINFO_SPEED_UPLOAD),
132 SPEC (header_size, "HeaderSize", dispatch_size, "bytes", CURLINFO_HEADER_SIZE),
133 SPEC (request_size, "RequestSize", dispatch_size, "bytes", CURLINFO_REQUEST_SIZE),
134 SPEC (content_length_download, "ContentLengthDownload", dispatch_gauge, "bytes", CURLINFO_CONTENT_LENGTH_DOWNLOAD),
135 SPEC (content_length_upload, "ContentLengthUpload", dispatch_gauge, "bytes", CURLINFO_CONTENT_LENGTH_UPLOAD),
136 SPEC (starttransfer_time, "StarttransferTime", dispatch_gauge, "duration", CURLINFO_STARTTRANSFER_TIME),
137 SPEC (redirect_time, "RedirectTime", dispatch_gauge, "duration", CURLINFO_REDIRECT_TIME),
138 SPEC (redirect_count, "RedirectCount", dispatch_size, "count", CURLINFO_REDIRECT_COUNT),
139 SPEC (num_connects, "NumConnects", dispatch_size, "count", CURLINFO_NUM_CONNECTS),
140 #ifdef HAVE_CURLINFO_APPCONNECT_TIME
141 SPEC (appconnect_time, "AppconnectTime", dispatch_gauge, "duration", CURLINFO_APPCONNECT_TIME),
142 #endif
144 #undef SPEC
145 };
147 static void enable_field (curl_stats_t *s, size_t offset)
148 {
149 *(bool *)((char *)s + offset) = true;
150 } /* enable_field */
152 static bool field_enabled (curl_stats_t *s, size_t offset)
153 {
154 return *(bool *)((char *)s + offset);
155 } /* field_enabled */
157 /*
158 * Public API
159 */
160 curl_stats_t *curl_stats_from_config (oconfig_item_t *ci)
161 {
162 curl_stats_t *s;
164 if (ci == NULL)
165 return NULL;
167 s = calloc (1, sizeof (*s));
168 if (s == NULL)
169 return NULL;
171 for (int i = 0; i < ci->children_num; ++i)
172 {
173 oconfig_item_t *c = ci->children + i;
174 size_t field;
176 _Bool enabled = 0;
178 for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field) {
179 if (! strcasecmp (c->key, field_specs[field].config_key))
180 break;
181 if (! strcasecmp (c->key, field_specs[field].name))
182 break;
183 }
184 if (field >= STATIC_ARRAY_SIZE (field_specs))
185 {
186 ERROR ("curl stats: Unknown field name %s", c->key);
187 free (s);
188 return NULL;
189 }
192 if (cf_util_get_boolean (c, &enabled) != 0) {
193 free (s);
194 return NULL;
195 }
196 if (enabled)
197 enable_field (s, field_specs[field].offset);
198 }
200 return s;
201 } /* curl_stats_from_config */
203 void curl_stats_destroy (curl_stats_t *s)
204 {
205 if (s != NULL)
206 free (s);
207 } /* curl_stats_destroy */
209 int curl_stats_dispatch (curl_stats_t *s, CURL *curl,
210 const char *hostname, const char *plugin, const char *plugin_instance)
211 {
212 value_list_t vl = VALUE_LIST_INIT;
214 if (s == NULL)
215 return 0;
216 if ((curl == NULL) || (hostname == NULL) || (plugin == NULL))
217 {
218 ERROR ("curl stats: dispatch() called with missing arguments "
219 "(curl=%p; hostname=%s; plugin=%s)", curl,
220 hostname == NULL ? "<NULL>" : hostname,
221 plugin == NULL ? "<NULL>" : plugin);
222 return -1;
223 }
225 sstrncpy (vl.host, hostname, sizeof (vl.host));
226 sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
227 if (plugin_instance != NULL)
228 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
230 for (size_t field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field)
231 {
232 int status;
234 if (! field_enabled (s, field_specs[field].offset))
235 continue;
237 sstrncpy (vl.type, field_specs[field].type, sizeof (vl.type));
238 sstrncpy (vl.type_instance, field_specs[field].name, sizeof (vl.type_instance));
240 vl.values = NULL;
241 vl.values_len = 0;
242 status = field_specs[field].dispatcher (curl, field_specs[field].info, &vl);
243 if (status < 0)
244 return status;
245 }
247 return 0;
248 } /* curl_stats_dispatch */