35 Commits

Author SHA1 Message Date
Kim Woelders 9ce6164d20 Bump version to 0.10 2018-09-12 13:13:09 +02:00
Kim Woelders cc95ed0ad2 Trivial cleanups 2018-09-12 12:14:47 +02:00
Kim Woelders 3231247534 Documentation stuff
- README:  Add notes on configure for cross-build
- AUTHORS: Added
- COPYING: Added (GPLv2)
2018-09-12 12:05:48 +02:00
Frank Rolsted Jensen c4c25d7dda version 0.9.005 2018-09-11 10:21:35 +02:00
Frank Rolsted Jensen b2747cf886 typo in README file 2018-09-11 10:17:36 +02:00
Frank Rolsted Jensen c5ca50b654 improved handling of modem/line status 2018-09-06 09:07:35 +02:00
Frank Rolsted Jensen 96329fc290 fix wrong decoding of the IAC escape char 2018-09-06 09:06:02 +02:00
Frank Rolsted Jensen 568f5ca733 version 0.9.004 2018-07-25 09:05:21 +02:00
Frank Rolsted Jensen 6bfdd85cfb fix compiler warnings(errors) 2018-07-25 09:03:42 +02:00
Frank Rolsted Jensen 7f4d2f3a51 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.
2018-07-24 13:57:05 +02:00
Kim Woelders 3cae838cf0 Use kernel termios struct (?!?) 2018-07-18 11:43:44 +02:00
Kim Woelders 16825bd70e Add some more modem line control ioctl debug 2018-07-17 15:55:27 +02:00
Kim Woelders 5520722723 Add TIOCGETD/TIOCSETD in ioctl name decoder 2018-07-17 15:47:50 +02:00
Kim Woelders a68edc5e57 Add exclusive mode ioctls 2018-07-17 15:42:56 +02:00
Kim Woelders 2c3441dda5 Refactor ioctl input/output buffer size handling 2018-07-17 15:37:11 +02:00
Kim Woelders 45e73de4b9 Debug: Show ioctl names 2018-07-17 14:34:54 +02:00
Kim Woelders 778242986f Debug tweaks
- Add gcc printf format check to _log()
- Show thread ids
- Enable setting debug level
2018-07-17 14:08:01 +02:00
Kim Woelders 137418ad1e When running in daemon mode, delay parent exit until device is created
Doing this is somewhat elaborate because cuse_lowlevel_main() when
daemonizing, the parent (the calling thread) calls _exit(0) instead of
returning.
2018-07-17 13:53:23 +02:00
Kim Woelders 9057376656 Fix -D option, add DBG() for debug 2018-07-05 09:26:19 +02:00
Kim Woelders cbbb0cd225 Tweak options
- Change -s to -S to avoid clash with fuses singlethread option
  (mostly for testing).
- Merge port option with server option (server = host:port)
  (simpler).
2018-07-05 09:26:12 +02:00
Kim Woelders 2fb5ad2599 Drop ttynvt prefix from "local" functions
Only keep it on ones that are direct cuse_lowlevel_ops.

Makes it easier to understand things, IMO.
2018-07-05 08:38:36 +02:00
Kim Woelders 4988368662 Cosmetics of various sorts
Also in srv_connect() (renamed from ttynvt_connect()), close newly
crated socket in a couple of error paths.
2018-07-05 08:38:36 +02:00
Kim Woelders f0697e83f3 v0.9.003 2018-07-02 13:53:52 +02:00
Kim Woelders 0f879c8b61 Fix zero size reads
In certain cases we may get zero size reads (e.g. if the server sends an
rfc2217 notification without any other data).
In this situation we must not propagate the zero size read to user space
as it may be interpreted as end of input.

I haven't found a way to tell fuse that it should just ignore this read
but passing EINTR seems to work.
2018-07-02 12:40:27 +02:00
Kim Woelders 5ca23ce107 Correct write size
ttynvt_write() must on succesful writes return the number of bytes
written.

