blob: 32ab6bee8997eb18c3277c955e48386176c7e99e [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001/* minimalistic telnet/network interface it might turn into a wire interface */
2/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
21#include <sys/socket.h>
22#include <netinet/in.h>
Sylvain Munauta9efc122012-03-01 20:41:40 +010023#include <errno.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020024#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
27#include <unistd.h>
28
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010029#include <osmocom/core/msgb.h>
Sylvain Munauta9efc122012-03-01 20:41:40 +010030#include <osmocom/core/socket.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010031#include <osmocom/core/talloc.h>
32#include <osmocom/core/logging.h>
Holger Hans Peter Freyther2c9168c2013-10-10 20:21:33 +020033#include <osmocom/core/signal.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020034
35#include <osmocom/vty/telnet_interface.h>
36#include <osmocom/vty/buffer.h>
Holger Hans Peter Freyther2e228fc2010-09-11 13:41:41 +080037#include <osmocom/vty/command.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020038
Harald Weltee881b1b2011-08-17 18:52:30 +020039/*! \addtogroup telnet_interface
Harald Welte7acb30c2011-08-17 17:13:48 +020040 * @{
41 */
42/*! \file telnet_interface.c */
43
Harald Welte3fb0b6f2010-05-19 19:02:52 +020044/* per connection data */
45LLIST_HEAD(active_connections);
46
47static void *tall_telnet_ctx;
48
49/* per network data */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020050static int telnet_new_connection(struct osmo_fd *fd, unsigned int what);
Harald Welte3fb0b6f2010-05-19 19:02:52 +020051
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020052static struct osmo_fd server_socket = {
Harald Welte3fb0b6f2010-05-19 19:02:52 +020053 .when = BSC_FD_READ,
54 .cb = telnet_new_connection,
55 .priv_nr = 0,
56};
57
Sylvain Munauta9efc122012-03-01 20:41:40 +010058/*! \brief Initialize telnet based VTY interface listening to 127.0.0.1
Harald Welte7acb30c2011-08-17 17:13:48 +020059 * \param[in] tall_ctx \ref talloc context
60 * \param[in] priv private data to be passed to callback
61 * \param[in] port UDP port number
62 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +020063int telnet_init(void *tall_ctx, void *priv, int port)
64{
Sylvain Munauta9efc122012-03-01 20:41:40 +010065 return telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port);
66}
67
68/*! \brief Initialize telnet based VTY interface
69 * \param[in] tall_ctx \ref talloc context
70 * \param[in] priv private data to be passed to callback
71 * \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...)
72 * \param[in] port UDP port number
73 */
74int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
75{
76 int rc;
Harald Welte3fb0b6f2010-05-19 19:02:52 +020077
78 tall_telnet_ctx = talloc_named_const(tall_ctx, 1,
Sylvain Munauta9efc122012-03-01 20:41:40 +010079 "telnet_connection");
Harald Welte3fb0b6f2010-05-19 19:02:52 +020080
Sylvain Munauta9efc122012-03-01 20:41:40 +010081 rc = osmo_sock_init_ofd(
82 &server_socket,
83 AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP,
84 ip, port, OSMO_SOCK_F_BIND
85 );
Harald Welte3fb0b6f2010-05-19 19:02:52 +020086
87 server_socket.data = priv;
Harald Welte3fb0b6f2010-05-19 19:02:52 +020088
Sylvain Munauta9efc122012-03-01 20:41:40 +010089 return (rc < 0) ? -1 : 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +020090}
91
Holger Hans Peter Freyther2e228fc2010-09-11 13:41:41 +080092extern struct host host;
Harald Welte3fb0b6f2010-05-19 19:02:52 +020093
Harald Welte7acb30c2011-08-17 17:13:48 +020094/*! \brief close a telnet connection */
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +020095int telnet_close_client(struct osmo_fd *fd)
Harald Welte3fb0b6f2010-05-19 19:02:52 +020096{
97 struct telnet_connection *conn = (struct telnet_connection*)fd->data;
98
99 close(fd->fd);
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200100 osmo_fd_unregister(fd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200101
102 if (conn->dbg) {
103 log_del_target(conn->dbg);
104 talloc_free(conn->dbg);
105 }
106
107 llist_del(&conn->entry);
108 talloc_free(conn);
109 return 0;
110}
111
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200112static int client_data(struct osmo_fd *fd, unsigned int what)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200113{
114 struct telnet_connection *conn = fd->data;
115 int rc = 0;
116
117 if (what & BSC_FD_READ) {
118 conn->fd.when &= ~BSC_FD_READ;
119 rc = vty_read(conn->vty);
120 }
121
122 /* vty might have been closed from vithin vty_read() */
123 if (!conn->vty)
124 return rc;
125
126 if (what & BSC_FD_WRITE) {
127 rc = buffer_flush_all(conn->vty->obuf, fd->fd);
128 if (rc == BUFFER_EMPTY)
129 conn->fd.when &= ~BSC_FD_WRITE;
130 }
131
132 return rc;
133}
134
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200135static int telnet_new_connection(struct osmo_fd *fd, unsigned int what)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200136{
137 struct telnet_connection *connection;
138 struct sockaddr_in sockaddr;
139 socklen_t len = sizeof(sockaddr);
140 int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
141
142 if (new_connection < 0) {
143 LOGP(0, LOGL_ERROR, "telnet accept failed\n");
144 return new_connection;
145 }
146
147 connection = talloc_zero(tall_telnet_ctx, struct telnet_connection);
148 connection->priv = fd->data;
149 connection->fd.data = connection;
150 connection->fd.fd = new_connection;
151 connection->fd.when = BSC_FD_READ;
152 connection->fd.cb = client_data;
Pablo Neira Ayusof7f89d02011-05-07 12:42:40 +0200153 osmo_fd_register(&connection->fd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200154 llist_add_tail(&connection->entry, &active_connections);
155
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200156 connection->vty = vty_create(new_connection, connection);
157 if (!connection->vty) {
158 LOGP(0, LOGL_ERROR, "couldn't create VTY\n");
159 close(new_connection);
160 talloc_free(connection);
161 return -1;
162 }
163
164 return 0;
165}
166
Harald Welte7acb30c2011-08-17 17:13:48 +0200167/*! \brief callback from core VTY code about VTY related events */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200168void vty_event(enum event event, int sock, struct vty *vty)
169{
Holger Hans Peter Freyther2c9168c2013-10-10 20:21:33 +0200170 struct vty_signal_data sig_data = { 0, };
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200171 struct telnet_connection *connection = vty->priv;
Holger Hans Peter Freyther2c9168c2013-10-10 20:21:33 +0200172 struct osmo_fd *bfd;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200173
174 if (vty->type != VTY_TERM)
175 return;
176
Holger Hans Peter Freyther2c9168c2013-10-10 20:21:33 +0200177 sig_data.event = event;
178 sig_data.sock = sock;
179 sig_data.vty = vty;
180 osmo_signal_dispatch(SS_L_VTY, S_VTY_EVENT, &sig_data);
181
182 if (!connection)
183 return;
184
185 bfd = &connection->fd;
186
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200187 switch (event) {
188 case VTY_READ:
189 bfd->when |= BSC_FD_READ;
190 break;
191 case VTY_WRITE:
192 bfd->when |= BSC_FD_WRITE;
193 break;
194 case VTY_CLOSED:
195 /* vty layer is about to free() vty */
196 connection->vty = NULL;
197 telnet_close_client(bfd);
198 break;
199 default:
200 break;
201 }
202}
203
Andreas.Eversbergdc3be0a2011-11-06 20:09:28 +0100204void telnet_exit(void)
205{
206 struct telnet_connection *tc, *tc2;
207
208 llist_for_each_entry_safe(tc, tc2, &active_connections, entry)
209 telnet_close_client(&tc->fd);
210
211 osmo_fd_unregister(&server_socket);
212 close(server_socket.fd);
213 talloc_free(tall_telnet_ctx);
214}
215
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200216/*! @} */