blob: 180452e2276e804280b0c584043eb861e9568f3f [file] [log] [blame]
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02001/*
2 * (C) 2015 by Sysmocom s.f.m.c. GmbH
3 *
4 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
5 *
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
24#include <osmocom/core/stats.h>
25
26#include <unistd.h>
27#include <string.h>
28#include <stdint.h>
29#include <errno.h>
30#include <stdio.h>
31#include <sys/socket.h>
32#include <netinet/ip.h>
33#include <arpa/inet.h>
34
35#include <osmocom/core/utils.h>
36#include <osmocom/core/logging.h>
37#include <osmocom/core/rate_ctr.h>
38#include <osmocom/core/stat_item.h>
39
40/* TODO: register properly */
41#define DSTATS DLGLOBAL
42
43
44static LLIST_HEAD(stats_reporter_list);
45static void *stats_ctx = NULL;
46
47static int stats_reporter_statsd_open(struct stats_reporter *srep);
48static int stats_reporter_statsd_close(struct stats_reporter *srep);
49static int stats_reporter_send(struct stats_reporter *srep, const char *data,
50 int data_len);
51
52static int update_srep_config(struct stats_reporter *srep)
53{
54 int rc = 0;
55
56 if (srep->type != STATS_REPORTER_STATSD) {
57 srep->enabled = 0;
58 return -ENOTSUP;
59 }
60
61 if (srep->running) {
62 rc = stats_reporter_statsd_close(srep);
63 srep->running = 0;
64 }
65
66 if (!srep->enabled)
67 return rc;
68
69 rc = stats_reporter_statsd_open(srep);
70 if (rc < 0)
71 srep->enabled = 0;
72 else
73 srep->running = 1;
74
75 return rc;
76}
77
78struct stats_reporter *stats_reporter_alloc(enum stats_reporter_type type,
79 const char *name)
80{
81 struct stats_reporter *srep;
82 srep = talloc_zero(stats_ctx, struct stats_reporter);
83 OSMO_ASSERT(srep);
84 srep->type = type;
85 if (name)
86 srep->name = talloc_strdup(srep, name);
87 srep->fd = -1;
88
89 llist_add(&srep->list, &stats_reporter_list);
90
91 return srep;
92}
93
94void stats_reporter_free(struct stats_reporter *srep)
95{
96 stats_reporter_disable(srep);
97 llist_del(&srep->list);
98 talloc_free(srep);
99}
100
101void stats_init(void *ctx)
102{
103 stats_ctx = ctx;
104}
105
106struct stats_reporter *stats_reporter_find(enum stats_reporter_type type,
107 const char *name)
108{
109 struct stats_reporter *srep;
110 llist_for_each_entry(srep, &stats_reporter_list, list) {
111 if (srep->type != type)
112 continue;
113 if (srep->name != name) {
114 if (name == NULL || srep->name == NULL ||
115 strcmp(name, srep->name) != 0)
116 continue;
117 }
118 return srep;
119 }
120 return NULL;
121}
122
123int stats_reporter_set_remote_addr(struct stats_reporter *srep, const char *addr)
124{
125 int rc;
126 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
127 struct in_addr inaddr;
128
129 OSMO_ASSERT(addr != NULL);
130
131 rc = inet_pton(AF_INET, addr, &inaddr);
132 if (rc <= 0)
133 return -EINVAL;
134
135 sock_addr->sin_addr = inaddr;
136 sock_addr->sin_family = AF_INET;
137 srep->dest_addr_len = sizeof(*sock_addr);
138
139 talloc_free(srep->dest_addr_str);
140 srep->dest_addr_str = talloc_strdup(srep, addr);
141
142 return update_srep_config(srep);
143}
144
145int stats_reporter_set_remote_port(struct stats_reporter *srep, int port)
146{
147 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
148
149 srep->dest_port = port;
150 sock_addr->sin_port = htons(port);
151
152 return update_srep_config(srep);
153}
154
155int stats_reporter_set_local_addr(struct stats_reporter *srep, const char *addr)
156{
157 int rc;
158 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
159 struct in_addr inaddr;
160
161 if (addr) {
162 rc = inet_pton(AF_INET, addr, &inaddr);
163 if (rc <= 0)
164 return -EINVAL;
165 } else {
166 addr = INADDR_ANY;
167 }
168
169 sock_addr->sin_addr = inaddr;
170 sock_addr->sin_family = AF_INET;
171 srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
172
173 talloc_free(srep->bind_addr_str);
174 srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
175
176 return update_srep_config(srep);
177}
178
179int stats_reporter_set_interval(struct stats_reporter *srep, int interval)
180{
181 srep->interval = interval;
182
183 return update_srep_config(srep);
184}
185
186int stats_reporter_set_name_prefix(struct stats_reporter *srep, const char *prefix)
187{
188 talloc_free(srep->name_prefix);
189 srep->name_prefix = prefix ? talloc_strdup(srep, prefix) : NULL;
190
191 return update_srep_config(srep);
192}
193
194int stats_reporter_enable(struct stats_reporter *srep)
195{
196 srep->enabled = 1;
197
198 return update_srep_config(srep);
199}
200
201int stats_reporter_disable(struct stats_reporter *srep)
202{
203 srep->enabled = 0;
204
205 return update_srep_config(srep);
206}
207
208static int stats_reporter_send(struct stats_reporter *srep, const char *data,
209 int data_len)
210{
211 int rc;
212
213 rc = sendto(srep->fd, data, data_len, MSG_NOSIGNAL | MSG_DONTWAIT,
214 &srep->dest_addr, srep->dest_addr_len);
215
216 if (rc == -1)
217 rc = -errno;
218
219 return rc;
220}
221
222/*** statsd reporter ***/
223
224struct stats_reporter *stats_reporter_create_statsd(const char *name)
225{
226 struct stats_reporter *srep;
227 srep = stats_reporter_alloc(STATS_REPORTER_STATSD, name);
228
229 return srep;
230}
231
232static int stats_reporter_statsd_open(struct stats_reporter *srep)
233{
234 int sock;
235 int rc;
236
237 if (srep->fd != -1)
238 stats_reporter_statsd_close(srep);
239
240 sock = socket(AF_INET, SOCK_DGRAM, 0);
241 if (sock == -1)
242 return -errno;
243
244 if (srep->bind_addr_len > 0) {
245 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
246 if (rc == -1)
247 goto failed;
248 }
249
250 srep->fd = sock;
251
252 return 0;
253
254failed:
255 rc = -errno;
256 close(sock);
257
258 return rc;
259}
260
261static int stats_reporter_statsd_close(struct stats_reporter *srep)
262{
263 int rc;
264 if (srep->fd == -1)
265 return -EBADF;
266
267 rc = close(srep->fd);
268 srep->fd = -1;
269 return rc == -1 ? -errno : 0;
270}
271
272static int stats_reporter_statsd_send(struct stats_reporter *srep,
273 const char *name1, int index1, const char *name2, int value,
274 const char *unit)
275{
276 char buf[256];
277 int nchars, rc;
278 char *fmt = NULL;
279
280 if (name1) {
281 if (index1 > 0)
282 fmt = "%1$s.%2$s.%3$d.%4$s:%5$d|%6$s";
283 else
284 fmt = "%1$s.%2$s.%4$s:%5$d|%6$s";
285 } else {
286 fmt = "%1$s.%4$s:%5$d|%6$s";
287 }
288 if (!srep->name_prefix)
289 fmt += 5; /* skip prefix part */
290
291 nchars = snprintf(buf, sizeof(buf), fmt,
292 srep->name_prefix, name1, index1, name2,
293 value, unit);
294
295 if (nchars >= sizeof(buf))
296 /* Truncated */
297 return -EMSGSIZE;
298
299 rc = stats_reporter_send(srep, buf, nchars);
300
301 return rc;
302}
303
304static int stats_reporter_statsd_send_counter(struct stats_reporter *srep,
305 const struct rate_ctr_group *ctrg,
306 const struct rate_ctr_desc *desc,
307 int64_t value, int64_t delta)
308{
309 if (ctrg)
310 return stats_reporter_statsd_send(srep,
311 ctrg->desc->group_name_prefix,
312 ctrg->idx,
313 desc->name, delta, "c");
314 else
315 return stats_reporter_statsd_send(srep,
316 NULL, -1,
317 desc->name, delta, "c");
318}
319
320static int stats_reporter_statsd_send_item(struct stats_reporter *srep,
321 const struct stat_item_group *statg,
322 const struct stat_item_desc *desc, int value)
323{
324 return stats_reporter_statsd_send(srep,
325 statg->desc->group_name_prefix, statg->idx,
326 desc->name, value, desc->unit);
327}
328
329/*** generic rate counter support ***/
330
331static int stats_reporter_send_counter(struct stats_reporter *srep,
332 const struct rate_ctr_group *ctrg,
333 const struct rate_ctr_desc *desc,
334 int64_t value, int64_t delta)
335{
336 int rc;
337
338 switch (srep->type) {
339 case STATS_REPORTER_STATSD:
340 rc = stats_reporter_statsd_send_counter(srep, ctrg, desc,
341 value, delta);
342 break;
343 }
344
345 return rc;
346}
347
348static int rate_ctr_handler(
349 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
350 const struct rate_ctr_desc *desc, void *sctx_)
351{
352 struct stats_reporter *srep;
353 int rc;
354 int64_t delta = rate_ctr_difference(ctr);
355
356 if (delta == 0)
357 return 0;
358
359 llist_for_each_entry(srep, &stats_reporter_list, list) {
360 if (!srep->running)
361 continue;
362
363 rc = stats_reporter_send_counter(srep, ctrg, desc,
364 ctr->current, delta);
365
366 /* TODO: handle rc (log?, inc counter(!)?) or remove it */
367 }
368
369 return 0;
370}
371
372static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
373{
374 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
375
376 return 0;
377}
378
379
380/*** main reporting function ***/
381
382int stats_report()
383{
384 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
385
386 return 0;
387}