blob: c023351b8f82e703f029997661b84103e61ec164 [file] [log] [blame]
Harald Weltef7365592020-04-15 21:48:45 +02001/* SPDX-License-Identifier: GPL-2.0 */
Pau Espin Pedrolafa22c62022-04-12 11:53:16 +02002#define _GNU_SOURCE
3#include <getopt.h>
Harald Weltef7365592020-04-15 21:48:45 +02004#include <unistd.h>
5#include <stdint.h>
6#include <stdbool.h>
7#include <stdlib.h>
8#include <string.h>
9#include <stdio.h>
10#include <assert.h>
11#include <sys/types.h>
Harald Weltef9bb1772020-04-21 22:46:34 +020012#include <sys/wait.h>
Harald Welte24557a72020-04-17 22:08:29 +020013#include <sys/signalfd.h>
Harald Weltef7365592020-04-15 21:48:45 +020014#include <signal.h>
15#include <errno.h>
16
17#include <pthread.h>
18
19#include <osmocom/core/linuxlist.h>
20#include <osmocom/core/talloc.h>
21#include <osmocom/core/select.h>
22#include <osmocom/core/application.h>
23#include <osmocom/core/logging.h>
24#include <osmocom/core/stats.h>
25#include <osmocom/core/rate_ctr.h>
26#include <osmocom/core/socket.h>
27#include <osmocom/vty/telnet_interface.h>
28#include <osmocom/vty/logging.h>
29#include <osmocom/vty/stats.h>
30#include <osmocom/vty/ports.h>
31#include <osmocom/vty/command.h>
32#include <osmocom/vty/misc.h>
33
34#include <osmocom/netif/stream.h>
Harald Weltef7365592020-04-15 21:48:45 +020035
36#include <jansson.h>
37
38#include "internal.h"
39#include "netns.h"
40#include "gtp.h"
41
Pau Espin Pedrolf9f5b302022-04-12 12:34:25 +020042static void *g_tall_ctx;
43static char *g_config_file = "osmo-uecups-daemon.cfg";
44static int g_daemonize;
45extern struct vty_app_info g_vty_info;
Harald Weltef7365592020-04-15 21:48:45 +020046
Harald Welte24557a72020-04-17 22:08:29 +020047#include <pwd.h>
48
Harald Weltef9bb1772020-04-21 22:46:34 +020049static void sigchild_cb(struct osmo_signalfd *osfd, const struct signalfd_siginfo *fdsi)
50{
51 struct gtp_daemon *d = osfd->data;
52 int pid, status;
53
54 OSMO_ASSERT(fdsi->ssi_signo == SIGCHLD);
55
Harald Weltef9bb1772020-04-21 22:46:34 +020056 /* it is known that classic signals coalesce: If you get multiple signals of the
Pau Espin Pedrol95ad8c92022-04-11 15:47:36 +020057 * same type before a process is scheduled, the subsequent signals are dropped. This
Harald Weltef9bb1772020-04-21 22:46:34 +020058 * makes sense for SIGINT or something like this, but for SIGCHLD carrying the PID of
59 * the terminated process, it doesn't really. Linux had the chance to fix this when
60 * introducing signalfd() - but the developers decided not to fix it. So the signalfd_siginfo
61 * contains the PID of one process that terminated - but there may be any number of other
62 * processes that also have terminated, and for which we don't get events this way. */
63
64 while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
65 child_terminated(d, pid, status);
66
67}
68
Harald Welte933dec72020-04-18 21:41:51 +020069static void signal_cb(struct osmo_signalfd *osfd, const struct signalfd_siginfo *fdsi)
70{
71 switch (fdsi->ssi_signo) {
72 case SIGCHLD:
73 sigchild_cb(osfd, fdsi);
74 break;
75 case SIGUSR1:
76 talloc_report_full(g_tall_ctx, stderr);
77 break;
78 default:
79 break;
80 }
81}
82
Harald Weltef7365592020-04-15 21:48:45 +020083static const struct log_info_cat log_categories[] = {
84 [DTUN] = {
85 .name ="DTUN",
86 .description = "Tunnel interface (tun device)",
87 .enabled = 1, .loglevel = LOGL_INFO,
88 },
89 [DEP] = {
90 .name = "DEP",
91 .description = "GTP endpoint (UDP socket)",
92 .enabled = 1, .loglevel = LOGL_INFO,
93 },
94 [DGT] = {
95 .name = "DGT",
96 .description = "GTP tunnel (session)",
97 .enabled = 1, .loglevel = LOGL_INFO,
98 },
99 [DUECUPS] = {
100 .name = "DUECUPS",
101 .description = "UE Control User Plane Separation",
102 .enabled = 1, .loglevel = LOGL_DEBUG,
103 },
104
105};
106
107static const struct log_info log_info = {
108 .cat = log_categories,
109 .num_cat = ARRAY_SIZE(log_categories),
110};
111
Pau Espin Pedrolafa22c62022-04-12 11:53:16 +0200112static void print_help()
113{
114 printf("Some useful options:\n");
115 printf(" -h --help is printing this text.\n");
116 printf(" -c --config-file filename The config file to use.\n");
117 printf(" -s --disable-color\n");
118 printf(" -D --daemonize Fork the process into a background daemon\n");
119 printf(" -V --version Print the version number\n");
120
121 printf("\nVTY reference generation:\n");
122 printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
123 printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n");
124}
125
126static void handle_long_options(const char *prog_name, const int long_option)
127{
128 static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
129
130 switch (long_option) {
131 case 1:
132 vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
133 if (vty_ref_mode < 0) {
134 fprintf(stderr, "%s: Unknown VTY reference generation "
135 "mode '%s'\n", prog_name, optarg);
136 exit(2);
137 }
138 break;
139 case 2:
140 fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
141 get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
142 get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
143 vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
144 exit(0);
145 default:
146 fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
147 exit(2);
148 }
149}
150
151static void handle_options(int argc, char **argv)
152{
153 while (1) {
154 int option_index = 0, c;
155 static int long_option = 0;
156 static struct option long_options[] = {
157 {"help", 0, 0, 'h'},
158 {"config-file", 1, 0, 'c'},
159 {"daemonize", 0, 0, 'D'},
160 {"version", 0, 0, 'V'},
161 {"disable-color", 0, 0, 's'},
162 {"vty-ref-mode", 1, &long_option, 1},
163 {"vty-ref-xml", 0, &long_option, 2},
164 {0, 0, 0, 0},
165 };
166
167 c = getopt_long(argc, argv, "hc:sVD", long_options, &option_index);
168
169 if (c == -1)
170 break;
171
172 switch (c) {
173 case 'h':
174 print_help();
175 exit(0);
176 break;
177 case 0:
178 handle_long_options(argv[0], long_option);
179 break;
180 case 'c':
181 g_config_file = talloc_strdup(g_tall_ctx, optarg);
182 break;
183 case 's':
184 log_set_use_color(osmo_stderr_target, 0);
185 break;
186 case 'V':
187 print_version(1);
188 exit(0);
189 break;
190 case 'D':
191 g_daemonize = 1;
192 break;
193 default:
194 /* ignore */
195 break;
196 };
197 }
198 if (argc > optind) {
199 fprintf(stderr, "Unsupported positional arguments on command line\n");
200 exit(2);
201 }
202}
203
Harald Weltef7365592020-04-15 21:48:45 +0200204int main(int argc, char **argv)
205{
Harald Weltef7365592020-04-15 21:48:45 +0200206 int rc;
207
Harald Welte933dec72020-04-18 21:41:51 +0200208 g_tall_ctx = talloc_named_const(NULL, 0, "root");
209 g_vty_info.tall_ctx = g_tall_ctx;
Harald Weltef7365592020-04-15 21:48:45 +0200210
211 osmo_init_ignore_signals();
Harald Welte933dec72020-04-18 21:41:51 +0200212 osmo_init_logging2(g_tall_ctx, &log_info);
Vadim Yanitskiy82809242022-01-28 20:32:12 +0600213 log_enable_multithread();
Harald Weltef7365592020-04-15 21:48:45 +0200214
Harald Welte933dec72020-04-18 21:41:51 +0200215 g_daemon = gtp_daemon_alloc(g_tall_ctx);
Harald Weltef7365592020-04-15 21:48:45 +0200216 OSMO_ASSERT(g_daemon);
217
Harald Weltea932e4e2020-04-20 12:10:58 +0200218 msgb_talloc_ctx_init(g_tall_ctx, 10);
Harald Welte933dec72020-04-18 21:41:51 +0200219 osmo_stats_init(g_tall_ctx);
Harald Weltef7365592020-04-15 21:48:45 +0200220 vty_init(&g_vty_info);
221 logging_vty_add_cmds();
222 osmo_talloc_vty_add_cmds();
223 osmo_stats_vty_add_cmds();
Harald Welte933dec72020-04-18 21:41:51 +0200224 rate_ctr_init(g_tall_ctx);
Harald Weltef7365592020-04-15 21:48:45 +0200225 gtpud_vty_init();
226
Pau Espin Pedrolafa22c62022-04-12 11:53:16 +0200227 handle_options(argc, argv);
228
Harald Weltef7365592020-04-15 21:48:45 +0200229 init_netns();
230
231 rc = vty_read_config_file(g_config_file, NULL);
232 if (rc < 0) {
233 fprintf(stderr, "Failed to open config file: '%s'\n", g_config_file);
234 exit(2);
235 }
236
arehbein08cc9dd2023-02-25 17:48:58 +0100237 rc = telnet_init_default(g_daemon, NULL, OSMO_VTY_PORT_UECUPS);
Harald Weltef7365592020-04-15 21:48:45 +0200238 if (rc < 0)
239 exit(1);
240
Pau Espin Pedrolf9f5b302022-04-12 12:34:25 +0200241 /* UECUPS socket for control from control plane side */
242 g_daemon->cups_link = cups_srv_link_create(g_daemon);
Harald Weltef7365592020-04-15 21:48:45 +0200243 if (!g_daemon->cups_link) {
244 fprintf(stderr, "Failed to create CUPS socket %s:%u (%s)\n",
245 g_daemon->cfg.cups_local_ip, g_daemon->cfg.cups_local_port, strerror(errno));
246 exit(1);
247 }
248
Harald Welte24557a72020-04-17 22:08:29 +0200249 /* block SIGCHLD via normal delivery; redirect it to signalfd */
250 sigset_t sigset;
251 sigemptyset(&sigset);
252 sigaddset(&sigset, SIGCHLD);
Harald Welte933dec72020-04-18 21:41:51 +0200253 sigaddset(&sigset, SIGUSR1);
Harald Welte24557a72020-04-17 22:08:29 +0200254 sigprocmask(SIG_BLOCK, &sigset, NULL);
Harald Welte933dec72020-04-18 21:41:51 +0200255 g_daemon->signalfd = osmo_signalfd_setup(g_daemon, sigset, signal_cb, g_daemon);
256 osmo_init_ignore_signals();
Harald Welte24557a72020-04-17 22:08:29 +0200257
Harald Weltef7365592020-04-15 21:48:45 +0200258 if (g_daemonize) {
259 rc = osmo_daemonize();
260 if (rc < 0) {
261 perror("Error during daemonize");
262 exit(1);
263 }
264 }
265
266 while (1) {
267 osmo_select_main(0);
268 }
269}