blob: ef4be82894ac786465a93af0db3585d08a95b30b [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>
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010039#include <osmocom/core/timer.h>
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +010040#include <osmocom/core/statistics.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020041
42/* TODO: register properly */
43#define DSTATS DLGLOBAL
44
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010045#define STATS_DEFAULT_INTERVAL 5 /* secs */
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020046
47static LLIST_HEAD(stats_reporter_list);
48static void *stats_ctx = NULL;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010049static int is_initialised = 0;
50static int32_t current_stat_item_index = 0;
51
52static struct stats_config s_stats_config = {
53 .interval = STATS_DEFAULT_INTERVAL,
54};
55struct stats_config *stats_config = &s_stats_config;
56
57static struct osmo_timer_list stats_timer;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020058
59static int stats_reporter_statsd_open(struct stats_reporter *srep);
60static int stats_reporter_statsd_close(struct stats_reporter *srep);
61static int stats_reporter_send(struct stats_reporter *srep, const char *data,
62 int data_len);
63
64static int update_srep_config(struct stats_reporter *srep)
65{
66 int rc = 0;
67
68 if (srep->type != STATS_REPORTER_STATSD) {
69 srep->enabled = 0;
70 return -ENOTSUP;
71 }
72
73 if (srep->running) {
74 rc = stats_reporter_statsd_close(srep);
75 srep->running = 0;
76 }
77
78 if (!srep->enabled)
79 return rc;
80
81 rc = stats_reporter_statsd_open(srep);
82 if (rc < 0)
83 srep->enabled = 0;
84 else
85 srep->running = 1;
86
87 return rc;
88}
89
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010090static void stats_timer_cb(void *data)
91{
92 int interval = stats_config->interval;
93
94 if (!llist_empty(&stats_reporter_list))
95 stats_report();
96
97 osmo_timer_schedule(&stats_timer, interval, 0);
98}
99
100static int start_timer()
101{
102 if (!is_initialised)
103 return -ESRCH;
104
105 stats_timer.cb = stats_timer_cb;
106 osmo_timer_schedule(&stats_timer, 0, 1);
107
108 return 0;
109}
110
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200111struct stats_reporter *stats_reporter_alloc(enum stats_reporter_type type,
112 const char *name)
113{
114 struct stats_reporter *srep;
115 srep = talloc_zero(stats_ctx, struct stats_reporter);
116 OSMO_ASSERT(srep);
117 srep->type = type;
118 if (name)
119 srep->name = talloc_strdup(srep, name);
120 srep->fd = -1;
121
122 llist_add(&srep->list, &stats_reporter_list);
123
124 return srep;
125}
126
127void stats_reporter_free(struct stats_reporter *srep)
128{
129 stats_reporter_disable(srep);
130 llist_del(&srep->list);
131 talloc_free(srep);
132}
133
134void stats_init(void *ctx)
135{
136 stats_ctx = ctx;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100137 stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100138
139 is_initialised = 1;
140 start_timer();
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200141}
142
143struct stats_reporter *stats_reporter_find(enum stats_reporter_type type,
144 const char *name)
145{
146 struct stats_reporter *srep;
147 llist_for_each_entry(srep, &stats_reporter_list, list) {
148 if (srep->type != type)
149 continue;
150 if (srep->name != name) {
151 if (name == NULL || srep->name == NULL ||
152 strcmp(name, srep->name) != 0)
153 continue;
154 }
155 return srep;
156 }
157 return NULL;
158}
159
160int stats_reporter_set_remote_addr(struct stats_reporter *srep, const char *addr)
161{
162 int rc;
163 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
164 struct in_addr inaddr;
165
166 OSMO_ASSERT(addr != NULL);
167
168 rc = inet_pton(AF_INET, addr, &inaddr);
169 if (rc <= 0)
170 return -EINVAL;
171
172 sock_addr->sin_addr = inaddr;
173 sock_addr->sin_family = AF_INET;
174 srep->dest_addr_len = sizeof(*sock_addr);
175
176 talloc_free(srep->dest_addr_str);
177 srep->dest_addr_str = talloc_strdup(srep, addr);
178
179 return update_srep_config(srep);
180}
181
182int stats_reporter_set_remote_port(struct stats_reporter *srep, int port)
183{
184 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
185
186 srep->dest_port = port;
187 sock_addr->sin_port = htons(port);
188
189 return update_srep_config(srep);
190}
191
192int stats_reporter_set_local_addr(struct stats_reporter *srep, const char *addr)
193{
194 int rc;
195 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
196 struct in_addr inaddr;
197
198 if (addr) {
199 rc = inet_pton(AF_INET, addr, &inaddr);
200 if (rc <= 0)
201 return -EINVAL;
202 } else {
203 addr = INADDR_ANY;
204 }
205
206 sock_addr->sin_addr = inaddr;
207 sock_addr->sin_family = AF_INET;
208 srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
209
210 talloc_free(srep->bind_addr_str);
211 srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
212
213 return update_srep_config(srep);
214}
215
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100216int stats_set_interval(int interval)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200217{
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100218 if (interval <= 0)
219 return -EINVAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200220
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100221 stats_config->interval = interval;
222 if (is_initialised)
223 start_timer();
224
225 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200226}
227
228int stats_reporter_set_name_prefix(struct stats_reporter *srep, const char *prefix)
229{
230 talloc_free(srep->name_prefix);
231 srep->name_prefix = prefix ? talloc_strdup(srep, prefix) : NULL;
232
233 return update_srep_config(srep);
234}
235
236int stats_reporter_enable(struct stats_reporter *srep)
237{
238 srep->enabled = 1;
239
240 return update_srep_config(srep);
241}
242
243int stats_reporter_disable(struct stats_reporter *srep)
244{
245 srep->enabled = 0;
246
247 return update_srep_config(srep);
248}
249
250static int stats_reporter_send(struct stats_reporter *srep, const char *data,
251 int data_len)
252{
253 int rc;
254
255 rc = sendto(srep->fd, data, data_len, MSG_NOSIGNAL | MSG_DONTWAIT,
256 &srep->dest_addr, srep->dest_addr_len);
257
258 if (rc == -1)
259 rc = -errno;
260
261 return rc;
262}
263
264/*** statsd reporter ***/
265
266struct stats_reporter *stats_reporter_create_statsd(const char *name)
267{
268 struct stats_reporter *srep;
269 srep = stats_reporter_alloc(STATS_REPORTER_STATSD, name);
270
271 return srep;
272}
273
274static int stats_reporter_statsd_open(struct stats_reporter *srep)
275{
276 int sock;
277 int rc;
278
279 if (srep->fd != -1)
280 stats_reporter_statsd_close(srep);
281
282 sock = socket(AF_INET, SOCK_DGRAM, 0);
283 if (sock == -1)
284 return -errno;
285
286 if (srep->bind_addr_len > 0) {
287 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
288 if (rc == -1)
289 goto failed;
290 }
291
292 srep->fd = sock;
293
294 return 0;
295
296failed:
297 rc = -errno;
298 close(sock);
299
300 return rc;
301}
302
303static int stats_reporter_statsd_close(struct stats_reporter *srep)
304{
305 int rc;
306 if (srep->fd == -1)
307 return -EBADF;
308
309 rc = close(srep->fd);
310 srep->fd = -1;
311 return rc == -1 ? -errno : 0;
312}
313
314static int stats_reporter_statsd_send(struct stats_reporter *srep,
315 const char *name1, int index1, const char *name2, int value,
316 const char *unit)
317{
318 char buf[256];
319 int nchars, rc;
320 char *fmt = NULL;
321
322 if (name1) {
323 if (index1 > 0)
324 fmt = "%1$s.%2$s.%3$d.%4$s:%5$d|%6$s";
325 else
326 fmt = "%1$s.%2$s.%4$s:%5$d|%6$s";
327 } else {
328 fmt = "%1$s.%4$s:%5$d|%6$s";
329 }
330 if (!srep->name_prefix)
331 fmt += 5; /* skip prefix part */
332
333 nchars = snprintf(buf, sizeof(buf), fmt,
334 srep->name_prefix, name1, index1, name2,
335 value, unit);
336
337 if (nchars >= sizeof(buf))
338 /* Truncated */
339 return -EMSGSIZE;
340
341 rc = stats_reporter_send(srep, buf, nchars);
342
343 return rc;
344}
345
346static int stats_reporter_statsd_send_counter(struct stats_reporter *srep,
347 const struct rate_ctr_group *ctrg,
348 const struct rate_ctr_desc *desc,
349 int64_t value, int64_t delta)
350{
351 if (ctrg)
352 return stats_reporter_statsd_send(srep,
353 ctrg->desc->group_name_prefix,
354 ctrg->idx,
355 desc->name, delta, "c");
356 else
357 return stats_reporter_statsd_send(srep,
358 NULL, -1,
359 desc->name, delta, "c");
360}
361
362static int stats_reporter_statsd_send_item(struct stats_reporter *srep,
363 const struct stat_item_group *statg,
364 const struct stat_item_desc *desc, int value)
365{
366 return stats_reporter_statsd_send(srep,
367 statg->desc->group_name_prefix, statg->idx,
368 desc->name, value, desc->unit);
369}
370
371/*** generic rate counter support ***/
372
373static int stats_reporter_send_counter(struct stats_reporter *srep,
374 const struct rate_ctr_group *ctrg,
375 const struct rate_ctr_desc *desc,
376 int64_t value, int64_t delta)
377{
378 int rc;
379
380 switch (srep->type) {
381 case STATS_REPORTER_STATSD:
382 rc = stats_reporter_statsd_send_counter(srep, ctrg, desc,
383 value, delta);
384 break;
385 }
386
387 return rc;
388}
389
390static int rate_ctr_handler(
391 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
392 const struct rate_ctr_desc *desc, void *sctx_)
393{
394 struct stats_reporter *srep;
395 int rc;
396 int64_t delta = rate_ctr_difference(ctr);
397
398 if (delta == 0)
399 return 0;
400
401 llist_for_each_entry(srep, &stats_reporter_list, list) {
402 if (!srep->running)
403 continue;
404
405 rc = stats_reporter_send_counter(srep, ctrg, desc,
406 ctr->current, delta);
407
408 /* TODO: handle rc (log?, inc counter(!)?) or remove it */
409 }
410
411 return 0;
412}
413
414static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
415{
416 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
417
418 return 0;
419}
420
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100421/*** stat item support ***/
422
423static int stats_reporter_send_item(struct stats_reporter *srep,
424 const struct stat_item_group *statg,
425 const struct stat_item_desc *desc,
426 int32_t value)
427{
428 int rc;
429
430 switch (srep->type) {
431 case STATS_REPORTER_STATSD:
432 rc = stats_reporter_statsd_send_item(srep, statg, desc,
433 value);
434 break;
435 }
436
437 return rc;
438}
439
440static int stat_item_handler(
441 struct stat_item_group *statg, struct stat_item *item, void *sctx_)
442{
443 struct stats_reporter *srep;
444 int rc;
445 int32_t idx = current_stat_item_index;
446 int32_t value;
447
448 while (stat_item_get_next(item, &idx, &value) > 0) {
449 llist_for_each_entry(srep, &stats_reporter_list, list) {
450 if (!srep->running)
451 continue;
452
453 rc = stats_reporter_send_item(srep, statg,
454 item->desc, value);
455 }
456 }
457
458 return 0;
459}
460
461static int stat_item_group_handler(struct stat_item_group *statg, void *sctx_)
462{
463 stat_item_for_each_item(statg, stat_item_handler, sctx_);
464 stat_item_discard_all(&current_stat_item_index);
465
466 return 0;
467}
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200468
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100469/*** osmo counter support ***/
470
471static int handle_counter(struct osmo_counter *counter, void *sctx_)
472{
473 struct stats_reporter *srep;
474 int rc;
475 struct rate_ctr_desc desc = {0};
476 /* Fake a rate counter description */
477 desc.name = counter->name;
478 desc.description = counter->description;
479
480 int delta = osmo_counter_difference(counter);
481
482 if (delta == 0)
483 return 0;
484
485 llist_for_each_entry(srep, &stats_reporter_list, list) {
486 if (!srep->running)
487 continue;
488
489 rc = stats_reporter_send_counter(srep, NULL, &desc,
490 counter->value, delta);
491
492 /* TODO: handle rc (log?, inc counter(!)?) */
493 }
494
495 return 0;
496}
497
498
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200499/*** main reporting function ***/
500
501int stats_report()
502{
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100503 osmo_counters_for_each(handle_counter, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200504 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100505 stat_item_for_each_group(stat_item_group_handler, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200506
507 return 0;
508}