source: rtems/cpukit/telnetd/pty.c @ 2aa8014

4.104.115
Last change on this file since 2aa8014 was 697cdd98, checked in by Ralf Corsepius <ralf.corsepius@…>, on 07/29/08 at 09:17:28

Add prototypes. Remove misc. C++-code pollution.

  • 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 *  $Id$
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#define DEBUG_WH    (1<<0)
29#define DEBUG_DETAIL  (1<<1)
30
31/* #define DEBUG DEBUG_WH */
32
33/*-----------------------------------------*/
34#include <termios.h>
35#include <rtems/termiostypes.h>
36#include <sys/ttycom.h>
37#include <rtems.h>
38#include <rtems/libio.h>
39#include <rtems/bspIo.h>
40#include <errno.h>
41#include <sys/socket.h>
42/*-----------------------------------------*/
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.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
93
94/* This procedure returns the devname for a pty slot free.
95 * If not slot availiable (field socket>=0)
96 *  then the socket argument is closed
97 */
98
99char *  telnet_get_pty(int socket)
100{
101  int ndx;
102
103  if (telnet_pty_inited) {
104#if 0
105    if ( rtems_telnetd_maximum_ptys < 5 )
106      rtems_telnetd_maximum_ptys = 5;
107
108    telnet_ptys = malloc( rtems_telnetd_maximum_ptys * sizeof (pty_t) );
109#endif
110    if ( !telnet_ptys ) {
111      return NULL;
112    }
113     
114    for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
115
116      if (telnet_ptys[ndx].socket<0) {
117        struct timeval t;
118        /* set a long polling interval to save CPU time */
119        t.tv_sec=2;
120        t.tv_usec=00000;
121        setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
122        telnet_ptys[ndx].socket=socket;
123        return telnet_ptys[ndx].devname;
124      };
125    };
126  }
127  close(socket);
128  return NULL;
129}
130
131
132/*-----------------------------------------------------------*/
133/*
134 * The NVT terminal is negociated in PollRead and PollWrite
135 * with every BYTE sendded or received.
136 * A litle status machine in the pty_read_byte(int minor)
137 *
138 */
139static const char IAC_AYT_RSP[]="\r\nAYT? Yes, RTEMS-SHELL is here\r\n";
140static const char IAC_BRK_RSP[]="<*Break*>";
141static const char IAC_IP_RSP []="<*Interrupt*>";
142
143static
144int send_iac(int minor,unsigned char mode,unsigned char option)
145{
146  unsigned char buf[3];
147
148  buf[0]=IAC_ESC;
149  buf[1]=mode;
150  buf[2]=option;
151  return write(telnet_ptys[minor].socket,buf,sizeof(buf));
152}
153
154static int
155handleSB(pty_t *pty)
156{
157  switch (pty->sb_buf[0]) {
158    case 31:  /* NAWS */
159      pty->width  = (pty->sb_buf[1]<<8) + pty->sb_buf[2];
160      pty->height = (pty->sb_buf[3]<<8) + pty->sb_buf[4];
161#if DEBUG & DEBUG_WH
162      fprintf(stderr,
163          "Setting width/height to %ix%i\n",
164          pty->width,
165          pty->height);
166#endif
167      break;
168    default:
169      break;
170  }
171  return 0;
172}
173
174static int read_pty(int minor)
175{ /* Characters written to the client side*/
176   unsigned char  value;
177   unsigned int  omod;
178   int      count;
179   int      result;
180   pty_t      *pty=telnet_ptys+minor;
181
182   count=read(pty->socket,&value,sizeof(value));
183   if (count<0)
184    return -1;
185
186   if (count<1) {
187      /* Unfortunately, there is no way of passing an EOF
188       * condition through the termios driver. Hence, we
189       * resort to an ugly hack. Setting cindex>ccount
190       * causes the termios driver to return a read count
191       * of '0' which is what we want here. We leave
192       * 'errno' untouched.
193       */
194      pty->ttyp->cindex=pty->ttyp->ccount+1;
195      return pty->ttyp->termios.c_cc[VEOF];
196   };
197
198   omod=pty->iac_mode;
199   pty->iac_mode=0;
200   switch(omod & 0xff) {
201       case IAC_ESC:
202           switch(value) {
203               case IAC_ESC :
204                   /* in case this is an ESC ESC sequence in SB mode */
205                   pty->iac_mode = omod>>8;
206                   return IAC_ESC;
207               case IAC_DONT:
208               case IAC_DO  :
209               case IAC_WONT:
210               case IAC_WILL:
211                   pty->iac_mode=value;
212                   return -1;
213               case IAC_SB  :
214#if DEBUG & DEBUG_DETAIL
215                   printk("SB\n");
216#endif
217                   pty->iac_mode=value;
218                   pty->sb_ind=0;
219                   return -100;
220               case IAC_GA  :
221                   return -1;
222               case IAC_EL  :
223                   return 0x03; /* Ctrl-C*/
224               case IAC_EC  :
225                   return '\b';
226               case IAC_AYT :
227                   write(pty->socket,IAC_AYT_RSP,strlen(IAC_AYT_RSP));
228                   return -1;
229               case IAC_AO  :
230                   return -1;
231               case IAC_IP  :
232                   write(pty->socket,IAC_IP_RSP,strlen(IAC_IP_RSP));
233                   return -1;
234               case IAC_BRK :
235                   write(pty->socket,IAC_BRK_RSP,strlen(IAC_BRK_RSP));
236                   return -1;
237               case IAC_DMARK:
238                   return -2;
239               case IAC_NOP :
240                   return -1;
241               case IAC_SE  :
242#if DEBUG & DEBUG_DETAIL
243                  {
244                  int i;
245                  printk("SE");
246                  for (i=0; i<pty->sb_ind; i++)
247                    printk(" %02x",pty->sb_buf[i]);
248                  printk("\n");
249                  }
250#endif
251                  handleSB(pty);
252               return -101;
253               case IAC_EOR :
254                   return -102;
255               default      :
256                   return -1;
257           };
258           break;
259
260       case IAC_SB:
261           pty->iac_mode=omod;
262           if (IAC_ESC==value) {
263             pty->iac_mode=(omod<<8)|value;
264           } else {
265             if (pty->sb_ind < SB_MAX)
266               pty->sb_buf[pty->sb_ind++]=value;
267           }
268           return -1;
269
270       case IAC_WILL:
271           if (value==34){
272              send_iac(minor,IAC_DONT,   34);  /*LINEMODE*/
273              send_iac(minor,IAC_DO  ,    1);  /*ECHO    */
274           } else if (value==31) {
275              send_iac(minor,IAC_DO  ,   31);  /*NAWS    */
276#if DEBUG & DEBUG_DETAIL
277              printk("replied DO NAWS\n");
278#endif
279           } else {
280              send_iac(minor,IAC_DONT,value);
281           }
282           return -1;
283       case IAC_DONT:
284           return -1;
285       case IAC_DO  :
286           if (value==3) {
287              send_iac(minor,IAC_WILL,    3);  /* GO AHEAD*/
288           } else  if (value==1) {                         
289              /* ECHO */
290           } else {
291              send_iac(minor,IAC_WONT,value);
292           };
293           return -1;
294       case IAC_WONT:
295           if (value==1) {
296             send_iac(minor,IAC_WILL,    1);
297           } else { /* ECHO */
298             send_iac(minor,IAC_WONT,value);
299           }
300           return -1;
301       default:
302           if (value==IAC_ESC) {
303              pty->iac_mode=value;
304              return -1;
305           } else {
306              result=value; 
307              if ( 0
308#if 0               /* pass CRLF through - they should use termios to handle it */
309                 ||  ((value=='\n') && (pty->last_cr))
310#endif
311                /* but 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 int ptyPollWrite(int minor, const char * buf,int 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 int
377ptyPollWrite(int minor, const char * buf,int len) {
378  int 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.