Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:27

0001 /**
0002  * @file
0003  * 
0004  * @brief /dev/ptyXX  (Support for pseudo-terminals)
0005  */
0006 
0007 /*
0008  * Copyright (c) 2001 Fernando Ruiz Casas <fruizcasas@gmail.com>
0009  *
0010  *  This program is distributed in the hope that it will be useful,
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0013  *
0014  *  Till Straumann <strauman@slac.stanford.edu>
0015  *
0016  *   - converted into a loadable module
0017  *   - NAWS support / ioctls for querying/setting the window
0018  *     size added.
0019  *   - don't delete the running task when the connection
0020  *     is closed. Rather let 'read()' return a 0 count so
0021  *     they may cleanup. Some magic hack works around termios
0022  *     limitation.
0023  */
0024 
0025 #ifdef HAVE_CONFIG_H
0026 #include "config.h"
0027 #endif
0028 
0029 #define DEBUG_WH    (1<<0)
0030 #define DEBUG_DETAIL  (1<<1)
0031 
0032 /* #define DEBUG DEBUG_WH */
0033 
0034 /*-----------------------------------------*/
0035 #include <sys/ttycom.h>
0036 #include <rtems/pty.h>
0037 #include <rtems/seterr.h>
0038 #include <errno.h>
0039 #include <sys/socket.h>
0040 /*-----------------------------------------*/
0041 #include <inttypes.h>
0042 #include <stdio.h>
0043 #include <stdlib.h>
0044 #include <string.h>
0045 #include <syslog.h>
0046 #include <unistd.h>
0047 /*-----------------------------------------*/
0048 #define IAC_ESC    255
0049 #define IAC_DONT   254
0050 #define IAC_DO     253
0051 #define IAC_WONT   252
0052 #define IAC_WILL   251
0053 #define IAC_SB     250
0054 #define IAC_GA     249
0055 #define IAC_EL     248
0056 #define IAC_EC     247
0057 #define IAC_AYT    246
0058 #define IAC_AO     245
0059 #define IAC_IP     244
0060 #define IAC_BRK    243
0061 #define IAC_DMARK  242
0062 #define IAC_NOP    241
0063 #define IAC_SE     240
0064 #define IAC_EOR    239
0065 
0066 #define SB_MAX     RTEMS_PTY_SB_MAX
0067 
0068 static bool ptyPollInitialize(rtems_termios_tty *,
0069   rtems_termios_device_context *, struct termios *,
0070   rtems_libio_open_close_args_t *);
0071 static void ptyShutdown(rtems_termios_tty *,
0072   rtems_termios_device_context *, rtems_libio_open_close_args_t *);
0073 static void ptyPollWrite(rtems_termios_device_context *, const char *, size_t);
0074 static int ptyPollRead(rtems_termios_device_context *);
0075 static bool ptySetAttributes(rtems_termios_device_context *,
0076   const struct termios *);
0077 static int my_pty_control(rtems_termios_device_context *,
0078   ioctl_command_t, void *);
0079 
0080 static const rtems_termios_device_handler pty_handler = {
0081   .first_open = ptyPollInitialize,
0082   .last_close = ptyShutdown,
0083   .poll_read = ptyPollRead,
0084   .write = ptyPollWrite,
0085   .set_attributes = ptySetAttributes,
0086   .ioctl = my_pty_control
0087 };
0088 
0089 static
0090 int send_iac(rtems_pty_context *pty, unsigned char mode, unsigned char option)
0091 {
0092   unsigned char buf[3];
0093 
0094   buf[0]=IAC_ESC;
0095   buf[1]=mode;
0096   buf[2]=option;
0097   return write(pty->socket, buf, sizeof(buf));
0098 }
0099 
0100 const char *rtems_pty_initialize(rtems_pty_context *pty, uintptr_t unique)
0101 {
0102   rtems_status_code sc;
0103 
0104   memset(pty, 0, sizeof(*pty));
0105   (void)snprintf(pty->name, sizeof(pty->name), "/dev/pty%" PRIuPTR, unique);
0106   rtems_termios_device_context_initialize(&pty->base, "pty");
0107   pty->socket = -1;
0108   sc = rtems_termios_device_install(pty->name, &pty_handler, NULL, &pty->base);
0109   if (sc != RTEMS_SUCCESSFUL) {
0110     return NULL;
0111   }
0112 
0113   return pty->name;
0114 }
0115 
0116 void rtems_pty_close_socket(rtems_pty_context *pty)
0117 {
0118   if (pty->socket >= 0) {
0119     (void)close(pty->socket);
0120     pty->socket = -1;
0121   }
0122 }
0123 
0124 void rtems_pty_set_socket(rtems_pty_context *pty, int socket)
0125 {
0126   struct timeval t;
0127 
0128   rtems_pty_close_socket(pty);
0129   pty->socket = socket;
0130 
0131   /* set a long polling interval to save CPU time */
0132   t.tv_sec=2;
0133   t.tv_usec=00000;
0134   (void)setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
0135 
0136   /* inform the client that we will echo */
0137   send_iac(pty, IAC_WILL, 1);
0138 }
0139 
0140 /*-----------------------------------------------------------*/
0141 /*
0142  * The NVT terminal is negociated in PollRead and PollWrite
0143  * with every BYTE sendded or received.
0144  * A litle status machine in the pty_read_byte(int minor)
0145  *
0146  */
0147 static const char IAC_AYT_RSP[]="\r\nAYT? Yes, RTEMS-SHELL is here\r\n";
0148 static const char IAC_BRK_RSP[]="<*Break*>";
0149 static const char IAC_IP_RSP []="<*Interrupt*>";
0150 
0151 static int
0152 handleSB(rtems_pty_context *pty)
0153 {
0154   switch (pty->sb_buf[0]) {
0155     case 31:  /* NAWS */
0156       pty->width  = (pty->sb_buf[1]<<8) + pty->sb_buf[2];
0157       pty->height = (pty->sb_buf[3]<<8) + pty->sb_buf[4];
0158 #if DEBUG & DEBUG_WH
0159       fprintf(stderr,
0160           "Setting width/height to %ix%i\n",
0161           pty->width,
0162           pty->height);
0163 #endif
0164       break;
0165     default:
0166       break;
0167   }
0168   return 0;
0169 }
0170 
0171 static int ptyPollRead(rtems_termios_device_context *base)
0172 { /* Characters written to the client side*/
0173    rtems_pty_context *pty = (rtems_pty_context *)base;
0174    unsigned char  value;
0175    unsigned int  omod;
0176    int      count;
0177    int      result;
0178 
0179    count=read(pty->socket,&value,sizeof(value));
0180    if (count<0)
0181     return -1;
0182 
0183    if (count<1) {
0184       /* Unfortunately, there is no way of passing an EOF
0185        * condition through the termios driver. Hence, we
0186        * resort to an ugly hack. Setting cindex>ccount
0187        * causes the termios driver to return a read count
0188        * of '0' which is what we want here. We leave
0189        * 'errno' untouched.
0190        */
0191       pty->ttyp->cindex=pty->ttyp->ccount+1;
0192       return pty->ttyp->termios.c_cc[VEOF];
0193    };
0194 
0195    omod=pty->iac_mode;
0196    pty->iac_mode=0;
0197    switch(omod & 0xff) {
0198        case IAC_ESC:
0199            switch(value) {
0200                case IAC_ESC :
0201                    /* in case this is an ESC ESC sequence in SB mode */
0202                    pty->iac_mode = omod>>8;
0203                    return IAC_ESC;
0204                case IAC_DONT:
0205                case IAC_DO  :
0206                case IAC_WONT:
0207                case IAC_WILL:
0208                    pty->iac_mode=value;
0209                    return -1;
0210                case IAC_SB  :
0211 #if DEBUG & DEBUG_DETAIL
0212                    printk("SB\n");
0213 #endif
0214                    pty->iac_mode=value;
0215                    pty->sb_ind=0;
0216                    return -100;
0217                case IAC_GA  :
0218                    return -1;
0219                case IAC_EL  :
0220                    return 0x03; /* Ctrl-C*/
0221                case IAC_EC  :
0222                    return '\b';
0223                case IAC_AYT :
0224                    write(pty->socket,IAC_AYT_RSP,strlen(IAC_AYT_RSP));
0225                    return -1;
0226                case IAC_AO  :
0227                    return -1;
0228                case IAC_IP  :
0229                    write(pty->socket,IAC_IP_RSP,strlen(IAC_IP_RSP));
0230                    return -1;
0231                case IAC_BRK :
0232                    write(pty->socket,IAC_BRK_RSP,strlen(IAC_BRK_RSP));
0233                    return -1;
0234                case IAC_DMARK:
0235                    return -2;
0236                case IAC_NOP :
0237                    return -1;
0238                case IAC_SE  :
0239 #if DEBUG & DEBUG_DETAIL
0240                   {
0241                   int i;
0242                   printk("SE");
0243                   for (i=0; i<pty->sb_ind; i++)
0244                     printk(" %02x",pty->sb_buf[i]);
0245                   printk("\n");
0246                   }
0247 #endif
0248                   handleSB(pty);
0249                return -101;
0250                case IAC_EOR :
0251                    return -102;
0252                default      :
0253                    return -1;
0254            };
0255            break;
0256 
0257        case IAC_SB:
0258            pty->iac_mode=omod;
0259            if (IAC_ESC==value) {
0260              pty->iac_mode=(omod<<8)|value;
0261            } else {
0262              if (pty->sb_ind < SB_MAX)
0263                pty->sb_buf[pty->sb_ind++]=value;
0264            }
0265            return -1;
0266 
0267        case IAC_WILL:
0268            if (value==34){
0269               send_iac(pty,IAC_DONT,   34);  /*LINEMODE*/
0270               send_iac(pty,IAC_DO  ,    1);  /*ECHO    */
0271            } else if (value==31) {
0272               send_iac(pty,IAC_DO  ,   31);  /*NAWS    */
0273 #if DEBUG & DEBUG_DETAIL
0274               printk("replied DO NAWS\n");
0275 #endif
0276            } else {
0277               send_iac(pty,IAC_DONT,value);
0278            }
0279            return -1;
0280        case IAC_DONT:
0281            return -1;
0282        case IAC_DO  :
0283            if (value==3) {
0284               send_iac(pty,IAC_WILL,    3);  /* GO AHEAD*/
0285            } else  if (value==1) {
0286               send_iac(pty,IAC_WILL,    1);  /* ECHO */
0287            } else {
0288               send_iac(pty,IAC_WONT,value);
0289            };
0290            return -1;
0291        case IAC_WONT:
0292            if (value==1) {
0293              send_iac(pty,IAC_WILL,    1);
0294            } else { /* ECHO */
0295              send_iac(pty,IAC_WONT,value);
0296            }
0297            return -1;
0298        default:
0299            if (value==IAC_ESC) {
0300               pty->iac_mode=value;
0301               return -1;
0302            } else {
0303               result=value;
0304               if ( 0
0305                 /* map CRLF to CR for symmetry */
0306                  || ((value=='\n') && pty->last_cr)
0307                 /* map telnet CRNUL to CR down here */
0308                  || ((value==0) && pty->last_cr)
0309                 ) result=-1;
0310                pty->last_cr=(value=='\r');
0311                return result;
0312            };
0313    };
0314   /* should never get here but keep compiler happy */
0315   return -1;
0316 }
0317 
0318 /*-----------------------------------------------------------*/
0319 /* Set the 'Hardware'                                        */
0320 /*-----------------------------------------------------------*/
0321 static bool
0322 ptySetAttributes(rtems_termios_device_context *base, const struct termios *t)
0323 {
0324   rtems_pty_context *pty = (rtems_pty_context *)base;
0325   pty->c_cflag = t->c_cflag;
0326   return true;
0327 }
0328 /*-----------------------------------------------------------*/
0329 static bool
0330 ptyPollInitialize(rtems_termios_tty *ttyp,
0331   rtems_termios_device_context *base, struct termios *t,
0332   rtems_libio_open_close_args_t *args)
0333 {
0334   rtems_pty_context *pty = (rtems_pty_context *)base;
0335   pty->ttyp = ttyp;
0336   pty->iac_mode = 0;
0337   pty->sb_ind = 0;
0338   pty->width = 0;
0339   pty->height = 0;
0340   return ptySetAttributes(&pty->base, t);
0341 }
0342 /*-----------------------------------------------------------*/
0343 static void
0344 ptyShutdown(rtems_termios_tty *ttyp,
0345   rtems_termios_device_context *base, rtems_libio_open_close_args_t *arg)
0346 {
0347   rtems_pty_context *pty = (rtems_pty_context *)base;
0348   close(pty->socket);
0349 }
0350 /*-----------------------------------------------------------*/
0351 /* Write Characters into pty device                          */
0352 /*-----------------------------------------------------------*/
0353 static void
0354 ptyPollWrite(rtems_termios_device_context *base, const char *buf, size_t len)
0355 {
0356   rtems_pty_context *pty = (rtems_pty_context *)base;
0357 
0358   while (len > 0) {
0359     ssize_t n = write(pty->socket, buf, len);
0360     if (n <= 0) {
0361       break;
0362     }
0363 
0364     buf += (size_t)n;
0365     len -= (size_t)n;
0366   }
0367 }
0368 
0369 static int
0370 my_pty_control(rtems_termios_device_context *base,
0371   ioctl_command_t request, void *buffer)
0372 {
0373   rtems_pty_context *p = (rtems_pty_context *)base;
0374   struct winsize *wp = buffer;
0375 
0376   switch (request) {
0377     case TIOCGWINSZ:
0378       wp->ws_row = p->height;
0379       wp->ws_col = p->width;
0380 #if DEBUG & DEBUG_WH
0381       fprintf(stderr,
0382           "ioctl(TIOCGWINSZ), returning %ix%i\n",
0383           wp->ws_col,
0384           wp->ws_row);
0385 #endif
0386       break;
0387     case TIOCSWINSZ:
0388 #if DEBUG & DEBUG_WH
0389       fprintf(stderr,
0390           "ioctl(TIOCGWINSZ), setting %ix%i\n",
0391           wp->ws_col,
0392           wp->ws_row);
0393 #endif
0394 
0395       p->height = wp->ws_row;
0396       p->width  = wp->ws_col;
0397       break;
0398     default:
0399       rtems_set_errno_and_return_minus_one(EINVAL);
0400       break;
0401   }
0402 
0403   return 0;
0404 }