source: rtems/cpukit/telnetd/pty.c @ 8d52a0e2

5
Last change on this file since 8d52a0e2 was 8d52a0e2, checked in by Sebastian Huber <sebastian.huber@…>, on 04/30/18 at 09:05:36

telnetd: Use syslog() instead of printk()

Update #3419.

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