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

4.104.114.95
Last change on this file since 8ad6681b was 8ad6681b, checked in by Joel Sherrill <joel.sherrill@…>, on 09/25/07 at 17:14:01

2007-09-25 Joel Sherrill <joel.sherrill@…>

  • telnetd/README, telnetd/pty.c, telnetd/pty.h, telnetd/telnetd.c, telnetd/telnetd.h: telnetd rewrite.
  • telnetd/check_passwd.c, telnetd/des.c, telnetd/genpw.c: New files.
  • Property mode set to 100644
File size: 17.8 KB
Line 
1/*
2 * /dev/ptyXX  (A first version for pseudo-terminals)
3 *
4 *  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 <bsp.h>
67#include <rtems/bspIo.h>
68#if 0
69#include <rtems/pty.h>
70#else
71#define MAX_PTYS 8
72#endif
73#include <errno.h>
74#include <sys/socket.h>
75#ifdef __cplusplus
76};
77#endif
78/*-----------------------------------------*/
79#include <stdio.h>
80#include <stdlib.h>
81#include <string.h>
82#include <unistd.h>
83/*-----------------------------------------*/
84#define IAC_ESC    255
85#define IAC_DONT   254
86#define IAC_DO     253
87#define IAC_WONT   252
88#define IAC_WILL   251
89#define IAC_SB     250
90#define IAC_GA     249
91#define IAC_EL     248
92#define IAC_EC     247
93#define IAC_AYT    246
94#define IAC_AO     245
95#define IAC_IP     244
96#define IAC_BRK    243
97#define IAC_DMARK  242
98#define IAC_NOP    241
99#define IAC_SE     240
100#define IAC_EOR    239
101
102#define SB_MAX          16
103
104struct pty_tt;
105typedef struct pty_tt pty_t;
106
107struct pty_tt {
108 char                     *devname;
109 struct rtems_termios_tty *ttyp;
110 tcflag_t                  c_cflag;
111 int                       opened;
112 int                       socket;
113 int                       last_cr;
114 unsigned                  iac_mode;   
115 unsigned char             sb_buf[SB_MAX];     
116 int                       sb_ind;
117 int                       width;
118 int                       height;
119};
120
121
122#ifdef __cplusplus
123
124extern "C" {
125int             printk(char*,...);
126
127#endif
128
129#if     MAX_PTYS > 5
130#undef  MAX_PTYS
131#define MAX_PTYS 5
132#endif
133
134
135static int   telnet_pty_inited=FALSE;
136static pty_t telnet_ptys[MAX_PTYS];
137
138static rtems_device_major_number pty_major;
139
140
141/* This procedure returns the devname for a pty slot free.
142 * If not slot availiable (field socket>=0)
143 *  then the socket argument is closed
144 */
145
146char *  telnet_get_pty(int socket) {
147  int ndx;
148  if (telnet_pty_inited) {
149    for (ndx=0;ndx<MAX_PTYS;ndx++) {
150      if (telnet_ptys[ndx].socket<0) {
151        struct timeval t;
152        /* set a long polling interval to save CPU time */
153        t.tv_sec=2;
154        t.tv_usec=00000;
155        setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
156        telnet_ptys[ndx].socket=socket;
157        return telnet_ptys[ndx].devname;
158      };
159    };
160  }
161  close(socket);
162  return NULL;
163}
164
165
166/*-----------------------------------------------------------*/
167/*
168 * The NVT terminal is negociated in PollRead and PollWrite
169 * with every BYTE sendded or received.
170 * A litle status machine in the pty_read_byte(int minor)
171 *
172 */
173static const char IAC_AYT_RSP[]="\r\nAYT? Yes, RTEMS-SHELL is here\r\n";
174static const char IAC_BRK_RSP[]="<*Break*>";
175static const char IAC_IP_RSP []="<*Interrupt*>";
176
177
178static
179int send_iac(int minor,unsigned char mode,unsigned char option) {
180  unsigned char buf[3];
181  buf[0]=IAC_ESC;
182  buf[1]=mode;
183  buf[2]=option;
184  return write(telnet_ptys[minor].socket,buf,sizeof(buf));
185}
186
187static int
188handleSB(pty_t *pty)
189{
190  switch (pty->sb_buf[0]) {
191    case 31:    /* NAWS */
192      pty->width  = (pty->sb_buf[1]<<8) + pty->sb_buf[2];
193      pty->height = (pty->sb_buf[3]<<8) + pty->sb_buf[4];
194#if DEBUG & DEBUG_WH
195      fprintf(stderr,
196          "Setting width/height to %ix%i\n",
197          pty->width,
198          pty->height);
199#endif
200      break;
201    default:
202      break;
203  }
204  return 0;
205}
206
207static int read_pty(int minor) { /* 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) {send_iac(minor,IAC_WILL,    1);} else /* ECHO */
328           {send_iac(minor,IAC_WONT,value);};
329           return -1;
330       default:
331           if (value==IAC_ESC) {
332              pty->iac_mode=value;
333              return -1;
334           } else {
335              result=value; 
336              if ( 0
337#if 0                                                    /* pass CRLF through - they should use termios to handle it */
338                 ||     ((value=='\n') && (pty->last_cr))
339#endif
340                /* but map telnet CRNUL to CR down here */
341                 || ((value==0) && pty->last_cr)
342                ) result=-1;
343               pty->last_cr=(value=='\r');
344               return result;
345           };
346   };
347  /* should never get here but keep compiler happy */
348  return -1;
349}
350
351/*-----------------------------------------------------------*/
352static int ptySetAttributes(int minor,const struct termios *t);
353static int ptyPollInitialize(int major,int minor,void * arg) ;
354static int ptyShutdown(int major,int minor,void * arg) ;
355static int ptyPollWrite(int minor, const char * buf,int len) ;
356static int ptyPollRead(int minor) ;
357static const rtems_termios_callbacks * pty_get_termios_handlers(int polled) ;
358/*-----------------------------------------------------------*/
359/* Set the 'Hardware'                                        */
360/*-----------------------------------------------------------*/
361static int
362ptySetAttributes(int minor,const struct termios *t) {
363  if (minor<MAX_PTYS) {
364   telnet_ptys[minor].c_cflag=t->c_cflag;       
365  } else {
366   return -1;
367  };
368  return 0;
369}
370/*-----------------------------------------------------------*/
371static int
372ptyPollInitialize(int major,int minor,void * arg) {
373  rtems_libio_open_close_args_t * args = (rtems_libio_open_close_args_t*)arg;
374  struct termios t;
375        if (minor<MAX_PTYS) {   
376         if (telnet_ptys[minor].socket<0) return -1;           
377   telnet_ptys[minor].opened=TRUE;
378   telnet_ptys[minor].ttyp= (struct rtems_termios_tty *) args->iop->data1;
379   telnet_ptys[minor].iac_mode=0;
380   telnet_ptys[minor].sb_ind=0;
381   telnet_ptys[minor].width=0;
382   telnet_ptys[minor].height=0;
383   t.c_cflag=B9600|CS8;/* termios default */
384   return ptySetAttributes(minor,&t);
385  } else {
386   return -1;
387  };
388}
389/*-----------------------------------------------------------*/
390static int
391ptyShutdown(int major,int minor,void * arg) {
392        if (minor<MAX_PTYS) {   
393   telnet_ptys[minor].opened=FALSE;
394         if (telnet_ptys[minor].socket>=0) close(telnet_ptys[minor].socket);
395   telnet_ptys[minor].socket=-1;
396   chown(telnet_ptys[minor].devname,2,0);
397  } else {
398   return -1;
399  };
400  return 0;
401}
402/*-----------------------------------------------------------*/
403/* Write Characters into pty device                          */
404/*-----------------------------------------------------------*/
405static int
406ptyPollWrite(int minor, const char * buf,int len) {
407  int count;
408        if (minor<MAX_PTYS) {   
409         if (telnet_ptys[minor].socket<0) return -1;           
410   count=write(telnet_ptys[minor].socket,buf,len);
411  } else {
412   count=-1;
413  };
414  return count;
415}
416/*-----------------------------------------------------------*/
417static int
418ptyPollRead(int minor) {
419  int result;
420        if (minor<MAX_PTYS) {   
421         if (telnet_ptys[minor].socket<0) return -1;           
422   result=read_pty(minor);
423   return result;
424  };
425  return -1;
426}
427/*-----------------------------------------------------------*/
428/*  pty_initialize
429 *
430 *  This routine initializes the pty IO driver.
431 *
432 *  Input parameters: NONE
433 *
434 *  Output parameters:  NONE
435 *
436 *  Return values:
437 */
438/*-----------------------------------------------------------*/
439static
440rtems_device_driver my_pty_initialize(
441  rtems_device_major_number  major,
442  rtems_device_minor_number  minor,
443  void                      *arg
444)
445{
446int ndx;       
447rtems_status_code status ;
448
449  /*
450   * Set up ptys
451   */
452
453  for (ndx=0;ndx<MAX_PTYS;ndx++) {
454    telnet_ptys[ndx].devname=(char*)malloc(strlen("/dev/ptyXX")+1);
455    sprintf(telnet_ptys[ndx].devname,"/dev/pty%X",ndx);
456    telnet_ptys[ndx].ttyp=NULL;
457    telnet_ptys[ndx].c_cflag=CS8|B9600;
458    telnet_ptys[ndx].socket=-1;
459    telnet_ptys[ndx].opened=FALSE;
460    telnet_ptys[ndx].sb_ind=0;
461    telnet_ptys[ndx].width=0;
462    telnet_ptys[ndx].height=0;
463
464  };
465
466  /*
467   * Register the devices
468   */
469  for (ndx=0;ndx<MAX_PTYS;ndx++) {
470    status = rtems_io_register_name(telnet_ptys[ndx].devname, major, ndx);
471    if (status != RTEMS_SUCCESSFUL)
472        rtems_fatal_error_occurred(status);
473    chmod(telnet_ptys[ndx].devname,0660);
474    chown(telnet_ptys[ndx].devname,2,0); /* tty,root*/
475  };
476  printk("Device: /dev/pty%X../dev/pty%X (%d)pseudo-terminals registered.\n",0,MAX_PTYS-1,MAX_PTYS);
477
478  return RTEMS_SUCCESSFUL;
479}
480
481static int pty_do_finalize()
482{
483int ndx;
484rtems_status_code status;
485
486    if ( !telnet_pty_inited )
487      return 0;
488
489    for (ndx=0;ndx<MAX_PTYS;ndx++) {
490      if (telnet_ptys[ndx].opened) {
491          fprintf(stderr,"There are still opened PTY devices, unable to proceed\n");
492          return -1;
493      }
494    }
495    if (RTEMS_SUCCESSFUL != rtems_io_unregister_driver(pty_major)) {
496        fprintf(stderr,"Unable to remove this driver\n");
497        return -1;
498    }
499    for (ndx=0;ndx<MAX_PTYS;ndx++) {
500        /* rtems_io_register_name() actually creates a node in the filesystem
501         * (mknod())
502         */
503        status = (rtems_status_code)unlink(telnet_ptys[ndx].devname);
504        if (status != RTEMS_SUCCESSFUL)
505          perror("removing pty device node from file system");
506        else
507          free(telnet_ptys[ndx].devname);
508    };
509    fprintf(stderr,"PTY driver unloaded successfully\n");
510    telnet_pty_inited=FALSE;
511    return 0;
512}
513
514/*
515 *  Open entry point
516 */
517
518static
519rtems_device_driver my_pty_open(
520  rtems_device_major_number major,
521  rtems_device_minor_number minor,
522  void                    * arg
523)
524{
525  rtems_status_code sc ;
526  sc = rtems_termios_open(major,minor,arg,pty_get_termios_handlers(FALSE));
527  return sc;
528}
529 
530/*
531 *  Close entry point
532 */
533
534static
535rtems_device_driver my_pty_close(
536  rtems_device_major_number major,
537  rtems_device_minor_number minor,
538  void                    * arg
539)
540{
541  return rtems_termios_close(arg);
542}
543
544/*
545 * read bytes from the pty
546 */
547
548static
549rtems_device_driver my_pty_read(
550  rtems_device_major_number major,
551  rtems_device_minor_number minor,
552  void                    * arg
553)
554{
555  return rtems_termios_read(arg);
556}
557
558/*
559 * write bytes to the pty
560 */
561
562static
563rtems_device_driver my_pty_write(
564  rtems_device_major_number major,
565  rtems_device_minor_number minor,
566  void                    * arg
567)
568{
569  return rtems_termios_write(arg);
570}
571
572/*
573 *  IO Control entry point
574 */
575
576static
577rtems_device_driver my_pty_control(
578  rtems_device_major_number major,
579  rtems_device_minor_number minor,
580  void                    * arg
581)
582{
583rtems_libio_ioctl_args_t *args = (rtems_libio_ioctl_args_t*)arg;
584struct winsize                   *wp = (struct winsize*)args->buffer;
585pty_t                                    *p=&telnet_ptys[minor];
586
587  switch (args->command) {
588
589    case TIOCGWINSZ:
590
591      wp->ws_row = p->height;
592      wp->ws_col = p->width;
593      args->ioctl_return=0;
594#if DEBUG & DEBUG_WH
595      fprintf(stderr,
596          "ioctl(TIOCGWINSZ), returning %ix%i\n",
597          wp->ws_col,
598          wp->ws_row);
599#endif
600
601      return RTEMS_SUCCESSFUL;
602
603    case TIOCSWINSZ:
604#if DEBUG & DEBUG_WH
605      fprintf(stderr,
606          "ioctl(TIOCGWINSZ), setting %ix%i\n",
607          wp->ws_col,
608          wp->ws_row);
609#endif
610
611      p->height = wp->ws_row;
612      p->width  = wp->ws_col;
613      args->ioctl_return=0;
614
615      return RTEMS_SUCCESSFUL;
616
617    default:
618
619        break;
620  }
621
622  return rtems_termios_ioctl(arg);
623}
624
625static rtems_driver_address_table drvPty = {
626    my_pty_initialize,
627    my_pty_open,
628    my_pty_close,
629    my_pty_read,
630    my_pty_write,
631    my_pty_control
632};
633
634/*-----------------------------------------------------------*/
635static const rtems_termios_callbacks pty_poll_callbacks = {
636  ptyPollInitialize,    /* FirstOpen*/
637  ptyShutdown,          /* LastClose*/
638  ptyPollRead,          /* PollRead  */
639  ptyPollWrite,         /* Write */
640  ptySetAttributes,     /* setAttributes */
641  NULL,                 /* stopRemoteTX */
642  NULL,                 /* StartRemoteTX */
643  0                     /* outputUsesInterrupts */
644};
645/*-----------------------------------------------------------*/
646static const rtems_termios_callbacks * pty_get_termios_handlers(int polled) {
647  return &pty_poll_callbacks;
648}
649/*-----------------------------------------------------------*/
650
651static int pty_do_initialize()
652{
653  if ( !telnet_pty_inited ) {
654    if (RTEMS_SUCCESSFUL==rtems_io_register_driver(0, &drvPty, &pty_major))
655      telnet_pty_inited=TRUE;
656    else
657      fprintf(stderr,"WARNING: registering the PTY driver FAILED\n");
658  }
659  return telnet_pty_inited;
660}
661
662#ifdef __cplusplus
663
664class TelnetPtyIni {
665public:
666  TelnetPtyIni() { if (!nest++) {
667            pty_do_initialize();
668        }
669       };
670  ~TelnetPtyIni(){ if (!--nest) {
671            pty_do_finalize();
672        }
673       };
674private:
675static int nest;
676};
677
678static TelnetPtyIni onlyInst;
679int TelnetPtyIni::nest=0;
680
681int telnet_pty_initialize()
682{
683  return telnet_pty_inited;
684}
685
686int telnet_pty_finalize()
687{
688  return telnet_pty_inited;
689}
690};
691#else
692int telnet_pty_initialize()
693{
694  return pty_do_initialize();
695}
696
697int telnet_pty_finalize()
698{
699  return pty_do_finalize();
700}
701#endif
Note: See TracBrowser for help on using the repository browser.