source: rtems/cpukit/telnetd/pty.c @ 66c0078

4.115
Last change on this file since 66c0078 was 90e2026, checked in by Sebastian Huber <sebastian.huber@…>, on 08/20/12 at 12:43:16

telnetd: Map CRLF to CR

It is not so easy to use Termios for that. We already map CRNUL to CR,
thus we cannot ignore CRs. We could change the shell code to map CRNL
to a single line end, but this is more intrusive than the change here.

  • Property mode set to 100644
File size: 16.9 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 <termios.h>
33#include <rtems/termiostypes.h>
34#include <sys/ttycom.h>
35#include <rtems.h>
36#include <rtems/libio.h>
37#include <rtems/bspIo.h>
38#include <errno.h>
39#include <sys/socket.h>
40/*-----------------------------------------*/
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45/*-----------------------------------------*/
46#define IAC_ESC    255
47#define IAC_DONT   254
48#define IAC_DO     253
49#define IAC_WONT   252
50#define IAC_WILL   251
51#define IAC_SB     250
52#define IAC_GA     249
53#define IAC_EL     248
54#define IAC_EC     247
55#define IAC_AYT    246
56#define IAC_AO     245
57#define IAC_IP     244
58#define IAC_BRK    243
59#define IAC_DMARK  242
60#define IAC_NOP    241
61#define IAC_SE     240
62#define IAC_EOR    239
63
64#define SB_MAX    16
65
66extern int rtems_telnetd_maximum_ptys;
67
68struct pty_tt;
69typedef struct pty_tt pty_t;
70
71struct pty_tt {
72 char                     *devname;
73 struct rtems_termios_tty *ttyp;
74 tcflag_t                  c_cflag;
75 int                       opened;
76 int                       socket;
77 int                       last_cr;
78 unsigned                  iac_mode;
79 unsigned char             sb_buf[SB_MAX];
80 int                       sb_ind;
81 int                       width;
82 int                       height;
83};
84
85
86static int    telnet_pty_inited=FALSE;
87static pty_t *telnet_ptys;
88
89static rtems_device_major_number pty_major;
90
91static
92int send_iac(int minor,unsigned char mode,unsigned char option)
93{
94  unsigned char buf[3];
95
96  buf[0]=IAC_ESC;
97  buf[1]=mode;
98  buf[2]=option;
99  return write(telnet_ptys[minor].socket,buf,sizeof(buf));
100}
101
102/* This procedure returns the devname for a pty slot free.
103 * If not slot availiable (field socket>=0)
104 *  then the socket argument is closed
105 */
106
107char *  telnet_get_pty(int socket)
108{
109  int ndx;
110
111  if (telnet_pty_inited) {
112#if 0
113    if ( rtems_telnetd_maximum_ptys < 5 )
114      rtems_telnetd_maximum_ptys = 5;
115
116    telnet_ptys = malloc( rtems_telnetd_maximum_ptys * sizeof (pty_t) );
117#endif
118    if ( !telnet_ptys ) {
119      return NULL;
120    }
121
122    for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
123
124      if (telnet_ptys[ndx].socket<0) {
125        struct timeval t;
126        /* set a long polling interval to save CPU time */
127        t.tv_sec=2;
128        t.tv_usec=00000;
129        setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
130        telnet_ptys[ndx].socket=socket;
131
132        /* inform the client that we will echo */
133        send_iac(ndx, IAC_WILL, 1);
134
135        return telnet_ptys[ndx].devname;
136      };
137    };
138  }
139  close(socket);
140  return NULL;
141}
142
143
144/*-----------------------------------------------------------*/
145/*
146 * The NVT terminal is negociated in PollRead and PollWrite
147 * with every BYTE sendded or received.
148 * A litle status machine in the pty_read_byte(int minor)
149 *
150 */
151static const char IAC_AYT_RSP[]="\r\nAYT? Yes, RTEMS-SHELL is here\r\n";
152static const char IAC_BRK_RSP[]="<*Break*>";
153static const char IAC_IP_RSP []="<*Interrupt*>";
154
155static int
156handleSB(pty_t *pty)
157{
158  switch (pty->sb_buf[0]) {
159    case 31:  /* NAWS */
160      pty->width  = (pty->sb_buf[1]<<8) + pty->sb_buf[2];
161      pty->height = (pty->sb_buf[3]<<8) + pty->sb_buf[4];
162#if DEBUG & DEBUG_WH
163      fprintf(stderr,
164          "Setting width/height to %ix%i\n",
165          pty->width,
166          pty->height);
167#endif
168      break;
169    default:
170      break;
171  }
172  return 0;
173}
174
175static int read_pty(int minor)
176{ /* Characters written to the client side*/
177   unsigned char  value;
178   unsigned int  omod;
179   int      count;
180   int      result;
181   pty_t      *pty=telnet_ptys+minor;
182
183   count=read(pty->socket,&value,sizeof(value));
184   if (count<0)
185    return -1;
186
187   if (count<1) {
188      /* Unfortunately, there is no way of passing an EOF
189       * condition through the termios driver. Hence, we
190       * resort to an ugly hack. Setting cindex>ccount
191       * causes the termios driver to return a read count
192       * of '0' which is what we want here. We leave
193       * 'errno' untouched.
194       */
195      pty->ttyp->cindex=pty->ttyp->ccount+1;
196      return pty->ttyp->termios.c_cc[VEOF];
197   };
198
199   omod=pty->iac_mode;
200   pty->iac_mode=0;
201   switch(omod & 0xff) {
202       case IAC_ESC:
203           switch(value) {
204               case IAC_ESC :
205                   /* in case this is an ESC ESC sequence in SB mode */
206                   pty->iac_mode = omod>>8;
207                   return IAC_ESC;
208               case IAC_DONT:
209               case IAC_DO  :
210               case IAC_WONT:
211               case IAC_WILL:
212                   pty->iac_mode=value;
213                   return -1;
214               case IAC_SB  :
215#if DEBUG & DEBUG_DETAIL
216                   printk("SB\n");
217#endif
218                   pty->iac_mode=value;
219                   pty->sb_ind=0;
220                   return -100;
221               case IAC_GA  :
222                   return -1;
223               case IAC_EL  :
224                   return 0x03; /* Ctrl-C*/
225               case IAC_EC  :
226                   return '\b';
227               case IAC_AYT :
228                   write(pty->socket,IAC_AYT_RSP,strlen(IAC_AYT_RSP));
229                   return -1;
230               case IAC_AO  :
231                   return -1;
232               case IAC_IP  :
233                   write(pty->socket,IAC_IP_RSP,strlen(IAC_IP_RSP));
234                   return -1;
235               case IAC_BRK :
236                   write(pty->socket,IAC_BRK_RSP,strlen(IAC_BRK_RSP));
237                   return -1;
238               case IAC_DMARK:
239                   return -2;
240               case IAC_NOP :
241                   return -1;
242               case IAC_SE  :
243#if DEBUG & DEBUG_DETAIL
244                  {
245                  int i;
246                  printk("SE");
247                  for (i=0; i<pty->sb_ind; i++)
248                    printk(" %02x",pty->sb_buf[i]);
249                  printk("\n");
250                  }
251#endif
252                  handleSB(pty);
253               return -101;
254               case IAC_EOR :
255                   return -102;
256               default      :
257                   return -1;
258           };
259           break;
260
261       case IAC_SB:
262           pty->iac_mode=omod;
263           if (IAC_ESC==value) {
264             pty->iac_mode=(omod<<8)|value;
265           } else {
266             if (pty->sb_ind < SB_MAX)
267               pty->sb_buf[pty->sb_ind++]=value;
268           }
269           return -1;
270
271       case IAC_WILL:
272           if (value==34){
273              send_iac(minor,IAC_DONT,   34);  /*LINEMODE*/
274              send_iac(minor,IAC_DO  ,    1);  /*ECHO    */
275           } else if (value==31) {
276              send_iac(minor,IAC_DO  ,   31);  /*NAWS    */
277#if DEBUG & DEBUG_DETAIL
278              printk("replied DO NAWS\n");
279#endif
280           } else {
281              send_iac(minor,IAC_DONT,value);
282           }
283           return -1;
284       case IAC_DONT:
285           return -1;
286       case IAC_DO  :
287           if (value==3) {
288              send_iac(minor,IAC_WILL,    3);  /* GO AHEAD*/
289           } else  if (value==1) {
290              send_iac(minor,IAC_WILL,    1);  /* ECHO */
291           } else {
292              send_iac(minor,IAC_WONT,value);
293           };
294           return -1;
295       case IAC_WONT:
296           if (value==1) {
297             send_iac(minor,IAC_WILL,    1);
298           } else { /* ECHO */
299             send_iac(minor,IAC_WONT,value);
300           }
301           return -1;
302       default:
303           if (value==IAC_ESC) {
304              pty->iac_mode=value;
305              return -1;
306           } else {
307              result=value;
308              if ( 0
309                /* map CRLF to CR for symmetry */
310                 || ((value=='\n') && pty->last_cr)
311                /* map telnet CRNUL to CR down here */
312                 || ((value==0) && pty->last_cr)
313                ) result=-1;
314               pty->last_cr=(value=='\r');
315               return result;
316           };
317   };
318  /* should never get here but keep compiler happy */
319  return -1;
320}
321
322/*-----------------------------------------------------------*/
323static int ptySetAttributes(int minor,const struct termios *t);
324static int ptyPollInitialize(int major,int minor,void * arg) ;
325static int ptyShutdown(int major,int minor,void * arg) ;
326static ssize_t ptyPollWrite(int minor, const char * buf, size_t len) ;
327static int ptyPollRead(int minor) ;
328static const rtems_termios_callbacks * pty_get_termios_handlers(int polled) ;
329/*-----------------------------------------------------------*/
330/* Set the 'Hardware'                                        */
331/*-----------------------------------------------------------*/
332static int
333ptySetAttributes(int minor,const struct termios *t) {
334  if (minor<rtems_telnetd_maximum_ptys) {
335   telnet_ptys[minor].c_cflag=t->c_cflag;
336  } else {
337   return -1;
338  };
339  return 0;
340}
341/*-----------------------------------------------------------*/
342static int
343ptyPollInitialize(int major,int minor,void * arg) {
344  rtems_libio_open_close_args_t * args = (rtems_libio_open_close_args_t*)arg;
345  struct termios t;
346        if (minor<rtems_telnetd_maximum_ptys) {
347         if (telnet_ptys[minor].socket<0) return -1;
348   telnet_ptys[minor].opened=TRUE;
349   telnet_ptys[minor].ttyp= (struct rtems_termios_tty *) args->iop->data1;
350   telnet_ptys[minor].iac_mode=0;
351   telnet_ptys[minor].sb_ind=0;
352   telnet_ptys[minor].width=0;
353   telnet_ptys[minor].height=0;
354   t.c_cflag=B9600|CS8;/* termios default */
355   return ptySetAttributes(minor,&t);
356  } else {
357   return -1;
358  }
359}
360/*-----------------------------------------------------------*/
361static int
362ptyShutdown(int major,int minor,void * arg) {
363  if (minor<rtems_telnetd_maximum_ptys) {
364    telnet_ptys[minor].opened=FALSE;
365    if (telnet_ptys[minor].socket>=0) close(telnet_ptys[minor].socket);
366      telnet_ptys[minor].socket=-1;
367    chown(telnet_ptys[minor].devname,2,0);
368  } else {
369    return -1;
370  }
371  return 0;
372}
373/*-----------------------------------------------------------*/
374/* Write Characters into pty device                          */
375/*-----------------------------------------------------------*/
376static ssize_t
377ptyPollWrite(int minor, const char * buf, size_t len) {
378  size_t count;
379  if (minor<rtems_telnetd_maximum_ptys) {
380    if (telnet_ptys[minor].socket<0)
381      return -1;
382    count=write(telnet_ptys[minor].socket,buf,len);
383  } else {
384   count=-1;
385  }
386  return count;
387}
388/*-----------------------------------------------------------*/
389static int
390ptyPollRead(int minor) {
391  int result;
392
393  if (minor<rtems_telnetd_maximum_ptys) {
394    if (telnet_ptys[minor].socket<0)
395      return -1;
396    result=read_pty(minor);
397    return result;
398  }
399  return -1;
400}
401/*-----------------------------------------------------------*/
402/*  pty_initialize
403 *
404 *  This routine initializes the pty IO driver.
405 *
406 *  Input parameters: NONE
407 *
408 *  Output parameters:  NONE
409 *
410 *  Return values:
411 */
412/*-----------------------------------------------------------*/
413static
414rtems_device_driver my_pty_initialize(
415  rtems_device_major_number  major,
416  rtems_device_minor_number  minor,
417  void                      *arg
418)
419{
420  int ndx;
421  rtems_status_code status;
422
423  if ( rtems_telnetd_maximum_ptys < 5 )
424    rtems_telnetd_maximum_ptys = 5;
425
426  telnet_ptys = malloc( rtems_telnetd_maximum_ptys * sizeof (pty_t) );
427
428  /*
429   * Set up ptys
430   */
431
432  for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
433    telnet_ptys[ndx].devname = (char*)malloc(strlen("/dev/ptyXX")+1);
434    sprintf(telnet_ptys[ndx].devname,"/dev/pty%X",ndx);
435    telnet_ptys[ndx].ttyp    =  NULL;
436    telnet_ptys[ndx].c_cflag = CS8|B9600;
437    telnet_ptys[ndx].socket  = -1;
438    telnet_ptys[ndx].opened  = FALSE;
439    telnet_ptys[ndx].sb_ind  = 0;
440    telnet_ptys[ndx].width   = 0;
441    telnet_ptys[ndx].height  = 0;
442
443  }
444
445  /*
446   * Register the devices
447   */
448  for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
449    status = rtems_io_register_name(telnet_ptys[ndx].devname, major, ndx);
450    if (status != RTEMS_SUCCESSFUL)
451        rtems_fatal_error_occurred(status);
452    chmod(telnet_ptys[ndx].devname,0660);
453    chown(telnet_ptys[ndx].devname,2,0); /* tty,root*/
454  };
455  printk("Device: /dev/pty%X../dev/pty%X (%d)pseudo-terminals registered.\n",
456          0,rtems_telnetd_maximum_ptys-1,rtems_telnetd_maximum_ptys);
457
458  return RTEMS_SUCCESSFUL;
459}
460
461static int pty_do_finalize(void)
462{
463    int ndx;
464    rtems_status_code status;
465
466    if ( !telnet_pty_inited )
467      return 0;
468
469    for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
470      if (telnet_ptys[ndx].opened) {
471          fprintf(stderr,
472            "There are still opened PTY devices, unable to proceed\n");
473          return -1;
474      }
475    }
476    if (RTEMS_SUCCESSFUL != rtems_io_unregister_driver(pty_major)) {
477        fprintf(stderr,"Unable to remove this driver\n");
478        return -1;
479    }
480    for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
481        /* rtems_io_register_name() actually creates a node in the filesystem
482         * (mknod())
483         */
484        status = (rtems_status_code)unlink(telnet_ptys[ndx].devname);
485        if (status != RTEMS_SUCCESSFUL)
486          perror("removing pty device node from file system");
487        else
488          free(telnet_ptys[ndx].devname);
489    };
490
491    free ( telnet_ptys );
492
493    fprintf(stderr,"PTY driver unloaded successfully\n");
494    telnet_pty_inited=FALSE;
495    return 0;
496}
497
498/*
499 *  Open entry point
500 */
501
502static
503rtems_device_driver my_pty_open(
504  rtems_device_major_number major,
505  rtems_device_minor_number minor,
506  void                    * arg
507)
508{
509  rtems_status_code sc;
510  sc = rtems_termios_open(major,minor,arg,pty_get_termios_handlers(FALSE));
511  return sc;
512}
513
514/*
515 *  Close entry point
516 */
517
518static
519rtems_device_driver my_pty_close(
520  rtems_device_major_number major,
521  rtems_device_minor_number minor,
522  void                    * arg
523)
524{
525  return rtems_termios_close(arg);
526}
527
528/*
529 * read bytes from the pty
530 */
531
532static
533rtems_device_driver my_pty_read(
534  rtems_device_major_number major,
535  rtems_device_minor_number minor,
536  void                    * arg
537)
538{
539  return rtems_termios_read(arg);
540}
541
542/*
543 * write bytes to the pty
544 */
545
546static
547rtems_device_driver my_pty_write(
548  rtems_device_major_number major,
549  rtems_device_minor_number minor,
550  void                    * arg
551)
552{
553  return rtems_termios_write(arg);
554}
555
556/*
557 *  IO Control entry point
558 */
559
560static
561rtems_device_driver my_pty_control(
562  rtems_device_major_number major,
563  rtems_device_minor_number minor,
564  void                    * arg
565)
566{
567  rtems_libio_ioctl_args_t *args = (rtems_libio_ioctl_args_t*)arg;
568  struct winsize           *wp = (struct winsize*)args->buffer;
569  pty_t                    *p = &telnet_ptys[minor];
570
571  switch (args->command) {
572
573    case TIOCGWINSZ:
574
575      wp->ws_row = p->height;
576      wp->ws_col = p->width;
577      args->ioctl_return=0;
578#if DEBUG & DEBUG_WH
579      fprintf(stderr,
580          "ioctl(TIOCGWINSZ), returning %ix%i\n",
581          wp->ws_col,
582          wp->ws_row);
583#endif
584
585      return RTEMS_SUCCESSFUL;
586
587    case TIOCSWINSZ:
588#if DEBUG & DEBUG_WH
589      fprintf(stderr,
590          "ioctl(TIOCGWINSZ), setting %ix%i\n",
591          wp->ws_col,
592          wp->ws_row);
593#endif
594
595      p->height = wp->ws_row;
596      p->width  = wp->ws_col;
597      args->ioctl_return=0;
598
599      return RTEMS_SUCCESSFUL;
600
601    default:
602
603     break;
604  }
605
606  return rtems_termios_ioctl(arg);
607}
608
609static rtems_driver_address_table drvPty = {
610    my_pty_initialize,
611    my_pty_open,
612    my_pty_close,
613    my_pty_read,
614    my_pty_write,
615    my_pty_control
616};
617
618/*-----------------------------------------------------------*/
619static const rtems_termios_callbacks pty_poll_callbacks = {
620  ptyPollInitialize,  /* FirstOpen */
621  ptyShutdown,        /* LastClose */
622  ptyPollRead,        /* PollRead  */
623  ptyPollWrite,       /* Write */
624  ptySetAttributes,   /* setAttributes */
625  NULL,               /* stopRemoteTX */
626  NULL,               /* StartRemoteTX */
627  0                   /* outputUsesInterrupts */
628};
629/*-----------------------------------------------------------*/
630static const rtems_termios_callbacks * pty_get_termios_handlers(int polled) {
631  return &pty_poll_callbacks;
632}
633/*-----------------------------------------------------------*/
634
635static int pty_do_initialize(void)
636{
637  if ( !telnet_pty_inited ) {
638    if (RTEMS_SUCCESSFUL==rtems_io_register_driver(0, &drvPty, &pty_major))
639      telnet_pty_inited=TRUE;
640    else
641      fprintf(stderr,"WARNING: registering the PTY driver FAILED\n");
642  }
643  return telnet_pty_inited;
644}
645
646int telnet_pty_initialize(void)
647{
648  return pty_do_initialize();
649}
650
651int telnet_pty_finalize(void)
652{
653  return pty_do_finalize();
654}
Note: See TracBrowser for help on using the repository browser.