blob: c9be0b74204467ef6837158113c65aff8a51adac [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 Erlbeckb1dbfb42015-10-26 11:58:38 +0100136
137 is_initialised = 1;
138 start_timer();
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200139}
140
141struct stats_reporter *stats_reporter_find(enum stats_reporter_type type,
142 const char *name)
143{
144 struct stats_reporter *srep;
145 llist_for_each_entry(srep, &stats_reporter_list, list) {
146 if (srep->type != type)
147 continue;
148 if (srep->name != name) {
149 if (name == NULL || srep->name == NULL ||
150 strcmp(name, srep->name) != 0)
151 continue;
152 }
153 return srep;
154 }
155 return NULL;
156}
157
158int stats_reporter_set_remote_addr(struct stats_reporter *srep, const char *addr)
159{
160 int rc;
161 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
162 struct in_addr inaddr;
163
164 OSMO_ASSERT(addr != NULL);
165
166 rc = inet_pton(AF_INET, addr, &inaddr);
167 if (rc <= 0)
168 return -EINVAL;
169
170 sock_addr->sin_addr = inaddr;
171 sock_addr->sin_family = AF_INET;
172 srep->dest_addr_len = sizeof(*sock_addr);
173
174 talloc_free(srep->dest_addr_str);
175 srep->dest_addr_str = talloc_strdup(srep, addr);
176
177 return update_srep_config(srep);
178}
179
180int stats_reporter_set_remote_port(struct stats_reporter *srep, int port)
181{
182 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
183
184 srep->dest_port = port;
185 sock_addr->sin_port = htons(port);
186
187 return update_srep_config(srep);
188}
189
190int stats_reporter_set_local_addr(struct stats_reporter *srep, const char *addr)
191{
192 int rc;
193 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
194 struct in_addr inaddr;
195
196 if (addr) {
197 rc = inet_pton(AF_INET, addr, &inaddr);
198 if (rc <= 0)
199 return -EINVAL;
200 } else {
201 addr = INADDR_ANY;
202 }
203
204 sock_addr->sin_addr = inaddr;
205 sock_addr->sin_family = AF_INET;
206 srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
207
208 talloc_free(srep->bind_addr_str);
209 srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
210
211 return update_srep_config(srep);
212}
213
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100214int stats_set_interval(int interval)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200215{
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100216 if (interval <= 0)
217 return -EINVAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200218
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100219 stats_config->interval = interval;
220 if (is_initialised)
221 start_timer();
222
223 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200224}
225
226int stats_reporter_set_name_prefix(struct stats_reporter *srep, const char *prefix)
227{
228 talloc_free(srep->name_prefix);
229 srep->name_prefix = prefix ? talloc_strdup(srep, prefix) : NULL;
230
231 return update_srep_config(srep);
232}
233
234int stats_reporter_enable(struct stats_reporter *srep)
235{
236 srep->enabled = 1;
237
238 return update_srep_config(srep);
239}
240
241int stats_reporter_disable(struct stats_reporter *srep)
242{
243 srep->enabled = 0;
244
245 return update_srep_config(srep);
246}
247
248static int stats_reporter_send(struct stats_reporter *srep, const char *data,
249 int data_len)
250{
251 int rc;
252
253 rc = sendto(srep->fd, data, data_len, MSG_NOSIGNAL | MSG_DONTWAIT,
254 &srep->dest_addr, srep->dest_addr_len);
255
256 if (rc == -1)
257 rc = -errno;
258
259 return rc;
260}
261
262/*** statsd reporter ***/
263
264struct stats_reporter *stats_reporter_create_statsd(const char *name)
265{
266 struct stats_reporter *srep;
267 srep = stats_reporter_alloc(STATS_REPORTER_STATSD, name);
268
269 return srep;
270}
271
272static int stats_reporter_statsd_open(struct stats_reporter *srep)
273{
274 int sock;
275 int rc;
276
277 if (srep->fd != -1)
278 stats_reporter_statsd_close(srep);
279
280 sock = socket(AF_INET, SOCK_DGRAM, 0);
281 if (sock == -1)
282 return -errno;
283
284 if (srep->bind_addr_len > 0) {
285 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
286 if (rc == -1)
287 goto failed;
288 }
289
290 srep->fd = sock;
291
292 return 0;
293
294failed:
295 rc = -errno;
296 close(sock);
297
298 return rc;
299}
300
301static int stats_reporter_statsd_close(struct stats_reporter *srep)
302{
303 int rc;
304 if (srep->fd == -1)
305 return -EBADF;
306
307 rc = close(srep->fd);
308 srep->fd = -1;
309 return rc == -1 ? -errno : 0;
310}
311
312static int stats_reporter_statsd_send(struct stats_reporter *srep,
313 const char *name1, int index1, const char *name2, int value,
314 const char *unit)
315{
316 char buf[256];
317 int nchars, rc;
318 char *fmt = NULL;
319
320 if (name1) {
321 if (index1 > 0)
322 fmt = "%1$s.%2$s.%3$d.%4$s:%5$d|%6$s";
323 else
324 fmt = "%1$s.%2$s.%4$s:%5$d|%6$s";
325 } else {
326 fmt = "%1$s.%4$s:%5$d|%6$s";
327 }
328 if (!srep->name_prefix)
329 fmt += 5; /* skip prefix part */
330
331 nchars = snprintf(buf, sizeof(buf), fmt,
332 srep->name_prefix, name1, index1, name2,
333 value, unit);
334
335 if (nchars >= sizeof(buf))
336 /* Truncated */
337 return -EMSGSIZE;
338
339 rc = stats_reporter_send(srep, buf, nchars);
340
341 return rc;
342}
343
344static int stats_reporter_statsd_send_counter(struct stats_reporter *srep,
345 const struct rate_ctr_group *ctrg,
346 const struct rate_ctr_desc *desc,
347 int64_t value, int64_t delta)
348{
349 if (ctrg)
350 return stats_reporter_statsd_send(srep,
351 ctrg->desc->group_name_prefix,
352 ctrg->idx,
353 desc->name, delta, "c");
354 else
355 return stats_reporter_statsd_send(srep,
356 NULL, -1,
357 desc->name, delta, "c");
358}
359
360static int stats_reporter_statsd_send_item(struct stats_reporter *srep,
361 const struct stat_item_group *statg,
362 const struct stat_item_desc *desc, int value)
363{
364 return stats_reporter_statsd_send(srep,
365 statg->desc->group_name_prefix, statg->idx,
366 desc->name, value, desc->unit);
367}
368
369/*** generic rate counter support ***/
370
371static int stats_reporter_send_counter(struct stats_reporter *srep,
372 const struct rate_ctr_group *ctrg,
373 const struct rate_ctr_desc *desc,
374 int64_t value, int64_t delta)
375{
376 int rc;
377
378 switch (srep->type) {
379 case STATS_REPORTER_STATSD:
380 rc = stats_reporter_statsd_send_counter(srep, ctrg, desc,
381 value, delta);
382 break;
383 }
384
385 return rc;
386}
387
388static int rate_ctr_handler(
389 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
390 const struct rate_ctr_desc *desc, void *sctx_)
391{
392 struct stats_reporter *srep;
393 int rc;
394 int64_t delta = rate_ctr_difference(ctr);
395
396 if (delta == 0)
397 return 0;
398
399 llist_for_each_entry(srep, &stats_reporter_list, list) {
400 if (!srep->running)
401 continue;
402
403 rc = stats_reporter_send_counter(srep, ctrg, desc,
404 ctr->current, delta);
405
406 /* TODO: handle rc (log?, inc counter(!)?) or remove it */
407 }
408
409 return 0;
410}
411
412static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
413{
414 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
415
416 return 0;
417}
418
419
420/*** main reporting function ***/
421
422int stats_report()
423{
424 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
425
426 return 0;
427}