blob: 0af1a897d4be614f37e15a0765e8d13d0e733b39 [file] [log] [blame]
Alexander Couzensc0d02cc2020-12-14 02:36:53 +01001
2#include <errno.h>
3#include <stdlib.h>
4#include <stdio.h>
5#include <getopt.h>
6#include <signal.h>
7
8#include <osmocom/core/select.h>
9#include <osmocom/core/application.h>
10#include <osmocom/core/stats.h>
11
12#include <osmocom/gprs/gprs_ns2.h>
13#include <osmocom/vty/vty.h>
14#include <osmocom/vty/telnet_interface.h>
15#include <osmocom/vty/command.h>
16#include <osmocom/vty/ports.h>
17#include <osmocom/vty/tdef_vty.h>
18#include <osmocom/vty/logging.h>
19#include <osmocom/vty/stats.h>
20#include <osmocom/vty/misc.h>
21
22#include "config.h"
23
24void *tall_nsdummy_ctx = NULL;
25static struct log_info log_info = {};
26static bool quit = false;
27static bool config_given = false;
28static bool daemonize = false;
29static int vty_port = 0;
30static char *config_file = NULL;
Harald Welte9bbb7032021-01-31 16:44:46 +010031struct gprs_ns2_inst *g_nsi;
Alexander Couzensc0d02cc2020-12-14 02:36:53 +010032
33static const char vty_copyright[] =
34 "Copyright (C) 2020 by by sysmocom - s.f.m.c. GmbH\r\n"
35 "Author: Alexander Couzens <lynxis@fe80.eu>\r\n"
36 "License GNU GPL version 2 or later\r\n"
37 "This is free software: you are free to change and redistribute it.\r\n"
38 "There is NO WARRANTY, to the extent permitted by law.\r\n";
39
40static struct vty_app_info vty_info = {
41 .name = "OsmoNSdummy",
42 .version = PACKAGE_VERSION,
43 .copyright = vty_copyright,
44};
45
46static void print_help()
47{
48 printf( "Some useful options:\n"
49 " -h --help This text\n"
50 " -c --config-file Specify the filename of the config file\n"
51 " -V --version Print version\n"
52 " -D --daemonize Fork the process into a background daemon\n"
53 " -p --vty-port PORT Set the vty port to listen on.\n"
54 "\nVTY reference generation:\n"
55 " --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
56 " --vty-ref-xml Generate the VTY reference XML output and exit.\n"
57 );
58}
59
60static void handle_long_options(const char *prog_name, const int long_option)
61{
62 static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
63
64 switch (long_option) {
65 case 1:
66 vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
67 if (vty_ref_mode < 0) {
68 fprintf(stderr, "%s: Unknown VTY reference generation "
69 "mode '%s'\n", prog_name, optarg);
70 exit(2);
71 }
72 break;
73 case 2:
74 fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
75 get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
76 get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
77 vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
78 exit(0);
79 default:
80 fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
81 exit(2);
82 }
83}
84
85static void handle_options(int argc, char **argv)
86{
87 while (1) {
88 int option_idx = 0, c;
89 static int long_option = 0;
90 static const struct option long_options[] = {
91 { "help", 0, 0, 'h' },
92 { "config-file", 1, 0, 'c' },
93 { "version", 0, 0, 'V' },
94 { "daemonize", 0, 0, 'D' },
95 { "vty-port", 1, 0, 'p' },
96 { "vty-ref-mode", 1, &long_option, 1 },
97 { "vty-ref-xml", 0, &long_option, 2 },
98 { 0, 0, 0, 0 }
99 };
100
101 c = getopt_long(argc, argv, "hc:p:VD",
102 long_options, &option_idx);
103 if (c == -1)
104 break;
105
106 switch (c) {
107 case 'h':
108 print_help();
109 exit(0);
110 break;
111 case 0:
112 handle_long_options(argv[0], long_option);
113 break;
114 case 'c':
115 if (config_file)
116 free(config_file);
117 config_file = optarg;
118 config_given = true;
119 break;
120 case 'p':
121 vty_port = atoi(optarg);
122 if (vty_port < 0 || vty_port > 65535) {
123 fprintf(stderr, "Invalid port %d given!\n", vty_port);
124 exit(1);
125 }
126 break;
127 case 'V':
128 print_version(1);
129 exit(0);
130 break;
131 case 'D':
132 daemonize = true;
133 break;
134 default:
135 fprintf(stderr, "Unknown option '%c'\n", c);
136 exit(0);
137 break;
138 }
139 }
140
141 if (!config_file)
142 config_file = "osmo-ns-dummy.cfg";
143 if (!vty_port) {
144 fprintf(stderr, "A vty port need to be specified (-p)\n");
145 exit(1);
146 }
147}
148
149void sighandler(int sigset)
150{
151 if (sigset == SIGPIPE)
152 return;
153
154 fprintf(stderr, "Signal %d received.\n", sigset);
155
156 switch (sigset) {
157 case SIGINT:
158 case SIGTERM:
159 /* If another signal is received afterwards, the program
160 * is terminated without finishing shutdown process.
161 */
162 signal(SIGINT, SIG_DFL);
163 signal(SIGTERM, SIG_DFL);
164 signal(SIGPIPE, SIG_DFL);
165 signal(SIGABRT, SIG_DFL);
166 signal(SIGUSR1, SIG_DFL);
167 signal(SIGUSR2, SIG_DFL);
168
169 quit = 1;
170 break;
171 case SIGABRT:
172 /* in case of abort, we want to obtain a talloc report and
173 * then run default SIGABRT handler, who will generate coredump
174 * and abort the process. abort() should do this for us after we
175 * return, but program wouldn't exit if an external SIGABRT is
176 * received.
177 */
178 talloc_report_full(tall_nsdummy_ctx, stderr);
179 signal(SIGABRT, SIG_DFL);
180 raise(SIGABRT);
181 break;
182 case SIGUSR1:
183 case SIGUSR2:
184 talloc_report_full(tall_nsdummy_ctx, stderr);
185 break;
186 }
187}
188
Harald Welte9bbb7032021-01-31 16:44:46 +0100189extern int g_mirror_mode;
190
Alexander Couzensc0d02cc2020-12-14 02:36:53 +0100191/* called by the ns layer */
192int gprs_ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
193{
Harald Welte9bbb7032021-01-31 16:44:46 +0100194 struct osmo_gprs_ns2_prim *nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
195
196 switch (oph->primitive) {
197 case GPRS_NS2_PRIM_UNIT_DATA:
198 if (g_mirror_mode) {
199 /* simply switch indication->request and resubmit */
200 oph->operation = PRIM_OP_REQUEST;
201 msgb_pull_to_l3(oph->msg);
202 nsp->u.unitdata.link_selector = rand(); /* ensure random distribution */
203 return gprs_ns2_recv_prim(g_nsi, oph);
204 }
205 break;
206 default:
207 break;
208 }
209
Alexander Couzensfd96dc52021-01-18 13:43:44 +0100210 if (oph->msg)
211 msgb_free(oph->msg);
212
Alexander Couzensc0d02cc2020-12-14 02:36:53 +0100213 return 0;
214}
215
216int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
217{
218 return 0;
219}
220
Harald Welte9bbb7032021-01-31 16:44:46 +0100221extern int nsdummy_vty_init(void);
Alexander Couzensc0d02cc2020-12-14 02:36:53 +0100222
223int main (int argc, char *argv[])
224{
225 void *ctx = tall_nsdummy_ctx = talloc_named_const(NULL, 0, "osmo-ns-dummy");
Alexander Couzensc0d02cc2020-12-14 02:36:53 +0100226 int rc = 0;
227
228 osmo_init_logging2(ctx, &log_info);
229 log_set_use_color(osmo_stderr_target, 0);
Pau Espin Pedrol01e0d3e2021-02-18 19:25:44 +0100230 log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
Alexander Couzensc0d02cc2020-12-14 02:36:53 +0100231 log_set_log_level(osmo_stderr_target, LOGL_INFO);
232 msgb_talloc_ctx_init(ctx, 0);
233 osmo_stats_init(ctx);
234 rate_ctr_init(ctx);
235
236 vty_info.tall_ctx = ctx;
237 vty_init(&vty_info);
238 logging_vty_add_cmds();
239 osmo_stats_vty_add_cmds();
240 osmo_talloc_vty_add_cmds();
241
242 handle_options(argc, argv);
243
Harald Welte9bbb7032021-01-31 16:44:46 +0100244 g_nsi = gprs_ns2_instantiate(ctx, gprs_ns_prim_cb, NULL);
245 if (!g_nsi) {
Alexander Couzensc0d02cc2020-12-14 02:36:53 +0100246 LOGP(DLNS, LOGL_ERROR, "Failed to create NS instance\n");
247 exit(1);
248 }
249
Harald Welte9bbb7032021-01-31 16:44:46 +0100250 gprs_ns2_vty_init(g_nsi);
251 nsdummy_vty_init();
Alexander Couzensc0d02cc2020-12-14 02:36:53 +0100252 rc = vty_read_config_file(config_file, NULL);
253 if (rc < 0 && config_given) {
254 fprintf(stderr, "Failed to parse the config file: '%s'\n",
255 config_file);
256 exit(1);
257 }
258 if (rc < 0)
259 fprintf(stderr, "No config file: '%s' Using default config.\n",
260 config_file);
261
262 rc = telnet_init_dynif(ctx, NULL, vty_get_bind_addr(),
263 vty_port);
264 if (rc < 0) {
265 fprintf(stderr, "Error initializing telnet\n");
266 exit(1);
267 }
268
269 signal(SIGINT, sighandler);
270 signal(SIGTERM, sighandler);
271 signal(SIGPIPE, sighandler);
272 signal(SIGABRT, sighandler);
273 signal(SIGUSR1, sighandler);
274 signal(SIGUSR2, sighandler);
275 osmo_init_ignore_signals();
276
277 if (daemonize) {
278 rc = osmo_daemonize();
279 if (rc < 0) {
280 perror("Error during daemonize");
281 exit(1);
282 }
283 }
284
285 while (!quit) {
286 osmo_select_main(0);
287 }
288
289 telnet_exit();
Harald Welte9bbb7032021-01-31 16:44:46 +0100290 gprs_ns2_free(g_nsi);
Alexander Couzensc0d02cc2020-12-14 02:36:53 +0100291
292 talloc_report_full(tall_nsdummy_ctx, stderr);
293 talloc_free(tall_nsdummy_ctx);
294
295 return 0;
296}