source: rtems-libbsd/rtemsbsd/telnetd/pty.c @ e58b898

5-freebsd-12freebsd-9.3
Last change on this file since e58b898 was e58b898, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 28, 2016 at 8:29:55 AM

telnetd: Use syslog() instead of printk()

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