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