telnet_tx() always returns 0 (implicitly assuming srv_write() always
succeeds).
2018-07-02 12:39:56 +02:00
Kim Woelders b3f6d61edf Use syslog instead of logfile 2018-06-28 13:41:57 +02:00
Kim Woelders 7a424bc10d Make ttynvt_param global struct and avoid secondary variables 2018-06-28 13:38:27 +02:00
Kim Woelders 8956f3f851 Cleanups
- Consequently use "unsigned int", not "unsigned"
- Indent
- Sort headers
2018-06-28 13:37:25 +02:00
Kim Woelders ac76071e0e Autotoolify 2018-06-28 11:58:03 +02:00
Frank Rolsted Jensen 6337ac09eb support for blocking devices.
- and better handling of broken network connections.
2018-06-21 16:20:28 +02:00
Frank Rolsted Jensen fd4313745f logging connection error messages 2018-06-20 10:10:42 +02:00
Frank Rolsted Jensen a7d760d719 compiler warnings/errors fixed 2018-06-20 08:49:16 +02:00
Frank Rolsted Jensen 9ca94921c8 Description changed (minor) 2018-06-19 16:11:28 +02:00
Frank Rolsted Jensen cf678db1fd version information added to log file 2018-06-19 16:05:45 +02:00
Frank Rolsted Jensen 6556a2ec50 initial 2018-06-19 15:38:46 +02:00
5 changed files with 159 additions and 373 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
m4_define([pkg_version], [0.14])
m4_define([pkg_version], [0.10])
#m4_define([pkg_revision], [005])
m4_define([pkg_version], m4_ifdef([pkg_revision], [pkg_version.pkg_revision], [pkg_version]))
+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, int *plen);
extern int telnet_rx(tn_ctx_t * tcc, char *buf, size_t * 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, unsigned int val);
extern void telnet_rfc2217_ctl(tn_ctx_t * tcc, int cmd);
/*RFC2217 modem state*/
#define TNS_STATE_DCD 0x80
#define TNS_STATE_CD 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, int *plen)
int telnet_rx(tn_ctx_t * tcc, char *buf, size_t * plen)
{
char *s, *p;
unsigned char *pu;
int len, rem;
size_t len, rem;
int np, nd;
int b1, b2;
len = *plen;
tcc->bytes_rx += len;
if (len < (int)tcc->off_rx)
if (len < (size_t) tcc->off_rx)
tcc->off_rx = 0;
for (s = buf + tcc->off_rx, rem = len - tcc->off_rx; rem > 0; s = p)
+2 -4
View File
@@ -43,12 +43,10 @@ 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, unsigned int val)
void telnet_rfc2217_ctl(tn_ctx_t * tcc, int cmd)
{
unsigned char byte = val;
if (!tcc->mode_rfc2217)
return;
telnet_set_opt(tcc, TNO_CPCO, TNS_SET_CONTROL, &byte, 1);
telnet_set_opt(tcc, TNO_CPCO, TNS_SET_CONTROL, &cmd, 1);
}
+144 -356
View File
@@ -24,7 +24,6 @@
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include "telnet.h"
@@ -33,8 +32,8 @@
#define TMP_BUF_SIZE 1024
#define FD_NET 0
#define FD_MASTER 1
#define FD_SLAVE 2
#define FD_SLAVE 1
#define FD_MASTER 2
#define _PRF_N_(no) __attribute__((__format__(__printf__, (no), (no) + 1)))
#define _PRF2_ _PRF_N_(2)
@@ -46,7 +45,6 @@ static struct ttynvt_param {
const char *dev_name;
char *server;
unsigned int debug;
int logstd;
/* Derived */
char host[64];
unsigned int port;
@@ -57,121 +55,53 @@ static struct ttynvt_param {
typedef struct {
pthread_mutex_t tty_lock;
pthread_mutex_t poll_lock;
pthread_t ptid_poll;
pthread_t ptid_read;
pthread_t thread_id;
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];
int net_cnt;
size_t net_cnt;
char tmp_buf[TMP_BUF_SIZE];
struct termios tio;
int cmcr; /* Commanded by ioctl */
volatile int smcr; /* Status from modem */
int smcr_last;
volatile int pollin;
volatile int pollout;
volatile int error;
int cmcr;
int epipe;
volatile int smcr;
volatile int error;
volatile int pollin;
tn_ctx_t *tn;
} ttynvt_t;
#include <sys/syscall.h>
#define gettid() (pid_t)syscall(__NR_gettid)
static const char *_hms_txt(char *buf, unsigned int len)
{
struct timeval tv;
unsigned int sod, hh, mm, ss;
gettimeofday(&tv, NULL);
sod = tv.tv_sec % (24 * 3600);
hh = sod / 3600;
mm = (sod - hh * 3600) / 60;
ss = sod - hh * 3600 - mm * 60;
snprintf(buf, len, "%02d:%02d:%02d.%06d",
hh, mm, ss, (unsigned int)tv.tv_usec);
return buf;
}
_PRF2_ static void _log(int prio, const char *fmt, ...)
{
va_list arg;
char buf[256], buft[64];
char buf[256];
va_start(arg, fmt);
#if 0
vsyslog(prio, fmt, arg);
#else
vsnprintf(buf, sizeof(buf), fmt, arg);
if (ttynvt_param.logstd)
{
printf("%s: [%d] %d: %s",
_hms_txt(buft, sizeof(buft)), gettid(), prio, buf);
}
else
{
syslog(prio, "[%d] %s", gettid(), buf);
}
#endif
va_end(arg);
}
#define BPL 16 /* Bytes per line */
void _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;
}
_log(prio, "%s\n", buf);
}
}
#define DBG(...) \
if (ttynvt_param.debug > 0) _log(LOG_DEBUG, __VA_ARGS__)
#define DBG2(...) \
if (ttynvt_param.debug > 1) _log(LOG_DEBUG, __VA_ARGS__)
#define DBG2_BUF(txt, ptr, len) \
if (ttynvt_param.debug > 1) _log_buf(LOG_DEBUG, txt, ptr, len)
static void _fd_close(int fd)
{
if (fd >= 0)
close(fd);
}
static volatile char _is_interrupted = 0;
static void sig_handler(int sig)
{
DBG("Interruped, signal = %d\n", sig);
_is_interrupted = 1;
if (sig != SIGUSR2)
exit(0);
}
static void set_signal_handler(void)
@@ -183,7 +113,6 @@ 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)
@@ -215,13 +144,12 @@ static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
_log(LOG_INFO, "connection closed\n");
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);
close(tty->fds[FD_NET].fd);
close(tty->fds[FD_MASTER].fd);
close(tty->fds[FD_SLAVE].fd);
pthread_cancel(tty->ptid_poll);
pthread_join(tty->ptid_poll, NULL);
pthread_cancel(tty->thread_id);
pthread_join(tty->thread_id, NULL);
pthread_mutex_destroy(&tty->tty_lock);
pthread_mutex_destroy(&tty->poll_lock);
@@ -256,117 +184,68 @@ 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;
char buf[TMP_BUF_SIZE];
int res;
ssize_t res;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
DBG2("%s: net_cnt=%d\n", __func__, tty->net_cnt);
while (!tty->rel_pending)
while (1)
{
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: %m\n");
_log(LOG_ERR, "net read error: %s\n", strerror(errno));
break;
}
if (res == 0)
else if (res == 0)
{
_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: %m\n");
_log(LOG_ERR, "master write error:%s\n", strerror(errno));
break;
}
tty->net_cnt = 0;
}
if (_net_read_check_exit(tty))
break;
}
if ((tty->fds[FD_MASTER].revents & POLLIN) && !tty->master_suspended)
if (tty->fds[FD_MASTER].revents & POLLIN)
{
res = read(tty->fds[FD_MASTER].fd, buf, TMP_BUF_SIZE);
DBG("%s: read res=%d pollout=%d\n", __func__, res, tty->pollout);
res = read(tty->fds[FD_MASTER].fd, tty->tmp_buf, TMP_BUF_SIZE);
if (res < 0)
{
_log(LOG_ERR, "master read error: %m\n");
_log(LOG_ERR, "master read error:%s\n", strerror(errno));
break;
}
if (tty->pollout == 0)
{
tty->pollout = 1;
_notify(tty);
telnet_tx(tty->tn, tty->tmp_buf, res);
}
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);
@@ -382,14 +261,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: %m\n");
_log(LOG_ERR, "Socket open failed: %s\n", strerror(errno));
return sockfd;
}
server = gethostbyname(host);
if (server == NULL)
{
_log(LOG_ERR, "Cannot resolve host name: %m\n");
_log(LOG_ERR, "Cannot resolve host name: %s\n", strerror(errno));
close(sockfd);
return -1;
}
@@ -403,7 +282,8 @@ 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: %m\n", host, port);
_log(LOG_ERR, "Failed to connect to: %s:%u:%s\n",
host, port, strerror(errno));
close(sockfd);
return -1;
}
@@ -416,13 +296,8 @@ 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;
}
@@ -431,12 +306,9 @@ 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;
@@ -444,15 +316,10 @@ 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 += res;
DBG2_BUF("Serv in a", tty->net_buf, tty->net_cnt);
tty->net_cnt += (size_t) res;
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;
}
@@ -465,25 +332,21 @@ static void _modem_status_cb(void *cctx, int status)
ttynvt_t *tty = cctx;
int mcr = 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');
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');
if (status & TNS_STATE_DCD)
if (status & TNS_STATE_CD)
mcr |= TIOCM_CD;
if (status & TNS_STATE_RI)
mcr |= TIOCM_RI;
if (status & TNS_STATE_DSR)
mcr |= TIOCM_DSR;
if (status & TNS_STATE_CTS)
mcr |= TIOCM_CTS;
tty->master_suspended = (mcr & TIOCM_CTS) ? 0 : 1;
DBG("%s: tty=%p mcr=%x MS=%d\n", __func__, tty, mcr,
tty->master_suspended);
if (status & TNS_STATE_DSR)
mcr |= TIOCM_DSR;
pthread_mutex_lock(&tty->tty_lock);
tty->smcr_last = tty->smcr;
tty->smcr = mcr;
pthread_mutex_unlock(&tty->tty_lock);
}
@@ -503,7 +366,7 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
int res;
int n;
DBG("%s\n", __func__);
DBG2("open: thread:%lu\n", pthread_self());
tty = calloc(1, sizeof(*tty));
if (!tty)
@@ -514,8 +377,6 @@ 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)
{
@@ -543,8 +404,6 @@ 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;
@@ -562,11 +421,6 @@ 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;
@@ -578,7 +432,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->ptid_poll, NULL, &_read_net, tty)))
(res = pthread_create(&tty->thread_id, NULL, &_read_net, tty)))
{
errno = res;
goto open_err;
@@ -597,11 +451,12 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
open_err:
_log(LOG_INFO, "Connection to server: %s:%d failed: %m\n",
ttynvt_param.host, ttynvt_param.port);
_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++)
_fd_close(tty->fds[n].fd);
if (tty->fds[n].fd >= 0)
close(tty->fds[n].fd);
free(tty->tn);
free(tty);
@@ -613,19 +468,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;
char buf[TMP_BUF_SIZE];
int res, nr;
DBG2("%s\n", __func__);
int res;
if (tty->error)
{
DBG2("%s: tty->error=%d\n", __func__, 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, NULL, 0);
fuse_reply_buf(req, tty->tmp_buf, 0);
}
else
fuse_reply_err(req, EBADFD);
@@ -636,34 +488,19 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
if (size > TMP_BUF_SIZE)
size = 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, 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 < (int)size)
{
tty->pollin = 0;
}
else
{
nr = 0;
ioctl(tty->fds[FD_SLAVE].fd, FIONREAD, &nr);
tty->pollin = nr > 0;
}
if (res < 0)
{
DBG2("%s: error: %m\n", __func__);
DBG2("read (slave) error: %s\n", strerror(errno));
fuse_reply_err(req, errno);
return;
}
DBG2("%s: fd=%d: sz=%u/%u: '%.*s'\n", __func__,
tty->fds[FD_SLAVE].fd, res, (int)size, res, buf);
DBG2("read (slave): size:%d, thread:%lu\n", res, pthread_self());
fuse_reply_buf(req, buf, res);
fuse_reply_buf(req, tty->tmp_buf, res);
}
static void
@@ -671,10 +508,8 @@ 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;
int res;
ssize_t res;
DBG2("%s: fd=%d: sz=%u: '%.*s'\n", __func__,
tty->fds[FD_SLAVE].fd, (int)size, (int)size, data);
if (size == 0)
{
@@ -684,7 +519,6 @@ 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;
}
@@ -692,15 +526,13 @@ 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("%s: error: %m\n", __func__);
DBG2("write (slave) error: %s\n", strerror(errno));
tty->error = 1;
fuse_reply_err(req, errno);
return;
}
else if (res != (int)size)
{
tty->pollout = 0;
}
DBG2("write (slave): size:%d, thread:%lu\n", (int)size, pthread_self());
fuse_reply_write(req, res);
}
@@ -743,6 +575,9 @@ static const char *_ioctl_name(int cmd)
CASE(TIOCINQ);
CASE(TIOCOUTQ);
CASE(TCFLSH);
CASE(TCIFLUSH);
CASE(TCOFLUSH);
CASE(TCIOFLUSH);
CASE(TIOCMGET);
CASE(TIOCMBIS);
CASE(TIOCMBIC);
@@ -750,9 +585,6 @@ 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);
@@ -765,71 +597,6 @@ static const char *_ioctl_name(int cmd)
}
}
static unsigned int _tio_parity(const struct termios *tio)
{
unsigned int par;
if (tio->c_cflag & PARENB && tio->c_iflag & IGNPAR)
par = 1;
#ifdef CMSPAR
else if (tio->c_cflag & PARENB && tio->c_iflag & CMSPAR)
{
if (tio->c_cflag & PARODD)
par = 4;
else
par = 5;
}
#endif
else if (tio->c_cflag & PARENB)
par = 3;
else
par = 1;
return par;
}
static unsigned int _tio_baud(const struct termios *tio)
{
return baudrate(tio->c_cflag & (CBAUD | CBAUDEX));
}
static unsigned int _tio_csize(const struct termios *tio)
{
unsigned int csize;
if ((tio->c_cflag & CSIZE) == CS5)
csize = 5;
else if ((tio->c_cflag & CSIZE) == CS6)
csize = 6;
else if ((tio->c_cflag & CSIZE) == CS7)
csize = 7;
else
csize = 8;
return csize;
}
static unsigned int _tio_stopb(const struct termios *tio)
{
unsigned int stopb;
if (tio->c_cflag & CSTOPB)
stopb = 2;
else
stopb = 1;
return stopb;
}
static void _show_termios(const char *txt, const struct termios *tio)
{
DBG("%s: %u%c%u%u %cCTSRTS %cCREAD %cHUPCL %cCLOCAL\n", txt,
_tio_baud(tio), pstr[_tio_parity(tio)], _tio_csize(tio),
_tio_stopb(tio),
tio->c_cflag & CRTSCTS ? '+' : '-', tio->c_cflag & CREAD ? '+' : '-',
tio->c_cflag & HUPCL ? '+' : '-', tio->c_cflag & CLOCAL ? '+' : '-');
}
#define CHECK_BUF_IN(sz) \
if (in_bufsz < sz) do { in_bufsz = sz; goto retry_in; } while(0)
#define CHECK_BUF_OUT(sz) \
@@ -841,14 +608,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 termios *tio = &tty->tio;
struct winsize ws;
unsigned int tiocm = 0;
int mcr;
static struct termios tio = {
.c_cflag = B115200 | CS8 | PARODD | CREAD | CLOCAL,
};
int val;
int res;
unsigned char byte;
unsigned int byte4;
DBG("%s: cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__,
_ioctl_name(cmd), arg, in_buf,
@@ -861,46 +628,72 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
case TCSETS:
CHECK_BUF_IN(sizeof(struct termios));
{
memcpy(tio, in_buf, in_bufsz);
speed_t speed;
int csize, par, sb;
byte4 = htonl(_tio_baud(tio));
telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &byte4, 4);
memcpy(&tio, in_buf, in_bufsz);
byte = _tio_csize(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &byte, 1);
speed = tio.c_cflag & (CBAUD | CBAUDEX);
val = htonl(baudrate(speed));
telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &val, 4);
byte = _tio_parity(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_PARITY, &byte, 1);
if ((tio.c_cflag & CSIZE) == CS5)
csize = 5;
else if ((tio.c_cflag & CSIZE) == CS6)
csize = 6;
else if ((tio.c_cflag & CSIZE) == CS7)
csize = 7;
else
csize = 8;
telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &csize, 1);
byte = _tio_stopb(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_STOPSIZE, &byte, 1);
if (tio->c_cflag & CRTSCTS)
if (tio.c_cflag & PARENB && tio.c_iflag & IGNPAR)
par = 1;
#ifdef CMSPAR
else if (tio.c_cflag & PARENB && tio.c_iflag & CMSPAR)
{
tty->cmcr |= TIOCM_RTS;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_CTSRTS);
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 (!(tty->cmcr & TIOCM_DTR) && _tio_baud(tio) != 0)
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)
telnet_rfc2217_ctl(tty->tn, TNS_CTL_CTSRTS);
if (!(tty->cmcr & TIOCM_DTR) && speed != B0)
{
tty->cmcr |= TIOCM_DTR;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_ON);
}
_show_termios("TCSETS", tio);
ioctl(tty->fds[FD_SLAVE].fd, cmd, tio);
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 ? '+' : '-');
if (cmd == TCSETSF)
tty->slave_suspended = 0; /* Re-enable polling of Slave */
ioctl(tty->fds[FD_SLAVE].fd, cmd, &tio);
fuse_reply_ioctl(req, 0, 0, 0);
}
break;
case TCGETS:
CHECK_BUF_OUT(sizeof(struct termios));
ioctl(tty->fds[FD_SLAVE].fd, TCGETS, tio);
_show_termios("TCGETS", tio);
fuse_reply_ioctl(req, 0, tio, sizeof(struct termios));
fuse_reply_ioctl(req, 0, &tio, sizeof(struct termios));
break;
case TIOCINQ: /* Get the number of bytes in the input
* buffer */
@@ -915,25 +708,33 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
fuse_reply_ioctl(req, 0, &val, sizeof(int));
break;
case TCFLSH:
switch ((size_t) arg)
if (arg == TCIFLUSH)
{
default:
/*todo */
}
else if ((size_t) arg == TCOFLUSH)
{
/*todo */
}
else if ((size_t) arg == TCIOFLUSH)
{
/*todo */
}
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TCIFLUSH:
byte = 1;
tty->slave_suspended = 0; /* Re-enable polling of Slave */
val = 1;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &val, 1);
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TCOFLUSH:
byte = 2;
tty->pollout = 1;
val = 2;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &val, 1);
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TCIOFLUSH:
byte = 3;
tty->slave_suspended = 0; /* Re-enable polling of Slave */
tty->pollout = 1;
break;
}
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &byte, 1);
ioctl(tty->fds[FD_SLAVE].fd, TCFLSH, arg);
val = 3;
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &val, 1);
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TIOCMGET:
@@ -988,9 +789,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: RTS=%c DTR=%c\n",
tty->cmcr & TIOCM_RTS ? '1' : '0',
tty->cmcr & TIOCM_DTR ? '1' : '0');
DBG("MCR: DTR=%c RTS=%c\n",
tty->cmcr & TIOCM_DTR ? '1' : '0',
tty->cmcr & TIOCM_RTS ? '1' : '0');
break;
case TCSBRK:
fuse_reply_ioctl(req, 0, 0, 0);
@@ -1032,6 +833,7 @@ 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);
@@ -1081,47 +883,34 @@ 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);
}
static const struct cuse_lowlevel_ops ttynvt_op = {
.init = ttynvt_init,
.open = ttynvt_open,
.read = ttynvt_read,
.write = ttynvt_write,
.ioctl = ttynvt_ioctl,
.poll = ttynvt_poll,
.release = ttynvt_release,
.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 }
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),
@@ -1139,7 +928,6 @@ static void print_help(void)
{
fprintf(stderr, "ttynvt Usage:\n"
"\t-D level\tEnable debug\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"
@@ -1221,14 +1009,14 @@ int main(int argc, char *argv[])
switch (cpid)
{
case -1:
_log(LOG_ERR, "fork(): %m\n");
_log(LOG_ERR, "fork(): %m");
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\n", rc);
_log(LOG_ERR, "ttynvt failed rc=%d", rc);
else
DBG("Child exited\n");
return rc;