blob: e3e165da4ece820ee9b8ee80a82a9a98fae0c677 [file] [log] [blame]
Neels Hofmeyr0fd615f2019-01-26 20:36:12 +01001/* Test implementation for osmo_tdef VTY configuration API. */
2/*
3 * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 *
5 * All Rights Reserved
6 *
7 * SPDX-License-Identifier: GPL-2.0+
8 *
9 * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
10 *
11 * 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.
Neels Hofmeyr0fd615f2019-01-26 20:36:12 +010020 */
21
22#define _GNU_SOURCE
23#include <getopt.h>
24#include <signal.h>
25#include <limits.h>
26#include <string.h>
27
28#include <osmocom/core/application.h>
29
30#include <osmocom/vty/command.h>
31#include <osmocom/vty/misc.h>
32#include <osmocom/vty/telnet_interface.h>
33
34#include <osmocom/core/tdef.h>
35#include <osmocom/vty/tdef_vty.h>
36
37#include <stdlib.h>
38
39#include "config.h"
40
41/* ------------------- HERE IS THE INTERESTING TDEF RELEVANT PART ------------------- */
42
43/* This example keeps a single global timer group and offers a custom 'timer' VTY command in a 'network' subnode below
44 * the CONFIG_NODE.
Vadim Yanitskiy52a38b42021-11-17 01:58:03 +030045 * the tdef_vty_config_subnode_test.vty transcript test.
Neels Hofmeyr0fd615f2019-01-26 20:36:12 +010046 */
47
48static struct osmo_tdef global_tdefs[] = {
49 { .T=1, .default_val=100, .desc="Testing a hundred seconds" }, // default is .unit=OSMO_TDEF_S == 0
50 { .T=2, .default_val=100, .unit=OSMO_TDEF_MS, .desc="Testing a hundred milliseconds" },
51 { .T=3, .default_val=100, .unit=OSMO_TDEF_M, .desc="Testing a hundred minutes" },
52 { .T=4, .default_val=100, .unit=OSMO_TDEF_CUSTOM, .desc="Testing a hundred potatoes" },
Neels Hofmeyr7b740f72019-02-06 01:08:43 +010053 { .T=0x7fffffff, .default_val=0xffffffff, .unit=OSMO_TDEF_M, .desc="Very large" },
54 { .T=-23, .default_val=239471, .desc="Negative T number" },
Neels Hofmeyr0fd615f2019-01-26 20:36:12 +010055 {} // <-- important! last entry shall be zero
56};
57
58enum tdef_vty_test_nodes {
59 GSMNET_NODE = _LAST_OSMOVTY_NODE + 1,
60};
61
62/* This example offers 'timer T123' commands within an "unrelated" already existing subnode. */
63static struct cmd_node gsmnet_node = {
64 GSMNET_NODE,
65 "%s(config-net)# ",
66 1,
67};
68
69DEFUN(show_timer, show_timer_cmd,
70 "show timer " OSMO_TDEF_VTY_ARG_T_OPTIONAL,
71 SHOW_STR "Show timers\n"
72 OSMO_TDEF_VTY_DOC_T)
73{
74 const char *T_arg = argc > 0 ? argv[0] : NULL;
75 return osmo_tdef_vty_show_cmd(vty, global_tdefs, T_arg, NULL);
76}
77
78DEFUN(cfg_net_timer, cfg_net_timer_cmd,
79 "timer " OSMO_TDEF_VTY_ARG_SET_OPTIONAL,
80 "Configure or show timers\n"
81 OSMO_TDEF_VTY_DOC_SET)
82{
83 /* If any arguments are missing, redirect to 'show' */
84 if (argc < 2)
85 return show_timer(self, vty, argc, argv);
86 return osmo_tdef_vty_set_cmd(vty, global_tdefs, argv);
87}
88
89DEFUN(cfg_net, cfg_net_cmd,
90 "network", "Enter network node\n")
91{
92 vty->node = GSMNET_NODE;
93 return CMD_SUCCESS;
94}
95
96static int config_write_gsmnet(struct vty *vty)
97{
98 vty_out(vty, "net%s", VTY_NEWLINE);
99 /* usually, here would be the output of any other 'net' config items... */
100
101 osmo_tdef_vty_write(vty, global_tdefs, " timer ");
102 return CMD_SUCCESS;
103}
104
Harald Weltee61d4592022-11-03 11:05:58 +0100105static void gsmnet_init_vty(void)
Neels Hofmeyr0fd615f2019-01-26 20:36:12 +0100106{
107 install_node(&gsmnet_node, config_write_gsmnet);
108 install_element(CONFIG_NODE, &cfg_net_cmd);
109
110 osmo_tdefs_reset(global_tdefs);
111 install_element_ve(&show_timer_cmd);
112 install_element(GSMNET_NODE, &cfg_net_timer_cmd);
113}
114
115/* ------------------- THE REST is just boilerplate osmo main() ------------------- */
116
117void *root_ctx = NULL;
118
Harald Weltee61d4592022-11-03 11:05:58 +0100119static void print_help(void)
Neels Hofmeyr0fd615f2019-01-26 20:36:12 +0100120{
121 printf( "options:\n"
122 " -h --help this text\n"
123 " -d --debug MASK Enable debugging (e.g. -d DRSL:DOML:DLAPDM)\n"
124 " -D --daemonize For the process into a background daemon\n"
125 " -c --config-file Specify the filename of the config file\n"
126 " -s --disable-color Don't use colors in stderr log output\n"
127 " -T --timestamp Prefix every log line with a timestamp\n"
128 " -V --version Print version information and exit\n"
129 " -e --log-level Set a global log-level\n"
130 );
131}
132
133static struct {
134 const char *config_file;
135 int daemonize;
136} cmdline_config = {};
137
138static void handle_options(int argc, char **argv)
139{
140 while (1) {
141 int option_idx = 0, c;
142 static const struct option long_options[] = {
143 { "help", 0, 0, 'h' },
144 { "debug", 1, 0, 'd' },
145 { "daemonize", 0, 0, 'D' },
146 { "config-file", 1, 0, 'c' },
147 { "disable-color", 0, 0, 's' },
148 { "timestamp", 0, 0, 'T' },
149 { "version", 0, 0, 'V' },
150 { "log-level", 1, 0, 'e' },
151 {}
152 };
153
154 c = getopt_long(argc, argv, "hc:d:Dc:sTVe:",
155 long_options, &option_idx);
156 if (c == -1)
157 break;
158
159 switch (c) {
160 case 'h':
161 print_help();
162 exit(0);
163 case 's':
164 log_set_use_color(osmo_stderr_target, 0);
165 break;
166 case 'd':
167 log_parse_category_mask(osmo_stderr_target, optarg);
168 break;
169 case 'D':
170 cmdline_config.daemonize = 1;
171 break;
172 case 'c':
173 cmdline_config.config_file = optarg;
174 break;
175 case 'T':
176 log_set_print_timestamp(osmo_stderr_target, 1);
177 break;
178 case 'e':
179 log_set_log_level(osmo_stderr_target, atoi(optarg));
180 break;
181 case 'V':
182 print_version(1);
183 exit(0);
184 break;
185 default:
186 /* catch unknown options *as well as* missing arguments. */
187 fprintf(stderr, "Error in command line options. Exiting.\n");
188 exit(-1);
189 }
190 }
191}
192
193static int quit = 0;
194
195static void signal_handler(int signal)
196{
197 fprintf(stdout, "signal %u received\n", signal);
198
199 switch (signal) {
200 case SIGINT:
201 case SIGTERM:
202 quit++;
203 break;
204 case SIGABRT:
205 osmo_generate_backtrace();
206 /* in case of abort, we want to obtain a talloc report
207 * and then return to the caller, who will abort the process */
208 case SIGUSR1:
209 talloc_report(tall_vty_ctx, stderr);
210 talloc_report_full(root_ctx, stderr);
211 break;
212 case SIGUSR2:
213 talloc_report_full(tall_vty_ctx, stderr);
214 break;
215 default:
216 break;
217 }
218}
219
220static struct vty_app_info vty_info = {
221 .name = "tdef_vty_test",
222 .version = PACKAGE_VERSION,
223};
224
225static const struct log_info_cat default_categories[] = {};
226
227const struct log_info log_info = {
228 .cat = default_categories,
229 .num_cat = ARRAY_SIZE(default_categories),
230};
231
232int main(int argc, char **argv)
233{
234 int rc;
235
236 root_ctx = talloc_named_const(NULL, 0, "tdef_vty_test");
237
238 osmo_init_logging2(root_ctx, &log_info);
239
240 vty_info.tall_ctx = root_ctx;
241 vty_init(&vty_info);
242 osmo_talloc_vty_add_cmds();
243
244 gsmnet_init_vty(); /* <--- relevant init for this example */
245
246 handle_options(argc, argv);
247
248 if (cmdline_config.config_file) {
249 rc = vty_read_config_file(cmdline_config.config_file, NULL);
250 if (rc < 0) {
251 fprintf(stderr, "Failed to parse the config file: '%s'\n", cmdline_config.config_file);
252 return 1;
253 }
254 }
255
arehbein0aa84cf2023-01-27 00:49:45 +0100256 rc = telnet_init_default(root_ctx, NULL, 42042);
Neels Hofmeyr0fd615f2019-01-26 20:36:12 +0100257 if (rc < 0)
258 return 2;
259
260 signal(SIGINT, &signal_handler);
261 signal(SIGTERM, &signal_handler);
262 signal(SIGABRT, &signal_handler);
263 signal(SIGUSR1, &signal_handler);
264 signal(SIGUSR2, &signal_handler);
265 osmo_init_ignore_signals();
266
267 if (cmdline_config.daemonize) {
268 rc = osmo_daemonize();
269 if (rc < 0) {
270 perror("Error during daemonize");
271 return 6;
272 }
273 }
274
275 while (!quit) {
276 log_reset_context();
277 osmo_select_main(0);
278 }
279
280 talloc_free(root_ctx);
281 talloc_free(tall_vty_ctx);
282
283 return 0;
284}