blob: 0472ad557eb97c2331e0ce238d49cca2a689040b [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 several separate timer groups and offers 'timer' VTY commands at the root of the config node. See
Vadim Yanitskiy52a38b42021-11-17 01:58:03 +030048 * the tdef_vty_config_root_test.vty transcript test.
Neels Hofmeyr0fd615f2019-01-26 20:36:12 +010049 */
50
51static struct osmo_tdef tdefs_test[] = {
52 { .T=1, .default_val=100, .desc="Testing a hundred seconds" }, // default is .unit=OSMO_TDEF_S == 0
53 { .T=2, .default_val=100, .unit=OSMO_TDEF_MS, .desc="Testing a hundred milliseconds" },
54 { .T=3, .default_val=100, .unit=OSMO_TDEF_M, .desc="Testing a hundred minutes" },
55 { .T=4, .default_val=100, .unit=OSMO_TDEF_CUSTOM, .desc="Testing a hundred potatoes" },
Neels Hofmeyr7b740f72019-02-06 01:08:43 +010056 { .T=0x7fffffff, .default_val=0xffffffff, .unit=OSMO_TDEF_M, .desc="Very large" },
57 { .T=-23, .default_val=239471, .desc="Negative T number" },
Pau Espin Pedrol0cbe8f02019-09-17 13:13:52 +020058 { .T=30, .default_val=50, .desc="Testing range min", .min_val=20 },
59 { .T=31, .default_val=50, .desc="Testing range max", .max_val=52 },
60 { .T=32, .default_val=50, .desc="Testing range both", .min_val=20, .max_val=52 },
Neels Hofmeyr0fd615f2019-01-26 20:36:12 +010061 {} // <-- important! last entry shall be zero
62};
63
64static struct osmo_tdef tdefs_tea[] = {
65 { .T=1, .default_val=50, .desc="Water Boiling Timeout" },
66 { .T=2, .default_val=300, .desc="Tea brewing" },
67 { .T=3, .default_val=5, .unit=OSMO_TDEF_M, .desc="Let tea cool down before drinking" },
68 { .T=4, .default_val=20, .unit=OSMO_TDEF_M, .desc="Forgot to drink tea while it's warm" },
69 {}
70};
71
72static struct osmo_tdef tdefs_software[] = {
73 { .T=1, .default_val=30, .unit=OSMO_TDEF_M, .desc="Write code" },
74 { .T=2, .default_val=20, .unit=OSMO_TDEF_MS, .desc="Hit segfault" },
75 { .T=3, .default_val=480, .unit=OSMO_TDEF_M, .desc="Fix bugs" },
76 {}
77};
78
79static struct osmo_tdef_group tdef_groups[] = {
80 {
81 .name = "tea",
82 .desc = "Tea time",
83 .tdefs = tdefs_tea,
84 },
85 {
86 .name = "test",
87 .desc = "Test timers",
88 .tdefs = tdefs_test,
89 },
90 {
91 .name = "software",
92 .desc = "Typical software development cycle",
93 .tdefs = tdefs_software,
94 },
95 {}
96};
97
98enum tdef_vty_test_nodes {
99 TIMER_NODE = _LAST_OSMOVTY_NODE + 1,
100};
101
102/* This example puts 'timer' configuration commands directly at the root of the CONFIG_NODE.
103 * This TIMER_NODE is merely needed as a hook for the vty_write() command, but becomes an empty node in the VTY docs.
104 * It is possible to cheat around needing this if you choose to config_write_timer() in another root nodes' write cb.
Vadim Yanitskiy52a38b42021-11-17 01:58:03 +0300105 * Another example using a 'network' subnode is \ref tdef_vty_config_subnode_test.c */
Neels Hofmeyr0fd615f2019-01-26 20:36:12 +0100106static struct cmd_node timer_node = {
107 TIMER_NODE,
108 "%s(config-timer)# ",
109 1,
110};
111
112static int config_write_timer(struct vty *vty)
113{
114 osmo_tdef_vty_groups_write(vty, "");
115 return CMD_SUCCESS;
116}
117
118static void timer_init_vty()
119{
120 /* Again, this is merely to get a vty write hook, see above. */
121 install_node(&timer_node, config_write_timer);
122
123 osmo_tdef_vty_groups_init(CONFIG_NODE, tdef_groups);
124}
125
126/* ------------------- THE REST is just boilerplate osmo main() ------------------- */
127
128void *root_ctx = NULL;
129
130static void print_help()
131{
132 printf( "options:\n"
133 " -h --help this text\n"
134 " -d --debug MASK Enable debugging (e.g. -d DRSL:DOML:DLAPDM)\n"
135 " -D --daemonize For the process into a background daemon\n"
136 " -c --config-file Specify the filename of the config file\n"
137 " -s --disable-color Don't use colors in stderr log output\n"
138 " -T --timestamp Prefix every log line with a timestamp\n"
139 " -V --version Print version information and exit\n"
140 " -e --log-level Set a global log-level\n"
141 );
142}
143
144static struct {
145 const char *config_file;
146 int daemonize;
147} cmdline_config = {};
148
149static void handle_options(int argc, char **argv)
150{
151 while (1) {
152 int option_idx = 0, c;
153 static const struct option long_options[] = {
154 { "help", 0, 0, 'h' },
155 { "debug", 1, 0, 'd' },
156 { "daemonize", 0, 0, 'D' },
157 { "config-file", 1, 0, 'c' },
158 { "disable-color", 0, 0, 's' },
159 { "timestamp", 0, 0, 'T' },
160 { "version", 0, 0, 'V' },
161 { "log-level", 1, 0, 'e' },
162 {}
163 };
164
165 c = getopt_long(argc, argv, "hc:d:Dc:sTVe:",
166 long_options, &option_idx);
167 if (c == -1)
168 break;
169
170 switch (c) {
171 case 'h':
172 print_help();
173 exit(0);
174 case 's':
175 log_set_use_color(osmo_stderr_target, 0);
176 break;
177 case 'd':
178 log_parse_category_mask(osmo_stderr_target, optarg);
179 break;
180 case 'D':
181 cmdline_config.daemonize = 1;
182 break;
183 case 'c':
184 cmdline_config.config_file = optarg;
185 break;
186 case 'T':
187 log_set_print_timestamp(osmo_stderr_target, 1);
188 break;
189 case 'e':
190 log_set_log_level(osmo_stderr_target, atoi(optarg));
191 break;
192 case 'V':
193 print_version(1);
194 exit(0);
195 break;
196 default:
197 /* catch unknown options *as well as* missing arguments. */
198 fprintf(stderr, "Error in command line options. Exiting.\n");
199 exit(-1);
200 }
201 }
202}
203
204static int quit = 0;
205
206static void signal_handler(int signal)
207{
208 fprintf(stdout, "signal %u received\n", signal);
209
210 switch (signal) {
211 case SIGINT:
212 case SIGTERM:
213 quit++;
214 break;
215 case SIGABRT:
216 osmo_generate_backtrace();
217 /* in case of abort, we want to obtain a talloc report
218 * and then return to the caller, who will abort the process */
219 case SIGUSR1:
220 talloc_report(tall_vty_ctx, stderr);
221 talloc_report_full(root_ctx, stderr);
222 break;
223 case SIGUSR2:
224 talloc_report_full(tall_vty_ctx, stderr);
225 break;
226 default:
227 break;
228 }
229}
230
231static struct vty_app_info vty_info = {
232 .name = "tdef_vty_test",
233 .version = PACKAGE_VERSION,
234};
235
236static const struct log_info_cat default_categories[] = {};
237
238const struct log_info log_info = {
239 .cat = default_categories,
240 .num_cat = ARRAY_SIZE(default_categories),
241};
242
243int main(int argc, char **argv)
244{
245 int rc;
246
247 root_ctx = talloc_named_const(NULL, 0, "tdef_vty_test");
248
249 osmo_init_logging2(root_ctx, &log_info);
250
251 vty_info.tall_ctx = root_ctx;
252 vty_init(&vty_info);
253 osmo_talloc_vty_add_cmds();
254
255 timer_init_vty(); /* <---- the only tdef relevant init */
256
257 handle_options(argc, argv);
258
259 if (cmdline_config.config_file) {
260 rc = vty_read_config_file(cmdline_config.config_file, NULL);
261 if (rc < 0) {
262 fprintf(stderr, "Failed to parse the config file: '%s'\n", cmdline_config.config_file);
263 return 1;
264 }
265 }
266
267 rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
268 if (rc < 0)
269 return 2;
270
271 signal(SIGINT, &signal_handler);
272 signal(SIGTERM, &signal_handler);
273 signal(SIGABRT, &signal_handler);
274 signal(SIGUSR1, &signal_handler);
275 signal(SIGUSR2, &signal_handler);
276 osmo_init_ignore_signals();
277
278 if (cmdline_config.daemonize) {
279 rc = osmo_daemonize();
280 if (rc < 0) {
281 perror("Error during daemonize");
282 return 6;
283 }
284 }
285
286 while (!quit) {
287 log_reset_context();
288 osmo_select_main(0);
289 }
290
291 talloc_free(root_ctx);
292 talloc_free(tall_vty_ctx);
293
294 return 0;
295}