support for line disciplines (TTY, PPP)

ttynvt now uses pseudoterminal (pty) to be able to get use of
the tty line disciplines.
Currently only the line discipline: TTY and PPP are supported.
This commit is contained in:
Frank Rolsted Jensen
2018-07-24 13:57:05 +02:00
parent 3cae838cf0
commit 7f4d2f3a51
2 changed files with 294 additions and 198 deletions
+27 -5
View File
@@ -1,3 +1,20 @@
ttynvt makes a virtual serial device (tty) and connects
the device to a Network Virtual Terminal (NVT).
+-ttynvt-+------------------------+
| |\ |
| | +---------------+ |
| | | |
| | | |
/dev/ttyNVT | pty /dev/ppp pty |
| | slave | master |
| | | | | |
--------------------------------------------------
| | | | | |
| | | | | |
+--FUSE--+ +-tty--ldisc------+ TCP--->host
1 Build environment
The gcc compiler is used by default (see Makefile).
libfuse-dev - Filesystem in Userspace (development) must be installed
@@ -9,8 +26,12 @@
libpthread and libfuse must be installed in the root filesystem.
3 Build
The default target in the Makefile builds the Network Virtual Terminal.
Output name is: ttynvt.
$ autoreconf -vif
$ ./configure ...
$ make
The default target in the Makefile builds the Network Virtual Terminal.
Output name is: ttynvt.
4 Run
The application (ttynvt) installs a virtual serial device in the /dev
@@ -20,7 +41,7 @@
is started (command-line options).
E.g.
./ttynvt -M 199 -m 6 -n ttyNVT0 -p 5020 -s 169.254.1.1
./ttynvt -M 199 -m 6 -n ttyNVT0 -s 169.254.1.1:5020
To get a list of all command-line options:
./ttynvt --help
@@ -28,7 +49,7 @@
5. Test
The serial server: ser2net (http://ser2net.sourceforge.net/) might be used
to test the virtual serial server on the target.
Install the server (ser2net) and add folling line to the configuration
Install the server (ser2net) and add following line to the configuration
file (/etc/ser2net.conf):
5020:telnet:0:/dev/ttyS0:115200 remctl NONE 1STOPBIT 8DATABITS RTSCTS HANGUP_WHEN_DONE
At the server, plug-in a loopback connector in the serial port (ttyS0).
@@ -37,7 +58,8 @@
minicom -D /dev/ttyNVT0
6 Diagnostic
Add the command-line option '-l' to enable logging of diagnostic information.
Add the command-line option '-D <level>' to enable logging of diagnostic information.
The log is forwarded to syslog with the debug level.
Wireshark is also very useful when debugging the interface between the client
and the server.
+267 -193
View File
@@ -19,6 +19,7 @@
#include <unistd.h>
#include <asm/termbits.h>
#include <linux/sockios.h>
#include <linux/ppp-ioctl.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
@@ -27,7 +28,12 @@
#include "telnet.h"
#define RX_BUF_SIZE 1024
#define NET_BUF_SIZE 1024
#define TMP_BUF_SIZE 1024
#define FD_NET 0
#define FD_SLAVE 1
#define FD_MASTER 2
#define _PRF_N_(no) __attribute__((__format__(__printf__, (no), (no) + 1)))
#define _PRF2_ _PRF_N_(2)
@@ -47,28 +53,24 @@ static struct ttynvt_param {
} ttynvt_param;
typedef struct {
int nonblock;
pthread_mutex_t tty_lock;
pthread_mutex_t poll_lock;
pthread_t thread_id;
pthread_cond_t cond;
struct fuse_pollhandle *ph;
struct pollfd fds[1];
struct pollfd fds[3];
struct termios tio;
unsigned char mcr;
char excl;
char buf[RX_BUF_SIZE];
size_t blen;
size_t rxcnt;
char net_buf[NET_BUF_SIZE];
size_t net_cnt;
char tmp_buf[TMP_BUF_SIZE];
int txbuf_size;
int epipe;
volatile sig_atomic_t error;
volatile sig_atomic_t pollin;
tn_ctx_t *tn;
} ttynvt_t;
@@ -97,6 +99,28 @@ _PRF2_ static void _log(int prio, const char *fmt, ...)
if (ttynvt_param.debug > 1) _log(LOG_DEBUG, __VA_ARGS__)
static void sig_handler(int sig)
{
}
static void set_signal_handler(void)
{
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = sig_handler;
sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
sigaction(SIGUSR2, &sa, NULL);
}
static void ttynvt_interrupted(fuse_req_t req, void *data)
{
pthread_t thread = (pthread_t) data;
pthread_kill(thread, SIGUSR2);
}
static int _check_dev(void)
{
int rc;
@@ -119,13 +143,14 @@ static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
_log(LOG_INFO, "connection closed\n");
close(tty->fds[0].fd);
close(tty->fds[FD_NET].fd);
close(tty->fds[FD_MASTER].fd);
close(tty->fds[FD_SLAVE].fd);
(void)pthread_cancel(tty->thread_id);
(void)pthread_join(tty->thread_id, NULL);
(void)pthread_mutex_destroy(&tty->tty_lock);
(void)pthread_mutex_destroy(&tty->poll_lock);
(void)pthread_cond_destroy(&tty->cond);
if (tty->ph)
fuse_pollhandle_destroy(tty->ph);
@@ -145,7 +170,6 @@ static void _notify(ttynvt_t * tty)
tty->ph = NULL;
}
pthread_mutex_unlock(&tty->poll_lock);
pthread_cond_signal(&tty->cond);
}
static void _update_notify(ttynvt_t * tty, struct fuse_pollhandle *ph)
@@ -159,44 +183,16 @@ static void _update_notify(ttynvt_t * tty, struct fuse_pollhandle *ph)
fuse_pollhandle_destroy(tmp_ph);
}
static unsigned int _write_avail(ttynvt_t * tty)
{
size_t write_avail;
int used;
pthread_mutex_lock(&tty->tty_lock);
if (ioctl(tty->fds[0].fd, SIOCOUTQ, &used) < 0)
return 0;
write_avail = tty->txbuf_size - used;
pthread_mutex_unlock(&tty->tty_lock);
return write_avail;
}
static int _read_avail(ttynvt_t * tty)
{
int read_avail;
pthread_mutex_lock(&tty->tty_lock);
read_avail = tty->rxcnt;
pthread_mutex_unlock(&tty->tty_lock);
return read_avail;
}
static void *_read_net(void *arg)
{
ttynvt_t *tty = (ttynvt_t *) arg;
ssize_t res;
int tn_res = -1;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
while (1)
{
res = poll(tty->fds, 1, -1);
res = poll(tty->fds, 3, -1);
if (res < 0)
{
if (errno == EINTR)
@@ -204,48 +200,56 @@ static void *_read_net(void *arg)
else
break;
}
else if (tty->fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
else if (tty->fds[FD_NET].revents & (POLLERR | POLLHUP | POLLNVAL))
{
_log(LOG_ERR, "net io error\n");
break;
}
else if (tty->fds[0].revents & POLLIN)
else if (tty->fds[FD_NET].revents & POLLIN)
{
pthread_mutex_lock(&tty->tty_lock);
int read_avail = RX_BUF_SIZE - tty->blen;
if (read_avail == 0)
res = read(tty->fds[FD_NET].fd, tty->net_buf + tty->net_cnt,
NET_BUF_SIZE - tty->net_cnt);
if (res < 0)
{
/*buffer overflow */
tty->blen = 0;
read_avail = RX_BUF_SIZE;
_log(LOG_WARNING, "Rx buffer overflow\n");
}
res = read(tty->fds[0].fd, tty->buf + tty->blen, read_avail);
if (res <= 0)
{
pthread_mutex_unlock(&tty->tty_lock);
_log(LOG_ERR, "net read error: %s\n", strerror(errno));
break;
}
else
else if (res == 0)
{
tty->blen += res;
tn_res = telnet_rx(tty->tn, tty->buf, &tty->blen);
if (tn_res == 0)
_log(LOG_INFO, "Connection closed by remote host\n");
break;
}
tty->net_cnt += res;
res = telnet_rx(tty->tn, tty->net_buf, &tty->net_cnt);
if (tty->net_cnt > 0 && res == 0)
{
res =
write(tty->fds[FD_MASTER].fd, tty->net_buf, tty->net_cnt);
if (res < 0)
{
tty->rxcnt = tty->blen;
_notify(tty);
_log(LOG_ERR, "master write error:%s\n", strerror(errno));
break;
}
pthread_mutex_unlock(&tty->tty_lock);
tty->net_cnt = 0;
}
}
if (tty->fds[FD_MASTER].revents & POLLIN)
{
res = read(tty->fds[FD_MASTER].fd, tty->tmp_buf, TMP_BUF_SIZE);
if (res < 0)
{
_log(LOG_ERR, "master read error:%s\n", strerror(errno));
break;
}
telnet_tx(tty->tn, tty->tmp_buf, res);
}
if (tty->fds[FD_SLAVE].revents & POLLIN)
{
tty->pollin = 1;
_notify(tty);
}
}
if (res == 0)
_log(LOG_INFO, "Connection closed by remote host\n");
else
_log(LOG_ERR, "Rx read error:%s\n", strerror(errno));
tty->error = 1;
_notify(tty);
@@ -296,7 +300,7 @@ static int _srv_write(void *cctx, const void *buf, int len)
ttynvt_t *tty = cctx;
int res;
res = write(tty->fds[0].fd, buf, len);
res = write(tty->fds[FD_NET].fd, buf, len);
return res;
}
@@ -308,23 +312,20 @@ static int _srv_read(void *cctx, int timeout)
while (1)
{
res = poll(tty->fds, 1, timeout);
res = poll(&tty->fds[FD_NET], 1, timeout);
if (res <= 0)
return res;
if (tty->fds[0].revents & POLLIN)
if (tty->fds[FD_NET].revents & POLLIN)
{
res = read(tty->fds[0].fd, tty->buf + tty->blen,
RX_BUF_SIZE - tty->blen);
res = read(tty->fds[FD_NET].fd, tty->net_buf + tty->net_cnt,
NET_BUF_SIZE - tty->net_cnt);
if (res <= 0)
return res;
tty->blen += (size_t) res;
res = telnet_rx(tty->tn, tty->buf, &tty->blen);
tty->net_cnt += (size_t) res;
res = telnet_rx(tty->tn, tty->net_buf, &tty->net_cnt);
if (res == 0)
{
tty->rxcnt = tty->blen;
break;
}
}
}
return 1;
@@ -334,9 +335,14 @@ static void _modem_status_cb(void *cctx, int status)
{
ttynvt_t *tty = cctx;
tty->mcr &= ~(TIOCM_CD | TIOCM_RI | TIOCM_CTS | TIOCM_DSR);
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');
_log(LOG_INFO, "Modem status: 0x%02x\n", status);
pthread_mutex_lock(&tty->tty_lock);
tty->mcr &= ~(TIOCM_CD | TIOCM_RI | TIOCM_CTS | TIOCM_DSR);
if (status & TNS_STATE_CD)
tty->mcr |= TIOCM_CD;
@@ -346,14 +352,26 @@ static void _modem_status_cb(void *cctx, int status)
tty->mcr |= TIOCM_CTS;
if (status & TNS_STATE_DSR)
tty->mcr |= TIOCM_DSR;
pthread_mutex_unlock(&tty->tty_lock);
}
static void ttynvt_init(void *userdata, struct fuse_conn_info *conn)
{
set_signal_handler();
}
static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
{
ttynvt_t *tty;
socklen_t len;
char *slave_name;
struct termios ios;
int sockfd;
int fd;
int res;
int n;
DBG2("open: thread:%lu\n", pthread_self());
tty = calloc(1, sizeof(*tty));
if (!tty)
@@ -361,6 +379,8 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
fuse_reply_err(req, ENOMEM);
return;
}
for (n = 0; n < 3; n++)
tty->fds[n].fd = -1;
tty->tn = telnet_ctx_init(tty, _srv_write, _srv_read, _modem_status_cb);
if (!tty->tn)
@@ -375,52 +395,77 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
{
free(tty->tn);
free(tty);
fuse_reply_err(req, EFAULT);
fuse_reply_err(req, EHOSTUNREACH);
return;
}
tty->fds[0].fd = sockfd;
tty->fds[0].events = POLLIN;
tty->blen = 0;
tty->rxcnt = 0;
tty->fds[FD_NET].fd = sockfd;
tty->fds[FD_NET].events = POLLIN;
tty->net_cnt = 0;
if ((fd = open("/dev/ptmx", O_RDWR)) < 0) /* open master */
goto open_err;
tty->fds[FD_MASTER].fd = fd;
tty->fds[FD_MASTER].events = POLLIN;
ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
ios.c_oflag &= ~OPOST;
ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
ios.c_cflag &= ~(CSIZE | PARENB);
ios.c_cflag |= CS8;
if (ioctl(fd, TCSETS, &ios) < 0)
goto open_err;
grantpt(fd); /* change permission of slave */
unlockpt(fd); /* unlock slave */
slave_name = ptsname(fd); /* get name of slave */
if ((fd = open(slave_name, O_RDWR | O_NOCTTY)) < 0) /* open slave */
goto open_err;
tty->fds[FD_SLAVE].fd = fd;
tty->fds[FD_SLAVE].events = POLLIN;
res = telnet_open(tty->tn);
if (res < 0)
{
free(tty->tn);
free(tty);
fuse_reply_err(req, EBADE);
return;
errno = EPROTO;
goto open_err;
}
len = sizeof(tty->txbuf_size);
if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &tty->txbuf_size, &len) < 0)
if ((res = pthread_mutex_init(&tty->tty_lock, NULL) < 0) ||
(res = pthread_mutex_init(&tty->poll_lock, NULL) < 0) ||
(res = pthread_create(&tty->thread_id, NULL, &_read_net, tty)))
{
free(tty->tn);
free(tty);
fuse_reply_err(req, EIO);
return;
}
if (pthread_mutex_init(&tty->tty_lock, NULL) < 0 ||
pthread_mutex_init(&tty->poll_lock, NULL) < 0 ||
pthread_cond_init(&tty->cond, NULL) < 0 ||
pthread_create(&tty->thread_id, NULL, &_read_net, tty))
{
free(tty->tn);
free(tty);
fuse_reply_err(req, ENOMEM);
return;
errno = res;
goto open_err;
}
info->fh = (uintptr_t) tty;
info->nonseekable = 1;
info->direct_io = 1;
_log(LOG_INFO, "Open connection to server: %s:%d\n",
_log(LOG_INFO, "Connected to server: %s:%d\n",
ttynvt_param.host, ttynvt_param.port);
fuse_reply_open(req, info);
return;
open_err:
_log(LOG_INFO, "Connection to server: %s:%d failed:%s\n",
ttynvt_param.host, ttynvt_param.port, strerror(errno));
for (n = 0; n < 3; n++)
if (tty->fds[n].fd >= 0)
close(tty->fds[n].fd);
free(tty->tn);
free(tty);
fuse_reply_err(req, errno);
}
static void
@@ -428,14 +473,16 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
struct fuse_file_info *info)
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
ssize_t res;
if (tty->error)
{
DBG2("read (slave) in error state, thread:%lu\n", pthread_self());
if (tty->epipe == 0)
{
_log(LOG_WARNING, "Rx EPIPE\n");
tty->epipe = 1;
fuse_reply_buf(req, tty->buf, 0);
fuse_reply_buf(req, tty->tmp_buf, 0);
}
else
fuse_reply_err(req, EBADFD);
@@ -443,34 +490,31 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
return;
}
pthread_mutex_lock(&tty->tty_lock);
if (tty->rxcnt == 0 && !(info->flags & O_NONBLOCK))
if (size > TMP_BUF_SIZE)
size = TMP_BUF_SIZE;
fuse_req_interrupt_func(req, ttynvt_interrupted, (void *)pthread_self());
res = read(tty->fds[FD_SLAVE].fd, tty->tmp_buf, size);
fuse_req_interrupt_func(req, NULL, NULL);
if (res < 0)
{
pthread_cond_wait(&tty->cond, &tty->tty_lock);
DBG2("read (slave) error: %s\n", strerror(errno));
fuse_reply_err(req, errno);
return;
}
if (size > tty->rxcnt)
size = tty->rxcnt;
if (size > 0)
{
fuse_reply_buf(req, tty->buf, size);
memmove(tty->buf, tty->buf + size, tty->blen - size);
tty->blen -= size;
tty->rxcnt -= size;
}
else
{
fuse_reply_err(req, EINTR);
}
pthread_mutex_unlock(&tty->tty_lock);
DBG2("read (slave): size:%ld, thread:%lu\n", res, pthread_self());
fuse_reply_buf(req, tty->tmp_buf, res);
}
static void
ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
struct fuse_file_info *info)
{
int res;
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
ssize_t res;
if (size == 0)
{
@@ -484,30 +528,18 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
return;
}
if (tty->nonblock || (info->flags & O_NONBLOCK))
{
size_t write_avail = _write_avail(tty);
if (size > write_avail)
size = write_avail;
}
res = telnet_tx(tty->tn, data, size);
res = write(tty->fds[FD_SLAVE].fd, data, size);
if (res < 0)
{
DBG2("write (slave) error: %s\n", strerror(errno));
tty->error = 1;
fuse_reply_err(req, EPIPE);
fuse_reply_err(req, errno);
return;
}
#if 0
else if (res == 0)
{
fuse_reply_err(req, EAGAIN);
return;
}
#endif
fuse_reply_write(req, size);
DBG2("write (slave): size:%ld, thread:%lu\n", size, pthread_self());
fuse_reply_write(req, res);
}
static int baudrate(speed_t speed)
@@ -557,6 +589,7 @@ static const char *_ioctl_name(int cmd)
CASE(TIOCMSET);
CASE(TCSBRK);
CASE(TIOCGWINSZ);
CASE(TIOCSWINSZ);
/* Exclusive mode */
CASE(TIOCEXCL);
CASE(TIOCGEXCL);
@@ -564,6 +597,8 @@ static const char *_ioctl_name(int cmd)
/* Line discipline */
CASE(TIOCGETD);
CASE(TIOCSETD);
/*PPP support */
CASE(PPPIOCGCHAN);
}
}
@@ -578,12 +613,13 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
struct winsize ws;
unsigned int tiocm = 0;
static unsigned int mcr = 0;
static struct termios tio = {
.c_cflag = B115200 | CS8 | PARODD | CREAD | CLOCAL,
};
int val;
int res;
DBG("%s: cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__,
_ioctl_name(cmd), arg, in_buf,
@@ -641,19 +677,24 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
if (tio.c_cflag & CRTSCTS)
telnet_rfc2217_ctl(tty->tn, TNS_CTL_CTSRTS);
if (!(mcr & TIOCM_DTR) && speed != B0)
#if 1
pthread_mutex_lock(&tty->tty_lock);
if (!(tty->mcr & TIOCM_DTR) && speed != B0)
{
mcr |= TIOCM_DTR;
tty->mcr |= TIOCM_DTR;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_ON);
}
DBG("CFG ioctl: %u%c%u%u %cCTSRTS %cCREAD %cHUPCL %cCLOCAL\n",
pthread_mutex_unlock(&tty->tty_lock);
#endif
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 ? '+' : '-');
ioctl(tty->fds[FD_SLAVE].fd, cmd, &tio);
fuse_reply_ioctl(req, 0, 0, 0);
}
break;
@@ -664,13 +705,13 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
case TIOCINQ: /* Get the number of bytes in the input
* buffer */
CHECK_BUF_OUT(sizeof(int));
val = _read_avail(tty);
ioctl(tty->fds[FD_SLAVE].fd, TIOCINQ, &val);
fuse_reply_ioctl(req, 0, &val, sizeof(int));
break;
case TIOCOUTQ: /* Get the number of bytes in the output
* buffer */
CHECK_BUF_OUT(sizeof(int));
val = _write_avail(tty);
ioctl(tty->fds[FD_SLAVE].fd, TIOCOUTQ, &val);
fuse_reply_ioctl(req, 0, &val, sizeof(int));
break;
case TCFLSH:
@@ -691,9 +732,6 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
case TCIFLUSH:
val = 1;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &val, 1);
pthread_mutex_lock(&tty->tty_lock);
tty->blen = 0;
pthread_mutex_unlock(&tty->tty_lock);
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TCOFLUSH:
@@ -708,88 +746,128 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
break;
case TIOCMGET:
CHECK_BUF_OUT(sizeof(int));
fuse_reply_ioctl(req, 0, &mcr, sizeof(int));
pthread_mutex_lock(&tty->tty_lock);
fuse_reply_ioctl(req, 0, &tty->mcr, sizeof(int));
pthread_mutex_unlock(&tty->tty_lock);
break;
case TIOCMBIS:
CHECK_BUF_IN(sizeof(int));
tiocm = *(unsigned int *)in_buf;
fuse_reply_ioctl(req, 0, 0, 0);
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
pthread_mutex_lock(&tty->tty_lock);
if (tiocm & TIOCM_RTS)
{
mcr |= TIOCM_RTS;
tty->mcr |= TIOCM_RTS;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_RTS_ON);
}
if (tiocm & TIOCM_DTR)
{
mcr |= TIOCM_DTR;
tty->mcr |= TIOCM_DTR;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_ON);
}
pthread_mutex_unlock(&tty->tty_lock);
goto show_mctl;
case TIOCMBIC:
CHECK_BUF_IN(sizeof(int));
tiocm = *(unsigned int *)in_buf;
fuse_reply_ioctl(req, 0, 0, 0);
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
pthread_mutex_lock(&tty->tty_lock);
if (tiocm & TIOCM_RTS)
{
mcr &= ~TIOCM_RTS;
tty->mcr &= ~TIOCM_RTS;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_RTS_OFF);
}
if (tiocm & TIOCM_DTR)
{
mcr &= ~TIOCM_DTR;
tty->mcr &= ~TIOCM_DTR;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_OFF);
}
pthread_mutex_unlock(&tty->tty_lock);
goto show_mctl;
case TIOCMSET:
CHECK_BUF_IN(sizeof(int));
tiocm = *(unsigned int *)in_buf;
fuse_reply_ioctl(req, 0, 0, 0);
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
mcr &= ~(TIOCM_RTS | TIOCM_DTR);
mcr |= tiocm & (TIOCM_RTS | TIOCM_DTR);
pthread_mutex_lock(&tty->tty_lock);
tty->mcr &= ~(TIOCM_RTS | TIOCM_DTR);
tty->mcr |= tiocm & (TIOCM_RTS | TIOCM_DTR);
telnet_rfc2217_ctl(tty->tn, (tiocm & TIOCM_DTR) ?
TNS_CTL_DTR_ON : TNS_CTL_DTR_OFF);
telnet_rfc2217_ctl(tty->tn, (tiocm & TIOCM_RTS) ?
TNS_CTL_RTS_ON : TNS_CTL_RTS_OFF);
pthread_mutex_unlock(&tty->tty_lock);
goto show_mctl;
show_mctl:
DBG("CFG ioctl: DTR=%c RTS=%c\n",
mcr & TIOCM_DTR ? '1' : '0', mcr & TIOCM_RTS ? '1' : '0');
DBG("MCR: DTR=%c RTS=%c\n",
tty->mcr & TIOCM_DTR ? '1' : '0',
tty->mcr & TIOCM_RTS ? '1' : '0');
break;
case TCSBRK:
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TIOCGWINSZ:
CHECK_BUF_OUT(sizeof(struct winsize));
res = ioctl(tty->fds[FD_SLAVE].fd, TIOCGWINSZ, &ws);
if (res < 0)
{
struct winsize ws = {.ws_row = 25,.ws_col = 80 };
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
fuse_reply_err(req, errno);
return;
}
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
break;
case TIOCSWINSZ:
CHECK_BUF_IN(sizeof(struct winsize));
ws = *(struct winsize *)in_buf;
res = ioctl(tty->fds[FD_SLAVE].fd, TIOCSWINSZ, &ws);
if (res < 0)
{
fuse_reply_err(req, errno);
return;
}
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
break;
case TIOCEXCL:
if (tty->excl)
{
fuse_reply_err(req, EBUSY);
}
else
{
tty->excl = 1;
fuse_reply_ioctl(req, 0, 0, 0);
}
ioctl(tty->fds[FD_SLAVE].fd, TIOCEXCL, NULL);
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TIOCGEXCL:
CHECK_BUF_OUT(sizeof(int));
val = tty->excl;
ioctl(tty->fds[FD_SLAVE].fd, TIOCGEXCL, &val);
fuse_reply_ioctl(req, 0, &val, sizeof(int));
break;
case TIOCNXCL:
tty->excl = 0;
ioctl(tty->fds[FD_SLAVE].fd, TIOCNXCL, NULL);
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TIOCSETD:
CHECK_BUF_IN(sizeof(int));
val = *(int *)in_buf;
res = ioctl(tty->fds[FD_SLAVE].fd, TIOCSETD, &val);
if (res < 0)
fuse_reply_err(req, errno);
else
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TIOCGETD:
CHECK_BUF_OUT(sizeof(int));
res = ioctl(tty->fds[FD_SLAVE].fd, TIOCGETD, &val);
if (res < 0)
fuse_reply_err(req, errno);
else
fuse_reply_ioctl(req, 0, &val, sizeof(int));
break;
case PPPIOCGCHAN:
CHECK_BUF_OUT(sizeof(int));
res = ioctl(tty->fds[FD_SLAVE].fd, PPPIOCGCHAN, &val);
if (res < 0)
fuse_reply_err(req, errno);
else
fuse_reply_ioctl(req, 0, &val, sizeof(int));
break;
default:
fuse_reply_err(req, ENOSYS);
break;
@@ -819,12 +897,11 @@ ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
_update_notify(tty, ph);
if (_write_avail(tty) > 0)
revents |= POLLOUT;
if (_read_avail(tty) > 0)
if (tty->pollin)
{
tty->pollin = 0;
revents |= POLLIN;
}
if (tty->error)
{
if (tty->epipe == 0)
@@ -837,12 +914,9 @@ ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
}
static const struct cuse_lowlevel_ops ttynvt_op = {
.open = ttynvt_open,
.read = ttynvt_read,
.write = ttynvt_write,
.ioctl = ttynvt_ioctl,
.poll = ttynvt_poll,
.release = ttynvt_release,
.init = ttynvt_init,
.open = ttynvt_open,.read = ttynvt_read,.write = ttynvt_write,.ioctl =
ttynvt_ioctl,.poll = ttynvt_poll,.release = ttynvt_release,
};
#define TTYNET_OPT(t, p, v) { t, offsetof(struct ttynvt_param, p), v }