telnetd: Convert pty driver to new Termios API

Update #3526.
This commit is contained in:
Sebastian Huber
2018-09-25 15:07:35 +02:00
parent ac9f80874f
commit b980f36306
3 changed files with 145 additions and 456 deletions

View File

@@ -16,58 +16,25 @@
extern "C" {
#endif
#include <rtems.h>
#include <rtems/termiostypes.h>
/* Number of ptys to setup */
extern size_t rtems_pty_maximum_ptys;
#define RTEMS_PTY_SB_MAX 16
/* Return the devname for a free pty slot.
* If no slot available (socket>=0)
* then the socket argument is closed
*/
char * rtems_pty_get(int socket);
typedef struct {
rtems_termios_device_context base;
rtems_termios_tty *ttyp;
tcflag_t c_cflag;
int socket;
int last_cr;
unsigned iac_mode;
unsigned char sb_buf[RTEMS_PTY_SB_MAX];
int sb_ind;
int width;
int height;
char name[sizeof("/dev/pty18446744073709551615")];
} rtems_pty_context;
/* OBSOLETE */
#define get_pty rtems_pty_get
rtems_device_driver pty_initialize(
rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg);
rtems_device_driver pty_open(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg);
rtems_device_driver pty_close(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg);
rtems_device_driver pty_read(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg);
rtems_device_driver pty_write(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg);
rtems_device_driver pty_control(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg);
#define PTY_DRIVER_TABLE_ENTRY \
{ pty_initialize , pty_open , pty_close , \
pty_read , pty_write , pty_control }
/* Internal functions */
int telnet_pty_initialize(void);
int telnet_pty_finalize(void);
char *telnet_get_pty(int);
char *telnet_get_pty(rtems_pty_context *ctx, int socket);
#ifdef __cplusplus
}

View File

