diff --git a/README b/README index 0b23dae..dba8354 100644 --- a/README +++ b/README @@ -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 ' 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. diff --git a/ttynvt.c b/ttynvt.c index 1025d8f..f677569 100644 --- a/ttynvt.c +++ b/ttynvt.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -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 }