47 Commits

Author SHA1 Message Date
Frank Rolsted Jensen eaf30f067c Clear pollin flag when input buffer is flushed
The pollin flag must be cleared when the input buffer (pty slave) is
flushed. This ensures that the POLLIN event is not signaled to the application,
when there is not data to read.
2021-10-27 15:44:09 +02:00
Kim Woelders f759c0e3cc Correct termios2 baud rate calculation
Based on suggestion by joluxer <linuxer@quantentunnel.de>.
2021-06-18 17:10:22 +02:00
Kim Woelders 0606891b78 Add indent profile + formatting tweak 2021-06-18 16:17:13 +02:00
joluxer 899596fd5b Use all baudrate for traditional termios known to linux kernel 5.10. 2021-06-18 16:17:13 +02:00
joluxer a21ba0cb9c Make some variable storage bit width agnostic for transport. 2021-06-18 16:17:13 +02:00
Kim Woelders 8f03384964 print_help(): Add missing options to run in foreground
Suggested by joluxer <linuxer@quantentunnel.de>.
2021-06-18 16:17:13 +02:00
Kim Woelders b314659125 Fix potentially incorrect speed check
We don't know if tio is termios or termios2.
2021-06-18 07:21:53 +02:00
Kim Woelders 42a4e2e062 ttynvt_ioctl(): Cleanups around TCSET.. handling
Avoid nested switches and unnecessary block level.
2021-06-18 07:21:53 +02:00
Kim Woelders 0b402cdb6b Remove pointless CFLAGS_OPT
It would normally be overridden by the default automake CFLAGS="-g -O2".
2021-06-14 18:35:10 +02:00
Kim Woelders 302ed6d796 Include asm/ioctls.h for TCSETSF2 and friends
Some targets appear to need that.
2021-01-20 18:34:53 +01:00
Kim Woelders bf11b1dd15 Add support for termios2 ioctls
Based on patch by Thomas Shaddack <shaddack@shaddack.mauriceward.com>
https://www.improwis.com/projects/sw_SerialOverIP_RFC2217/
2021-01-14 07:41:43 +01:00
Kim Woelders 33de2e875c Add a couple of debug logs
From patch by Thomas Shaddack <shaddack@shaddack.mauriceward.com>
https://www.improwis.com/projects/sw_SerialOverIP_RFC2217/
2021-01-14 07:40:40 +01:00
Kim Woelders eccd32fdfb telnet_param.h: Show hex codes too
Inspired by Thomas Shaddack <shaddack@shaddack.mauriceward.com>
https://www.improwis.com/projects/sw_SerialOverIP_RFC2217/
2021-01-14 07:19:05 +01:00
Kim Woelders 089b733f27 Indent 2021-01-14 07:18:48 +01:00
Kim Woelders 41802ce7e9 Include poll.h, not sys/poll.h
... the posixly correct location.
2020-10-08 17:36:55 +02:00
Kim Woelders e51a98e4b2 Handle CRTSCTS properly 2019-09-10 06:52:00 +02:00
Kim Woelders 9db6c5d83c Move logging functions to separate file 2019-09-05 11:36:49 +02:00
Kim Woelders e3675b9e89 Move ttynvt source to src/ 2019-09-05 11:36:49 +02:00
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
Kim Woelders 85394b243e ttynvt version 0.12 2018-10-17 14:53:59 +02:00
Kim Woelders 673ddd9739 Fix RFC2217 server death
We now properly kick the connected clients hanging in reads (cu) instead
of locking up.
2018-10-17 13:51:55 +02:00
Kim Woelders 1e05632ec0 Eliminate ttynvt_t::tmp_buf
ttynvt_read() and _read_net() both used this buffer which could cause
things to mess up entirely.
2018-10-17 11:15:00 +02:00
Kim Woelders 86aebe372e Add TIOCSCTTY/TIOCNOTTY name decoding 2018-10-17 08:11:46 +02:00
Kim Woelders 71de4b1ab4 Set RTS if CRTSCTS is set 2018-10-17 08:11:46 +02:00
Kim Woelders a05b17f142 Properly exit on SIGTERM
Previously we could get hung when killed, e.g. when using cu.
When using minicom or screen this problem did not seem to occur.
2018-10-17 08:11:46 +02:00
Kim Woelders ef3d01556e Properly initialize termios struct before use in ttynvt_open() 2018-10-17 08:11:46 +02:00
Kim Woelders 2fe0428d92 Use struct termios in ttynvt_t and drop static one.
Also set termios on pty slave on init.
2018-10-17 08:11:03 +02:00
Kim Woelders af3c3d4162 Always set POLLOUT on slave poll 2018-10-17 08:11:03 +02:00
Kim Woelders 540ddd5659 Fix polling of pty slave 2018-10-17 08:11:03 +02:00
Kim Woelders 6dc40e44e2 Enable debug logging data too 2018-10-17 08:11:03 +02:00
Kim Woelders 08e7fb1d7e More debug 2018-10-17 08:11:03 +02:00
Kim Woelders d85251400f Optionally print debug to stdout 2018-10-17 08:11:03 +02:00
Kim Woelders e8913f62af Logging cleanups
- Consistently use %m instead of strerror(errno)
- Add some missing \n's
2018-10-17 08:11:03 +02:00
Kim Woelders 44ddf67386 Avoid (s)size_t for portability 2018-10-17 08:11:03 +02:00
Kim Woelders 2d993212c5 Cosmetics 2018-10-17 08:11:03 +02:00
13 changed files with 664 additions and 338 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ Makefile
# build
*.o
/ttynvt
/src/ttynvt
/ttynvt-*.tar.gz
/ttynvt-*.tar.xz
Vendored
+6
View File
@@ -0,0 +1,6 @@
-kr -i4 --no-tabs --line-length79 -di16
--braces-after-if-line --brace-indent0 --dont-cuddle-else --cuddle-do-while
--blank-lines-after-declarations --blank-lines-after-procedures
--break-after-boolean-operator
--no-space-after-casts
--else-endif-column0
+2 -15
View File
@@ -1,20 +1,7 @@
# Build ttynvt
#
MAINTAINERCLEANFILES = \
aclocal.m4 autom4te.cache compile config.h.in config.h.in~ configure \
depcomp install-sh missing \
Makefile.in
Makefile.in */Makefile.in
CFLAGS_OPT = -O3
CFLAGS_WARN = -Wall -Wextra -Werror -Wno-unused-parameter
bin_PROGRAMS = ttynvt
ttynvt_SOURCES = \
ttynvt.c \
telnet_basic.c telnet_rfc2217.c telnet.h telnet_param.h
ttynvt_CPPFLAGS = -I$(top_builddir) $(FUSE_CFLAGS) $(CFLAGS_OPT) $(CFLAGS_WARN)
ttynvt_LDADD = $(FUSE_LIBS)
SUBDIRS = src
+2 -1
View File
@@ -1,4 +1,4 @@
m4_define([pkg_version], [0.11])
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]))
@@ -32,6 +32,7 @@ PKG_CHECK_MODULES(FUSE, fuse)
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_OUTPUT
+12
View File
@@ -0,0 +1,12 @@
CFLAGS_WARN = -Wall -Wextra -Werror -Wno-unused-parameter
bin_PROGRAMS = ttynvt
ttynvt_SOURCES = \
ttynvt.c nvt_log.c nvt_log.h \
telnet_basic.c telnet_rfc2217.c telnet.h telnet_param.h
ttynvt_CPPFLAGS = -I$(top_builddir) $(FUSE_CFLAGS) $(CFLAGS_WARN)
ttynvt_LDADD = $(FUSE_LIBS)
+84
View File
@@ -0,0 +1,84 @@
/*
* Logging functions
*/
#include <stdarg.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include "nvt_log.h"
#include <sys/syscall.h>
#define gettid() (pid_t)syscall(__NR_gettid)
char log_level = 0;
char log_stdout = 0;
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;
}
void nvt_log(int prio, const char *fmt, ...)
{
va_list arg;
char buf[256], buft[64];
const char *eol;
int len;
va_start(arg, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, arg);
if (log_stdout)
{
eol = (len > 0 && buf[len - 1] != '\n') ? "\n" : "";
printf("%s: [%d] %d: %s%s",
_hms_txt(buft, sizeof(buft)), gettid(), prio, buf, eol);
}
else
{
syslog(prio, "[%d] %s", gettid(), buf);
}
va_end(arg);
}
#define BPL 16 /* Bytes per line */
void nvt_log_buf(int prio, const char *txt, const void *ptr_, unsigned int len)
{
unsigned int ix, i, imax;
int l, n;
char buf[128];
const unsigned char *ptr = ptr_;
for (ix = 0; ix < len || (len == 0 && ix == 0); ix += BPL)
{
n = snprintf(buf, sizeof(buf), "%s: %04x:", txt, ix);
if (n <= 0)
return; /* Just playing it safe */
imax = (len - ix < BPL) ? len - ix : BPL;
for (i = 0; i < imax; i++)
{
l = snprintf(buf + n, sizeof(buf) - n, " %02x", *ptr++);
if (l <= 0)
return; /* Just playing it safe */
n += l;
}
nvt_log(prio, "%s\n", buf);
}
}
+26
View File
@@ -0,0 +1,26 @@
/*
* Logging functions
*/
#ifndef NVT_LOG_H
#define NVT_LOG_H
#include <syslog.h>
#define _PRF_N_(no) __attribute__((__format__(__printf__, (no), (no) + 1)))
#define _PRF2_ _PRF_N_(2)
_PRF2_ void nvt_log(int prio, const char *fmt, ...);
void nvt_log_buf(int prio, const char *txt,
const void *ptr, unsigned int len);
extern char log_level;
extern char log_stdout;
#define DBG(...) \
if (log_level > 0) nvt_log(LOG_DEBUG, __VA_ARGS__)
#define DBG2(...) \
if (log_level > 1) nvt_log(LOG_DEBUG, __VA_ARGS__)
#define DBG2_BUF(txt, ptr, len) \
if (log_level > 1) nvt_log_buf(LOG_DEBUG, txt, ptr, len)
#endif /* NVT_LOG_H */
+3 -3
View File
@@ -31,7 +31,7 @@ extern tn_ctx_t *telnet_ctx_init(void *cctx, srv_tx_f * ftx, srv_rx_f * frx,
extern int telnet_open(tn_ctx_t * tcc);
extern int telnet_rx(tn_ctx_t * tcc, char *buf, size_t * plen);
extern int telnet_rx(tn_ctx_t * tcc, char *buf, int *plen);
extern int telnet_tx(tn_ctx_t * tcc, const char *buf, int len);
extern void telnet_set_opt(tn_ctx_t * tcc, int opt, int prm,
@@ -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
+3 -3
View File
@@ -121,18 +121,18 @@ static void _telnet_handle_opt(tn_ctx_t * tcc, int opt,
}
}
int telnet_rx(tn_ctx_t * tcc, char *buf, size_t * plen)
int telnet_rx(tn_ctx_t * tcc, char *buf, int *plen)
{
char *s, *p;
unsigned char *pu;
size_t len, rem;
int len, rem;
int np, nd;
int b1, b2;
len = *plen;
tcc->bytes_rx += len;
if (len < (size_t) tcc->off_rx)
if (len < (int)tcc->off_rx)
tcc->off_rx = 0;
for (s = buf + tcc->off_rx, rem = len - tcc->off_rx; rem > 0; s = p)
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright (c) 2018 Lars Thrane A/S
* SPDX-License-Identifier: GPL-2.0
*
* Telnet parameters
*/
#ifndef TELNET_PARAM_H
#define TELNET_PARAM_H
/*
* Telnet commands
*/
#define TNC_F0_SE (0xf0) /* End of subnegotiation parameters */
#define TNC_F1_NOP (0xf1) /* No operation */
#define TNC_F2_MARK (0xf2) /* The data stream portion of a Synch.
This should always be accompanied
by a TCP Urgent notification. */
#define TNC_F3_BRK (0xf3) /* NVT character BRK. */
#define TNC_F4_IP (0xf4) /* The function IP (interrupt process) */
#define TNC_F5_AO (0xf5) /* The function AO (abort output) */
#define TNC_F6_AYT (0xf6) /* The function AYT (are you there) */
#define TNC_F7_EC (0xf7) /* The function EC (erase charceter) */
#define TNC_F8_EL (0xf8) /* The function EL (erase line) */
#define TNC_F9_GA (0xf9) /* The GA signal (go ahead) */
#define TNC_FA_SB (0xfa) /* Subnegotiation begin */
#define TNC_FB_WILL (0xfb) /* Indicates the desire to begin
performing, or confirmation that
you are now performing, the
indicated option. */
#define TNC_FC_WONT (0xfc) /* Indicates the refusal to perform,
or continue performing, the
indicated option. */
#define TNC_FD_DO (0xfd) /* Indicates the request that the
other party perform, or
confirmation that you are expecting
the other party to perform, the
indicated option. */
#define TNC_FE_DONT (0xfe) /* Indicates the demand that the
other party stop performing,
or confirmation that you are no
longer expecting the other party
to perform, the indicated option. */
#define TNC_FF_IAC (0xff) /* Interpret as Command / data byte */
/*
* Telnet options
*/
/*
* https://www.iana.org/assignments/telnet-options/telnet-options.xhtml
*
* 0 00 Binary Transmission [RFC856]
* 1 01 Echo [RFC857]
* 2 02 Reconnection [DDN Protocol Handbook, ... ]
* 3 03 Suppress Go Ahead [RFC858]
* 4 04 Approx Message Size Negotiation ["The Ethernet, ..." ]
* 5 05 Status [RFC859]
* 6 06 Timing Mark [RFC860]
* 7 07 Remote Controlled Trans and Echo [RFC726]
* 8 08 Output Line Width [DDN Protocol Handbook, ... ]
* 9 09 Output Page Size [DDN Protocol Handbook, ... ]
* 10 0a Output Carriage-Return Disposition [RFC652]
* 11 0b Output Horizontal Tab Stops [RFC653]
* 12 0c Output Horizontal Tab Disposition [RFC654]
* 13 0d Output Formfeed Disposition [RFC655]
* 14 0e Output Vertical Tabstops [RFC656]
* 15 0f Output Vertical Tab Disposition [RFC657]
* 16 10 Output Linefeed Disposition [RFC658]
* 17 11 Extended ASCII [RFC698]
* 18 12 Logout [RFC727]
* 19 13 Byte Macro [RFC735]
* 20 14 Data Entry Terminal [RFC1043][RFC732]
* 21 15 SUPDUP [RFC736][RFC734]
* 22 16 SUPDUP Output [RFC749]
* 23 17 Send Location [RFC779]
* 24 18 Terminal Type [RFC1091]
* 25 19 End of Record [RFC885]
* 26 1a TACACS User Identification [RFC927]
* 27 1b Output Marking [RFC933]
* 28 1c Terminal Location Number [RFC946]
* 29 1d Telnet 3270 Regime [RFC1041]
* 30 1e X.3 PAD [RFC1053]
* 31 1f Negotiate About Window Size [RFC1073]
* 32 20 Terminal Speed [RFC1079]
* 33 21 Remote Flow Control [RFC1372]
* 34 22 Linemode [RFC1184]
* 35 23 X Display Location [RFC1096]
* 36 24 Environment Option [RFC1408]
* 37 25 Authentication Option [RFC2941]
* 38 26 Encryption Option [RFC2946]
* 39 27 New Environment Option [RFC1572]
* 40 28 TN3270E [RFC2355]
* 41 29 XAUTH [Rob_Earhart]
* 42 2a CHARSET [RFC2066]
* 43 2b Telnet Remote Serial Port (RSP) [Robert_Barnes]
* 44 2c Com Port Control Option [RFC2217]
* 45 2d Telnet Suppress Local Echo [Wirt_Atmar]
* 46 2e Telnet Start TLS [Michael_Boe]
* 47 2f KERMIT [RFC2840]
* 48 30 SEND-URL [David_Croft]
* 49 31 FORWARD_X [Jeffrey_Altman]
* 50-137 32-89 Unassigned [IANA]
* 138 8a TELOPT PRAGMA LOGON [Steve_McGregory]
* 139 8b TELOPT SSPI LOGON [Steve_McGregory]
* 140 8c TELOPT PRAGMA HEARTBEAT [Steve_McGregory]
* 141-254 8d-fe Unassigned
* 255 ff Extended-Options-List [RFC861]
*/
#define TNO_BINARY 0 /* 00 Binary Transmission */
#define TNO_ECHO 1 /* 01 Echo */
#define TNO_SUPP_GA 3 /* 03 Suppress Go Ahead */
#define TNO_LOGOUT 18 /* 12 Logout */
#define TNO_TTYP 24 /* 18 Terminal Type */
#define TNO_NAWS 31 /* 1f Negotiate About Window Size */
#define TNO_NEO 39 /* 27 New Environment Option */
#define TNO_CPCO 44 /* 2c RFC 2217 */
#define TNO_KERMIT 47 /* 2f Kermit */
#endif /* TELNET_PARAM_H */
+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);
}
+387 -179
View File
@@ -8,8 +8,10 @@
#include <errno.h>
#include <fuse_opt.h>
#include <netdb.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
@@ -17,26 +19,23 @@
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <asm/ioctls.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>
#include <sys/socket.h>
#include <sys/types.h>
#include "nvt_log.h"
#include "telnet.h"
#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)
#define FD_MASTER 1
#define FD_SLAVE 2
static struct ttynvt_param {
@@ -45,6 +44,7 @@ static struct ttynvt_param {
const char *dev_name;
char *server;
unsigned int debug;
int logstd;
/* Derived */
char host[64];
unsigned int port;
@@ -55,56 +55,47 @@ static struct ttynvt_param {
typedef struct {
pthread_mutex_t tty_lock;
pthread_mutex_t poll_lock;
pthread_t thread_id;
pthread_t ptid_poll;
pthread_t ptid_read;
struct fuse_pollhandle *ph;
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];
size_t net_cnt;
char tmp_buf[TMP_BUF_SIZE];
int net_cnt;
struct termios tio;
int cmcr;
int epipe;
struct termios2 tio;
int cmcr; /* Commanded by ioctl */
volatile int smcr; /* Status from modem */
int smcr_last;
volatile int smcr;
volatile int error;
volatile int pollin;
volatile int pollout;
volatile int error;
int epipe;
tn_ctx_t *tn;
} ttynvt_t;
#include <sys/syscall.h>
#define gettid() (pid_t)syscall(__NR_gettid)
_PRF2_ static void _log(int prio, const char *fmt, ...)
static void _fd_close(int fd)
{
va_list arg;
char buf[256];
va_start(arg, fmt);
#if 0
vsyslog(prio, fmt, arg);
#else
vsnprintf(buf, sizeof(buf), fmt, arg);
syslog(prio, "[%d] %s", gettid(), buf);
#endif
va_end(arg);
if (fd >= 0)
close(fd);
}
#define DBG(...) \
if (ttynvt_param.debug > 0) _log(LOG_DEBUG, __VA_ARGS__)
#define DBG2(...) \
if (ttynvt_param.debug > 1) _log(LOG_DEBUG, __VA_ARGS__)
static volatile char _is_interrupted = 0;
static void sig_handler(int sig)
{
DBG("Interruped, signal = %d\n", sig);
_is_interrupted = 1;
if (sig != SIGUSR2)
exit(0);
}
static void set_signal_handler(void)
@@ -116,6 +107,7 @@ static void set_signal_handler(void)
sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
sigaction(SIGUSR2, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
}
static void ttynvt_interrupted(fuse_req_t req, void *data)
@@ -145,14 +137,15 @@ static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
_log(LOG_INFO, "connection closed\n");
nvt_log(LOG_INFO, "connection closed\n");
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->thread_id);
pthread_join(tty->thread_id, NULL);
pthread_cancel(tty->ptid_poll);
pthread_join(tty->ptid_poll, NULL);
pthread_mutex_destroy(&tty->tty_lock);
pthread_mutex_destroy(&tty->poll_lock);
@@ -187,68 +180,117 @@ 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;
ssize_t res;
char buf[TMP_BUF_SIZE];
int res;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
DBG2("%s: net_cnt=%d\n", __func__, tty->net_cnt);
while (1)
while (!tty->rel_pending)
{
tty->fds[FD_NET].revents = tty->fds[FD_MASTER].revents =
tty->fds[FD_SLAVE].revents = 0;
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;
else
break;
}
if (tty->rel_pending)
break;
if (tty->fds[FD_NET].revents & POLLIN)
{
res = read(tty->fds[FD_NET].fd, tty->net_buf + tty->net_cnt,
NET_BUF_SIZE - tty->net_cnt);
if (res < 0)
{
_log(LOG_ERR, "net read error: %s\n", strerror(errno));
nvt_log(LOG_ERR, "net read error: %m\n");
break;
}
else if (res == 0)
if (res == 0)
{
_log(LOG_INFO, "Connection closed by remote host\n");
nvt_log(LOG_INFO, "Connection closed by remote host\n");
break;
}
tty->net_cnt += res;
DBG2_BUF("Serv in A", tty->net_buf, tty->net_cnt);
res = telnet_rx(tty->tn, tty->net_buf, &tty->net_cnt);
DBG2_BUF("Serv in B", tty->net_buf, tty->net_cnt);
if (tty->net_cnt > 0 && res == 0)
{
DBG2_BUF("TtyM out ", tty->net_buf, tty->net_cnt);
res =
write(tty->fds[FD_MASTER].fd, tty->net_buf, tty->net_cnt);
if (res < 0)
{
_log(LOG_ERR, "master write error:%s\n", strerror(errno));
nvt_log(LOG_ERR, "master write error: %m\n");
break;
}
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));
if (_net_read_check_exit(tty))
break;
}
telnet_tx(tty->tn, tty->tmp_buf, res);
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)
{
nvt_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);
}
if (tty->fds[FD_SLAVE].revents & POLLIN)
{
tty->pollin = 1;
tty->slave_suspended = 1; /* Disable polling of Slave */
_notify(tty);
}
}
DBG2("%s: done\n", __func__);
if (tty->ptid_read)
pthread_kill(tty->ptid_read, SIGUSR2);
tty->error = 1;
_notify(tty);
@@ -264,14 +306,14 @@ static int _srv_connect(const char *host, unsigned int port)
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
_log(LOG_ERR, "Socket open failed: %s\n", strerror(errno));
nvt_log(LOG_ERR, "Socket open failed: %m\n");
return sockfd;
}
server = gethostbyname(host);
if (server == NULL)
{
_log(LOG_ERR, "Cannot resolve host name: %s\n", strerror(errno));
nvt_log(LOG_ERR, "Cannot resolve host name: %m\n");
close(sockfd);
return -1;
}
@@ -285,8 +327,7 @@ static int _srv_connect(const char *host, unsigned int port)
err = connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (err < 0)
{
_log(LOG_ERR, "Failed to connect to: %s:%u:%s\n",
host, port, strerror(errno));
nvt_log(LOG_ERR, "Failed to connect to: %s:%u: %m\n", host, port);
close(sockfd);
return -1;
}
@@ -299,8 +340,13 @@ static int _srv_write(void *cctx, const void *buf, int len)
ttynvt_t *tty = cctx;
int res;
DBG2_BUF("Serv out ", buf, len);
res = write(tty->fds[FD_NET].fd, buf, len);
DBG2("%s: fd=%d: len=%u: res=%d\n", __func__,
tty->fds[FD_NET].fd, len, res);
return res;
}
@@ -309,9 +355,12 @@ static int _srv_read(void *cctx, int timeout)
ttynvt_t *tty = cctx;
int res;
DBG2("%s: net_cnt=%d\n", __func__, tty->net_cnt);
while (1)
{
res = poll(&tty->fds[FD_NET], 1, timeout);
DBG("%s: res=%d events-N=%#x\n",
__func__, res, tty->fds[FD_NET].revents);
if (res <= 0)
return res;
@@ -319,10 +368,15 @@ static int _srv_read(void *cctx, int timeout)
{
res = read(tty->fds[FD_NET].fd, tty->net_buf + tty->net_cnt,
NET_BUF_SIZE - tty->net_cnt);
DBG2("%s: fd=%d: res=%d net_cnt=%d\n", __func__,
tty->fds[FD_NET].fd, res, tty->net_cnt);
if (res <= 0)
return res;
tty->net_cnt += (size_t) res;
tty->net_cnt += res;
DBG2_BUF("Serv in a", tty->net_buf, tty->net_cnt);
res = telnet_rx(tty->tn, tty->net_buf, &tty->net_cnt);
DBG2_BUF("Serv in b", tty->net_buf, tty->net_cnt);
DBG2("%s: res=%d net_cnt=%d\n", __func__, res, tty->net_cnt);
if (res == 0)
break;
}
@@ -335,21 +389,26 @@ 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;
if (tty->tio.c_cflag & CRTSCTS)
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);
}
@@ -369,7 +428,7 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
int res;
int n;
DBG2("open: thread:%lu\n", pthread_self());
DBG("%s\n", __func__);
tty = calloc(1, sizeof(*tty));
if (!tty)
@@ -380,6 +439,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)
{
@@ -407,6 +468,8 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
tty->fds[FD_MASTER].fd = fd;
tty->fds[FD_MASTER].events = POLLIN;
memset(&ios, 0, sizeof(ios));
ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
ios.c_oflag &= ~OPOST;
@@ -424,6 +487,11 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
if ((fd = open(slave_name, O_RDWR | O_NOCTTY)) < 0) /* open slave */
goto open_err;
ioctl(fd, TCGETS, &tty->tio);
tty->tio.c_cflag &= ~(CSIZE | PARENB);
tty->tio.c_cflag |= B115200 | CS8 | CREAD | CLOCAL;
ioctl(fd, TCSETS, &tty->tio);
tty->fds[FD_SLAVE].fd = fd;
tty->fds[FD_SLAVE].events = POLLIN;
@@ -435,7 +503,7 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
}
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)))
(res = pthread_create(&tty->ptid_poll, NULL, &_read_net, tty)))
{
errno = res;
goto open_err;
@@ -445,7 +513,7 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
info->nonseekable = 1;
info->direct_io = 1;
_log(LOG_INFO, "Connected to server: %s:%d\n",
nvt_log(LOG_INFO, "Connected to server: %s:%d\n",
ttynvt_param.host, ttynvt_param.port);
fuse_reply_open(req, info);
@@ -454,12 +522,11 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
open_err:
_log(LOG_INFO, "Connection to server: %s:%d failed:%s\n",
ttynvt_param.host, ttynvt_param.port, strerror(errno));
nvt_log(LOG_INFO, "Connection to server: %s:%d failed: %m\n",
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);
@@ -471,16 +538,19 @@ 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;
int res;
char buf[TMP_BUF_SIZE];
int res, nr;
DBG2("%s\n", __func__);
if (tty->error)
{
DBG2("read (slave) in error state, thread:%lu\n", pthread_self());
DBG2("%s: tty->error=%d\n", __func__, tty->error);
if (tty->epipe == 0)
{
_log(LOG_WARNING, "Rx EPIPE\n");
nvt_log(LOG_WARNING, "Rx EPIPE\n");
tty->epipe = 1;
fuse_reply_buf(req, tty->tmp_buf, 0);
fuse_reply_buf(req, NULL, 0);
}
else
fuse_reply_err(req, EBADFD);
@@ -491,21 +561,34 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
if (size > TMP_BUF_SIZE)
size = TMP_BUF_SIZE;
fuse_req_interrupt_func(req, ttynvt_interrupted, (void *)pthread_self());
res =
_is_interrupted ? -2 : read(tty->fds[FD_SLAVE].fd, tty->tmp_buf, size);
tty->ptid_read = pthread_self();
fuse_req_interrupt_func(req, ttynvt_interrupted, (void *)tty->ptid_read);
res = _is_interrupted ? -2 : read(tty->fds[FD_SLAVE].fd, buf, size);
_is_interrupted = 0;
tty->ptid_read = 0;
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("read (slave) error: %s\n", strerror(errno));
DBG2("%s: error: %m\n", __func__);
fuse_reply_err(req, errno);
return;
}
DBG2("read (slave): size:%d, thread:%lu\n", res, pthread_self());
DBG2("%s: fd=%d: sz=%u/%u: '%.*s'\n", __func__,
tty->fds[FD_SLAVE].fd, res, (int)size, res, buf);
fuse_reply_buf(req, tty->tmp_buf, res);
fuse_reply_buf(req, buf, res);
}
static void
@@ -513,8 +596,10 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
struct fuse_file_info *info)
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
ssize_t res;
int res;
DBG2("%s: fd=%d: sz=%u: '%.*s'\n", __func__,
tty->fds[FD_SLAVE].fd, (int)size, (int)size, data);
if (size == 0)
{
@@ -524,6 +609,7 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
if (tty->error)
{
DBG2("%s: tty->error=%d\n", __func__, tty->error);
fuse_reply_err(req, EPIPE);
return;
}
@@ -531,22 +617,29 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
res = write(tty->fds[FD_SLAVE].fd, data, size);
if (res < 0)
{
DBG2("write (slave) error: %s\n", strerror(errno));
DBG2("%s: error: %m\n", __func__);
tty->error = 1;
fuse_reply_err(req, errno);
return;
}
DBG2("write (slave): size:%d, thread:%lu\n", (int)size, pthread_self());
else if (res != (int)size)
{
DBG("%s: caution: tty->pollout set to 0\n", __func__);
tty->pollout = 0;
}
fuse_reply_write(req, res);
}
static int baudrate(speed_t speed)
{
/**INDENT-OFF**/
return speed == B50 ? 50 :
speed == B75 ? 75 :
speed == B110 ? 110 :
speed == B134 ? 134 :
speed == B150 ? 150 :
speed == B200 ? 200 :
speed == B300 ? 300 :
speed == B600 ? 600 :
speed == B1200 ? 1200 :
@@ -557,7 +650,21 @@ static int baudrate(speed_t speed)
speed == B19200 ? 19200 :
speed == B38400 ? 38400 :
speed == B57600 ? 57600 :
speed == B115200 ? 115200 : speed == B230400 ? 230400 : 0;
speed == B115200 ? 115200 :
speed == B230400 ? 230400 :
speed == B460800 ? 460800 :
speed == B500000 ? 500000 :
speed == B576000 ? 576000 :
speed == B921600 ? 921600 :
speed == B1000000 ? 1000000 :
speed == B1152000 ? 1152000 :
speed == B1500000 ? 1500000 :
speed == B2000000 ? 2000000 :
speed == B2500000 ? 2500000 :
speed == B3000000 ? 3000000 :
speed == B3500000 ? 3500000 :
speed == B4000000 ? 4000000 : 0;
/**INDENT-ON**/
}
static const char pstr[] = { ' ', 'N', 'O', 'E', 'M', 'S' };
@@ -574,15 +681,16 @@ static const char *_ioctl_name(int cmd)
snprintf(buf, sizeof(buf), "0x%x", cmd);
return buf;
CASE(TCSETSF);
CASE(TCSETSF2);
CASE(TCSETSW);
CASE(TCSETSW2);
CASE(TCSETS);
CASE(TCSETS2);
CASE(TCGETS);
CASE(TCGETS2);
CASE(TIOCINQ);
CASE(TIOCOUTQ);
CASE(TCFLSH);
CASE(TCIFLUSH);
CASE(TCOFLUSH);
CASE(TCIOFLUSH);
CASE(TIOCMGET);
CASE(TIOCMBIS);
CASE(TIOCMBIC);
@@ -590,6 +698,9 @@ static const char *_ioctl_name(int cmd)
CASE(TCSBRK);
CASE(TIOCGWINSZ);
CASE(TIOCSWINSZ);
/* Controlling terminal */
CASE(TIOCSCTTY);
CASE(TIOCNOTTY);
/* Exclusive mode */
CASE(TIOCEXCL);
CASE(TIOCGEXCL);
@@ -602,6 +713,89 @@ static const char *_ioctl_name(int cmd)
}
}
static unsigned int _tio_parity(const struct termios2 *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 termios2 *tio)
{
return baudrate(tio->c_cflag & (CBAUD | CBAUDEX));
}
static unsigned int _tio2_baud(const struct termios2 *tio2)
{
if ((tio2->c_cflag & CBAUD) == BOTHER)
return tio2->c_ospeed;
return _tio_baud(tio2);
}
static unsigned int _tio_csize(const struct termios2 *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 termios2 *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 termios2 *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 ? '+' : '-');
}
static void _show_termios2(const char *txt, const struct termios2 *tio)
{
DBG("%s: %u%c%u%u %cCTSRTS %cCREAD %cHUPCL %cCLOCAL\n", txt,
_tio2_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) \
@@ -613,14 +807,14 @@ 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 termios2 *tio = &tty->tio;
struct winsize ws;
unsigned int tiocm = 0;
unsigned int tiocm, baud;
int mcr;
static struct termios tio = {
.c_cflag = B115200 | CS8 | PARODD | CREAD | CLOCAL,
};
int val;
int res;
uint8_t byte;
uint32_t byte4;
DBG("%s: cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__,
_ioctl_name(cmd), arg, in_buf,
@@ -632,73 +826,78 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
case TCSETSW:
case TCSETS:
CHECK_BUF_IN(sizeof(struct termios));
memcpy(tio, in_buf, in_bufsz);
baud = _tio_baud(tio);
goto do_tcset;
case TCSETSF2:
case TCSETSW2:
case TCSETS2:
CHECK_BUF_IN(sizeof(struct termios2));
memcpy(tio, in_buf, in_bufsz);
baud = _tio2_baud(tio);
goto do_tcset;
do_tcset:
/* BEWARE of termios/termios2 buf structure difference! */
byte4 = htonl(baud);
telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &byte4, 4);
byte = _tio_csize(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &byte, 1);
byte = _tio_parity(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_PARITY, &byte, 1);
byte = _tio_stopb(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_STOPSIZE, &byte, 1);
if (tio->c_cflag & CRTSCTS)
{
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);
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);
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);
if (tio.c_cflag & CSTOPB)
sb = 2;
else
sb = 1;
telnet_rfc2217_cfg(tty->tn, TNS_SET_STOPSIZE, &sb, 1);
if (tio.c_cflag & CRTSCTS)
tty->master_suspended = (tty->smcr & TIOCM_CTS) ? 0 : 1;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_CTSRTS);
}
else
{
tty->master_suspended = 0;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_NOFLOW);
}
if (!(tty->cmcr & TIOCM_DTR) && speed != B0)
if (!(tty->cmcr & TIOCM_DTR) && baud != 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 ? '+' : '-');
switch (cmd)
{
case TCSETSF:
case TCSETSW:
case TCSETS:
_show_termios("TCSETS", tio);
break;
case TCSETSF2:
case TCSETSW2:
case TCSETS2:
_show_termios2("TCSETS2", tio);
break;
}
ioctl(tty->fds[FD_SLAVE].fd, cmd, tio);
ioctl(tty->fds[FD_SLAVE].fd, cmd, &tio);
if (cmd == TCSETSF || cmd == TCSETSF2)
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));
fuse_reply_ioctl(req, 0, &tio, 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 TCGETS2:
CHECK_BUF_OUT(sizeof(struct termios2));
ioctl(tty->fds[FD_SLAVE].fd, TCGETS2, tio);
_show_termios2("TCGETS2", tio);
fuse_reply_ioctl(req, 0, tio, sizeof(struct termios2));
break;
case TIOCINQ: /* Get the number of bytes in the input
* buffer */
@@ -713,33 +912,27 @@ 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 */
}
else if ((size_t) arg == TCOFLUSH)
{
/*todo */
}
else if ((size_t) arg == TCIOFLUSH)
{
/*todo */
}
fuse_reply_ioctl(req, 0, 0, 0);
break;
default:
case TCIFLUSH:
val = 1;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &val, 1);
fuse_reply_ioctl(req, 0, 0, 0);
byte = 1;
tty->slave_suspended = 0; /* Re-enable polling of Slave */
tty->pollin = 0;
break;
case TCOFLUSH:
val = 2;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &val, 1);
fuse_reply_ioctl(req, 0, 0, 0);
byte = 2;
tty->pollout = 1;
break;
case TCIOFLUSH:
val = 3;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &val, 1);
byte = 3;
tty->slave_suspended = 0; /* Re-enable polling of Slave */
tty->pollout = 1;
tty->pollin = 0;
break;
}
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:
@@ -794,9 +987,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);
@@ -838,7 +1031,6 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
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);
@@ -862,6 +1054,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
fuse_reply_ioctl(req, 0, &val, sizeof(int));
break;
default:
DBG("ERROR: Unsupported IOCTL\n");
fuse_reply_err(req, ENOSYS);
break;
@@ -888,21 +1081,29 @@ ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
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);
if (tty->pollout)
{
revents = POLLOUT;
}
if (tty->pollin)
{
tty->pollin = 0;
revents |= POLLIN;
}
if (tty->error)
{
if (tty->epipe == 0)
revents = POLLHUP | POLLIN;
revents |= POLLHUP | POLLIN;
else
revents = 0;
}
DBG2("%s: revents=%#x\n", __func__, revents);
fuse_reply_poll(req, revents);
}
@@ -920,6 +1121,7 @@ static const struct cuse_lowlevel_ops ttynvt_op = {
static const struct fuse_opt ttynvt_opts[] = {
TTYNET_OPT("-D %d", debug, 1),
TTYNET_OPT("-E", logstd, 1),
TTYNET_OPT("-M %u", major, 1),
TTYNET_OPT("--maj=%u", major, 1),
TTYNET_OPT("-m %u", minor, 1),
@@ -937,6 +1139,9 @@ static void print_help(void)
{
fprintf(stderr, "ttynvt Usage:\n"
"\t-D level\tEnable debug\n"
"\t-d\t\tStay in foreground, do not detach\n"
"\t-f\t\tStay in foreground, do not detach\n"
"\t-E\t\tPrint debug to stdout (default is syslog)\n"
"\t-M major, --maj=major\n"
"\t-m minor, --min=minor\n"
"\t-n name, --name=name (default: ttyNVT0)\n"
@@ -976,6 +1181,9 @@ int main(int argc, char *argv[])
return 1;
}
log_level = ttynvt_param.debug;
log_stdout = ttynvt_param.logstd;
if (!ttynvt_param.dev_name)
ttynvt_param.dev_name = "ttyNVT0";
@@ -1018,14 +1226,14 @@ int main(int argc, char *argv[])
switch (cpid)
{
case -1:
_log(LOG_ERR, "fork(): %m");
nvt_log(LOG_ERR, "fork(): %m\n");
return -1;
case 0: /* Child */
DBG("Child proceeding...\n");
rc = cuse_lowlevel_main(args.argc, args.argv, &ci, &ttynvt_op, NULL);
if (rc != 0)
_log(LOG_ERR, "ttynvt failed rc=%d", rc);
nvt_log(LOG_ERR, "ttynvt failed rc=%d\n", rc);
else
DBG("Child exited\n");
return rc;
-123
View File
@@ -1,123 +0,0 @@
/*
* Copyright (c) 2018 Lars Thrane A/S
* SPDX-License-Identifier: GPL-2.0
*
* Telnet parameters
*/
#ifndef TELNET_PARAM_H
#define TELNET_PARAM_H
/*
* Telnet commands
*/
#define TNC_F0_SE (0xf0) /* End of subnegotiation parameters */
#define TNC_F1_NOP (0xf1) /* No operation */
#define TNC_F2_MARK (0xf2) /* The data stream portion of a Synch.
This should always be accompanied
by a TCP Urgent notification. */
#define TNC_F3_BRK (0xf3) /* NVT character BRK. */
#define TNC_F4_IP (0xf4) /* The function IP (interrupt process) */
#define TNC_F5_AO (0xf5) /* The function AO (abort output) */
#define TNC_F6_AYT (0xf6) /* The function AYT (are you there) */
#define TNC_F7_EC (0xf7) /* The function EC (erase charceter) */
#define TNC_F8_EL (0xf8) /* The function EL (erase line) */
#define TNC_F9_GA (0xf9) /* The GA signal (go ahead) */
#define TNC_FA_SB (0xfa) /* Subnegotiation begin */
#define TNC_FB_WILL (0xfb) /* Indicates the desire to begin
performing, or confirmation that
you are now performing, the
indicated option. */
#define TNC_FC_WONT (0xfc) /* Indicates the refusal to perform,
or continue performing, the
indicated option. */
#define TNC_FD_DO (0xfd) /* Indicates the request that the
other party perform, or
confirmation that you are expecting
the other party to perform, the
indicated option. */
#define TNC_FE_DONT (0xfe) /* Indicates the demand that the
other party stop performing,
or confirmation that you are no
longer expecting the other party
to perform, the indicated option. */
#define TNC_FF_IAC (0xff) /* Interpret as Command / data byte */
/*
* Telnet options
*/
/*
* https://www.iana.org/assignments/telnet-options/telnet-options.xhtml
*
* 0 Binary Transmission [RFC856]
* 1 Echo [RFC857]
* 2 Reconnection [DDN Protocol Handbook, ... ]
* 3 Suppress Go Ahead [RFC858]
* 4 Approx Message Size Negotiation ["The Ethernet, ... ]
* 5 Status [RFC859]
* 6 Timing Mark [RFC860]
* 7 Remote Controlled Trans and Echo [RFC726]
* 8 Output Line Width [DDN Protocol Handbook, ... ]
* 9 Output Page Size [DDN Protocol Handbook, ... ]
* 10 Output Carriage-Return Disposition [RFC652]
* 11 Output Horizontal Tab Stops [RFC653]
* 12 Output Horizontal Tab Disposition [RFC654]
* 13 Output Formfeed Disposition [RFC655]
* 14 Output Vertical Tabstops [RFC656]
* 15 Output Vertical Tab Disposition [RFC657]
* 16 Output Linefeed Disposition [RFC658]
* 17 Extended ASCII [RFC698]
* 18 Logout [RFC727]
* 19 Byte Macro [RFC735]
* 20 Data Entry Terminal [RFC1043][RFC732]
* 21 SUPDUP [RFC736][RFC734]
* 22 SUPDUP Output [RFC749]
* 23 Send Location [RFC779]
* 24 Terminal Type [RFC1091]
* 25 End of Record [RFC885]
* 26 TACACS User Identification [RFC927]
* 27 Output Marking [RFC933]
* 28 Terminal Location Number [RFC946]
* 29 Telnet 3270 Regime [RFC1041]
* 30 X.3 PAD [RFC1053]
* 31 Negotiate About Window Size [RFC1073]
* 32 Terminal Speed [RFC1079]
* 33 Remote Flow Control [RFC1372]
* 34 Linemode [RFC1184]
* 35 X Display Location [RFC1096]
* 36 Environment Option [RFC1408]
* 37 Authentication Option [RFC2941]
* 38 Encryption Option [RFC2946]
* 39 New Environment Option [RFC1572]
* 40 TN3270E [RFC2355]
* 41 XAUTH [Rob_Earhart]
* 42 CHARSET [RFC2066]
* 43 Telnet Remote Serial Port (RSP) [Robert_Barnes]
* 44 Com Port Control Option [RFC2217]
* 45 Telnet Suppress Local Echo [Wirt_Atmar]
* 46 Telnet Start TLS [Michael_Boe]
* 47 KERMIT [RFC2840]
* 48 SEND-URL [David_Croft]
* 49 FORWARD_X [Jeffrey_Altman]
* 50-137 Unassigned [IANA]
* 138 TELOPT PRAGMA LOGON [Steve_McGregory]
* 139 TELOPT SSPI LOGON [Steve_McGregory]
* 140 TELOPT PRAGMA HEARTBEAT [Steve_McGregory]
* 141-254 Unassigned
* 255 Extended-Options-List [RFC861]
*/
#define TNO_BINARY 0 /* Binary Transmission */
#define TNO_ECHO 1 /* Echo */
#define TNO_SUPP_GA 3 /* Suppress Go Ahead */
#define TNO_LOGOUT 18 /* Logout */
#define TNO_TTYP 24 /* Terminal Type */
#define TNO_NAWS 31 /* Negotiate About Window Size */
#define TNO_NEO 39 /* New Environment Option */
#define TNO_CPCO 44 /* RFC 2217 */
#define TNO_KERMIT 47 /* Kermit */
#endif /* TELNET_PARAM_H */