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:
@@ -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
|
1 Build environment
|
||||||
The gcc compiler is used by default (see Makefile).
|
The gcc compiler is used by default (see Makefile).
|
||||||
libfuse-dev - Filesystem in Userspace (development) must be installed
|
libfuse-dev - Filesystem in Userspace (development) must be installed
|
||||||
@@ -9,8 +26,12 @@
|
|||||||
libpthread and libfuse must be installed in the root filesystem.
|
libpthread and libfuse must be installed in the root filesystem.
|
||||||
|
|
||||||
3 Build
|
3 Build
|
||||||
The default target in the Makefile builds the Network Virtual Terminal.
|
$ autoreconf -vif
|
||||||
Output name is: ttynvt.
|
$ ./configure ...
|
||||||
|
$ make
|
||||||
|
|
||||||
|
The default target in the Makefile builds the Network Virtual Terminal.
|
||||||
|
Output name is: ttynvt.
|
||||||
|
|
||||||
4 Run
|
4 Run
|
||||||
The application (ttynvt) installs a virtual serial device in the /dev
|
The application (ttynvt) installs a virtual serial device in the /dev
|
||||||
@@ -20,7 +41,7 @@
|
|||||||
is started (command-line options).
|
is started (command-line options).
|
||||||
|
|
||||||
E.g.
|
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:
|
To get a list of all command-line options:
|
||||||
./ttynvt --help
|
./ttynvt --help
|
||||||
@@ -28,7 +49,7 @@
|
|||||||
5. Test
|
5. Test
|
||||||
The serial server: ser2net (http://ser2net.sourceforge.net/) might be used
|
The serial server: ser2net (http://ser2net.sourceforge.net/) might be used
|
||||||
to test the virtual serial server on the target.
|
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):
|
file (/etc/ser2net.conf):
|
||||||
5020:telnet:0:/dev/ttyS0:115200 remctl NONE 1STOPBIT 8DATABITS RTSCTS HANGUP_WHEN_DONE
|
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).
|
At the server, plug-in a loopback connector in the serial port (ttyS0).
|
||||||
@@ -37,7 +58,8 @@
|
|||||||
minicom -D /dev/ttyNVT0
|
minicom -D /dev/ttyNVT0
|
||||||
|
|
||||||
6 Diagnostic
|
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
|
Wireshark is also very useful when debugging the interface between the client
|
||||||
and the server.
|
and the server.
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <asm/termbits.h>
|
#include <asm/termbits.h>
|
||||||
#include <linux/sockios.h>
|
#include <linux/sockios.h>
|
||||||
|
#include <linux/ppp-ioctl.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
@@ -27,7 +28,12 @@
|
|||||||
|
|
||||||
#include "telnet.h"
|
#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 _PRF_N_(no) __attribute__((__format__(__printf__, (no), (no) + 1)))
|
||||||
#define _PRF2_ _PRF_N_(2)
|
#define _PRF2_ _PRF_N_(2)
|
||||||
@@ -47,28 +53,24 @@ static struct ttynvt_param {
|
|||||||
} ttynvt_param;
|
} ttynvt_param;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int nonblock;
|
|
||||||
pthread_mutex_t tty_lock;
|
pthread_mutex_t tty_lock;
|
||||||
pthread_mutex_t poll_lock;
|
pthread_mutex_t poll_lock;
|
||||||
pthread_t thread_id;
|
pthread_t thread_id;
|
||||||
|
|
||||||
pthread_cond_t cond;
|
|
||||||
|
|
||||||
struct fuse_pollhandle *ph;
|
struct fuse_pollhandle *ph;
|
||||||
|
|
||||||
struct pollfd fds[1];
|
struct pollfd fds[3];
|
||||||
|
|
||||||
struct termios tio;
|
struct termios tio;
|
||||||
unsigned char mcr;
|
unsigned char mcr;
|
||||||
char excl;
|
|
||||||
|
|
||||||
char buf[RX_BUF_SIZE];
|
char net_buf[NET_BUF_SIZE];
|
||||||
size_t blen;
|
size_t net_cnt;
|
||||||
size_t rxcnt;
|
char tmp_buf[TMP_BUF_SIZE];
|
||||||
|
|
||||||
int txbuf_size;
|
|
||||||
int epipe;
|
int epipe;
|
||||||
volatile sig_atomic_t error;
|
volatile sig_atomic_t error;
|
||||||
|
volatile sig_atomic_t pollin;
|
||||||
|
|
||||||
tn_ctx_t *tn;
|
tn_ctx_t *tn;
|
||||||
} ttynvt_t;
|
} 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__)
|
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)
|
static int _check_dev(void)
|
||||||
{
|
{
|
||||||
int rc;
|
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");
|
_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_cancel(tty->thread_id);
|
||||||
(void)pthread_join(tty->thread_id, NULL);
|
(void)pthread_join(tty->thread_id, NULL);
|
||||||
(void)pthread_mutex_destroy(&tty->tty_lock);
|
(void)pthread_mutex_destroy(&tty->tty_lock);
|
||||||
(void)pthread_mutex_destroy(&tty->poll_lock);
|
(void)pthread_mutex_destroy(&tty->poll_lock);
|
||||||
(void)pthread_cond_destroy(&tty->cond);
|
|
||||||
|
|
||||||
if (tty->ph)
|
if (tty->ph)
|
||||||
fuse_pollhandle_destroy(tty->ph);
|
fuse_pollhandle_destroy(tty->ph);
|
||||||
@@ -145,7 +170,6 @@ static void _notify(ttynvt_t * tty)
|
|||||||
tty->ph = NULL;
|
tty->ph = NULL;
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&tty->poll_lock);
|
pthread_mutex_unlock(&tty->poll_lock);
|
||||||
pthread_cond_signal(&tty->cond);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _update_notify(ttynvt_t * tty, struct fuse_pollhandle *ph)
|
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);
|
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)
|
static void *_read_net(void *arg)
|
||||||
{
|
{
|
||||||
ttynvt_t *tty = (ttynvt_t *) arg;
|
ttynvt_t *tty = (ttynvt_t *) arg;
|
||||||
ssize_t res;
|
ssize_t res;
|
||||||
int tn_res = -1;
|
|
||||||
|
|
||||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
res = poll(tty->fds, 1, -1);
|
res = poll(tty->fds, 3, -1);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
@@ -204,48 +200,56 @@ static void *_read_net(void *arg)
|
|||||||
else
|
else
|
||||||
break;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
else if (tty->fds[0].revents & POLLIN)
|
else if (tty->fds[FD_NET].revents & POLLIN)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&tty->tty_lock);
|
res = read(tty->fds[FD_NET].fd, tty->net_buf + tty->net_cnt,
|
||||||
int read_avail = RX_BUF_SIZE - tty->blen;
|
NET_BUF_SIZE - tty->net_cnt);
|
||||||
|
if (res < 0)
|
||||||
if (read_avail == 0)
|
|
||||||
{
|
{
|
||||||
/*buffer overflow */
|
_log(LOG_ERR, "net read error: %s\n", strerror(errno));
|
||||||
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);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else if (res == 0)
|
||||||
{
|
{
|
||||||
tty->blen += res;
|
_log(LOG_INFO, "Connection closed by remote host\n");
|
||||||
|
break;
|
||||||
tn_res = telnet_rx(tty->tn, tty->buf, &tty->blen);
|
}
|
||||||
if (tn_res == 0)
|
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;
|
_log(LOG_ERR, "master write error:%s\n", strerror(errno));
|
||||||
_notify(tty);
|
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;
|
tty->error = 1;
|
||||||
_notify(tty);
|
_notify(tty);
|
||||||
|
|
||||||
@@ -296,7 +300,7 @@ static int _srv_write(void *cctx, const void *buf, int len)
|
|||||||
ttynvt_t *tty = cctx;
|
ttynvt_t *tty = cctx;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
res = write(tty->fds[0].fd, buf, len);
|
res = write(tty->fds[FD_NET].fd, buf, len);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -308,23 +312,20 @@ static int _srv_read(void *cctx, int timeout)
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
res = poll(tty->fds, 1, timeout);
|
res = poll(&tty->fds[FD_NET], 1, timeout);
|
||||||
if (res <= 0)
|
if (res <= 0)
|
||||||
return res;
|
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,
|
res = read(tty->fds[FD_NET].fd, tty->net_buf + tty->net_cnt,
|
||||||
RX_BUF_SIZE - tty->blen);
|
NET_BUF_SIZE - tty->net_cnt);
|
||||||
if (res <= 0)
|
if (res <= 0)
|
||||||
return res;
|
return res;
|
||||||
tty->blen += (size_t) res;
|
tty->net_cnt += (size_t) res;
|
||||||
res = telnet_rx(tty->tn, tty->buf, &tty->blen);
|
res = telnet_rx(tty->tn, tty->net_buf, &tty->net_cnt);
|
||||||
if (res == 0)
|
if (res == 0)
|
||||||
{
|
|
||||||
tty->rxcnt = tty->blen;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@@ -334,9 +335,14 @@ static void _modem_status_cb(void *cctx, int status)
|
|||||||
{
|
{
|
||||||
ttynvt_t *tty = cctx;
|
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)
|
if (status & TNS_STATE_CD)
|
||||||
tty->mcr |= TIOCM_CD;
|
tty->mcr |= TIOCM_CD;
|
||||||
@@ -346,14 +352,26 @@ static void _modem_status_cb(void *cctx, int status)
|
|||||||
tty->mcr |= TIOCM_CTS;
|
tty->mcr |= TIOCM_CTS;
|
||||||
if (status & TNS_STATE_DSR)
|
if (status & TNS_STATE_DSR)
|
||||||
tty->mcr |= TIOCM_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)
|
static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
|
||||||
{
|
{
|
||||||
ttynvt_t *tty;
|
ttynvt_t *tty;
|
||||||
socklen_t len;
|
char *slave_name;
|
||||||
|
struct termios ios;
|
||||||
int sockfd;
|
int sockfd;
|
||||||
|
int fd;
|
||||||
int res;
|
int res;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
DBG2("open: thread:%lu\n", pthread_self());
|
||||||
|
|
||||||
tty = calloc(1, sizeof(*tty));
|
tty = calloc(1, sizeof(*tty));
|
||||||
if (!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);
|
fuse_reply_err(req, ENOMEM);
|
||||||
return;
|
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);
|
tty->tn = telnet_ctx_init(tty, _srv_write, _srv_read, _modem_status_cb);
|
||||||
if (!tty->tn)
|
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->tn);
|
||||||
free(tty);
|
free(tty);
|
||||||
fuse_reply_err(req, EFAULT);
|
fuse_reply_err(req, EHOSTUNREACH);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tty->fds[0].fd = sockfd;
|
tty->fds[FD_NET].fd = sockfd;
|
||||||
tty->fds[0].events = POLLIN;
|
tty->fds[FD_NET].events = POLLIN;
|
||||||
tty->blen = 0;
|
tty->net_cnt = 0;
|
||||||
tty->rxcnt = 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);
|
res = telnet_open(tty->tn);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
free(tty->tn);
|
errno = EPROTO;
|
||||||
free(tty);
|
goto open_err;
|
||||||
fuse_reply_err(req, EBADE);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
if ((res = pthread_mutex_init(&tty->tty_lock, NULL) < 0) ||
|
||||||
len = sizeof(tty->txbuf_size);
|
(res = pthread_mutex_init(&tty->poll_lock, NULL) < 0) ||
|
||||||
if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &tty->txbuf_size, &len) < 0)
|
(res = pthread_create(&tty->thread_id, NULL, &_read_net, tty)))
|
||||||
{
|
{
|
||||||
free(tty->tn);
|
errno = res;
|
||||||
free(tty);
|
goto open_err;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info->fh = (uintptr_t) tty;
|
info->fh = (uintptr_t) tty;
|
||||||
info->nonseekable = 1;
|
info->nonseekable = 1;
|
||||||
info->direct_io = 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);
|
ttynvt_param.host, ttynvt_param.port);
|
||||||
|
|
||||||
fuse_reply_open(req, info);
|
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
|
static void
|
||||||
@@ -428,14 +473,16 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
|
|||||||
struct fuse_file_info *info)
|
struct fuse_file_info *info)
|
||||||
{
|
{
|
||||||
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
||||||
|
ssize_t res;
|
||||||
|
|
||||||
if (tty->error)
|
if (tty->error)
|
||||||
{
|
{
|
||||||
|
DBG2("read (slave) in error state, thread:%lu\n", pthread_self());
|
||||||
if (tty->epipe == 0)
|
if (tty->epipe == 0)
|
||||||
{
|
{
|
||||||
_log(LOG_WARNING, "Rx EPIPE\n");
|
_log(LOG_WARNING, "Rx EPIPE\n");
|
||||||
tty->epipe = 1;
|
tty->epipe = 1;
|
||||||
fuse_reply_buf(req, tty->buf, 0);
|
fuse_reply_buf(req, tty->tmp_buf, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
fuse_reply_err(req, EBADFD);
|
fuse_reply_err(req, EBADFD);
|
||||||
@@ -443,34 +490,31 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&tty->tty_lock);
|
if (size > TMP_BUF_SIZE)
|
||||||
if (tty->rxcnt == 0 && !(info->flags & O_NONBLOCK))
|
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;
|
DBG2("read (slave): size:%ld, thread:%lu\n", res, pthread_self());
|
||||||
if (size > 0)
|
|
||||||
{
|
fuse_reply_buf(req, tty->tmp_buf, res);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
|
ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
|
||||||
struct fuse_file_info *info)
|
struct fuse_file_info *info)
|
||||||
{
|
{
|
||||||
int res;
|
|
||||||
|
|
||||||
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
||||||
|
ssize_t res;
|
||||||
|
|
||||||
|
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
{
|
{
|
||||||
@@ -484,30 +528,18 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tty->nonblock || (info->flags & O_NONBLOCK))
|
res = write(tty->fds[FD_SLAVE].fd, data, size);
|
||||||
{
|
|
||||||
size_t write_avail = _write_avail(tty);
|
|
||||||
|
|
||||||
if (size > write_avail)
|
|
||||||
size = write_avail;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = telnet_tx(tty->tn, data, size);
|
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
|
DBG2("write (slave) error: %s\n", strerror(errno));
|
||||||
tty->error = 1;
|
tty->error = 1;
|
||||||
fuse_reply_err(req, EPIPE);
|
fuse_reply_err(req, errno);
|
||||||
return;
|
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)
|
static int baudrate(speed_t speed)
|
||||||
@@ -557,6 +589,7 @@ static const char *_ioctl_name(int cmd)
|
|||||||
CASE(TIOCMSET);
|
CASE(TIOCMSET);
|
||||||
CASE(TCSBRK);
|
CASE(TCSBRK);
|
||||||
CASE(TIOCGWINSZ);
|
CASE(TIOCGWINSZ);
|
||||||
|
CASE(TIOCSWINSZ);
|
||||||
/* Exclusive mode */
|
/* Exclusive mode */
|
||||||
CASE(TIOCEXCL);
|
CASE(TIOCEXCL);
|
||||||
CASE(TIOCGEXCL);
|
CASE(TIOCGEXCL);
|
||||||
@@ -564,6 +597,8 @@ static const char *_ioctl_name(int cmd)
|
|||||||
/* Line discipline */
|
/* Line discipline */
|
||||||
CASE(TIOCGETD);
|
CASE(TIOCGETD);
|
||||||
CASE(TIOCSETD);
|
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)
|
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
|
||||||
{
|
{
|
||||||
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
||||||
|
struct winsize ws;
|
||||||
unsigned int tiocm = 0;
|
unsigned int tiocm = 0;
|
||||||
static unsigned int mcr = 0;
|
|
||||||
static struct termios tio = {
|
static struct termios tio = {
|
||||||
.c_cflag = B115200 | CS8 | PARODD | CREAD | CLOCAL,
|
.c_cflag = B115200 | CS8 | PARODD | CREAD | CLOCAL,
|
||||||
};
|
};
|
||||||
int val;
|
int val;
|
||||||
|
int res;
|
||||||
|
|
||||||
DBG("%s: cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__,
|
DBG("%s: cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__,
|
||||||
_ioctl_name(cmd), arg, in_buf,
|
_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)
|
if (tio.c_cflag & CRTSCTS)
|
||||||
telnet_rfc2217_ctl(tty->tn, TNS_CTL_CTSRTS);
|
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);
|
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_ON);
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&tty->tty_lock);
|
||||||
DBG("CFG ioctl: %u%c%u%u %cCTSRTS %cCREAD %cHUPCL %cCLOCAL\n",
|
#endif
|
||||||
|
DBG("termios: %u%c%u%u %cCTSRTS %cCREAD %cHUPCL %cCLOCAL\n",
|
||||||
baudrate(speed), pstr[par], csize, sb,
|
baudrate(speed), pstr[par], csize, sb,
|
||||||
tio.c_cflag & CRTSCTS ? '+' : '-',
|
tio.c_cflag & CRTSCTS ? '+' : '-',
|
||||||
tio.c_cflag & CREAD ? '+' : '-',
|
tio.c_cflag & CREAD ? '+' : '-',
|
||||||
tio.c_cflag & HUPCL ? '+' : '-',
|
tio.c_cflag & HUPCL ? '+' : '-',
|
||||||
tio.c_cflag & CLOCAL ? '+' : '-');
|
tio.c_cflag & CLOCAL ? '+' : '-');
|
||||||
|
|
||||||
|
ioctl(tty->fds[FD_SLAVE].fd, cmd, &tio);
|
||||||
|
|
||||||
fuse_reply_ioctl(req, 0, 0, 0);
|
fuse_reply_ioctl(req, 0, 0, 0);
|
||||||
}
|
}
|
||||||
break;
|
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
|
case TIOCINQ: /* Get the number of bytes in the input
|
||||||
* buffer */
|
* buffer */
|
||||||
CHECK_BUF_OUT(sizeof(int));
|
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));
|
fuse_reply_ioctl(req, 0, &val, sizeof(int));
|
||||||
break;
|
break;
|
||||||
case TIOCOUTQ: /* Get the number of bytes in the output
|
case TIOCOUTQ: /* Get the number of bytes in the output
|
||||||
* buffer */
|
* buffer */
|
||||||
CHECK_BUF_OUT(sizeof(int));
|
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));
|
fuse_reply_ioctl(req, 0, &val, sizeof(int));
|
||||||
break;
|
break;
|
||||||
case TCFLSH:
|
case TCFLSH:
|
||||||
@@ -691,9 +732,6 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
|||||||
case TCIFLUSH:
|
case TCIFLUSH:
|
||||||
val = 1;
|
val = 1;
|
||||||
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &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);
|
fuse_reply_ioctl(req, 0, 0, 0);
|
||||||
break;
|
break;
|
||||||
case TCOFLUSH:
|
case TCOFLUSH:
|
||||||
@@ -708,88 +746,128 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
|||||||
break;
|
break;
|
||||||
case TIOCMGET:
|
case TIOCMGET:
|
||||||
CHECK_BUF_OUT(sizeof(int));
|
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;
|
break;
|
||||||
case TIOCMBIS:
|
case TIOCMBIS:
|
||||||
CHECK_BUF_IN(sizeof(int));
|
CHECK_BUF_IN(sizeof(int));
|
||||||
tiocm = *(unsigned int *)in_buf;
|
tiocm = *(unsigned int *)in_buf;
|
||||||
fuse_reply_ioctl(req, 0, 0, 0);
|
fuse_reply_ioctl(req, 0, 0, 0);
|
||||||
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
|
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
|
||||||
|
pthread_mutex_lock(&tty->tty_lock);
|
||||||
if (tiocm & TIOCM_RTS)
|
if (tiocm & TIOCM_RTS)
|
||||||
{
|
{
|
||||||
mcr |= TIOCM_RTS;
|
tty->mcr |= TIOCM_RTS;
|
||||||
telnet_rfc2217_ctl(tty->tn, TNS_CTL_RTS_ON);
|
telnet_rfc2217_ctl(tty->tn, TNS_CTL_RTS_ON);
|
||||||
}
|
}
|
||||||
if (tiocm & TIOCM_DTR)
|
if (tiocm & TIOCM_DTR)
|
||||||
{
|
{
|
||||||
mcr |= TIOCM_DTR;
|
tty->mcr |= TIOCM_DTR;
|
||||||
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_ON);
|
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_ON);
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&tty->tty_lock);
|
||||||
goto show_mctl;
|
goto show_mctl;
|
||||||
case TIOCMBIC:
|
case TIOCMBIC:
|
||||||
CHECK_BUF_IN(sizeof(int));
|
CHECK_BUF_IN(sizeof(int));
|
||||||
tiocm = *(unsigned int *)in_buf;
|
tiocm = *(unsigned int *)in_buf;
|
||||||
fuse_reply_ioctl(req, 0, 0, 0);
|
fuse_reply_ioctl(req, 0, 0, 0);
|
||||||
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
|
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
|
||||||
|
pthread_mutex_lock(&tty->tty_lock);
|
||||||
if (tiocm & TIOCM_RTS)
|
if (tiocm & TIOCM_RTS)
|
||||||
{
|
{
|
||||||
mcr &= ~TIOCM_RTS;
|
tty->mcr &= ~TIOCM_RTS;
|
||||||
telnet_rfc2217_ctl(tty->tn, TNS_CTL_RTS_OFF);
|
telnet_rfc2217_ctl(tty->tn, TNS_CTL_RTS_OFF);
|
||||||
}
|
}
|
||||||
if (tiocm & TIOCM_DTR)
|
if (tiocm & TIOCM_DTR)
|
||||||
{
|
{
|
||||||
mcr &= ~TIOCM_DTR;
|
tty->mcr &= ~TIOCM_DTR;
|
||||||
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_OFF);
|
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_OFF);
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&tty->tty_lock);
|
||||||
goto show_mctl;
|
goto show_mctl;
|
||||||
case TIOCMSET:
|
case TIOCMSET:
|
||||||
CHECK_BUF_IN(sizeof(int));
|
CHECK_BUF_IN(sizeof(int));
|
||||||
tiocm = *(unsigned int *)in_buf;
|
tiocm = *(unsigned int *)in_buf;
|
||||||
fuse_reply_ioctl(req, 0, 0, 0);
|
fuse_reply_ioctl(req, 0, 0, 0);
|
||||||
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
|
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
|
||||||
mcr &= ~(TIOCM_RTS | TIOCM_DTR);
|
pthread_mutex_lock(&tty->tty_lock);
|
||||||
mcr |= tiocm & (TIOCM_RTS | TIOCM_DTR);
|
tty->mcr &= ~(TIOCM_RTS | TIOCM_DTR);
|
||||||
|
tty->mcr |= tiocm & (TIOCM_RTS | TIOCM_DTR);
|
||||||
telnet_rfc2217_ctl(tty->tn, (tiocm & TIOCM_DTR) ?
|
telnet_rfc2217_ctl(tty->tn, (tiocm & TIOCM_DTR) ?
|
||||||
TNS_CTL_DTR_ON : TNS_CTL_DTR_OFF);
|
TNS_CTL_DTR_ON : TNS_CTL_DTR_OFF);
|
||||||
telnet_rfc2217_ctl(tty->tn, (tiocm & TIOCM_RTS) ?
|
telnet_rfc2217_ctl(tty->tn, (tiocm & TIOCM_RTS) ?
|
||||||
TNS_CTL_RTS_ON : TNS_CTL_RTS_OFF);
|
TNS_CTL_RTS_ON : TNS_CTL_RTS_OFF);
|
||||||
|
pthread_mutex_unlock(&tty->tty_lock);
|
||||||
goto show_mctl;
|
goto show_mctl;
|
||||||
show_mctl:
|
show_mctl:
|
||||||
DBG("CFG ioctl: DTR=%c RTS=%c\n",
|
DBG("MCR: DTR=%c RTS=%c\n",
|
||||||
mcr & TIOCM_DTR ? '1' : '0', mcr & TIOCM_RTS ? '1' : '0');
|
tty->mcr & TIOCM_DTR ? '1' : '0',
|
||||||
|
tty->mcr & TIOCM_RTS ? '1' : '0');
|
||||||
break;
|
break;
|
||||||
case TCSBRK:
|
case TCSBRK:
|
||||||
fuse_reply_ioctl(req, 0, 0, 0);
|
fuse_reply_ioctl(req, 0, 0, 0);
|
||||||
break;
|
break;
|
||||||
case TIOCGWINSZ:
|
case TIOCGWINSZ:
|
||||||
CHECK_BUF_OUT(sizeof(struct winsize));
|
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_err(req, errno);
|
||||||
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
|
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;
|
break;
|
||||||
|
|
||||||
case TIOCEXCL:
|
case TIOCEXCL:
|
||||||
if (tty->excl)
|
ioctl(tty->fds[FD_SLAVE].fd, TIOCEXCL, NULL);
|
||||||
{
|
fuse_reply_ioctl(req, 0, 0, 0);
|
||||||
fuse_reply_err(req, EBUSY);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tty->excl = 1;
|
|
||||||
fuse_reply_ioctl(req, 0, 0, 0);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case TIOCGEXCL:
|
case TIOCGEXCL:
|
||||||
CHECK_BUF_OUT(sizeof(int));
|
CHECK_BUF_OUT(sizeof(int));
|
||||||
val = tty->excl;
|
ioctl(tty->fds[FD_SLAVE].fd, TIOCGEXCL, &val);
|
||||||
fuse_reply_ioctl(req, 0, &val, sizeof(int));
|
fuse_reply_ioctl(req, 0, &val, sizeof(int));
|
||||||
break;
|
break;
|
||||||
case TIOCNXCL:
|
case TIOCNXCL:
|
||||||
tty->excl = 0;
|
ioctl(tty->fds[FD_SLAVE].fd, TIOCNXCL, NULL);
|
||||||
fuse_reply_ioctl(req, 0, 0, 0);
|
fuse_reply_ioctl(req, 0, 0, 0);
|
||||||
break;
|
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:
|
default:
|
||||||
fuse_reply_err(req, ENOSYS);
|
fuse_reply_err(req, ENOSYS);
|
||||||
break;
|
break;
|
||||||
@@ -819,12 +897,11 @@ ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
|
|||||||
|
|
||||||
_update_notify(tty, ph);
|
_update_notify(tty, ph);
|
||||||
|
|
||||||
if (_write_avail(tty) > 0)
|
if (tty->pollin)
|
||||||
revents |= POLLOUT;
|
{
|
||||||
|
tty->pollin = 0;
|
||||||
if (_read_avail(tty) > 0)
|
|
||||||
revents |= POLLIN;
|
revents |= POLLIN;
|
||||||
|
}
|
||||||
if (tty->error)
|
if (tty->error)
|
||||||
{
|
{
|
||||||
if (tty->epipe == 0)
|
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 = {
|
static const struct cuse_lowlevel_ops ttynvt_op = {
|
||||||
.open = ttynvt_open,
|
.init = ttynvt_init,
|
||||||
.read = ttynvt_read,
|
.open = ttynvt_open,.read = ttynvt_read,.write = ttynvt_write,.ioctl =
|
||||||
.write = ttynvt_write,
|
ttynvt_ioctl,.poll = ttynvt_poll,.release = ttynvt_release,
|
||||||
.ioctl = ttynvt_ioctl,
|
|
||||||
.poll = ttynvt_poll,
|
|
||||||
.release = ttynvt_release,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TTYNET_OPT(t, p, v) { t, offsetof(struct ttynvt_param, p), v }
|
#define TTYNET_OPT(t, p, v) { t, offsetof(struct ttynvt_param, p), v }
|
||||||
|
|||||||
Reference in New Issue
Block a user