Merge branch 'collectd-5.7'
[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
29 #include "common.h"
30 #include "utils_curl_stats.h"
31
32 #include <stdbool.h>
33 #include <stddef.h>
34
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 };
54
55 /*
56  * Private functions
57  */
58
59 static int dispatch_gauge(CURL *curl, CURLINFO info, value_list_t *vl) {
60   CURLcode code;
61   value_t v;
62
63   code = curl_easy_getinfo(curl, info, &v.gauge);
64   if (code != CURLE_OK)
65     return -1;
66
67   vl->values = &v;
68   vl->values_len = 1;
69
70   return plugin_dispatch_values(vl);
71 } /* dispatch_gauge */
72
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;
77
78   code = curl_easy_getinfo(curl, info, &v.gauge);
79   if (code != CURLE_OK)
80     return -1;
81
82   v.gauge *= 8;
83
84   vl->values = &v;
85   vl->values_len = 1;
86
87   return plugin_dispatch_values(vl);
88 } /* dispatch_speed */
89
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;
95
96   code = curl_easy_getinfo(curl, info, &raw);
97   if (code != CURLE_OK)
98     return -1;
99
100   v.gauge = (double)raw;
101
102   vl->values = &v;
103   vl->values_len = 1;
104
105   return plugin_dispatch_values(vl);
106 } /* dispatch_size */
107
108 static struct {
109   const char *name;
110   const char *config_key;
111   size_t offset;
112
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 }
119
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
156
157 #undef SPEC
158 };
159
160 static void enable_field(curl_stats_t *s, size_t offset) {
161   *(bool *)((char *)s + offset) = true;
162 } /* enable_field */
163
164 static bool field_enabled(curl_stats_t *s, size_t offset) {
165   return *(bool *)((char *)s + offset);
166 } /* field_enabled */
167
168 /*
169  * Public API
170  */
171 curl_stats_t *curl_stats_from_config(oconfig_item_t *ci) {
172   curl_stats_t *s;
173
174   if (ci == NULL)
175     return NULL;
176
177   s = calloc(1, sizeof(*s));
178   if (s == NULL)
179     return NULL;
180
181   for (int i = 0; i < ci->children_num; ++i) {
182     oconfig_item_t *c = ci->children + i;
183     size_t field;
184
185     _Bool enabled = 0;
186
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     }
198
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   }
206
207   return s;
208 } /* curl_stats_from_config */
209
210 void curl_stats_destroy(curl_stats_t *s) {
211   if (s != NULL)
212     free(s);
213 } /* curl_stats_destroy */
214
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;
218
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   }
228
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));
233
234   for (size_t field = 0; field < STATIC_ARRAY_SIZE(field_specs); ++field) {
235     int status;
236
237     if (!field_enabled(s, field_specs[field].offset))
238       continue;
239
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));
243
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   }
250
251   return 0;
252 } /* curl_stats_dispatch */