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

4.104.114.95
Last change on this file since b5bf8cd1 was b5bf8cd1, checked in by Joel Sherrill <joel.sherrill@…>, on 09/24/07 at 21:35:10

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

PR 1262/filesystem

  • libcsupport/Makefile.am, libnetworking/libc/herror.c, libnetworking/libc/res_send.c, libnetworking/sys/uio.h, telnetd/Makefile.am, telnetd/README, telnetd/preinstall.am, telnetd/pty.c, telnetd/telnetd.c: Add support for readv() and writev() including documentation and test case.
  • libcsupport/src/readv.c, libcsupport/src/writev.c: New files.
  • Property mode set to 100644
File size: 16.7 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.