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"
28 #include "common.h"
29 #include "utils_curl_stats.h"
31 #include <stdbool.h>
32 #include <stddef.h>
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 };
55 /*
56 * Private functions
57 */
59 static int dispatch_gauge (CURL *curl, CURLINFO info, value_list_t *vl)
60 {
61 CURLcode code;
62 value_t v;
64 code = curl_easy_getinfo (curl, info, &v.gauge);
65 if (code != CURLE_OK)
66 return -1;
68 vl->values = &v;
69 vl->values_len = 1;
71 return plugin_dispatch_values (vl);
72 } /* dispatch_gauge */
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;
80 code = curl_easy_getinfo (curl, info, &v.gauge);
81 if (code != CURLE_OK)
82 return -1;
84 v.gauge *= 8;
86 vl->values = &v;
87 vl->values_len = 1;
89 return plugin_dispatch_values (vl);
90 } /* dispatch_speed */
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;
99 code = curl_easy_getinfo (curl, info, &raw);
100 if (code != CURLE_OK)
101 return -1;
103 v.gauge = (double)raw;
105 vl->values = &v;
106 vl->values_len = 1;
108 return plugin_dispatch_values (vl);
109 } /* dispatch_size */
111 static struct {
112 const char *name;
113 size_t offset;
115 int (*dispatcher)(CURL *, CURLINFO, value_list_t *);
116 const char *type;
117 CURLINFO info;
118 } field_specs[] = {
119 #define SPEC(name, dispatcher, type, info) \
120 { #name, offsetof (curl_stats_t, name), dispatcher, type, info }
122 SPEC (total_time, dispatch_gauge, "duration", CURLINFO_TOTAL_TIME),
123 SPEC (namelookup_time, dispatch_gauge, "duration", CURLINFO_NAMELOOKUP_TIME),
124 SPEC (connect_time, dispatch_gauge, "duration", CURLINFO_CONNECT_TIME),
125 SPEC (pretransfer_time, dispatch_gauge, "duration", CURLINFO_PRETRANSFER_TIME),
126 SPEC (size_upload, dispatch_gauge, "bytes", CURLINFO_SIZE_UPLOAD),
127 SPEC (size_download, dispatch_gauge, "bytes", CURLINFO_SIZE_DOWNLOAD),
128 SPEC (speed_download, dispatch_speed, "bitrate", CURLINFO_SPEED_DOWNLOAD),
129 SPEC (speed_upload, dispatch_speed, "bitrate", CURLINFO_SPEED_UPLOAD),
130 SPEC (header_size, dispatch_size, "bytes", CURLINFO_HEADER_SIZE),
131 SPEC (request_size, dispatch_size, "bytes", CURLINFO_REQUEST_SIZE),
132 SPEC (content_length_download, dispatch_gauge, "bytes", CURLINFO_CONTENT_LENGTH_DOWNLOAD),
133 SPEC (content_length_upload, dispatch_gauge, "bytes", CURLINFO_CONTENT_LENGTH_UPLOAD),
134 SPEC (starttransfer_time, dispatch_gauge, "duration", CURLINFO_STARTTRANSFER_TIME),
135 SPEC (redirect_time, dispatch_gauge, "duration", CURLINFO_REDIRECT_TIME),
136 SPEC (redirect_count, dispatch_size, "count", CURLINFO_REDIRECT_COUNT),
137 SPEC (num_connects, dispatch_size, "count", CURLINFO_NUM_CONNECTS),
138 SPEC (appconnect_time, dispatch_gauge, "duration", CURLINFO_APPCONNECT_TIME),
140 #undef SPEC
141 };
143 static void enable_field (curl_stats_t *s, size_t offset)
144 {
145 *(bool *)((char *)s + offset) = true;
146 } /* enable_field */
148 static bool field_enabled (curl_stats_t *s, size_t offset)
149 {
150 return *(bool *)((char *)s + offset);
151 } /* field_enabled */
153 /*
154 * Public API
155 */
156 curl_stats_t *curl_stats_from_config (oconfig_item_t *ci)
157 {
158 curl_stats_t *s;
159 int i;
161 if (ci == NULL)
162 return NULL;
164 s = calloc (sizeof (*s), 1);
165 if (s == NULL)
166 return NULL;
168 for (i = 0; i < ci->children_num; ++i)
169 {
170 oconfig_item_t *c = ci->children + i;
171 size_t field;
173 for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field)
174 if (! strcasecmp (c->key, field_specs[field].name))
175 break;
176 if (field >= STATIC_ARRAY_SIZE (field_specs))
177 {
178 ERROR ("curl stats: Unknown field name %s", c->key);
179 free (s);
180 return NULL;
181 }
183 if ((c->values_num != 1)
184 || ((c->values[0].type != OCONFIG_TYPE_STRING)
185 && (c->values[0].type != OCONFIG_TYPE_BOOLEAN))) {
186 ERROR ("curl stats: `%s' expects a single boolean argument", c->key);
187 free (s);
188 return NULL;
189 }
191 if (((c->values[0].type == OCONFIG_TYPE_STRING)
192 && IS_TRUE (c->values[0].value.string))
193 || ((c->values[0].type == OCONFIG_TYPE_BOOLEAN)
194 && c->values[0].value.boolean))
195 enable_field (s, field_specs[field].offset);
196 }
198 return s;
199 } /* curl_stats_from_config */
201 void curl_stats_destroy (curl_stats_t *s)
202 {
203 if (s != NULL)
204 free (s);
205 } /* curl_stats_destroy */
207 int curl_stats_dispatch (curl_stats_t *s, CURL *curl,
208 const char *hostname, const char *plugin, const char *plugin_instance,
209 const char *instance_prefix)
210 {
211 value_list_t vl = VALUE_LIST_INIT;
212 size_t field;
214 if (s == NULL)
215 return 0;
216 if (curl == NULL)
217 return -1;
219 if (hostname != NULL)
220 sstrncpy (vl.host, hostname, sizeof (vl.host));
221 if (plugin != NULL)
222 sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
223 if (plugin_instance != NULL)
224 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
226 for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field)
227 {
228 int status;
230 if (! field_enabled (s, field_specs[field].offset))
231 continue;
233 sstrncpy (vl.type, field_specs[field].type, sizeof (vl.type));
234 ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
235 instance_prefix ? instance_prefix : "", field_specs[field].name);
237 vl.values = NULL;
238 vl.values_len = 0;
239 status = field_specs[field].dispatcher (curl, field_specs[field].info, &vl);
240 if (status < 0)
241 return status;
242 }
244 return 0;
245 } /* curl_stats_dispatch */