source: rtems/cpukit/telnetd/pty.c @ 9b4422a2

4.115
Last change on this file since 9b4422a2 was 9b4422a2, checked in by Joel Sherrill <joel.sherrill@…>, on 05/03/12 at 15:09:24

Remove All CVS Id Strings Possible Using a Script

Script does what is expected and tries to do it as
smartly as possible.

+ remove occurrences of two blank comment lines

next to each other after Id string line removed.

+ remove entire comment blocks which only exited to

contain CVS Ids

+ If the processing left a blank line at the top of

a file, it was removed.

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