From 650ac700664238b842f13523cc8b1604d24b1794 Mon Sep 17 00:00:00 2001 From: Vijay Kumar Banerjee Date: Tue, 13 Apr 2021 11:57:21 -0600 Subject: Revert "cpukit: Remove telnetd" This reverts commit 3299dda2454a8847c670a732f6c12ef1f2cc5dd0. --- cpukit/telnetd/pty.c | 401 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 cpukit/telnetd/pty.c (limited to 'cpukit/telnetd/pty.c') diff --git a/cpukit/telnetd/pty.c b/cpukit/telnetd/pty.c new file mode 100644 index 0000000000..a593bbd224 --- /dev/null +++ b/cpukit/telnetd/pty.c @@ -0,0 +1,401 @@ +/* + * /dev/ptyXX (Support for pseudo-terminals) + * + * Original Author: Fernando RUIZ CASAS (fernando.ruiz@ctv.es) + * May 2001 + * + * 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. + * + * Till Straumann + * + * - converted into a loadable module + * - NAWS support / ioctls for querying/setting the window + * size added. + * - don't delete the running task when the connection + * is closed. Rather let 'read()' return a 0 count so + * they may cleanup. Some magic hack works around termios + * limitation. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define DEBUG_WH (1<<0) +#define DEBUG_DETAIL (1<<1) + +/* #define DEBUG DEBUG_WH */ + +/*-----------------------------------------*/ +#include +#include +#include +#include +#include +/*-----------------------------------------*/ +#include +#include +#include +#include +#include +#include +/*-----------------------------------------*/ +#define IAC_ESC 255 +#define IAC_DONT 254 +#define IAC_DO 253 +#define IAC_WONT 252 +#define IAC_WILL 251 +#define IAC_SB 250 +#define IAC_GA 249 +#define IAC_EL 248 +#define IAC_EC 247 +#define IAC_AYT 246 +#define IAC_AO 245 +#define IAC_IP 244 +#define IAC_BRK 243 +#define IAC_DMARK 242 +#define IAC_NOP 241 +#define IAC_SE 240 +#define IAC_EOR 239 + +#define SB_MAX RTEMS_PTY_SB_MAX + +static bool ptyPollInitialize(rtems_termios_tty *, + rtems_termios_device_context *, struct termios *, + rtems_libio_open_close_args_t *); +static void ptyShutdown(rtems_termios_tty *, + rtems_termios_device_context *, rtems_libio_open_close_args_t *); +static void ptyPollWrite(rtems_termios_device_context *, const char *, size_t); +static int ptyPollRead(rtems_termios_device_context *); +static bool ptySetAttributes(rtems_termios_device_context *, + const struct termios *); +static int my_pty_control(rtems_termios_device_context *, + ioctl_command_t, void *); + +static const rtems_termios_device_handler pty_handler = { + .first_open = ptyPollInitialize, + .last_close = ptyShutdown, + .poll_read = ptyPollRead, + .write = ptyPollWrite, + .set_attributes = ptySetAttributes, + .ioctl = my_pty_control +}; + +static +int send_iac(rtems_pty_context *pty, unsigned char mode, unsigned char option) +{ + unsigned char buf[3]; + + buf[0]=IAC_ESC; + buf[1]=mode; + buf[2]=option; + return write(pty->socket, buf, sizeof(buf)); +} + +const char *rtems_pty_initialize(rtems_pty_context *pty, uintptr_t unique) +{ + rtems_status_code sc; + + memset(pty, 0, sizeof(*pty)); + (void)snprintf(pty->name, sizeof(pty->name), "/dev/pty%" PRIuPTR, unique); + rtems_termios_device_context_initialize(&pty->base, "pty"); + pty->socket = -1; + sc = rtems_termios_device_install(pty->name, &pty_handler, NULL, &pty->base); + if (sc != RTEMS_SUCCESSFUL) { + return NULL; + } + + return pty->name; +} + +void rtems_pty_close_socket(rtems_pty_context *pty) +{ + if (pty->socket >= 0) { + (void)close(pty->socket); + pty->socket = -1; + } +} + +void rtems_pty_set_socket(rtems_pty_context *pty, int socket) +{ + struct timeval t; + + rtems_pty_close_socket(pty); + pty->socket = socket; + + /* set a long polling interval to save CPU time */ + t.tv_sec=2; + t.tv_usec=00000; + (void)setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)); + + /* inform the client that we will echo */ + send_iac(pty, IAC_WILL, 1); +} + +/*-----------------------------------------------------------*/ +/* + * The NVT terminal is negociated in PollRead and PollWrite + * with every BYTE sendded or received. + * A litle status machine in the pty_read_byte(int minor) + * + */ +static const char IAC_AYT_RSP[]="\r\nAYT? Yes, RTEMS-SHELL is here\r\n"; +static const char IAC_BRK_RSP[]="<*Break*>"; +static const char IAC_IP_RSP []="<*Interrupt*>"; + +static int +handleSB(rtems_pty_context *pty) +{ + switch (pty->sb_buf[0]) { + case 31: /* NAWS */ + pty->width = (pty->sb_buf[1]<<8) + pty->sb_buf[2]; + pty->height = (pty->sb_buf[3]<<8) + pty->sb_buf[4]; +#if DEBUG & DEBUG_WH + fprintf(stderr, + "Setting width/height to %ix%i\n", + pty->width, + pty->height); +#endif + break; + default: + break; + } + return 0; +} + +static int ptyPollRead(rtems_termios_device_context *base) +{ /* Characters written to the client side*/ + rtems_pty_context *pty = (rtems_pty_context *)base; + unsigned char value; + unsigned int omod; + int count; + int result; + + count=read(pty->socket,&value,sizeof(value)); + if (count<0) + return -1; + + if (count<1) { + /* Unfortunately, there is no way of passing an EOF + * condition through the termios driver. Hence, we + * resort to an ugly hack. Setting cindex>ccount + * causes the termios driver to return a read count + * of '0' which is what we want here. We leave + * 'errno' untouched. + */ + pty->ttyp->cindex=pty->ttyp->ccount+1; + return pty->ttyp->termios.c_cc[VEOF]; + }; + + omod=pty->iac_mode; + pty->iac_mode=0; + switch(omod & 0xff) { + case IAC_ESC: + switch(value) { + case IAC_ESC : + /* in case this is an ESC ESC sequence in SB mode */ + pty->iac_mode = omod>>8; + return IAC_ESC; + case IAC_DONT: + case IAC_DO : + case IAC_WONT: + case IAC_WILL: + pty->iac_mode=value; + return -1; + case IAC_SB : +#if DEBUG & DEBUG_DETAIL + printk("SB\n"); +#endif + pty->iac_mode=value; + pty->sb_ind=0; + return -100; + case IAC_GA : + return -1; + case IAC_EL : + return 0x03; /* Ctrl-C*/ + case IAC_EC : + return '\b'; + case IAC_AYT : + write(pty->socket,IAC_AYT_RSP,strlen(IAC_AYT_RSP)); + return -1; + case IAC_AO : + return -1; + case IAC_IP : + write(pty->socket,IAC_IP_RSP,strlen(IAC_IP_RSP)); + return -1; + case IAC_BRK : + write(pty->socket,IAC_BRK_RSP,strlen(IAC_BRK_RSP)); + return -1; + case IAC_DMARK: + return -2; + case IAC_NOP : + return -1; + case IAC_SE : +#if DEBUG & DEBUG_DETAIL + { + int i; + printk("SE"); + for (i=0; isb_ind; i++) + printk(" %02x",pty->sb_buf[i]); + printk("\n"); + } +#endif + handleSB(pty); + return -101; + case IAC_EOR : + return -102; + default : + return -1; + }; + break; + + case IAC_SB: + pty->iac_mode=omod; + if (IAC_ESC==value) { + pty->iac_mode=(omod<<8)|value; + } else { + if (pty->sb_ind < SB_MAX) + pty->sb_buf[pty->sb_ind++]=value; + } + return -1; + + case IAC_WILL: + if (value==34){ + send_iac(pty,IAC_DONT, 34); /*LINEMODE*/ + send_iac(pty,IAC_DO , 1); /*ECHO */ + } else if (value==31) { + send_iac(pty,IAC_DO , 31); /*NAWS */ +#if DEBUG & DEBUG_DETAIL + printk("replied DO NAWS\n"); +#endif + } else { + send_iac(pty,IAC_DONT,value); + } + return -1; + case IAC_DONT: + return -1; + case IAC_DO : + if (value==3) { + send_iac(pty,IAC_WILL, 3); /* GO AHEAD*/ + } else if (value==1) { + send_iac(pty,IAC_WILL, 1); /* ECHO */ + } else { + send_iac(pty,IAC_WONT,value); + }; + return -1; + case IAC_WONT: + if (value==1) { + send_iac(pty,IAC_WILL, 1); + } else { /* ECHO */ + send_iac(pty,IAC_WONT,value); + } + return -1; + default: + if (value==IAC_ESC) { + pty->iac_mode=value; + return -1; + } else { + result=value; + if ( 0 + /* map CRLF to CR for symmetry */ + || ((value=='\n') && pty->last_cr) + /* map telnet CRNUL to CR down here */ + || ((value==0) && pty->last_cr) + ) result=-1; + pty->last_cr=(value=='\r'); + return result; + }; + }; + /* should never get here but keep compiler happy */ + return -1; +} + +/*-----------------------------------------------------------*/ +/* Set the 'Hardware' */ +/*-----------------------------------------------------------*/ +static bool +ptySetAttributes(rtems_termios_device_context *base, const struct termios *t) +{ + rtems_pty_context *pty = (rtems_pty_context *)base; + pty->c_cflag = t->c_cflag; + return true; +} +/*-----------------------------------------------------------*/ +static bool +ptyPollInitialize(rtems_termios_tty *ttyp, + rtems_termios_device_context *base, struct termios *t, + rtems_libio_open_close_args_t *args) +{ + rtems_pty_context *pty = (rtems_pty_context *)base; + pty->ttyp = ttyp; + pty->iac_mode = 0; + pty->sb_ind = 0; + pty->width = 0; + pty->height = 0; + return ptySetAttributes(&pty->base, t); +} +/*-----------------------------------------------------------*/ +static void +ptyShutdown(rtems_termios_tty *ttyp, + rtems_termios_device_context *base, rtems_libio_open_close_args_t *arg) +{ + rtems_pty_context *pty = (rtems_pty_context *)base; + close(pty->socket); +} +/*-----------------------------------------------------------*/ +/* Write Characters into pty device */ +/*-----------------------------------------------------------*/ +static void +ptyPollWrite(rtems_termios_device_context *base, const char *buf, size_t len) +{ + rtems_pty_context *pty = (rtems_pty_context *)base; + + while (len > 0) { + ssize_t n = write(pty->socket, buf, len); + if (n <= 0) { + break; + } + + buf += (size_t)n; + len -= (size_t)n; + } +} + +static int +my_pty_control(rtems_termios_device_context *base, + ioctl_command_t request, void *buffer) +{ + rtems_pty_context *p = (rtems_pty_context *)base; + struct winsize *wp = buffer; + + switch (request) { + case TIOCGWINSZ: + wp->ws_row = p->height; + wp->ws_col = p->width; +#if DEBUG & DEBUG_WH + fprintf(stderr, + "ioctl(TIOCGWINSZ), returning %ix%i\n", + wp->ws_col, + wp->ws_row); +#endif + break; + case TIOCSWINSZ: +#if DEBUG & DEBUG_WH + fprintf(stderr, + "ioctl(TIOCGWINSZ), setting %ix%i\n", + wp->ws_col, + wp->ws_row); +#endif + + p->height = wp->ws_row; + p->width = wp->ws_col; + break; + default: + rtems_set_errno_and_return_minus_one(EINVAL); + break; + } + + return 0; +} -- cgit v1.2.3