diff options
author | Harald Welte <laforge@osmocom.org> | 2020-06-29 16:57:09 +0200 |
---|---|---|
committer | Harald Welte <laforge@osmocom.org> | 2020-06-29 22:12:42 +0200 |
commit | 9144ba03738504d770a8cf3a644f64a14ebd7e8b (patch) | |
tree | f935808384ed4f7c7075e645b63f0887828f4907 | |
parent | 0b621b43e75f45a9c64eef42b5aae83f248a42d5 (diff) |
VTY interface support
Let's add a VTY interface on TCP port 4269. The purpose is - for now -
not for configuration storage, but for state introspection.
Change-Id: I47b6e4efaad52e68e2b50a7993076f3706f86628
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/ctl.c | 12 | ||||
-rw-r--r-- | src/e1d.h | 6 | ||||
-rw-r--r-- | src/osmo-e1d.c | 82 | ||||
-rw-r--r-- | src/vty.c | 139 |
6 files changed, 238 insertions, 9 deletions
diff --git a/configure.ac b/configure.ac index c0508eb..5be845b 100644 --- a/configure.ac +++ b/configure.ac @@ -33,6 +33,7 @@ PKG_PROG_PKG_CONFIG([0.20]) PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1]) PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.1.120) +PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty) PKG_CHECK_MODULES(LIBOSMOUSB, libosmousb) PKG_CHECK_MODULES(LIBUSB, libusb-1.0 >= 1.0.21) diff --git a/src/Makefile.am b/src/Makefile.am index bc05998..aa796f2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,7 +4,8 @@ LIBVERSION=0:0:0 AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall -Wno-unused-result $(LIBOSMOCORE_CFLAGS) $(LIBOSMOUSB_LIBS) $(LIBUSB_CFLAGS) +AM_CFLAGS=-Wall -Wno-unused-result $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOVTY_LIBS) $(LIBOSMOUSB_LIBS) $(LIBUSB_CFLAGS) lib_LTLIBRARIES = libosmo-e1d.la @@ -34,6 +35,8 @@ osmo_e1d_SOURCES = \ log.c \ osmo-e1d.c \ usb.c \ + vty.c \ $(NULL) -osmo_e1d_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOUSB_LIBS) $(LIBUSB_LIBS) libosmo-e1d.la +osmo_e1d_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOUSB_LIBS) $(LIBUSB_LIBS) libosmo-e1d.la @@ -38,8 +38,8 @@ #include "e1d.h" -static struct e1_intf * -_e1d_find_intf(struct e1_daemon *e1d, uint8_t id) +struct e1_intf * +e1d_find_intf(struct e1_daemon *e1d, uint8_t id) { struct e1_intf *intf; @@ -137,7 +137,7 @@ _e1d_ctl_intf_query(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd) /* Process query and find interface */ if (hdr->intf != E1DP_INVALID) { - intf = _e1d_find_intf(e1d, hdr->intf); + intf = e1d_find_intf(e1d, hdr->intf); n = intf ? 1 : 0; } else { n = llist_count(&e1d->interfaces); @@ -174,7 +174,7 @@ _e1d_ctl_line_query(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd) int n; /* Process query and find line */ - intf = _e1d_find_intf(e1d, hdr->intf); + intf = e1d_find_intf(e1d, hdr->intf); if (!intf) return 0; @@ -216,7 +216,7 @@ _e1d_ctl_ts_query(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd) int n; /* Process query and find timeslot */ - intf = _e1d_find_intf(e1d, hdr->intf); + intf = e1d_find_intf(e1d, hdr->intf); if (!intf) return 0; @@ -262,7 +262,7 @@ _e1d_ctl_ts_open(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd) int ret; /* Process query and find timeslot */ - intf = _e1d_find_intf(e1d, hdr->intf); + intf = e1d_find_intf(e1d, hdr->intf); if (!intf) return 0; @@ -84,6 +84,9 @@ struct e1_daemon { struct e1_intf * e1_intf_new(struct e1_daemon *e1d, void *drv_data); +struct e1_intf * +e1d_find_intf(struct e1_daemon *e1d, uint8_t id); + struct e1_line * e1_line_new(struct e1_intf *intf, void *drv_data); @@ -92,3 +95,6 @@ e1_line_mux_out(struct e1_line *line, uint8_t *buf, int fts); int e1_line_demux_in(struct e1_line *line, const uint8_t *buf, int size); + +void +e1d_vty_init(struct e1_daemon *e1d); diff --git a/src/osmo-e1d.c b/src/osmo-e1d.c index 92d98d3..808bd68 100644 --- a/src/osmo-e1d.c +++ b/src/osmo-e1d.c @@ -28,23 +28,30 @@ #include <stdio.h> #include <string.h> +#define _GNU_SOURCE +#include <getopt.h> + #include <talloc.h> #include <osmocom/core/application.h> #include <osmocom/core/msgb.h> #include <osmocom/core/select.h> +#include <osmocom/vty/telnet_interface.h> #include <osmocom/e1d/proto_srv.h> #include "e1d.h" #include "log.h" +#ifndef OSMO_VTY_PORT_E1D +#define OSMO_VTY_PORT_E1D 4269 +#endif extern struct osmo_e1dp_server_handler e1d_ctl_handlers[]; extern int e1_usb_probe(struct e1_daemon *e1d); - +static const char *g_config_file = "osmo-e1d.cfg"; static void *g_e1d_ctx = NULL; static int g_shutdown = 0; @@ -71,6 +78,61 @@ static void sig_handler(int signo) } } +static struct vty_app_info vty_info = { + .name = "osmo-e1d", + .version = PACKAGE_VERSION, + .copyright = + "(C) 2019 by Sylvain Munaut <tnt@246tNt.com>\r\n", + "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n", +}; + +static void print_help(void) +{ + printf(" Some useful help...\n"); + printf(" -h --help This text.\n"); + printf(" -d --debug option --debug=DE1D:DXFR enable debugging.\n"); + printf(" -c --config-file filename The config file to use.\n"); +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_index = 0, c; + static const struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 1, 0, 'd'}, + {"config-file", 1, 0, 'c'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hd:c:", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_help(); + exit(0); + break; + case 'd': + log_parse_category_mask(osmo_stderr_target, optarg); + break; + case 'c': + g_config_file = optarg; + break; + default: + fprintf(stderr, "Error in command line options. Exiting.\n"); + exit(-1); + } + } + + if (argc > optind) { + fprintf(stderr, "Unsupported positional arguments on command line\n"); + exit(2); + } +} int main(int argc, char *argv[]) { @@ -82,6 +144,7 @@ int main(int argc, char *argv[]) /* talloc init */ g_e1d_ctx = talloc_named_const(NULL, 0, "osmo-e1d"); msgb_talloc_ctx_init(g_e1d_ctx, 0); + vty_info.tall_ctx = g_e1d_ctx; /* logging init */ osmo_init_logging2(g_e1d_ctx, &log_info); @@ -108,6 +171,23 @@ int main(int argc, char *argv[]) OSMO_ASSERT(e1d); INIT_LLIST_HEAD(&e1d->interfaces); + vty_init(&vty_info); + e1d_vty_init(e1d); + + handle_options(argc, argv); + + rv = vty_read_config_file(g_config_file, NULL); + if (rv < 0) { + LOGP(DE1D, LOGL_FATAL, "Failed to parse the config file '%s'\n", g_config_file); + exit(2); + } + + rv = telnet_init_dynif(g_e1d_ctx, e1d, vty_get_bind_addr(), OSMO_VTY_PORT_E1D); + if (rv != 0) { + LOGP(DE1D, LOGL_FATAL, "Failed to bind VTY interface to %s:%u\n", + vty_get_bind_addr(), OSMO_VTY_PORT_E1D); + exit(1); + } /* probe devices */ rv = e1_usb_probe(e1d); diff --git a/src/vty.c b/src/vty.c new file mode 100644 index 0000000..88b6257 --- /dev/null +++ b/src/vty.c @@ -0,0 +1,139 @@ +/* osmo-e1d VTY interface */ +/* (C) 2020 by Harald Welte <laforge@osmocom.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#define _GNU_SOURCE /* struct ucred */ +#include <sys/socket.h> + +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <time.h> + +#include <osmocom/core/linuxlist.h> + +#include <osmocom/vty/command.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/stats.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/misc.h> +#include <osmocom/vty/tdef_vty.h> + +#include <osmocom/e1d/proto.h> +#include "e1d.h" + +static struct e1_daemon *vty_e1d; + +#if 0 +static void vty_dump_ts(struct vty *vty, const struct e1_ts *ts) +{ +} +#endif + +static void vty_dump_intf(struct vty *vty, const struct e1_intf *intf) +{ + vty_out(vty, "Interface #%u, Driver: FIXME%s", intf->id, VTY_NEWLINE); +} + +DEFUN(show_intf, show_intf_cmd, "show interface [<0-255>]", + SHOW_STR "Display information about an E1 Interface/Card\n") +{ + struct e1_intf *intf; + + if (argc) { + int id = atoi(argv[0]); + intf = e1d_find_intf(vty_e1d, id); + if (!intf) { + vty_out(vty, "%% Unknown interface %u%s\n", id, VTY_NEWLINE); + return CMD_WARNING; + } + vty_dump_intf(vty, intf); + } else { + llist_for_each_entry(intf, &vty_e1d->interfaces, list) + vty_dump_intf(vty, intf); + } + + return CMD_SUCCESS; +} + +static int get_remote_pid(int fd) +{ + struct ucred uc; + socklen_t len = sizeof(uc); + int rc; + + rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len); + if (rc != 0) + return -1; + return uc.pid; +} + +static const struct value_string e1_ts_mode_names[] = { + { E1_TS_MODE_OFF, "OFF" }, + { E1_TS_MODE_RAW, "RAW" }, + { E1_TS_MODE_HDLCFCS, "HDLC-FCS" }, + { 0, NULL } +}; + +static void vty_dump_line(struct vty *vty, const struct e1_line *line) +{ + int tn; + + vty_out(vty, "Interface #%u, Line #%u:%s", line->intf->id, line->id, VTY_NEWLINE); + + for (tn = 0; tn < ARRAY_SIZE(line->ts); tn++) { + const struct e1_ts *ts = &line->ts[tn]; + vty_out(vty, " TS%02u: Mode %s, FD %d, Peer PID %d%s", + ts->id, get_value_string(e1_ts_mode_names, ts->mode), + ts->fd, get_remote_pid(ts->fd), VTY_NEWLINE); + } +} + +DEFUN(show_line, show_line_cmd, "show line [<0-255>]", + SHOW_STR "Display information about an E1 Line\n") +{ + struct e1_line *line; + struct e1_intf *intf; + + if (argc) { + int id = atoi(argv[0]); + intf = e1d_find_intf(vty_e1d, id); + if (!intf) { + vty_out(vty, "%% Unknown interface %u%s\n", id, VTY_NEWLINE); + return CMD_WARNING; + } + llist_for_each_entry(line, &intf->lines, list) + vty_dump_line(vty, line); + } else { + llist_for_each_entry(intf, &vty_e1d->interfaces, list) { + llist_for_each_entry(line, &intf->lines, list) + vty_dump_line(vty, line); + } + } + + return CMD_SUCCESS; +} + +void e1d_vty_init(struct e1_daemon *e1d) +{ + vty_e1d = e1d; + install_element_ve(&show_intf_cmd); + install_element_ve(&show_line_cmd); +} |