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