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