13 Commits

Author SHA1 Message Date
Kim Woelders cf6b334bcc ttynvt version 0.14 2019-03-21 08:52:05 +01:00
Kim Woelders 09e96e72f7 Add some more debug 2019-03-21 08:46:08 +01:00
Kim Woelders 688fc53caf Proper flow-control handling
Patch by FRJ.
2019-03-21 08:45:36 +01:00
Kim Woelders 4d1d6978e4 Don't clear readable flag on poll
When the slave becomes readable we set the pollin flag, and cleared it
in ttynvt_poll().
So if ttynvt_poll() is called twice, the readable flag was lost.

Now we clear it in ttynvt_read() if there actually are no more data to
be read.
2019-01-16 11:17:18 +01:00
Kim Woelders 8f03aabe30 Add timestamp when logging to stdout 2018-10-31 12:09:37 +01:00
Kim Woelders 3cad2c060d ttynvt version 0.13 2018-10-25 11:24:45 +02:00
Kim Woelders 89f8ec8018 Reorder MCR debug printouts
More logical, IMO...
2018-10-25 11:19:46 +02:00
Kim Woelders de0845a9ce Consistently use DCD, not CD 2018-10-25 11:18:45 +02:00
Kim Woelders 732eb8da25 Refactor termios parsing for rfc2217 and debug
And show the termios on TCGETS too.
2018-10-25 11:18:45 +02:00
Kim Woelders 0f95e0779e Avoid close error on FD_MASTER after DCD drop 2018-10-25 11:18:45 +02:00
Kim Woelders 0cc9b58a65 Close master pty on DCD drop if not CLOCAL 2018-10-25 11:18:24 +02:00
Kim Woelders 314e4b1b99 Flushing ioctl fixups
- Propagate TCFLSH to slave.
- Actually send flush requests to server.
- TCIFLUSH, TCOFLUSH, and TCIOFLUSH are not ioctls but TCFLSH arguments.
- Re-enable read polls if input is flushed.
2018-10-25 07:27:21 +02:00
Kim Woelders effca015a8 Fix running on big endian
RFC2217 options with one byte values were always sent with value = 0.
2018-10-25 07:27:21 +02:00
4 changed files with 223 additions and 115 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
m4_define([pkg_version], [0.12])
m4_define([pkg_version], [0.14])
#m4_define([pkg_revision], [005])
m4_define([pkg_version], m4_ifdef([pkg_revision], [pkg_version.pkg_revision], [pkg_version]))
+2 -2
View File
@@ -42,10 +42,10 @@ extern void telnet_rfc2217_handle_opt(tn_ctx_t * tcc, int opt,
extern void telnet_rfc2217_cfg(tn_ctx_t * tcc, int cmd,
const void *val, int nd);
extern void telnet_rfc2217_ctl(tn_ctx_t * tcc, int cmd);
extern void telnet_rfc2217_ctl(tn_ctx_t * tcc, unsigned int val);
/*RFC2217 modem state*/
#define TNS_STATE_CD 0x80
#define TNS_STATE_DCD 0x80
#define TNS_STATE_RI 0x40
#define TNS_STATE_DSR 0x20
#define TNS_STATE_CTS 0x10
+4 -2
View File
@@ -43,10 +43,12 @@ void telnet_rfc2217_cfg(tn_ctx_t * tcc, int cmd, const void *val, int nd)
telnet_set_opt(tcc, TNO_CPCO, cmd, val, nd);
}
void telnet_rfc2217_ctl(tn_ctx_t * tcc, int cmd)
void telnet_rfc2217_ctl(tn_ctx_t * tcc, unsigned int val)
{
unsigned char byte = val;
if (!tcc->mode_rfc2217)
return;
telnet_set_opt(tcc, TNO_CPCO, TNS_SET_CONTROL, &cmd, 1);
telnet_set_opt(tcc, TNO_CPCO, TNS_SET_CONTROL, &byte, 1);
}
+216 -110
View File
@@ -24,6 +24,7 @@
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include "telnet.h"
@@ -61,7 +62,9 @@ typedef struct {
struct fuse_pollhandle *ph;
volatile char n_fds;
volatile int rel_pending;
volatile int slave_suspended; /*waiting for application to read data */
volatile int master_suspended; /*server is not ready to receive data */
struct pollfd fds[3];
char net_buf[NET_BUF_SIZE];
@@ -69,10 +72,12 @@ typedef struct {
struct termios tio;
int cmcr;
volatile int smcr;
int cmcr; /* Commanded by ioctl */
volatile int smcr; /* Status from modem */
int smcr_last;
volatile int pollin;
volatile int pollout;
volatile int error;
int epipe;
@@ -82,18 +87,40 @@ typedef struct {
#include <sys/syscall.h>
#define gettid() (pid_t)syscall(__NR_gettid)
static const char *_hms_txt(char *buf, unsigned int len)
{
struct timeval tv;
unsigned int sod, hh, mm, ss;
gettimeofday(&tv, NULL);
sod = tv.tv_sec % (24 * 3600);
hh = sod / 3600;
mm = (sod - hh * 3600) / 60;
ss = sod - hh * 3600 - mm * 60;
snprintf(buf, len, "%02d:%02d:%02d.%06d",
hh, mm, ss, (unsigned int)tv.tv_usec);
return buf;
}
_PRF2_ static void _log(int prio, const char *fmt, ...)
{
va_list arg;
char buf[256];
char buf[256], buft[64];
va_start(arg, fmt);
vsnprintf(buf, sizeof(buf), fmt, arg);
if (ttynvt_param.logstd)
printf("[%d] %d: %s", gettid(), prio, buf);
{
printf("%s: [%d] %d: %s",
_hms_txt(buft, sizeof(buft)), gettid(), prio, buf);
}
else
{
syslog(prio, "[%d] %s", gettid(), buf);
}
va_end(arg);
}
@@ -131,6 +158,12 @@ void _log_buf(int prio, const char *txt, const void *ptr_, unsigned int len)
#define DBG2_BUF(txt, ptr, len) \
if (ttynvt_param.debug > 1) _log_buf(LOG_DEBUG, txt, ptr, len)
static void _fd_close(int fd)
{
if (fd >= 0)
close(fd);
}
static volatile char _is_interrupted = 0;
static void sig_handler(int sig)
@@ -182,10 +215,10 @@ static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
_log(LOG_INFO, "connection closed\n");
tty->n_fds = 0;
close(tty->fds[FD_NET].fd);
close(tty->fds[FD_MASTER].fd);
close(tty->fds[FD_SLAVE].fd);
tty->rel_pending = 1;
_fd_close(tty->fds[FD_NET].fd);
_fd_close(tty->fds[FD_MASTER].fd);
_fd_close(tty->fds[FD_SLAVE].fd);
pthread_cancel(tty->ptid_poll);
pthread_join(tty->ptid_poll, NULL);
@@ -223,6 +256,22 @@ static void _update_notify(ttynvt_t * tty, struct fuse_pollhandle *ph)
fuse_pollhandle_destroy(tmp_ph);
}
static int _net_read_check_exit(ttynvt_t * tty)
{
if ((tty->tio.c_cflag & CLOCAL) == 0 &&
(tty->smcr_last & TIOCM_CD) != 0 && (tty->smcr & TIOCM_CD) == 0)
{
/* CLOCAL is not set (i.e. do not ignore modem control lines) AND
* DCD dropped */
DBG("GOT DCD DROP\n");
close(tty->fds[FD_MASTER].fd);
tty->fds[FD_MASTER].fd = -1;
return 1;
}
return 0;
}
static void *_read_net(void *arg)
{
ttynvt_t *tty = (ttynvt_t *) arg;
@@ -232,22 +281,26 @@ static void *_read_net(void *arg)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
DBG2("%s: net_cnt=%d\n", __func__, tty->net_cnt);
while (tty->n_fds > 0)
while (!tty->rel_pending)
{
tty->fds[FD_NET].revents = tty->fds[FD_MASTER].revents =
tty->fds[FD_SLAVE].revents = 0;
res = poll(tty->fds, tty->n_fds, -1);
DBG2("%s: res=%d N=%d events-N/M/S=%#x/%#x/%#x\n", __func__,
res, tty->n_fds,
tty->fds[FD_NET].revents, tty->fds[FD_MASTER].revents,
tty->fds[FD_SLAVE].revents);
tty->fds[FD_SLAVE].events = tty->slave_suspended ? POLLPRI : POLLIN;
tty->fds[FD_MASTER].events = tty->master_suspended ? POLLPRI : POLLIN;
res = poll(tty->fds, 3, -1);
DBG2("%s: res=%d events-N/M/S=%x:%x/%x:%x/%x:%x SS=%d MS=%d\n",
__func__, res, tty->fds[FD_NET].events, tty->fds[FD_NET].revents,
tty->fds[FD_MASTER].events, tty->fds[FD_MASTER].revents,
tty->fds[FD_SLAVE].events, tty->fds[FD_SLAVE].revents,
tty->slave_suspended, tty->master_suspended);
if (res < 0)
{
if (errno == EINTR)
continue;
break;
}
if (tty->n_fds == 0)
if (tty->rel_pending)
break;
if (tty->fds[FD_NET].revents & POLLIN)
@@ -280,16 +333,24 @@ static void *_read_net(void *arg)
}
tty->net_cnt = 0;
}
if (_net_read_check_exit(tty))
break;
}
if (tty->fds[FD_MASTER].revents & POLLIN)
if ((tty->fds[FD_MASTER].revents & POLLIN) && !tty->master_suspended)
{
res = read(tty->fds[FD_MASTER].fd, buf, TMP_BUF_SIZE);
DBG("%s: read res=%d pollout=%d\n", __func__, res, tty->pollout);
if (res < 0)
{
_log(LOG_ERR, "master read error: %m\n");
break;
}
if (tty->pollout == 0)
{
tty->pollout = 1;
_notify(tty);
}
DBG2_BUF("TtyM in ", buf, res);
telnet_tx(tty->tn, buf, res);
}
@@ -297,7 +358,7 @@ static void *_read_net(void *arg)
if (tty->fds[FD_SLAVE].revents & POLLIN)
{
tty->pollin = 1;
tty->n_fds = 2; /* Disable polling of Slave */
tty->slave_suspended = 1; /* Disable polling of Slave */
_notify(tty);
}
}
@@ -404,21 +465,25 @@ static void _modem_status_cb(void *cctx, int status)
ttynvt_t *tty = cctx;
int mcr = 0;
DBG("MCR: DSR=%c CTS=%c RI=%c CD=%c\n",
status & TNS_STATE_DSR ? '1' : '0',
status & TNS_STATE_CTS ? '1' : '0',
status & TNS_STATE_RI ? '1' : '0', status & TNS_STATE_CD ? '1' : '0');
DBG("MCR: CTS=%c DSR=%c RI=%c DCD=%c\n",
status & TNS_STATE_CTS ? '1' : '0', status & TNS_STATE_DSR ? '1' : '0',
status & TNS_STATE_RI ? '1' : '0', status & TNS_STATE_DCD ? '1' : '0');
if (status & TNS_STATE_CD)
if (status & TNS_STATE_DCD)
mcr |= TIOCM_CD;
if (status & TNS_STATE_RI)
mcr |= TIOCM_RI;
if (status & TNS_STATE_CTS)
mcr |= TIOCM_CTS;
if (status & TNS_STATE_DSR)
mcr |= TIOCM_DSR;
if (status & TNS_STATE_CTS)
mcr |= TIOCM_CTS;
tty->master_suspended = (mcr & TIOCM_CTS) ? 0 : 1;
DBG("%s: tty=%p mcr=%x MS=%d\n", __func__, tty, mcr,
tty->master_suspended);
pthread_mutex_lock(&tty->tty_lock);
tty->smcr_last = tty->smcr;
tty->smcr = mcr;
pthread_mutex_unlock(&tty->tty_lock);
}
@@ -449,6 +514,8 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
for (n = 0; n < 3; n++)
tty->fds[n].fd = -1;
tty->pollout = 1;
tty->tn = telnet_ctx_init(tty, _srv_write, _srv_read, _modem_status_cb);
if (!tty->tn)
{
@@ -517,8 +584,6 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
goto open_err;
}
tty->n_fds = 3; /* Initially poll Net, Master, and Slave */
info->fh = (uintptr_t) tty;
info->nonseekable = 1;
info->direct_io = 1;
@@ -536,8 +601,7 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
ttynvt_param.host, ttynvt_param.port);
for (n = 0; n < 3; n++)
if (tty->fds[n].fd >= 0)
close(tty->fds[n].fd);
_fd_close(tty->fds[n].fd);
free(tty->tn);
free(tty);
@@ -550,7 +614,7 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
char buf[TMP_BUF_SIZE];
int res;
int res, nr;
DBG2("%s\n", __func__);
@@ -577,8 +641,18 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
res = _is_interrupted ? -2 : read(tty->fds[FD_SLAVE].fd, buf, size);
_is_interrupted = 0;
tty->ptid_read = 0;
tty->n_fds = 3; /* Enable polling of Slave */
tty->slave_suspended = 0; /* Enable polling of Slave */
fuse_req_interrupt_func(req, NULL, NULL);
if (res < (int)size)
{
tty->pollin = 0;
}
else
{
nr = 0;
ioctl(tty->fds[FD_SLAVE].fd, FIONREAD, &nr);
tty->pollin = nr > 0;
}
if (res < 0)
{
DBG2("%s: error: %m\n", __func__);
@@ -623,6 +697,10 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
fuse_reply_err(req, errno);
return;
}
else if (res != (int)size)
{
tty->pollout = 0;
}
fuse_reply_write(req, res);
}
@@ -665,9 +743,6 @@ static const char *_ioctl_name(int cmd)
CASE(TIOCINQ);
CASE(TIOCOUTQ);
CASE(TCFLSH);
CASE(TCIFLUSH);
CASE(TCOFLUSH);
CASE(TCIOFLUSH);
CASE(TIOCMGET);
CASE(TIOCMBIS);
CASE(TIOCMBIC);
@@ -690,6 +765,71 @@ static const char *_ioctl_name(int cmd)
}
}
static unsigned int _tio_parity(const struct termios *tio)
{
unsigned int par;
if (tio->c_cflag & PARENB && tio->c_iflag & IGNPAR)
par = 1;
#ifdef CMSPAR
else if (tio->c_cflag & PARENB && tio->c_iflag & CMSPAR)
{
if (tio->c_cflag & PARODD)
par = 4;
else
par = 5;
}
#endif
else if (tio->c_cflag & PARENB)
par = 3;
else
par = 1;
return par;
}
static unsigned int _tio_baud(const struct termios *tio)
{
return baudrate(tio->c_cflag & (CBAUD | CBAUDEX));
}
static unsigned int _tio_csize(const struct termios *tio)
{
unsigned int csize;
if ((tio->c_cflag & CSIZE) == CS5)
csize = 5;
else if ((tio->c_cflag & CSIZE) == CS6)
csize = 6;
else if ((tio->c_cflag & CSIZE) == CS7)
csize = 7;
else
csize = 8;
return csize;
}
static unsigned int _tio_stopb(const struct termios *tio)
{
unsigned int stopb;
if (tio->c_cflag & CSTOPB)
stopb = 2;
else
stopb = 1;
return stopb;
}
static void _show_termios(const char *txt, const struct termios *tio)
{
DBG("%s: %u%c%u%u %cCTSRTS %cCREAD %cHUPCL %cCLOCAL\n", txt,
_tio_baud(tio), pstr[_tio_parity(tio)], _tio_csize(tio),
_tio_stopb(tio),
tio->c_cflag & CRTSCTS ? '+' : '-', tio->c_cflag & CREAD ? '+' : '-',
tio->c_cflag & HUPCL ? '+' : '-', tio->c_cflag & CLOCAL ? '+' : '-');
}
#define CHECK_BUF_IN(sz) \
if (in_bufsz < sz) do { in_bufsz = sz; goto retry_in; } while(0)
#define CHECK_BUF_OUT(sz) \
@@ -707,6 +847,8 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
int mcr;
int val;
int res;
unsigned char byte;
unsigned int byte4;
DBG("%s: cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__,
_ioctl_name(cmd), arg, in_buf,
@@ -719,47 +861,19 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
case TCSETS:
CHECK_BUF_IN(sizeof(struct termios));
{
speed_t speed;
int csize, par, sb;
memcpy(tio, in_buf, in_bufsz);
speed = tio->c_cflag & (CBAUD | CBAUDEX);
val = htonl(baudrate(speed));
telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &val, 4);
byte4 = htonl(_tio_baud(tio));
telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &byte4, 4);
if ((tio->c_cflag & CSIZE) == CS5)
csize = 5;
else if ((tio->c_cflag & CSIZE) == CS6)
csize = 6;
else if ((tio->c_cflag & CSIZE) == CS7)
csize = 7;
else
csize = 8;
telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &csize, 1);
byte = _tio_csize(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &byte, 1);
if (tio->c_cflag & PARENB && tio->c_iflag & IGNPAR)
par = 1;
#ifdef CMSPAR
else if (tio->c_cflag & PARENB && tio->c_iflag & CMSPAR)
{
if (tio->c_cflag & PARODD)
par = 4;
else
par = 5;
}
#endif
else if (tio->c_cflag & PARENB)
par = 3;
else
par = 1;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PARITY, &par, 1);
byte = _tio_parity(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_PARITY, &byte, 1);
if (tio->c_cflag & CSTOPB)
sb = 2;
else
sb = 1;
telnet_rfc2217_cfg(tty->tn, TNS_SET_STOPSIZE, &sb, 1);
byte = _tio_stopb(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_STOPSIZE, &byte, 1);
if (tio->c_cflag & CRTSCTS)
{
@@ -767,26 +881,25 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
telnet_rfc2217_ctl(tty->tn, TNS_CTL_CTSRTS);
}
if (!(tty->cmcr & TIOCM_DTR) && speed != B0)
if (!(tty->cmcr & TIOCM_DTR) && _tio_baud(tio) != 0)
{
tty->cmcr |= TIOCM_DTR;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_ON);
}
DBG("termios: %u%c%u%u %cCTSRTS %cCREAD %cHUPCL %cCLOCAL\n",
baudrate(speed), pstr[par], csize, sb,
tio->c_cflag & CRTSCTS ? '+' : '-',
tio->c_cflag & CREAD ? '+' : '-',
tio->c_cflag & HUPCL ? '+' : '-',
tio->c_cflag & CLOCAL ? '+' : '-');
_show_termios("TCSETS", tio);
ioctl(tty->fds[FD_SLAVE].fd, cmd, tio);
if (cmd == TCSETSF)
tty->slave_suspended = 0; /* Re-enable polling of Slave */
fuse_reply_ioctl(req, 0, 0, 0);
}
break;
case TCGETS:
CHECK_BUF_OUT(sizeof(struct termios));
ioctl(tty->fds[FD_SLAVE].fd, TCGETS, tio);
_show_termios("TCGETS", tio);
fuse_reply_ioctl(req, 0, tio, sizeof(struct termios));
break;
case TIOCINQ: /* Get the number of bytes in the input
@@ -802,33 +915,25 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
fuse_reply_ioctl(req, 0, &val, sizeof(int));
break;
case TCFLSH:
if (arg == TCIFLUSH)
switch ((size_t) arg)
{
/*todo */
default:
case TCIFLUSH:
byte = 1;
tty->slave_suspended = 0; /* Re-enable polling of Slave */
break;
case TCOFLUSH:
byte = 2;
tty->pollout = 1;
break;
case TCIOFLUSH:
byte = 3;
tty->slave_suspended = 0; /* Re-enable polling of Slave */
tty->pollout = 1;
break;
}
else if ((size_t) arg == TCOFLUSH)
{
/*todo */
}
else if ((size_t) arg == TCIOFLUSH)
{
/*todo */
}
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TCIFLUSH:
val = 1;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &val, 1);
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TCOFLUSH:
val = 2;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &val, 1);
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TCIOFLUSH:
val = 3;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &val, 1);
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &byte, 1);
ioctl(tty->fds[FD_SLAVE].fd, TCFLSH, arg);
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TIOCMGET:
@@ -883,9 +988,9 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
TNS_CTL_RTS_ON : TNS_CTL_RTS_OFF);
goto show_mctl;
show_mctl:
DBG("MCR: DTR=%c RTS=%c\n",
tty->cmcr & TIOCM_DTR ? '1' : '0',
tty->cmcr & TIOCM_RTS ? '1' : '0');
DBG("MCR: RTS=%c DTR=%c\n",
tty->cmcr & TIOCM_RTS ? '1' : '0',
tty->cmcr & TIOCM_DTR ? '1' : '0');
break;
case TCSBRK:
fuse_reply_ioctl(req, 0, 0, 0);
@@ -974,18 +1079,19 @@ ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
struct fuse_pollhandle *ph)
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
int revents;
int revents = 0;
DBG2("%s: tty->pollin=%d tty->error/epipe=%d/%d\n",
__func__, tty->pollin, tty->error, tty->epipe);
_update_notify(tty, ph);
revents = POLLOUT;
if (tty->pollout)
{
revents = POLLOUT;
}
if (tty->pollin)
{
tty->pollin = 0;
revents |= POLLIN;
}
if (tty->error)