summaryrefslogtreecommitdiffstats
path: root/src/host/layer23/src/ui/telnet_interface.c
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2011-12-01 12:06:51 +0100
committerAndreas Eversberg <jolly@eversberg.eu>2016-09-25 08:11:47 +0200
commit958292f3a4024c54115035cb3379e301168fbfde (patch)
tree7f8225d258ae7ec27d145ec05a8a6e01c51a19d6 /src/host/layer23/src/ui/telnet_interface.c
parenta5a5cd1eff13e8079c6f0058d5e5d94e8dd99ba5 (diff)
layer23/mobile: Added generic user interface
The user interface instance is currently accessed via telnet. It supports different views to display or enter something. It is still not complete.
Diffstat (limited to 'src/host/layer23/src/ui/telnet_interface.c')
-rw-r--r--src/host/layer23/src/ui/telnet_interface.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/src/host/layer23/src/ui/telnet_interface.c b/src/host/layer23/src/ui/telnet_interface.c
new file mode 100644
index 00000000..d7e9bfb0
--- /dev/null
+++ b/src/host/layer23/src/ui/telnet_interface.c
@@ -0,0 +1,353 @@
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/telnet.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+#include <osmocom/vty/buffer.h>
+
+#include <osmocom/bb/ui/ui.h>
+#include <osmocom/bb/ui/telnet_interface.h>
+
+/* Send WILL TELOPT_ECHO to remote server. */
+static void vty_will_echo(struct ui_inst *ui)
+{
+ unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
+ ui_telnet_puts(ui, (char *)cmd);
+}
+
+/* Make suppress Go-Ahead telnet option. */
+static void vty_will_suppress_go_ahead(struct ui_inst *ui)
+{
+ unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
+ ui_telnet_puts(ui, (char *)cmd);
+}
+
+/* Make don't use linemode over telnet. */
+static void vty_dont_linemode(struct ui_inst *ui)
+{
+ unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
+ ui_telnet_puts(ui, (char *)cmd);
+}
+
+/* Use window size. */
+static void vty_do_window_size(struct ui_inst *ui)
+{
+ unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
+ ui_telnet_puts(ui, (char *)cmd);
+}
+
+static int telnet_new_connection(struct osmo_fd *fd, unsigned int what);
+
+int ui_telnet_init(struct ui_inst *ui, void *tall_ctx, int port)
+{
+ struct sockaddr_in sock_addr;
+ int fd, rc, on = 1;
+
+ ui->tall_telnet_ctx = talloc_named_const(tall_ctx, 1,
+ "ui_telnet_connection");
+
+ INIT_LLIST_HEAD(&ui->active_connections);
+
+ /* FIXME: use new socket.c code of libosmocore */
+ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (fd < 0) {
+ LOGP(0, LOGL_ERROR, "Telnet interface socket creation "
+ "failed\n");
+ talloc_free(ui->tall_telnet_ctx);
+ return fd;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_port = htons(port);
+ sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ rc = bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr));
+ if (rc < 0) {
+ LOGP(0, LOGL_ERROR, "Telnet interface failed to bind\n");
+ close(fd);
+ talloc_free(ui->tall_telnet_ctx);
+ return rc;
+ }
+
+ rc = listen(fd, 0);
+ if (rc < 0) {
+ LOGP(0, LOGL_ERROR, "Telnet interface failed to listen\n");
+ close(fd);
+ talloc_free(ui->tall_telnet_ctx);
+ return rc;
+ }
+
+ ui->server_socket.when = BSC_FD_READ;
+ ui->server_socket.cb = telnet_new_connection;
+ ui->server_socket.priv_nr = 0;
+ ui->server_socket.data = ui;
+ ui->server_socket.fd = fd;
+ osmo_fd_register(&ui->server_socket);
+
+ return 0;
+}
+
+int ui_telnet_puts(struct ui_inst *ui, const char *text)
+{
+ struct ui_telnet_connection *conn;
+
+ llist_for_each_entry(conn, &ui->active_connections, entry) {
+ buffer_put(conn->obuf, text, strlen(text));
+ conn->fd.when |= BSC_FD_WRITE;
+ }
+
+ return 0;
+}
+
+static int telnet_close_client(struct osmo_fd *fd)
+{
+ struct ui_telnet_connection *conn =
+ (struct ui_telnet_connection*)fd->data;
+ struct ui_inst *ui = conn->ui;
+
+ close(fd->fd);
+ osmo_fd_unregister(fd);
+
+ buffer_free(conn->obuf);
+
+ llist_del(&conn->entry);
+ talloc_free(conn);
+
+ /* notify about connection */
+ ui->telnet_cb(ui);
+
+ return 0;
+}
+
+//#define DEBUG_SEQEUENCES
+static int telnet_getc(struct ui_telnet_connection *conn, unsigned char p)
+{
+#ifdef DEBUG_SEQEUENCES
+ printf("k: %d\n", p);
+#endif
+
+ if (conn->sb) {
+ if (p == SE) {
+#ifdef DEBUG_SEQEUENCES
+ puts("se");
+#endif
+ conn->sb = 0;
+ conn->iac = 0;
+ }
+ return 0;
+ }
+ if (conn->iac) {
+ if (conn->iac == 1) {
+ if (p == SB) {
+ conn->sb = 1;
+#ifdef DEBUG_SEQEUENCES
+ puts("sb");
+#endif
+ return 0;
+ }
+ if (p == IAC) {
+ conn->iac = 0;
+#ifdef DEBUG_SEQEUENCES
+ puts("iac iac (ende)");
+#endif
+ return 0;
+ }
+ conn->iac = 2;
+ return 0;
+ }
+ conn->iac = 0;
+#ifdef DEBUG_SEQEUENCES
+ puts("iac ende");
+#endif
+ return 0;
+ }
+ if (p == IAC) {
+ conn->iac = 1;
+#ifdef DEBUG_SEQEUENCES
+ puts("iac");
+#endif
+ return 0;
+ }
+
+ if (conn->esc) {
+ if (conn->esc == 1) {
+ if (p == 91) {
+ conn->esc = 2;
+ return 0;
+ }
+ if (p == 79) {
+ conn->esc = 3;
+ return 0;
+ }
+ conn->esc = 0;
+#ifdef DEBUG_SEQEUENCES
+ puts("esc abort");
+#endif
+ return 0;
+ }
+ if (conn->esc == 2) {
+ if (p == 65)
+ ui_inst_keypad(conn->ui, UI_KEY_UP);
+ if (p == 66)
+ ui_inst_keypad(conn->ui, UI_KEY_DOWN);
+ if (p == 67)
+ ui_inst_keypad(conn->ui, UI_KEY_RIGHT);
+ if (p == 68)
+ ui_inst_keypad(conn->ui, UI_KEY_LEFT);
+ if (p == 72)
+ ui_inst_keypad(conn->ui, UI_KEY_PICKUP);
+ if (p == 70)
+ ui_inst_keypad(conn->ui, UI_KEY_HANGUP);
+ }
+ if (conn->esc == 3) {
+ if (p == 80)
+ ui_inst_keypad(conn->ui, UI_KEY_F1);
+ if (p == 81)
+ ui_inst_keypad(conn->ui, UI_KEY_F2);
+ }
+ conn->esc = 0;
+#ifdef DEBUG_SEQEUENCES
+ puts("esc ende");
+#endif
+ return 0;
+ }
+ if (p == 27) {
+ conn->esc = 1;
+#ifdef DEBUG_SEQEUENCES
+ puts("esc");
+#endif
+ return 0;
+ }
+
+ if (p == 3 || p == 4)
+ return -1;
+
+ /* refresh */
+ if (p == 12) {
+ ui_inst_refresh(conn->ui);
+ return 0;
+ }
+
+ ui_inst_keypad(conn->ui, p);
+
+ return 0;
+}
+
+static int client_data(struct osmo_fd *fd, unsigned int what)
+{
+ struct ui_telnet_connection *conn = fd->data;
+ int rc = 0;
+
+ if (what & BSC_FD_READ) {
+ char buffer[16], *p = buffer;
+ int nbytes;
+ nbytes = read(fd->fd, buffer, sizeof(buffer));
+ if (nbytes == 0) {
+ conn->ui = NULL;
+ telnet_close_client(fd);
+ return rc;
+ }
+ if (nbytes > 0) {
+ while (nbytes--) {
+ rc = telnet_getc(conn, *p++);
+ if (rc < 0) {
+ telnet_close_client(&conn->fd);
+ /* telnet conn is gone, must exit! */
+ return 0;
+ }
+ }
+ }
+ }
+
+ if (what & BSC_FD_WRITE) {
+ rc = buffer_flush_all(conn->obuf, fd->fd);
+ if (rc == BUFFER_EMPTY)
+ conn->fd.when &= ~BSC_FD_WRITE;
+ }
+
+ return rc;
+}
+
+static int telnet_new_connection(struct osmo_fd *fd, unsigned int what)
+{
+ struct ui_telnet_connection *conn;
+ struct sockaddr_in sockaddr;
+ socklen_t len = sizeof(sockaddr);
+ int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
+ struct ui_inst *ui = (struct ui_inst *) fd->data;
+
+ if (new_connection < 0) {
+ LOGP(0, LOGL_ERROR, "telnet accept failed\n");
+ return new_connection;
+ }
+
+ conn = talloc_zero(ui->tall_telnet_ctx, struct ui_telnet_connection);
+ conn->ui = ui;
+ conn->fd.data = conn;
+ conn->fd.fd = new_connection;
+ conn->fd.when = BSC_FD_READ;
+ conn->fd.cb = client_data;
+ osmo_fd_register(&conn->fd);
+ llist_add_tail(&conn->entry, &ui->active_connections);
+
+ conn->obuf = buffer_new(ui->tall_telnet_ctx, 0);
+
+ vty_will_echo(ui);
+ vty_will_suppress_go_ahead(ui);
+ vty_dont_linemode(ui);
+ vty_do_window_size(ui);
+
+ /* notify about connection */
+ ui->telnet_cb(ui);
+
+ return 0;
+}
+
+void ui_telnet_exit(struct ui_inst *ui)
+{
+ struct ui_telnet_connection *tc, *tc2;
+
+ if (ui->server_socket.fd <= 0)
+ return;
+
+ llist_for_each_entry_safe(tc, tc2, &ui->active_connections, entry)
+ telnet_close_client(&tc->fd);
+
+ osmo_fd_unregister(&ui->server_socket);
+ close(ui->server_socket.fd);
+ ui->server_socket.fd = -1;
+ talloc_free(ui->tall_telnet_ctx);
+}
+