source: rtems/cpukit/telnetd/pty.c @ b7fa289

4.104.114.95
Last change on this file since b7fa289 was b7fa289, checked in by Joel Sherrill <joel.sherrill@…>, on 10/11/07 at 12:49:27

2007-10-11 Joel Sherrill <joel.sherrill@…>

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