source: rtems/cpukit/telnetd/pty.c @ 0f0e130

5
Last change on this file since 0f0e130 was 2806e10, checked in by Sebastian Huber <sebastian.huber@…>, on 10/09/18 at 06:38:11

telnetd: Ignore setsockopt() return status

Update #3529.

  • Property mode set to 100644
File size: 11.0 KB
Line 
1/*
2 * /dev/ptyXX  (Support for pseudo-terminals)
3 *
4 *  Original Author: Fernando RUIZ CASAS (fernando.ruiz@ctv.es)
5 *  May 2001
6 *
7 *  This program is distributed in the hope that it will be useful,
8 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 *
11 *  Till Straumann <strauman@slac.stanford.edu>
12 *
13 *   - converted into a loadable module
14 *   - NAWS support / ioctls for querying/setting the window
15 *     size added.
16 *   - don't delete the running task when the connection
17 *     is closed. Rather let 'read()' return a 0 count so
18 *     they may cleanup. Some magic hack works around termios
19 *     limitation.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
25
26#define DEBUG_WH    (1<<0)
27#define DEBUG_DETAIL  (1<<1)
28
29/* #define DEBUG DEBUG_WH */
30
31/*-----------------------------------------*/
32#include <sys/ttycom.h>
33#include <rtems/pty.h>
34#include <rtems/seterr.h>
35#include <errno.h>
36#include <sys/socket.h>
37/*-----------------------------------------*/
38#include <inttypes.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <syslog.h>
43#include <unistd.h>
44/*-----------------------------------------*/
45#define IAC_ESC    255
46#define IAC_DONT   254
47#define IAC_DO     253
48#define IAC_WONT   252
49#define IAC_WILL   251
50#define IAC_SB     250
51#define IAC_GA     249
52#define IAC_EL     248
53#define IAC_EC     247
54#define IAC_AYT    246
55#define IAC_AO     245
56#define IAC_IP     244
57#define IAC_BRK    243
58#define IAC_DMARK  242
59#define IAC_NOP    241
60#define IAC_SE     240
61#define IAC_EOR    239
62
63#define SB_MAX     RTEMS_PTY_SB_MAX
64
65static bool ptyPollInitialize(rtems_termios_tty *,
66  rtems_termios_device_context *, struct termios *,
67  rtems_libio_open_close_args_t *);
68static void ptyShutdown(rtems_termios_tty *,
69  rtems_termios_device_context *, rtems_libio_open_close_args_t *);
70static void ptyPollWrite(rtems_termios_device_context *, const char *, size_t);
71static int ptyPollRead(rtems_termios_device_context *);
72static bool ptySetAttributes(rtems_termios_device_context *,
73  const struct termios *);
74static int my_pty_control(rtems_termios_device_context *,
75  ioctl_command_t, void *);
76
77static const rtems_termios_device_handler pty_handler = {
78  .first_open = ptyPollInitialize,
79  .last_close = ptyShutdown,
80  .poll_read = ptyPollRead,
81  .write = ptyPollWrite,
82  .set_attributes = ptySetAttributes,
83  .ioctl = my_pty_control
84};
85
86static
87int send_iac(rtems_pty_context *pty, unsigned char mode, unsigned char option)
88{
89  unsigned char buf[3];
90
91  buf[0]=IAC_ESC;
92  buf[1]=mode;
93  buf[2]=option;
94  return write(pty->socket, buf, sizeof(buf));
95}
96
97/* This procedure returns the devname for a pty slot free.
98 * If not slot availiable (field socket>=0)
99 *  then the socket argument is closed
100 */
101
102char *telnet_get_pty(rtems_pty_context *pty, int socket)
103{
104  rtems_status_code sc;
105  struct timeval t;
106
107  memset(pty, 0, sizeof(*pty));
108  snprintf(pty->name, sizeof(pty->name), "/dev/pty%" PRIuPTR, (uintptr_t)pty);
109  rtems_termios_device_context_initialize(&pty->base, "pty");
110  pty->socket = socket;
111  sc = rtems_termios_device_install(pty->name, &pty_handler, NULL, &pty->base);
112  if (sc != RTEMS_SUCCESSFUL) {
113    close(socket);
114    return NULL;
115  }
116
117  /* set a long polling interval to save CPU time */
118  t.tv_sec=2;
119  t.tv_usec=00000;
120  (void)setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
121
122  /* inform the client that we will echo */
123  send_iac(pty, IAC_WILL, 1);
124
125  return pty->name;
126}
127
128/*-----------------------------------------------------------*/
129/*
130 * The NVT terminal is negociated in PollRead and PollWrite
131 * with every BYTE sendded or received.
132 * A litle status machine in the pty_read_byte(int minor)
133 *
134 */
135static const char IAC_AYT_RSP[]="\r\nAYT? Yes, RTEMS-SHELL is here\r\n";
136static const char IAC_BRK_RSP[]="<*Break*>";
137static const char IAC_IP_RSP []="<*Interrupt*>";
138
139static int
140handleSB(rtems_pty_context *pty)
141{
142  switch (pty->sb_buf[0]) {
143    case 31:  /* NAWS */
144      pty->width  = (pty->sb_buf[1]<<8) + pty->sb_buf[2];
145      pty->height = (pty->sb_buf[3]<<8) + pty->sb_buf[4];
146#if DEBUG & DEBUG_WH
147      fprintf(stderr,
148          "Setting width/height to %ix%i\n",
149          pty->width,
150          pty->height);
151#endif
152      break;
153    default:
154      break;
155  }
156  return 0;
157}
158
159static int ptyPollRead(rtems_termios_device_context *base)
160{ /* Characters written to the client side*/
161   rtems_pty_context *pty = (rtems_pty_context *)base;
162   unsigned char  value;
163   unsigned int  omod;
164   int      count;
165   int      result;
166
167   count=read(pty->socket,&value,sizeof(value));
168   if (count<0)
169    return -1;
170
171   if (count<1) {
172      /* Unfortunately, there is no way of passing an EOF
173       * condition through the termios driver. Hence, we
174       * resort to an ugly hack. Setting cindex>ccount
175       * causes the termios driver to return a read count
176       * of '0' which is what we want here. We leave
177       * 'errno' untouched.
178       */
179      pty->ttyp->cindex=pty->ttyp->ccount+1;
180      return pty->ttyp->termios.c_cc[VEOF];
181   };
182
183   omod=pty->iac_mode;
184   pty->iac_mode=0;
185   switch(omod & 0xff) {
186       case IAC_ESC:
187           switch(value) {
188               case IAC_ESC :
189                   /* in case this is an ESC ESC sequence in SB mode */
190                   pty->iac_mode = omod>>8;
191                   return IAC_ESC;
192               case IAC_DONT:
193               case IAC_DO  :
194               case IAC_WONT:
195               case IAC_WILL:
196                   pty->iac_mode=value;
197                   return -1;
198               case IAC_SB  :
199#if DEBUG & DEBUG_DETAIL
200                   printk("SB\n");
201#endif
202                   pty->iac_mode=value;
203                   pty->sb_ind=0;
204                   return -100;
205               case IAC_GA  :
206                   return -1;
207               case IAC_EL  :
208                   return 0x03; /* Ctrl-C*/
209               case IAC_EC  :
210                   return '\b';
211               case IAC_AYT :
212                   write(pty->socket,IAC_AYT_RSP,strlen(IAC_AYT_RSP));
213                   return -1;
214               case IAC_AO  :
215                   return -1;
216               case IAC_IP  :
217                   write(pty->socket,IAC_IP_RSP,strlen(IAC_IP_RSP));
218                   return -1;
219               case IAC_BRK :
220                   write(pty->socket,IAC_BRK_RSP,strlen(IAC_BRK_RSP));
221                   return -1;
222               case IAC_DMARK:
223                   return -2;
224               case IAC_NOP :
225                   return -1;
226               case IAC_SE  :
227#if DEBUG & DEBUG_DETAIL
228                  {
229                  int i;
230                  printk("SE");
231                  for (i=0; i<pty->sb_ind; i++)
232                    printk(" %02x",pty->sb_buf[i]);
233                  printk("\n");
234                  }
235#endif
236                  handleSB(pty);
237               return -101;
238               case IAC_EOR :
239                   return -102;
240               default      :
241                   return -1;
242           };
243           break;
244
245       case IAC_SB:
246           pty->iac_mode=omod;
247           if (IAC_ESC==value) {
248             pty->iac_mode=(omod<<8)|value;
249           } else {
250             if (pty->sb_ind < SB_MAX)
251               pty->sb_buf[pty->sb_ind++]=value;
252           }
253           return -1;
254
255       case IAC_WILL:
256           if (value==34){
257              send_iac(pty,IAC_DONT,   34);  /*LINEMODE*/
258              send_iac(pty,IAC_DO  ,    1);  /*ECHO    */
259           } else if (value==31) {
260              send_iac(pty,IAC_DO  ,   31);  /*NAWS    */
261#if DEBUG & DEBUG_DETAIL
262              printk("replied DO NAWS\n");
263#endif
264           } else {
265              send_iac(pty,IAC_DONT,value);
266           }
267           return -1;
268       case IAC_DONT:
269           return -1;
270       case IAC_DO  :
271           if (value==3) {
272              send_iac(pty,IAC_WILL,    3);  /* GO AHEAD*/
273           } else  if (value==1) {
274              send_iac(pty,IAC_WILL,    1);  /* ECHO */
275           } else {
276              send_iac(pty,IAC_WONT,value);
277           };
278           return -1;
279       case IAC_WONT:
280           if (value==1) {
281             send_iac(pty,IAC_WILL,    1);
282           } else { /* ECHO */
283             send_iac(pty,IAC_WONT,value);
284           }
285           return -1;
286       default:
287           if (value==IAC_ESC) {
288              pty->iac_mode=value;
289              return -1;
290           } else {
291              result=value;
292              if ( 0
293                /* map CRLF to CR for symmetry */
294                 || ((value=='\n') && pty->last_cr)
295                /* map telnet CRNUL to CR down here */
296                 || ((value==0) && pty->last_cr)
297                ) result=-1;
298               pty->last_cr=(value=='\r');
299               return result;
300           };
301   };
302  /* should never get here but keep compiler happy */
303  return -1;
304}
305
306/*-----------------------------------------------------------*/
307/* Set the 'Hardware'                                        */
308/*-----------------------------------------------------------*/
309static bool
310ptySetAttributes(rtems_termios_device_context *base, const struct termios *t)
311{
312  rtems_pty_context *pty = (rtems_pty_context *)base;
313  pty->c_cflag = t->c_cflag;
314  return true;
315}
316/*-----------------------------------------------------------*/
317static bool
318ptyPollInitialize(rtems_termios_tty *ttyp,
319  rtems_termios_device_context *base, struct termios *t,
320  rtems_libio_open_close_args_t *args)
321{
322  rtems_pty_context *pty = (rtems_pty_context *)base;
323  pty->ttyp = ttyp;
324  pty->iac_mode = 0;
325  pty->sb_ind = 0;
326  pty->width = 0;
327  pty->height = 0;
328  return ptySetAttributes(&pty->base, t);
329}
330/*-----------------------------------------------------------*/
331static void
332ptyShutdown(rtems_termios_tty *ttyp,
333  rtems_termios_device_context *base, rtems_libio_open_close_args_t *arg)
334{
335  rtems_pty_context *pty = (rtems_pty_context *)base;
336  close(pty->socket);
337}
338/*-----------------------------------------------------------*/
339/* Write Characters into pty device                          */
340/*-----------------------------------------------------------*/
341static void
342ptyPollWrite(rtems_termios_device_context *base, const char *buf, size_t len)
343{
344  rtems_pty_context *pty = (rtems_pty_context *)base;
345
346  while (len > 0) {
347    ssize_t n = write(pty->socket, buf, len);
348    if (n <= 0) {
349      break;
350    }
351
352    buf += (size_t)n;
353    len -= (size_t)n;
354  }
355}
356
357static int
358my_pty_control(rtems_termios_device_context *base,
359  ioctl_command_t request, void *buffer)
360{
361  rtems_pty_context *p = (rtems_pty_context *)base;
362  struct winsize *wp = buffer;
363
364  switch (request) {
365    case TIOCGWINSZ:
366      wp->ws_row = p->height;
367      wp->ws_col = p->width;
368#if DEBUG & DEBUG_WH
369      fprintf(stderr,
370          "ioctl(TIOCGWINSZ), returning %ix%i\n",
371          wp->ws_col,
372          wp->ws_row);
373#endif
374      break;
375    case TIOCSWINSZ:
376#if DEBUG & DEBUG_WH
377      fprintf(stderr,
378          "ioctl(TIOCGWINSZ), setting %ix%i\n",
379          wp->ws_col,
380          wp->ws_row);
381#endif
382
383      p->height = wp->ws_row;
384      p->width  = wp->ws_col;
385      break;
386    default:
387      rtems_set_errno_and_return_minus_one(EINVAL);
388      break;
389  }
390
391  return 0;
392}
Note: See TracBrowser for help on using the repository browser.