blob: 696a519218c932f6035a9151ec62cdb9d52919bc [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file utils.c
2 * Utility routines for printing common objects in the Osmocom world. */
3/*
4 * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
Harald Weltee08da972017-11-13 01:00:26 +09005 * (C) 2013,2015 by sysmocom - s.f.m.c. GmbH
Harald Welte3fb0b6f2010-05-19 19:02:52 +02006 *
7 * All Rights Reserved
8 *
Harald Weltee08da972017-11-13 01:00:26 +09009 * SPDX-License-Identifier: GPL-2.0+
10 *
Harald Welte3fb0b6f2010-05-19 19:02:52 +020011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
Harald Welte3fb0b6f2010-05-19 19:02:52 +020021 */
22
23#include <stdint.h>
24#include <inttypes.h>
Harald Weltefab0ae92012-08-17 12:17:38 +020025#include <string.h>
Holger Hans Peter Freytherb321b932012-09-11 10:39:29 +020026#include <ctype.h>
Jacob Erlbeck59b90bc2015-11-03 16:21:40 +010027#include <limits.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020028
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010029#include <osmocom/core/linuxlist.h>
30#include <osmocom/core/talloc.h>
31#include <osmocom/core/timer.h>
32#include <osmocom/core/rate_ctr.h>
Jacob Erlbeck738d9e22015-10-06 15:21:56 +020033#include <osmocom/core/stat_item.h>
Harald Weltefab0ae92012-08-17 12:17:38 +020034#include <osmocom/core/utils.h>
Harald Welte216338c2017-10-15 19:46:19 +020035#include <osmocom/core/counter.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020036
37#include <osmocom/vty/vty.h>
38
Harald Welte7acb30c2011-08-17 17:13:48 +020039/*! \addtogroup rate_ctr
40 * @{
41 */
42
Jacob Erlbeckaec583f2015-10-19 15:06:01 +020043struct vty_out_context {
44 struct vty *vty;
45 const char *prefix;
Jacob Erlbeck59b90bc2015-11-03 16:21:40 +010046 int max_level;
Jacob Erlbeckaec583f2015-10-19 15:06:01 +020047};
48
49static int rate_ctr_handler(
50 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
51 const struct rate_ctr_desc *desc, void *vctx_)
52{
53 struct vty_out_context *vctx = vctx_;
54 struct vty *vty = vctx->vty;
55
56 vty_out(vty, " %s%s: %8" PRIu64 " "
57 "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s",
58 vctx->prefix, desc->description, ctr->current,
59 ctr->intv[RATE_CTR_INTV_SEC].rate,
60 ctr->intv[RATE_CTR_INTV_MIN].rate,
61 ctr->intv[RATE_CTR_INTV_HOUR].rate,
62 ctr->intv[RATE_CTR_INTV_DAY].rate,
63 VTY_NEWLINE);
64
65 return 0;
66}
67
Neels Hofmeyr87e45502017-06-20 00:17:59 +020068/*! print a rate counter group to given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +020069 * \param[in] vty The VTY to which it should be printed
70 * \param[in] prefix Any additional log prefix ahead of each line
71 * \param[in] ctrg Rate counter group to be printed
72 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020073void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
74 struct rate_ctr_group *ctrg)
75{
Jacob Erlbeckaec583f2015-10-19 15:06:01 +020076 struct vty_out_context vctx = {vty, prefix};
Harald Welte3fb0b6f2010-05-19 19:02:52 +020077
78 vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE);
Jacob Erlbeckaec583f2015-10-19 15:06:01 +020079
80 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, &vctx);
81}
82
Stefan Sperling97d3da22018-05-24 16:42:43 +020083static char *
84pad_append_str(char *s, const char *a, int minwidth)
85{
86 s = talloc_asprintf_append(s, "%*s", minwidth, a);
87 OSMO_ASSERT(s);
88 return s;
89}
90
91static char *
Stefan Sperling4df22512018-05-25 13:05:58 +020092pad_append_ctr(char *s, uint64_t ctr, int minwidth)
Stefan Sperling97d3da22018-05-24 16:42:43 +020093{
94 s = talloc_asprintf_append(s, "%*" PRIu64, minwidth, ctr);
95 OSMO_ASSERT(s);
96 return s;
97}
98
99static int rate_ctr_handler_fmt(
100 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
101 const struct rate_ctr_desc *desc, void *vctx_)
102{
103 struct vty_out_context *vctx = vctx_;
104 struct vty *vty = vctx->vty;
105 const char *fmt = vctx->prefix;
106 char *s = talloc_strdup(vty, "");
107 OSMO_ASSERT(s);
108
109 while (*fmt) {
110 int ch, minwidth = 0, sign = 1;
111 char *p = strchr(fmt, '%');
112
113 if (p == NULL) {
114 /* No further % directives in format string. Copy rest verbatim and exit. */
115 s = talloc_strdup_append_buffer(s, fmt);
116 OSMO_ASSERT(s);
117 break;
118 } else {
119 ptrdiff_t len;
120
121 OSMO_ASSERT(p >= fmt);
122 len = p - fmt;
123 if (len) {
124 /* Copy bytes verbatim until next '%' byte. */
125 s = talloc_strndup_append_buffer(s, fmt, len);
126 OSMO_ASSERT(s);
127 }
128 fmt = (const char *)(p + 1); /* skip past '%' */
129 if (*fmt == '\0')
130 break;
131 }
132
133 ch = *fmt++;
134 if (ch == '-' && isdigit(*fmt)) {
135 sign = -1;
136 ch = *fmt++;
137 }
138 while (isdigit(ch) && *fmt != '\0') {
139 minwidth *= 10;
140 minwidth += (ch - '0');
141 ch = *fmt++;
142 }
143 minwidth *= sign;
144
145 switch (ch) {
146 case '%':
147 s = talloc_asprintf_append(s, "%c", ch);
148 OSMO_ASSERT(s);
149 break;
150 case 'd':
151 s = pad_append_str(s, desc->description, minwidth);
152 break;
153 case 'n':
154 s = pad_append_str(s, desc->name, minwidth);
155 break;
156 case 'c':
Stefan Sperling4df22512018-05-25 13:05:58 +0200157 s = pad_append_ctr(s, ctr->current, minwidth);
Stefan Sperling97d3da22018-05-24 16:42:43 +0200158 break;
159 case 'p':
Stefan Sperling4df22512018-05-25 13:05:58 +0200160 s = pad_append_ctr(s, ctr->previous, minwidth);
Stefan Sperling97d3da22018-05-24 16:42:43 +0200161 break;
162 case 'S':
Stefan Sperling4df22512018-05-25 13:05:58 +0200163 s = pad_append_ctr(s, ctr->intv[RATE_CTR_INTV_SEC].rate, minwidth);
Stefan Sperling97d3da22018-05-24 16:42:43 +0200164 break;
165 case 'M':
Stefan Sperling4df22512018-05-25 13:05:58 +0200166 s = pad_append_ctr(s, ctr->intv[RATE_CTR_INTV_MIN].rate, minwidth);
Stefan Sperling97d3da22018-05-24 16:42:43 +0200167 break;
168 case 'H':
Stefan Sperling4df22512018-05-25 13:05:58 +0200169 s = pad_append_ctr(s, ctr->intv[RATE_CTR_INTV_HOUR].rate, minwidth);
Stefan Sperling97d3da22018-05-24 16:42:43 +0200170 break;
171 case 'D':
Stefan Sperling4df22512018-05-25 13:05:58 +0200172 s = pad_append_ctr(s, ctr->intv[RATE_CTR_INTV_DAY].rate, minwidth);
Stefan Sperling97d3da22018-05-24 16:42:43 +0200173 break;
174 default:
175 break;
176 }
177 }
178
179 vty_out(vty, "%s%s", s, VTY_NEWLINE);
180 talloc_free(s);
181
182 return 0;
183}
184
185/*! print a rate counter group to given VTY, formatting the line for each counter according to a format string.
186 *
187 * The following format string directives are supported:
188 * - %d: The description of the counter
189 * - %n: The name of the counter
190 * - %c: The current value of the counter
191 * - %p: The previous value of the counter
192 * - %S: The interval of the counter in seconds
193 * - %M: The interval of the counter in minutes
194 * - %H: The interval of the counter in hours
195 * - %D: The interval of the counter in days
196 * - %%: Print a literal %.
197 *
198 * An optional number between % and the letter in a format directive may be used to set a minimum field width.
199 * If the expanded format directive is smaller than this width (according to strlen()) the string will be
200 * left-padded (if the number is positive) or right-padded (if the number is negative) with spaces.
201 * For example, "%25n" prints the counter name left-padded up to a minimum width of 25 columns.
202 *
203 * VTY_NEWLINE will be appended to the format string when it is printed.
204 *
205 * \param[in] vty The VTY to which it should be printed
206 * \param[in] ctrg Rate counter group to be printed
207 * \param[in] fmt A format which may contain the above directives.
208 */
209void vty_out_rate_ctr_group_fmt(struct vty *vty, const char *fmt,
210 struct rate_ctr_group *ctrg)
211{
212 struct vty_out_context vctx = {vty, fmt};
Stefan Sperling97d3da22018-05-24 16:42:43 +0200213 rate_ctr_for_each_counter(ctrg, rate_ctr_handler_fmt, &vctx);
214}
215
Harald Welte96e2a002017-06-12 21:44:18 +0200216static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_)
217{
218 struct vty_out_context *vctx = vctx_;
219 struct vty *vty = vctx->vty;
220
221 if (ctrg->desc->class_id > vctx->max_level)
222 return 0;
223
Pau Espin Pedrol09f075f2021-05-31 13:10:24 +0200224 vty_out(vty, "%s%s (%d)", vctx->prefix, ctrg->desc->group_description, ctrg->idx);
225 if (ctrg->name)
226 vty_out(vty, "('%s')", ctrg->name);
227 vty_out(vty, ":%s", VTY_NEWLINE);
Harald Welte96e2a002017-06-12 21:44:18 +0200228
229 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, vctx);
230
231 return 0;
232}
233
234/*! @} */
235
236
237/*! \addtogroup stats
238 * @{
239 */
240
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100241static int osmo_stat_item_handler(
242 struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *vctx_)
Jacob Erlbeckaec583f2015-10-19 15:06:01 +0200243{
244 struct vty_out_context *vctx = vctx_;
245 struct vty *vty = vctx->vty;
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200246 const struct osmo_stat_item_desc *desc = osmo_stat_item_get_desc(item);
247 const char *unit = (desc->unit != OSMO_STAT_ITEM_NO_UNIT) ? desc->unit : "";
Jacob Erlbeckaec583f2015-10-19 15:06:01 +0200248
249 vty_out(vty, " %s%s: %8" PRIi32 " %s%s",
Neels Hofmeyre90c7172021-09-14 14:37:38 +0200250 vctx->prefix, desc->description,
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100251 osmo_stat_item_get_last(item),
Jacob Erlbeckaf5bad52015-11-27 18:54:58 +0100252 unit, VTY_NEWLINE);
Jacob Erlbeckaec583f2015-10-19 15:06:01 +0200253
254 return 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200255}
Harald Welte7acb30c2011-08-17 17:13:48 +0200256
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200257/*! print a stat item group to given VTY
Jacob Erlbeck738d9e22015-10-06 15:21:56 +0200258 * \param[in] vty The VTY to which it should be printed
259 * \param[in] prefix Any additional log prefix ahead of each line
260 * \param[in] statg Stat item group to be printed
261 */
262void vty_out_stat_item_group(struct vty *vty, const char *prefix,
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100263 struct osmo_stat_item_group *statg)
Jacob Erlbeck738d9e22015-10-06 15:21:56 +0200264{
Jacob Erlbeckaec583f2015-10-19 15:06:01 +0200265 struct vty_out_context vctx = {vty, prefix};
Jacob Erlbeck738d9e22015-10-06 15:21:56 +0200266
267 vty_out(vty, "%s%s:%s", prefix, statg->desc->group_description,
268 VTY_NEWLINE);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100269 osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, &vctx);
Jacob Erlbeck738d9e22015-10-06 15:21:56 +0200270}
271
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100272static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *vctx_)
Jacob Erlbeck7211fe12015-10-19 15:11:50 +0200273{
274 struct vty_out_context *vctx = vctx_;
275 struct vty *vty = vctx->vty;
276
Jacob Erlbeck59b90bc2015-11-03 16:21:40 +0100277 if (statg->desc->class_id > vctx->max_level)
278 return 0;
279
Pau Espin Pedrol09f075f2021-05-31 13:10:24 +0200280 vty_out(vty, "%s%s (%d)", vctx->prefix, statg->desc->group_description, statg->idx);
281 if (statg->name)
282 vty_out(vty, "('%s')", statg->name);
283 vty_out(vty, ":%s", VTY_NEWLINE);
Jacob Erlbeck7211fe12015-10-19 15:11:50 +0200284
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100285 osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, vctx);
Jacob Erlbeck7211fe12015-10-19 15:11:50 +0200286
287 return 0;
288}
289
Harald Welte96e2a002017-06-12 21:44:18 +0200290/*! @} */
Jacob Erlbeck7211fe12015-10-19 15:11:50 +0200291
Harald Welte96e2a002017-06-12 21:44:18 +0200292/*! \addtogroup vty
293 * @{
294 */
Jacob Erlbeck7211fe12015-10-19 15:11:50 +0200295
296static int handle_counter(struct osmo_counter *counter, void *vctx_)
297{
298 struct vty_out_context *vctx = vctx_;
299 struct vty *vty = vctx->vty;
Alexander Couzens3e432e12016-10-04 11:24:02 +0200300 const char *description = counter->description;
301
302 if (!counter->description)
303 description = counter->name;
Jacob Erlbeck7211fe12015-10-19 15:11:50 +0200304
305 vty_out(vty, " %s%s: %8lu%s",
Alexander Couzens3e432e12016-10-04 11:24:02 +0200306 vctx->prefix, description,
Jacob Erlbeck7211fe12015-10-19 15:11:50 +0200307 osmo_counter_get(counter), VTY_NEWLINE);
308
309 return 0;
310}
311
Jacob Erlbeck59b90bc2015-11-03 16:21:40 +0100312void vty_out_statistics_partial(struct vty *vty, const char *prefix,
313 int max_level)
Jacob Erlbeck7211fe12015-10-19 15:11:50 +0200314{
Jacob Erlbeck59b90bc2015-11-03 16:21:40 +0100315 struct vty_out_context vctx = {vty, prefix, max_level};
Jacob Erlbeck7211fe12015-10-19 15:11:50 +0200316
317 vty_out(vty, "%sUngrouped counters:%s", prefix, VTY_NEWLINE);
318 osmo_counters_for_each(handle_counter, &vctx);
319 rate_ctr_for_each_group(rate_ctr_group_handler, &vctx);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100320 osmo_stat_item_for_each_group(osmo_stat_item_group_handler, &vctx);
Jacob Erlbeck7211fe12015-10-19 15:11:50 +0200321}
322
Jacob Erlbeck59b90bc2015-11-03 16:21:40 +0100323void vty_out_statistics_full(struct vty *vty, const char *prefix)
324{
325 vty_out_statistics_partial(vty, prefix, INT_MAX);
326}
327
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200328/*! Generate a VTY command string from value_string */
Harald Weltefab0ae92012-08-17 12:17:38 +0200329char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals,
330 const char *prefix, const char *sep,
331 const char *end, int do_lower)
332{
333 int len = 0, offset = 0, ret, rem;
Jacob Erlbeckcd195fa2013-08-06 14:29:15 +0200334 int size = strlen(prefix) + strlen(end);
335 int sep_len = strlen(sep);
Harald Weltefab0ae92012-08-17 12:17:38 +0200336 const struct value_string *vs;
337 char *str;
338
339 for (vs = vals; vs->value || vs->str; vs++)
Jacob Erlbeckcd195fa2013-08-06 14:29:15 +0200340 size += strlen(vs->str) + sep_len;
Harald Weltefab0ae92012-08-17 12:17:38 +0200341
342 rem = size;
343 str = talloc_zero_size(ctx, size);
344 if (!str)
345 return NULL;
346
Jacob Erlbeckae15a2c2013-08-06 14:29:14 +0200347 ret = snprintf(str + offset, rem, "%s", prefix);
Harald Weltefab0ae92012-08-17 12:17:38 +0200348 if (ret < 0)
349 goto err;
350 OSMO_SNPRINTF_RET(ret, rem, offset, len);
351
352 for (vs = vals; vs->value || vs->str; vs++) {
353 if (vs->str) {
354 int j, name_len = strlen(vs->str)+1;
355 char name[name_len];
356
357 for (j = 0; j < name_len; j++)
358 name[j] = do_lower ?
359 tolower(vs->str[j]) : vs->str[j];
360
361 name[name_len-1] = '\0';
362 ret = snprintf(str + offset, rem, "%s%s", name, sep);
363 if (ret < 0)
364 goto err;
365 OSMO_SNPRINTF_RET(ret, rem, offset, len);
366 }
367 }
Jacob Erlbeckcd195fa2013-08-06 14:29:15 +0200368 offset -= sep_len; /* to remove the trailing sep */
369 rem += sep_len;
Harald Weltefab0ae92012-08-17 12:17:38 +0200370
Jacob Erlbeckae15a2c2013-08-06 14:29:14 +0200371 ret = snprintf(str + offset, rem, "%s", end);
Harald Weltefab0ae92012-08-17 12:17:38 +0200372 if (ret < 0)
373 goto err;
374 OSMO_SNPRINTF_RET(ret, rem, offset, len);
375err:
376 str[size-1] = '\0';
377 return str;
378}
379
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200380/*! @} */