@@ -29,16 +29,13 @@
/* #define DEBUG DEBUG_WH */
/*-----------------------------------------*/
#include <termios.h>
#include <rtems/termiostypes.h>
#include <sys/ttycom.h>
#include <rtems.h>
#include <rtems/libio.h>
#include <rtems/bspIo.h>
#include <rtems/pty.h>
#include <rtems/seterr.h>
#include <errno.h>
#include <sys/socket.h>
/*-----------------------------------------*/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -63,42 +60,38 @@
#define IAC_SE 240
#define IAC_EOR 239
#define SB_MAX 16
#define SB_MAX RTEMS_PTY_SB_MAX
extern int rtems_telnetd_maximum_ptys;
static bool ptyPollInitialize(rtems_termios_tty *,
rtems_termios_device_context *, struct termios *,
rtems_libio_open_close_args_t *);
static void ptyShutdown(rtems_termios_tty *,
rtems_termios_device_context *, rtems_libio_open_close_args_t *);
static void ptyPollWrite(rtems_termios_device_context *, const char *, size_t);
static int ptyPollRead(rtems_termios_device_context *);
static bool ptySetAttributes(rtems_termios_device_context *,
const struct termios *);
static int my_pty_control(rtems_termios_device_context *,
ioctl_command_t, void *);
struct pty_tt;
typedef struct pty_tt pty_t;
struct pty_tt {
char devname[17];
struct rtems_termios_tty *ttyp;
tcflag_t c_cflag;
int opened;
int socket;
int last_cr;
unsigned iac_mode;
unsigned char sb_buf[SB_MAX];
int sb_ind;
int width;
int height;
static const rtems_termios_device_handler pty_handler = {
.first_open = ptyPollInitialize,
.last_close = ptyShutdown,
.poll_read = ptyPollRead,
.write = ptyPollWrite,
.set_attributes = ptySetAttributes,
.ioctl = my_pty_control
};
static int telnet_pty_inited=FALSE;
static pty_t *telnet_ptys;
static rtems_device_major_number pty_major;
static
int send_iac(int minor,unsigned char mode,unsigned char option)
int send_iac(rtems_pty_context *pty, unsigned char mode, unsigned char option)
{
unsigned char buf[3];
buf[0]=IAC_ESC;
buf[1]=mode;
buf[2]=option;
return write(telnet_ptys[minor].socket,buf,sizeof(buf));
return write(pty->socket, buf, sizeof(buf));
}
/* This procedure returns the devname for a pty slot free.
@@ -106,42 +99,31 @@ int send_iac(int minor,unsigned char mode,unsigned char option)
* then the socket argument is closed
*/
char * telnet_get_pty(int socket)
char *telnet_get_pty(rtems_pty_context *pty, int socket)
{
int ndx;
rtems_status_code sc;
struct timeval t;
if (telnet_pty_inited) {
#if 0
if ( rtems_telnetd_maximum_ptys < 5 )
rtems_telnetd_maximum_ptys = 5;
telnet_ptys = malloc( rtems_telnetd_maximum_ptys * sizeof (pty_t) );
#endif
if ( !telnet_ptys ) {
return NULL;
}
for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
if (telnet_ptys[ndx].socket<0) {
struct timeval t;
/* set a long polling interval to save CPU time */
t.tv_sec=2;
t.tv_usec=00000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
telnet_ptys[ndx].socket=socket;
/* inform the client that we will echo */
send_iac(ndx, IAC_WILL, 1);
return telnet_ptys[ndx].devname;
};
};
memset(pty, 0, sizeof(*pty));
snprintf(pty->name, sizeof(pty->name), "/dev/pty%" PRIuPTR, (uintptr_t)pty);
rtems_termios_device_context_initialize(&pty->base, "pty");
pty->socket = socket;
sc = rtems_termios_device_install(pty->name, &pty_handler, NULL, &pty->base);
if (sc != RTEMS_SUCCESSFUL) {
close(socket);
return NULL;
}
close(socket);
return NULL;
}
/* set a long polling interval to save CPU time */
t.tv_sec=2;
t.tv_usec=00000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
/* inform the client that we will echo */
send_iac(pty, IAC_WILL, 1);
return pty->name;
}
/*-----------------------------------------------------------*/
/*
@@ -155,7 +137,7 @@ static const char IAC_BRK_RSP[]="<*Break*>";
static const char IAC_IP_RSP []="<*Interrupt*>";
static int
handleSB(pty_t *pty)
handleSB(rtems_pty_context *pty)
{
switch (pty->sb_buf[0]) {
case 31: /* NAWS */
@@ -174,13 +156,13 @@ handleSB(pty_t *pty)
return 0;
}
static int read_pty(int minor)
static int ptyPollRead(rtems_termios_device_context *base)
{ /* Characters written to the client side*/
rtems_pty_context *pty = (rtems_pty_context *)base;
unsigned char value;
unsigned int omod;
int count;
int result;
pty_t *pty=telnet_ptys+minor;
count=read(pty->socket,&value,sizeof(value));
if (count<0)
@@ -272,33 +254,33 @@ static int read_pty(int minor)
case IAC_WILL:
if (value==34){
send_iac(minor,IAC_DONT, 34); /*LINEMODE*/
send_iac(minor,IAC_DO , 1); /*ECHO */
send_iac(pty,IAC_DONT, 34); /*LINEMODE*/
send_iac(pty,IAC_DO , 1); /*ECHO */
} else if (value==31) {
send_iac(minor,IAC_DO , 31); /*NAWS */
send_iac(pty,IAC_DO , 31); /*NAWS */
#if DEBUG & DEBUG_DETAIL
printk("replied DO NAWS\n");
#endif
} else {
send_iac(minor,IAC_DONT,value);
send_iac(pty,IAC_DONT,value);
}
return -1;
case IAC_DONT:
return -1;
case IAC_DO :
if (value==3) {
send_iac(minor,IAC_WILL, 3); /* GO AHEAD*/
send_iac(pty,IAC_WILL, 3); /* GO AHEAD*/
} else if (value==1) {
send_iac(minor,IAC_WILL, 1); /* ECHO */
send_iac(pty,IAC_WILL, 1); /* ECHO */
} else {
send_iac(minor,IAC_WONT,value);
send_iac(pty,IAC_WONT,value);
};
return -1;
case IAC_WONT:
if (value==1) {
send_iac(minor,IAC_WILL, 1);
send_iac(pty,IAC_WILL, 1);
} else { /* ECHO */
send_iac(minor,IAC_WONT,value);
send_iac(pty,IAC_WONT,value);
}
return -1;
default:
@@ -321,277 +303,75 @@ static int read_pty(int minor)
return -1;
}
/*-----------------------------------------------------------*/
static int ptySetAttributes(int minor,const struct termios *t);
static int ptyPollInitialize(int major,int minor,void * arg) ;
static int ptyShutdown(int major,int minor,void * arg) ;
static ssize_t ptyPollWrite(int minor, const char * buf, size_t len) ;
static int ptyPollRead(int minor) ;
static const rtems_termios_callbacks * pty_get_termios_handlers(int polled) ;
/*-----------------------------------------------------------*/
/* Set the 'Hardware' */
/*-----------------------------------------------------------*/
static int
ptySetAttributes(int minor,const struct termios *t) {
if (minor<rtems_telnetd_maximum_ptys) {
telnet_ptys[minor].c_cflag=t->c_cflag;
} else {
return -1;
};
return 0;
static bool
ptySetAttributes(rtems_termios_device_context *base, const struct termios *t)
{
rtems_pty_context *pty = (rtems_pty_context *)base;
pty->c_cflag = t->c_cflag;
return true;
}
/*-----------------------------------------------------------*/
static int
ptyPollInitialize(int major,int minor,void * arg) {
rtems_libio_open_close_args_t * args = (rtems_libio_open_close_args_t*)arg;
struct termios t;
if (minor<rtems_telnetd_maximum_ptys) {
if (telnet_ptys[minor].socket<0) return -1;
telnet_ptys[minor].opened=TRUE;
telnet_ptys[minor].ttyp= (struct rtems_termios_tty *) args->iop->data1;
telnet_ptys[minor].iac_mode=0;
telnet_ptys[minor].sb_ind=0;
telnet_ptys[minor].width=0;
telnet_ptys[minor].height=0;
t.c_cflag=B9600|CS8;/* termios default */
return ptySetAttributes(minor,&t);
} else {
return -1;
}
static bool
ptyPollInitialize(rtems_termios_tty *ttyp,
rtems_termios_device_context *base, struct termios *t,
rtems_libio_open_close_args_t *args)
{
rtems_pty_context *pty = (rtems_pty_context *)base;
pty->ttyp = ttyp;
pty->iac_mode = 0;
pty->sb_ind = 0;
pty->width = 0;
pty->height = 0;
return ptySetAttributes(&pty->base, t);
}
/*-----------------------------------------------------------*/
static int
ptyShutdown(int major,int minor,void * arg) {
if (minor<rtems_telnetd_maximum_ptys) {
telnet_ptys[minor].opened=FALSE;
if (telnet_ptys[minor].socket>=0) close(telnet_ptys[minor].socket);
telnet_ptys[minor].socket=-1;
chown(telnet_ptys[minor].devname,2,0);
} else {
return -1;
}
return 0;
static void
ptyShutdown(rtems_termios_tty *ttyp,
rtems_termios_device_context *base, rtems_libio_open_close_args_t *arg)
{
rtems_pty_context *pty = (rtems_pty_context *)base;
close(pty->socket);
}
/*-----------------------------------------------------------*/
/* Write Characters into pty device */
/*-----------------------------------------------------------*/
static ssize_t
ptyPollWrite(int minor, const char * buf, size_t len) {
size_t count;
if (minor<rtems_telnetd_maximum_ptys) {
if (telnet_ptys[minor].socket<0)
return -1;
count=write(telnet_ptys[minor].socket,buf,len);
} else {
count=-1;
static void
ptyPollWrite(rtems_termios_device_context *base, const char *buf, size_t len)
{
rtems_pty_context *pty = (rtems_pty_context *)base;
while (len > 0) {
ssize_t n = write(pty->socket, buf, len);
if (n <= 0) {
break;
}
buf += (size_t)n;
len -= (size_t)n;
}
return count;
}
/*-----------------------------------------------------------*/
static int
ptyPollRead(int minor) {
int result;
if (minor<rtems_telnetd_maximum_ptys) {
if (telnet_ptys[minor].socket<0)
return -1;
result=read_pty(minor);
return result;
}
return -1;
}
/*-----------------------------------------------------------*/
/* pty_initialize
*
* This routine initializes the pty IO driver.
*
* Input parameters: NONE
*
* Output parameters: NONE
*
* Return values:
*/
/*-----------------------------------------------------------*/
static
rtems_device_driver my_pty_initialize(
rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg
)
my_pty_control(rtems_termios_device_context *base,
ioctl_command_t request, void *buffer)
{
int ndx;
rtems_status_code status;
if ( rtems_telnetd_maximum_ptys < 5 )
rtems_telnetd_maximum_ptys = 5;
telnet_ptys = malloc( rtems_telnetd_maximum_ptys * sizeof (pty_t) );
/*
* Set up ptys
*/
for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
/* devname is included in the structure */
sprintf(telnet_ptys[ndx].devname,"/dev/pty%02X",ndx);
telnet_ptys[ndx].ttyp = NULL;
telnet_ptys[ndx].c_cflag = CS8|B9600;
telnet_ptys[ndx].socket = -1;
telnet_ptys[ndx].opened = FALSE;
telnet_ptys[ndx].sb_ind = 0;
telnet_ptys[ndx].width = 0;
telnet_ptys[ndx].height = 0;
}
/*
* Register the devices
*/
for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
status = rtems_io_register_name(telnet_ptys[ndx].devname, major, ndx);
if (status != RTEMS_SUCCESSFUL)
rtems_fatal_error_occurred(status);
chmod(telnet_ptys[ndx].devname,0660);
chown(telnet_ptys[ndx].devname,2,0); /* tty,root*/
};
syslog(
LOG_KERN | LOG_INFO,
"/dev/pty%X../dev/pty%X (%d) pseudo-terminals registered.\n",
0,
rtems_telnetd_maximum_ptys - 1,
rtems_telnetd_maximum_ptys
);
return RTEMS_SUCCESSFUL;
}
static int pty_do_finalize(void)
{
int ndx;
rtems_status_code status;
if ( !telnet_pty_inited )
return 0;
for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
if (telnet_ptys[ndx].opened) {
fprintf(stderr,
"There are still opened PTY devices, unable to proceed\n");
return -1;
}
}
if (RTEMS_SUCCESSFUL != rtems_io_unregister_driver(pty_major)) {
fprintf(stderr,"Unable to remove this driver\n");
return -1;
}
for (ndx=0;ndx<rtems_telnetd_maximum_ptys;ndx++) {
/* rtems_io_register_name() actually creates a node in the filesystem
* (mknod())
*/
status = (rtems_status_code)unlink(telnet_ptys[ndx].devname);
if (status != RTEMS_SUCCESSFUL)
perror("removing pty device node from file system");
else {
telnet_ptys[ndx].devname[0] = '\0';
}
};
free ( telnet_ptys );
fprintf(stderr,"PTY driver unloaded successfully\n");
telnet_pty_inited=FALSE;
return 0;
}
/*
* Open entry point
*/
static
rtems_device_driver my_pty_open(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
rtems_status_code sc;
sc = rtems_termios_open(major,minor,arg,pty_get_termios_handlers(FALSE));
return sc;
}
/*
* Close entry point
*/
static
rtems_device_driver my_pty_close(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
return rtems_termios_close(arg);
}
/*
* read bytes from the pty
*/
static
rtems_device_driver my_pty_read(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
return rtems_termios_read(arg);
}
/*
* write bytes to the pty
*/
static
rtems_device_driver my_pty_write(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
return rtems_termios_write(arg);
}
/*
* IO Control entry point
*/
static
rtems_device_driver my_pty_control(
rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
rtems_libio_ioctl_args_t *args = (rtems_libio_ioctl_args_t*)arg;
struct winsize *wp = (struct winsize*)args->buffer;
pty_t *p = &telnet_ptys[minor];
switch (args->command) {
rtems_pty_context *p = (rtems_pty_context *)base;
struct winsize *wp = buffer;
switch (request) {
case TIOCGWINSZ:
wp->ws_row = p->height;
wp->ws_col = p->width;
args->ioctl_return=0;
#if DEBUG & DEBUG_WH
fprintf(stderr,
"ioctl(TIOCGWINSZ), returning %ix%i\n",
wp->ws_col,
wp->ws_row);
#endif
return RTEMS_SUCCESSFUL;
break;
case TIOCSWINSZ:
#if DEBUG & DEBUG_WH
fprintf(stderr,
@@ -602,61 +382,11 @@ rtems_device_driver my_pty_control(
p->height = wp->ws_row;
p->width = wp->ws_col;
args->ioctl_return=0;
return RTEMS_SUCCESSFUL;
break;
default:
break;
rtems_set_errno_and_return_minus_one(EINVAL);
break;
}
return rtems_termios_ioctl(arg);
}
static rtems_driver_address_table drvPty = {
my_pty_initialize,
my_pty_open,
my_pty_close,
my_pty_read,
my_pty_write,
my_pty_control
};
/*-----------------------------------------------------------*/
static const rtems_termios_callbacks pty_poll_callbacks = {
ptyPollInitialize, /* FirstOpen */
ptyShutdown, /* LastClose */
ptyPollRead, /* PollRead */
ptyPollWrite, /* Write */
ptySetAttributes, /* setAttributes */
NULL, /* stopRemoteTX */
NULL, /* StartRemoteTX */
0 /* outputUsesInterrupts */
};
/*-----------------------------------------------------------*/
static const rtems_termios_callbacks * pty_get_termios_handlers(int polled) {
return &pty_poll_callbacks;
}
/*-----------------------------------------------------------*/
static int pty_do_initialize(void)
{
if ( !telnet_pty_inited ) {
if (RTEMS_SUCCESSFUL==rtems_io_register_driver(0, &drvPty, &pty_major))
telnet_pty_inited=TRUE;
else
fprintf(stderr,"WARNING: registering the PTY driver FAILED\n");
}
return telnet_pty_inited;
}
int telnet_pty_initialize(void)
{
return pty_do_initialize();
}
int telnet_pty_finalize(void)
{
return pty_do_finalize();
return 0;
}

View File

@@ -64,14 +64,11 @@
#define PARANOIA
extern char *telnet_get_pty(int socket);
extern int telnet_pty_initialize(void);
struct shell_args {
char *devname;
void *arg;
char peername[16];
char delete_myself;
rtems_pty_context pty;
void *arg;
char peername[16];
char delete_myself;
};
typedef union uni_sa {
@@ -107,17 +104,23 @@ static const rtems_id (*telnetd_spawn_task)(
void *
) = telnetd_dflt_spawn;
static char *grab_a_Connection(
static struct shell_args *grab_a_Connection(
int des_socket,
uni_sa *srv,
char *peername,
int sz
)
{
char *rval = 0;
struct shell_args *args = NULL;
socklen_t size_adr = sizeof(srv->sin);
int acp_sock;
args = malloc(sizeof(*args));
if (args == NULL) {
perror("telnetd:malloc");
goto bailout;
}
acp_sock = accept(des_socket,&srv->sa,&size_adr);
if (acp_sock<0) {
@@ -125,7 +128,7 @@ static char *grab_a_Connection(
goto bailout;
};
if ( !(rval=telnet_get_pty(acp_sock)) ) {
if (telnet_get_pty(&args->pty, acp_sock) == NULL) {
syslog( LOG_DAEMON | LOG_ERR, "telnetd: unable to obtain PTY");
/* NOTE: failing 'do_get_pty()' closed the socket */
goto bailout;
@@ -138,12 +141,12 @@ static char *grab_a_Connection(
syslog(LOG_DAEMON | LOG_INFO,
"telnetd: accepted connection from %s on %s",
peername,
rval);
args->pty.name);
#endif
bailout:
return rval;
return args;
}
@@ -160,6 +163,7 @@ static void release_a_Connection(char *devname, char *peername, FILE **pstd, int
while (--n>=0)
if (pstd[n]) fclose(pstd[n]);
unlink(devname);
}
static int sockpeername(int sock, char *buf, int bufsz)
@@ -186,7 +190,6 @@ rtems_task_telnetd(void *task_argument)
{
int des_socket;
uni_sa srv;
char *devname;
char peername[16];
int i=1;
int size_adr;
@@ -243,22 +246,19 @@ rtems_task_telnetd(void *task_argument)
);
}
} else {
devname = grab_a_Connection(des_socket, &srv, peername, sizeof(peername));
arg = grab_a_Connection(des_socket, &srv, peername, sizeof(peername));
if ( !devname ) {
if (arg == NULL) {
/* if something went wrong, sleep for some time */
sleep(10);
continue;
}
arg = malloc( sizeof(*arg) );
arg->devname = devname;
arg->arg = telnetd_config->arg;
strncpy(arg->peername, peername, sizeof(arg->peername));
telnetd_task_id = telnetd_spawn_task(
devname,
arg->pty.name,
telnetd_config->priority,
telnetd_config->stack_size,
spawned_shell,
@@ -278,9 +278,9 @@ rtems_task_telnetd(void *task_argument)
* a stream...
*/
if ( !(dummy=fopen(devname,"r+")) )
if ( !(dummy=fopen(arg->pty.name,"r+")) )
perror("Unable to dummy open the pty, losing a slot :-(");
release_a_Connection(devname, peername, &dummy, 1);
release_a_Connection(arg->pty.name, peername, &dummy, 1);
free(arg);
sleep(2); /* don't accept connections too fast */
}
@@ -315,14 +315,6 @@ rtems_status_code rtems_telnetd_start(const rtems_telnetd_config_table* config)
return RTEMS_NO_MEMORY;
}
if ( !telnet_pty_initialize() ) {
fprintf(stderr, "telnetd cannot initialize PTY driver\n");
free(telnetd_config);
telnetd_config = NULL;
return RTEMS_IO_ERROR;
}
*telnetd_config = *config;
/* Check priority */
@@ -396,7 +388,7 @@ spawned_shell(void *targ)
/* redirect stdio */
for (i=0; i<3; i++) {
if ( !(nstd[i]=fopen(arg->devname,"r+")) ) {
if ( !(nstd[i]=fopen(arg->pty.name,"r+")) ) {
perror("unable to open stdio");
goto cleanup;
}
@@ -418,13 +410,13 @@ spawned_shell(void *targ)
start = rtems_shell_login_prompt(
stdin,
stderr,
arg->devname,
arg->pty.name,
telnetd_config->login_check
);
login_failed = !start;
}
if (start) {
telnetd_config->command( arg->devname, arg->arg);
telnetd_config->command( arg->pty.name, arg->arg);
}
stdin = ostd[0];
@@ -440,7 +432,7 @@ spawned_shell(void *targ)
}
cleanup:
release_a_Connection(arg->devname, arg->peername, nstd, i);
release_a_Connection(arg->pty.name, arg->peername, nstd, i);
free(arg);
}