6 Commits

Author SHA1 Message Date
Frank Rolsted Jensen 7c80995256 fix missing deref of tty data when destroyed (#2) 2024-03-21 14:54:34 +01:00
Frank Rolsted Jensen 299cfcabb5 fix mssing deref of tty data when destroyed 2024-03-21 13:03:43 +01:00
Frank Rolsted Jensen b47cb98a69 Comments updated 2023-10-13 09:24:44 +02:00
Frank Rolsted Jensen cb6509861f Client index included in debug output 2023-10-13 09:23:18 +02:00
Frank Rolsted Jensen 2f0cea6f73 fix mutex unlock if open fails 2023-10-13 09:21:25 +02:00
Frank Rolsted Jensen 015da8a55e Allow multiple clients to open the ttynvt device
This patch allows multiple clients to open the ttynvt device at the same time.
This is useful if an application wants to get/set termios (ioctls) while the
device is already opened by an other application.
2023-10-04 10:53:39 +02:00
16 changed files with 230 additions and 649 deletions
-8
View File
@@ -9,11 +9,3 @@ if BUILD_TESTTOOLS
SUBDIRS += nvtsrv SUBDIRS += nvtsrv
SUBDIRS += nvttest SUBDIRS += nvttest
endif endif
if BUILD_TEST
SUBDIRS += test
endif
.PHONY: $(SUBDIRS) test
$(SUBDIRS) test:
$(MAKE) -C $@
test: $(SUBDIRS)
+2 -5
View File
@@ -1,4 +1,4 @@
AC_INIT([ttynvt],[0.17],[frj@thrane.eu]) AC_INIT([ttynvt],[0.16],[frj@thrane.eu])
AM_INIT_AUTOMAKE([foreign dist-xz]) AM_INIT_AUTOMAKE([foreign dist-xz])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -6,10 +6,10 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_USE_SYSTEM_EXTENSIONS AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC AC_PROG_CC
AC_PROG_CXX
AC_PROG_INSTALL AC_PROG_INSTALL
define([AC_LIBTOOL_LANG_CXX_CONFIG], [:])dnl
define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl
define([AC_LIBTOOL_LANG_GCJ_CONFIG], [:])dnl define([AC_LIBTOOL_LANG_GCJ_CONFIG], [:])dnl
@@ -31,8 +31,6 @@ AC_ARG_ENABLE(testtools,
enable_testtools=no) enable_testtools=no)
AM_CONDITIONAL([BUILD_TESTTOOLS], [test "x$enable_testtools" = "xyes"]) AM_CONDITIONAL([BUILD_TESTTOOLS], [test "x$enable_testtools" = "xyes"])
AM_CONDITIONAL(BUILD_TEST, false)
CFLAGS_WARN="$CFLAGS_WARN -Wall -Wextra -Werror -Wno-unused-parameter" CFLAGS_WARN="$CFLAGS_WARN -Wall -Wextra -Werror -Wno-unused-parameter"
CFLAGS_WARN="$CFLAGS_WARN -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes" CFLAGS_WARN="$CFLAGS_WARN -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes"
CFLAGS_WARN="$CFLAGS_WARN -Waggregate-return -Wpointer-arith -Wshadow -Wwrite-strings" CFLAGS_WARN="$CFLAGS_WARN -Waggregate-return -Wpointer-arith -Wshadow -Wwrite-strings"
@@ -46,7 +44,6 @@ lib/Makefile
src/Makefile src/Makefile
nvtsrv/Makefile nvtsrv/Makefile
nvttest/Makefile nvttest/Makefile
test/Makefile
]) ])
AC_OUTPUT AC_OUTPUT
+15 -83
View File
@@ -7,7 +7,6 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h>
#include "nvt_log.h" #include "nvt_log.h"
#include "nvt_socket.h" #include "nvt_socket.h"
@@ -19,109 +18,42 @@ typedef struct {
union { union {
struct sockaddr gen; struct sockaddr gen;
struct sockaddr_in ipv4; struct sockaddr_in ipv4;
struct sockaddr_in6 ipv6;
struct sockaddr_un un;
}; };
} sa_t; } sa_t;
static int _sa_host_lookup(sa_t *sa, const char *host, const char *port) static int _sa_host_lookup(sa_t * sa, const char *host)
{ {
struct addrinfo hint, *adrinf = NULL, *p; struct hostent *server;
int ret = -1;
memset(&hint, 0, sizeof(struct addrinfo)); server = gethostbyname(host);
if (server == NULL)
if (getaddrinfo(host, port, &hint, &adrinf) != 0)
{ {
nvt_log(LOG_ERR, "Cannot resolve host name: %m\n"); nvt_log(LOG_ERR, "Cannot resolve host name: %m\n");
return -1; return -1;
} }
// Priority is given to the use of IPv4
// This code must be modified if the preffered protocol is changed or varied.
for (p = adrinf; p; p = p->ai_next)
{
if (p->ai_family == AF_INET)
{
sa->slen = sizeof(sa->ipv4); sa->slen = sizeof(sa->ipv4);
memcpy(&sa->ipv4, p->ai_addr, sizeof(struct sockaddr_in));
ret = 0; sa->ipv4.sin_family = AF_INET;
goto exit_host_lookup; memcpy(&sa->ipv4.sin_addr.s_addr, server->h_addr, server->h_length);
}
} return 0;
for (p = adrinf; p; p = p->ai_next)
{
if (p->ai_family == AF_INET6)
{
sa->slen = sizeof(sa->ipv6);
memcpy(&sa->ipv6, p->ai_addr, sizeof(struct sockaddr_in6));
ret = 0;
goto exit_host_lookup;
}
}
exit_host_lookup:
freeaddrinfo(adrinf);
return ret;
} }
static int _sa_addr_parse(sa_t *sa, const char *addr) static int _sa_addr_parse(sa_t * sa, const char *addr)
{ {
ssize_t host_len; char host[64], port[64];
char host[64];
const char *port;
memset(sa, 0, sizeof(sa_t)); memset(sa, 0, sizeof(sa_t));
if (strncmp(addr, "unix:", 5) == 0) sscanf(addr, "%63[^:]:%63s", host, port);
{ // case of unix domain sockets ...
addr += 5; // skip "unix:" prefix
if (strlen(addr) > sizeof(sa->un.sun_path) - 1)
{
nvt_log(LOG_ERR, "UNIX domain socket path is too long.\n");
return -1;
}
sa->slen = sizeof(sa->un);
sa->un.sun_family = AF_UNIX;
strcpy(sa->un.sun_path, addr);
sa->type = SOCK_STREAM;
return 0;
}
if (!(port = strrchr(addr, ':'))) if (_sa_host_lookup(sa, host) != 0)
{
nvt_log(LOG_ERR, "bad server address format.\n");
return -1;
}
host_len = (port++) - addr;
// *port indicates the last ':', its next is 1st char of the port number
if (host_len >= 2 && addr[0] == '[' && addr[host_len - 1] == ']')
{
// IPv6 addresses are enclosed in pair of square brackets.
addr += 1;
host_len -= 2;
}
else
{
if (strchr(addr, ':') != port - 1)
{
nvt_log(LOG_ERR,
"addresses containing colons must be enclosed in square brackets.\n");
return -1;
}
}
if ((ssize_t) sizeof(host) <= host_len)
{
nvt_log(LOG_ERR, "host name is too long.\n");
return -1;
}
strncpy(host, addr, host_len);
host[host_len] = '\0'; // strncpy doesn't add null char
if (_sa_host_lookup(sa, host, port) != 0)
return -1; return -1;
sa->type = SOCK_STREAM; sa->type = SOCK_STREAM;
sa->ipv4.sin_port = htons(atoi(port));
return 0; return 0;
} }
@@ -171,7 +103,7 @@ int socket_create_server(const char *addr)
return -1; return -1;
} }
if (sa.gen.sa_family == AF_INET || sa.gen.sa_family == AF_INET6) if (sa.gen.sa_family == AF_INET)
{ {
/* Avoid TIME_WAIT state on socket close */ /* Avoid TIME_WAIT state on socket close */
opt = 1; opt = 1;
+11 -17
View File
@@ -14,15 +14,9 @@
#include "telnet.h" #include "telnet.h"
/**INDENT-OFF**/ #define HELP \
static const char help_text[] = "Usage: nvtsrv -deq\n"
"Usage: nvtsrv OPTIONS... HOST:PORT\n"
"OPTIONS:\n"
" -d : Enable debug\n"
" -e : Echo received data\n"
" -q : Quiet\n"
;
/**INDENT-ON**/
struct { struct {
unsigned char mline; unsigned char mline;
@@ -36,10 +30,10 @@ int nfds;
#define fd_cli pfds[2].fd #define fd_cli pfds[2].fd
static void _usage(int rc) static void _nvtsrv_usage(void)
{ {
printf(help_text); printf("Usage:\n" " nvtsrv host:port\n");
exit(rc); exit(0);
} }
static void _tn_cli_close(void) static void _tn_cli_close(void)
@@ -74,7 +68,7 @@ static void _tn_tx(void *cctx, const void *buf, int len)
static void _tn_ss(void *cctx, int signal, int value) static void _tn_ss(void *cctx, int signal, int value)
{ {
DBG("RFC2217 signal %d: %02x", signal, value); nvt_log(LOG_INFO, "RFC2217 signal %d: %02x", signal, value);
} }
static void _nvtsrv_init(void) static void _nvtsrv_init(void)
@@ -86,7 +80,7 @@ static void _nvtsrv_init(void)
dd.tnct = telnet_ctx_init(NULL, _tn_tx, _tn_ss); dd.tnct = telnet_ctx_init(NULL, _tn_tx, _tn_ss);
} }
static void _nvtsrv_cmd(tn_ctx_t *tcc, const char *line, int len) static void _nvtsrv_cmd(tn_ctx_t * tcc, const char *line, int len)
{ {
int val; int val;
@@ -133,8 +127,8 @@ int main(int argc, char **argv)
switch (opt) switch (opt)
{ {
default: /* '?' */ default: /* '?' */
_usage(0); fprintf(stderr, HELP);
break; exit(EXIT_FAILURE);
case 'd': case 'd':
debug += 1; debug += 1;
break; break;
@@ -150,7 +144,7 @@ int main(int argc, char **argv)
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (argc <= 0) if (argc <= 0)
_usage(1); _nvtsrv_usage();
nvt_log_level(quiet, debug); nvt_log_level(quiet, debug);
nvt_log_dest(NVT_LOG_STDOUT); nvt_log_dest(NVT_LOG_STDOUT);
+6 -6
View File
@@ -36,7 +36,7 @@ static const unsigned char tn_opt[256] = {
}; };
static void _telnet_handle_req(tn_ctx_t *tcc, int cmd, int opt) static void _telnet_handle_req(tn_ctx_t * tcc, int cmd, int opt)
{ {
char buf[3]; char buf[3];
@@ -63,7 +63,7 @@ static void _telnet_handle_req(tn_ctx_t *tcc, int cmd, int opt)
telnet_rfc2217_init(tcc, TNI_ON); telnet_rfc2217_init(tcc, TNI_ON);
} }
void telnet_reply_opt(tn_ctx_t *tcc, int opt, int prm, void telnet_reply_opt(tn_ctx_t * tcc, int opt, int prm,
const void *val, int len) const void *val, int len)
{ {
unsigned char buf[128], *pu = buf; unsigned char buf[128], *pu = buf;
@@ -94,7 +94,7 @@ void telnet_reply_opt(tn_ctx_t *tcc, int opt, int prm,
tcc->cli_tx(tcc->cctx, buf, len); tcc->cli_tx(tcc->cctx, buf, len);
} }
static void _telnet_handle_opt(tn_ctx_t *tcc, int opt, static void _telnet_handle_opt(tn_ctx_t * tcc, int opt,
unsigned char *pu, int nd) unsigned char *pu, int nd)
{ {
DBG2_BUF("opti", pu, nd); DBG2_BUF("opti", pu, nd);
@@ -111,7 +111,7 @@ 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, int *plen)
{ {
char *s, *p; char *s, *p;
unsigned char *pu; unsigned char *pu;
@@ -230,7 +230,7 @@ int telnet_rx(tn_ctx_t *tcc, char *buf, int *plen)
return 0; return 0;
} }
int telnet_tx(tn_ctx_t *tcc, const char *buf, int len) int telnet_tx(tn_ctx_t * tcc, const char *buf, int len)
{ {
const char *s, *p; const char *s, *p;
int rem, nd; int rem, nd;
@@ -260,7 +260,7 @@ int telnet_tx(tn_ctx_t *tcc, const char *buf, int len)
return 0; return 0;
} }
tn_ctx_t *telnet_ctx_init(void *cctx, cli_tx_f *ftx, cli_ss_f *fss) tn_ctx_t *telnet_ctx_init(void *cctx, cli_tx_f * ftx, cli_ss_f * fss)
{ {
tn_ctx_t *tcc; tn_ctx_t *tcc;
+8 -6
View File
@@ -10,7 +10,7 @@
#include "telnet.h" #include "telnet.h"
#include "telnet_param.h" #include "telnet_param.h"
void telnet_rfc2217_init(tn_ctx_t *tcc, int when) void telnet_rfc2217_init(tn_ctx_t * tcc, int when)
{ {
switch (when) switch (when)
{ {
@@ -39,7 +39,7 @@ void telnet_rfc2217_init(tn_ctx_t *tcc, int when)
} }
void telnet_rfc2217_handle_opt(tn_ctx_t *tcc, int opt, void telnet_rfc2217_handle_opt(tn_ctx_t * tcc, int opt,
unsigned char *pu, int nd) unsigned char *pu, int nd)
{ {
unsigned int sub, vali, valo, val4; unsigned int sub, vali, valo, val4;
@@ -138,7 +138,8 @@ void telnet_rfc2217_handle_opt(tn_ctx_t *tcc, int opt,
case 9: /* Set DTR Signal State OFF */ case 9: /* Set DTR Signal State OFF */
valo = vali; valo = vali;
tcc->dtr_state = (valo == 8) ? 1 : 0; tcc->dtr_state = (valo == 8) ? 1 : 0;
DBG("RFC2217: Set DTR %s", tcc->dtr_state ? "on" : "off"); nvt_log(LOG_INFO, "RFC2217: Set DTR %s",
tcc->dtr_state ? "on" : "off");
goto do_ctl_reply; goto do_ctl_reply;
case 10: /* Request RTS Signal State */ case 10: /* Request RTS Signal State */
@@ -148,7 +149,8 @@ void telnet_rfc2217_handle_opt(tn_ctx_t *tcc, int opt,
case 12: /* Set RTS Signal State OFF */ case 12: /* Set RTS Signal State OFF */
valo = vali; valo = vali;
tcc->rts_state = (valo == 11) ? 1 : 0; tcc->rts_state = (valo == 11) ? 1 : 0;
DBG("RFC2217: Set RTS %s", tcc->rts_state ? "on" : "off"); nvt_log(LOG_INFO, "RFC2217: Set RTS %s",
tcc->rts_state ? "on" : "off");
goto do_ctl_reply; goto do_ctl_reply;
case 13: /* Request Com Port Flow Control Setting (inbound) */ case 13: /* Request Com Port Flow Control Setting (inbound) */
@@ -218,7 +220,7 @@ void telnet_rfc2217_handle_opt(tn_ctx_t *tcc, int opt,
} }
} }
void telnet_rfc2217_change_linestate(tn_ctx_t *tcc, int mask) void telnet_rfc2217_change_linestate(tn_ctx_t * tcc, int mask)
{ {
unsigned char buf[1]; unsigned char buf[1];
@@ -236,7 +238,7 @@ void telnet_rfc2217_change_linestate(tn_ctx_t *tcc, int mask)
tcc->linestate = mask; tcc->linestate = mask;
} }
void telnet_rfc2217_change_modemstate(tn_ctx_t *tcc, int mask) void telnet_rfc2217_change_modemstate(tn_ctx_t * tcc, int mask)
{ {
unsigned char buf[1]; unsigned char buf[1];
+1 -1
View File
@@ -6,4 +6,4 @@ nvttest_SOURCES = \
nvttest_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(CFLAGS_WARN) nvttest_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(CFLAGS_WARN)
nvttest_LDADD = $(top_builddir)/lib/libnvt.la -lpthread nvttest_LDADD = $(top_builddir)/lib/libnvt.la
+22 -67
View File
@@ -7,7 +7,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
@@ -17,20 +16,10 @@
#include "lib/nvt_log.h" #include "lib/nvt_log.h"
/**INDENT-OFF**/ #define HELP \
static const char help_text[] = "Usage: nvttest -dqr [-n loops] [-t threads]\n"
"Usage: nvttest OPTIONS...\n"
"OPTIONS:\n"
" -D name : Specify device name (/dev/name, default /dev/ttyNVT0)\n"
" -d : Enable debug\n"
" -n loops : Test loops per thread\n"
" -q : Quiet\n"
" -r : Insert random delay in loops\n"
" -t threads : N. threads\n"
;
/**INDENT-ON**/
static char dev[64] = "/dev/ttyNVT0"; static const char *dev = "/dev/ttyNVT0";
typedef struct { typedef struct {
int nthread; int nthread;
@@ -39,37 +28,22 @@ typedef struct {
bool opt_rand_delay; bool opt_rand_delay;
} topts_t; } topts_t;
typedef struct {
pthread_t thr;
topts_t opts;
int errors;
} thr_t;
static void _usage(int rc)
{
printf(help_text);
exit(rc);
}
static void *_worker(void *arg) static void *_worker(void *arg)
{ {
thr_t *thr = arg; topts_t *topts = arg;
int iloop; int iloop;
int fd, err, nw; int fd, err, nw;
struct termios ios; struct termios ios;
for (iloop = 0; iloop < thr->opts.nloop; iloop++) for (iloop = 0; iloop < topts->nloop; iloop++)
{ {
nvt_log(LOG_INFO, "Thread %d/%d run %d/%d\n", nvt_log(LOG_INFO, "Thread %d/%d run %d/%d\n",
thr->opts.ithread, thr->opts.nthread, topts->ithread, topts->nthread, iloop + 1, topts->nloop);
iloop + 1, thr->opts.nloop);
{ {
fd = open(dev, O_RDWR); fd = open(dev, O_RDWR);
if (fd < 0) if (fd < 0)
{ {
nvt_log(LOG_ERR, "Open '%s' failed: %m\n", dev); nvt_log(LOG_ERR, "Open '%s' failed: %m\n", dev);
thr->errors++;
break; break;
} }
@@ -78,14 +52,12 @@ static void *_worker(void *arg)
if (err != 0) if (err != 0)
{ {
nvt_log(LOG_ERR, "ioctl(TCGETS) failed: %m\n"); nvt_log(LOG_ERR, "ioctl(TCGETS) failed: %m\n");
thr->errors++;
break; break;
} }
err = ioctl(fd, TCSETS, &ios); err = ioctl(fd, TCSETS, &ios);
if (err != 0) if (err != 0)
{ {
nvt_log(LOG_ERR, "ioctl(TCSETS) failed: %m\n"); nvt_log(LOG_ERR, "ioctl(TCSETS) failed: %m\n");
thr->errors++;
break; break;
} }
close(fd); close(fd);
@@ -102,7 +74,6 @@ static void *_worker(void *arg)
if (fd < 0) if (fd < 0)
{ {
nvt_log(LOG_ERR, "Open '%s' failed: %m\n", dev); nvt_log(LOG_ERR, "Open '%s' failed: %m\n", dev);
thr->errors++;
break; break;
} }
@@ -113,20 +84,18 @@ static void *_worker(void *arg)
if (nw < 0) if (nw < 0)
{ {
nvt_log(LOG_ERR, "write failed: %m\n"); nvt_log(LOG_ERR, "write failed: %m\n");
thr->errors++;
break; break;
} }
if (nw < len) if (nw < len)
{ {
nvt_log(LOG_ERR, "write short: %u/%u\n", nw, len); nvt_log(LOG_ERR, "write short: %u/%u\n", nw, len);
thr->errors++;
break; break;
} }
} }
close(fd); close(fd);
} }
if (thr->opts.opt_rand_delay) if (topts->opt_rand_delay)
usleep(rand() & 0xfff); usleep(rand() & 0xfff);
} }
@@ -135,28 +104,24 @@ static void *_worker(void *arg)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int err, errors; int err;
int opt; int opt;
int ithr, nthr; int ithr, nthr;
thr_t *threads, *thr; topts_t topts = { }, *popts;
topts_t topts = { }; pthread_t *ptids;
int debug, quiet; int debug, quiet;
struct stat st;
debug = quiet = 0; debug = quiet = 0;
nthr = 1; nthr = 1;
topts.nloop = 1; topts.nloop = 1;
while ((opt = getopt(argc, argv, "D:dn:qrt:")) != -1) while ((opt = getopt(argc, argv, "dn:qrt:")) != -1)
{ {
switch (opt) switch (opt)
{ {
default: /* '?' */ default: /* '?' */
_usage(1); fprintf(stderr, HELP);
break; exit(EXIT_FAILURE);
case 'D':
snprintf(dev, sizeof(dev), "/dev/%s", optarg);
break;
case 'd': case 'd':
debug += 1; debug += 1;
break; break;
@@ -175,28 +140,22 @@ int main(int argc, char **argv)
} }
} }
if (stat(dev, &st) || !S_ISCHR(st.st_mode))
{
fprintf(stderr, "Invalid device file: '%s'\n", dev);
exit(EXIT_FAILURE);
}
nvt_log_level(quiet, debug); nvt_log_level(quiet, debug);
nvt_log_dest(NVT_LOG_STDOUT); nvt_log_dest(NVT_LOG_STDOUT);
topts.nthread = nthr; topts.nthread = nthr;
threads = calloc(nthr, sizeof(thr_t)); ptids = malloc(nthr * sizeof(pthread_t));
for (ithr = 0; ithr < nthr; ithr++) for (ithr = 0; ithr < nthr; ithr++)
{ {
nvt_log(LOG_INFO, "Create worker thread %d/%d\n", ithr + 1, nthr); nvt_log(LOG_INFO, "Create worker thread %d/%d\n", ithr + 1, nthr);
thr = &threads[ithr]; popts = malloc(sizeof(topts_t));
thr->opts = topts; *popts = topts;
thr->opts.ithread = ithr + 1; popts->ithread = ithr + 1;
err = pthread_create(&thr->thr, NULL, _worker, thr); err = pthread_create(&ptids[ithr], NULL, _worker, popts);
if (err) if (err)
{ {
nvt_log(LOG_ERR, "Failed to create thread %d/%d\n", ithr, nthr); nvt_log(LOG_ERR, "Failed to create thread %d/%d\n", ithr, nthr);
@@ -204,17 +163,13 @@ int main(int argc, char **argv)
} }
} }
errors = 0;
for (ithr = 0; ithr < nthr; ithr++) for (ithr = 0; ithr < nthr; ithr++)
{ {
thr = &threads[ithr]; if (ptids[ithr] == 0)
if (thr->thr == 0)
break; break;
pthread_join(thr->thr, NULL); pthread_join(ptids[ithr], NULL);
errors += thr->errors; nvt_log(LOG_INFO, "Joined worker thread %d/%d\n", ithr + 1, nthr);
nvt_log(LOG_INFO, "Joined worker thread %d/%d (err=%d)\n",
ithr + 1, nthr, thr->errors);
} }
return errors ? 1 : 0; return 0;
} }
+13 -14
View File
@@ -7,7 +7,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <errno.h>
#include "telnet.h" #include "telnet.h"
#include "telnet_param.h" #include "telnet_param.h"
@@ -39,7 +38,7 @@ static const unsigned char tn_opt[256] = {
}; };
static void _telnet_handle_req(tn_ctx_t *tcc, int cmd, int opt) static void _telnet_handle_req(tn_ctx_t * tcc, int cmd, int opt)
{ {
char buf[3]; char buf[3];
@@ -68,7 +67,7 @@ static void _telnet_handle_req(tn_ctx_t *tcc, int cmd, int opt)
tcc->srv_tx(tcc->cctx, buf, 3); tcc->srv_tx(tcc->cctx, buf, 3);
} }
static void _telnet_handle_resp(tn_ctx_t *tcc, int cmd, int opt) static void _telnet_handle_resp(tn_ctx_t * tcc, int cmd, int opt)
{ {
if (opt == TNO_CPCO) if (opt == TNO_CPCO)
{ {
@@ -77,7 +76,7 @@ static void _telnet_handle_resp(tn_ctx_t *tcc, int cmd, int opt)
} }
} }
void telnet_set_opt(tn_ctx_t *tcc, int opt, int prm, const void *val, int len) void telnet_set_opt(tn_ctx_t * tcc, int opt, int prm, const void *val, int len)
{ {
unsigned char buf[128], *pu = buf; unsigned char buf[128], *pu = buf;
const unsigned char *pv; const unsigned char *pv;
@@ -107,7 +106,7 @@ void telnet_set_opt(tn_ctx_t *tcc, int opt, int prm, const void *val, int len)
tcc->srv_tx(tcc->cctx, buf, len); tcc->srv_tx(tcc->cctx, buf, len);
} }
static void _telnet_handle_opt(tn_ctx_t *tcc, int opt, static void _telnet_handle_opt(tn_ctx_t * tcc, int opt,
unsigned char *pu, int nd) unsigned char *pu, int nd)
{ {
switch (opt) switch (opt)
@@ -121,7 +120,7 @@ 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, int *plen)
{ {
char *s, *p; char *s, *p;
unsigned char *pu; unsigned char *pu;
@@ -234,7 +233,7 @@ int telnet_rx(tn_ctx_t *tcc, char *buf, int *plen)
return 0; return 0;
} }
int telnet_tx(tn_ctx_t *tcc, const char *buf, int len) int telnet_tx(tn_ctx_t * tcc, const char *buf, int len)
{ {
const char *s, *p; const char *s, *p;
int rem, nd; int rem, nd;
@@ -264,7 +263,7 @@ int telnet_tx(tn_ctx_t *tcc, const char *buf, int len)
return 0; return 0;
} }
int telnet_open(tn_ctx_t *tcc, int mode) int telnet_open(tn_ctx_t * tcc, int mode)
{ {
time_t t = time(NULL);; time_t t = time(NULL);;
char buf[3]; char buf[3];
@@ -285,17 +284,17 @@ int telnet_open(tn_ctx_t *tcc, int mode)
while (1) while (1)
{ {
res = tcc->srv_rx(tcc->cctx, 1000); res = tcc->srv_rx(tcc->cctx, 1000);
if (res < 0) if (res <= 0)
return errno; return -1;
else if (tcc->mode_rfc2217) else if (tcc->mode_rfc2217)
return 0; return 0;
else if (time(NULL) - t > 10) else if (t - time(NULL) > 2)
return EPROTO; return -1;
} }
} }
tn_ctx_t *telnet_ctx_init(void *cctx, srv_tx_f *ftx, tn_ctx_t *telnet_ctx_init(void *cctx, srv_tx_f * ftx,
srv_rx_f *frx, srv_ms_f *fms) srv_rx_f * frx, srv_ms_f * fms)
{ {
tn_ctx_t *tcc; tn_ctx_t *tcc;
+3 -3
View File
@@ -7,7 +7,7 @@
#include "telnet.h" #include "telnet.h"
#include "telnet_param.h" #include "telnet_param.h"
void telnet_rfc2217_handle_opt(tn_ctx_t *tcc, int opt, void telnet_rfc2217_handle_opt(tn_ctx_t * tcc, int opt,
unsigned char *pu, int nd) unsigned char *pu, int nd)
{ {
unsigned int sub; unsigned int sub;
@@ -32,7 +32,7 @@ void telnet_rfc2217_handle_opt(tn_ctx_t *tcc, int opt,
} }
} }
void telnet_rfc2217_cfg(tn_ctx_t *tcc, int cmd, const void *val, int nd) void telnet_rfc2217_cfg(tn_ctx_t * tcc, int cmd, const void *val, int nd)
{ {
if (!tcc->mode_rfc2217) if (!tcc->mode_rfc2217)
return; return;
@@ -40,7 +40,7 @@ void telnet_rfc2217_cfg(tn_ctx_t *tcc, int cmd, const void *val, int nd)
telnet_set_opt(tcc, TNO_CPCO, cmd, val, 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, unsigned int val)
{ {
unsigned char byte = val; unsigned char byte = val;
+146 -71
View File
@@ -55,14 +55,17 @@ static struct ttynvt_param {
typedef struct { typedef struct {
pthread_mutex_t tty_lock; pthread_mutex_t tty_lock;
pthread_mutex_t poll_lock; pthread_mutex_t poll_lock;
pthread_mutex_t read_lock;
pthread_mutex_t write_lock;
pthread_mutex_t ioctl_lock;
pthread_t ptid_poll; pthread_t ptid_poll;
pthread_t ptid_read; pthread_t ptid_read;
struct fuse_pollhandle *ph; struct fuse_pollhandle *ph;
volatile int rel_pending; volatile int rel_pending;
volatile int slave_suspended; /*waiting for application to read data */ volatile int slave_suspended; /* Waiting for application to read data */
volatile int master_suspended; /*server is not ready to receive data */ volatile int master_suspended; /* Server is not ready to receive data */
struct pollfd fds[3]; struct pollfd fds[3];
char net_buf[NET_BUF_SIZE]; char net_buf[NET_BUF_SIZE];
@@ -80,8 +83,18 @@ typedef struct {
int epipe; int epipe;
tn_ctx_t *tn; tn_ctx_t *tn;
int seq;
struct ttynvt_cli_s *cli;
} ttynvt_t; } ttynvt_t;
static ttynvt_t *ptty = NULL;
typedef struct ttynvt_cli_s {
int id;
ttynvt_t *tty;
struct ttynvt_cli_s *next;
} ttynvt_cli_t;
static pthread_mutex_t access_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t access_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -114,7 +127,6 @@ static void ttynvt_interrupted(fuse_req_t req, void *data)
pthread_kill(thread, SIGUSR2); pthread_kill(thread, SIGUSR2);
} }
static void _ttynvt_sleep_us(unsigned int tus) static void _ttynvt_sleep_us(unsigned int tus)
{ {
struct timespec ts; struct timespec ts;
@@ -143,14 +155,13 @@ static int _check_dev(void)
return rc; return rc;
} }
static void _fd_close(int fd) static void _fd_close(int fd)
{ {
if (fd >= 0) if (fd >= 0)
close(fd); close(fd);
} }
static void _notify(ttynvt_t *tty) static void _notify(ttynvt_t * tty)
{ {
pthread_mutex_lock(&tty->poll_lock); pthread_mutex_lock(&tty->poll_lock);
if (tty->ph) if (tty->ph)
@@ -162,7 +173,7 @@ static void _notify(ttynvt_t *tty)
pthread_mutex_unlock(&tty->poll_lock); pthread_mutex_unlock(&tty->poll_lock);
} }
static void _update_notify(ttynvt_t *tty, struct fuse_pollhandle *ph) static void _update_notify(ttynvt_t * tty, struct fuse_pollhandle *ph)
{ {
pthread_mutex_lock(&tty->poll_lock); pthread_mutex_lock(&tty->poll_lock);
struct fuse_pollhandle *tmp_ph = tty->ph; struct fuse_pollhandle *tmp_ph = tty->ph;
@@ -173,7 +184,7 @@ static void _update_notify(ttynvt_t *tty, struct fuse_pollhandle *ph)
fuse_pollhandle_destroy(tmp_ph); fuse_pollhandle_destroy(tmp_ph);
} }
static int _net_read_check_exit(ttynvt_t *tty) static int _net_read_check_exit(ttynvt_t * tty)
{ {
if ((tty->tio.c_cflag & CLOCAL) == 0 && if ((tty->tio.c_cflag & CLOCAL) == 0 &&
(tty->smcr_last & TIOCM_CD) != 0 && (tty->smcr & TIOCM_CD) == 0) (tty->smcr_last & TIOCM_CD) != 0 && (tty->smcr & TIOCM_CD) == 0)
@@ -397,7 +408,7 @@ static ttynvt_t *_ttynvt_ctx_create(void)
return tty; return tty;
} }
static void _ttynvt_ctx_destroy(ttynvt_t *tty) static void _ttynvt_ctx_destroy(ttynvt_t * tty)
{ {
if (tty) if (tty)
free(tty->tn); free(tty->tn);
@@ -406,24 +417,49 @@ static void _ttynvt_ctx_destroy(ttynvt_t *tty)
static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info) static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
{ {
ttynvt_t *tty; ttynvt_t *tty = ptty;
ttynvt_cli_t *tty_cli;
char *slave_name; char *slave_name;
struct termios ios; struct termios ios;
int fd; int fd;
int res; int res;
int n; int n;
pthread_mutex_lock(&access_lock);
nvt_log(LOG_INFO, "Connecting to server: %s\n", ttynvt_param.server); tty_cli = calloc(1, sizeof(ttynvt_cli_t));
if (!tty_cli)
tty = _ttynvt_ctx_create();
if (!tty)
{ {
fuse_reply_err(req, ENOMEM); fuse_reply_err(req, ENOMEM);
return; return;
} }
pthread_mutex_lock(&access_lock);
if (tty == NULL)
tty = _ttynvt_ctx_create();
if (!tty)
{
fuse_reply_err(req, ENOMEM);
free(tty_cli);
pthread_mutex_unlock(&access_lock);
return;
}
tty_cli->id = tty->seq++;
tty_cli->tty = tty;
tty_cli->next = tty->cli;
tty->cli = tty_cli;
nvt_log(LOG_INFO, "Client open (%d)\n", tty_cli->id);
if (ptty)
goto out;
ptty = tty;
nvt_log(LOG_INFO, "Connecting to server: %s\n", ttynvt_param.server);
fd = socket_create_client(ttynvt_param.server); fd = socket_create_client(ttynvt_param.server);
if (fd < 0) if (fd < 0)
{ {
@@ -435,7 +471,7 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
tty->fds[FD_NET].events = POLLIN; tty->fds[FD_NET].events = POLLIN;
tty->net_cnt = 0; tty->net_cnt = 0;
if ((fd = open("/dev/ptmx", O_RDWR)) < 0) /* open master */ if ((fd = open("/dev/ptmx", O_RDWR)) < 0) /* Open master */
goto open_err; goto open_err;
tty->fds[FD_MASTER].fd = fd; tty->fds[FD_MASTER].fd = fd;
@@ -453,11 +489,11 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
if (ioctl(fd, TCSETS, &ios) < 0) if (ioctl(fd, TCSETS, &ios) < 0)
goto open_err; goto open_err;
grantpt(fd); /* change permission of slave */ grantpt(fd); /* Change permission of slave */
unlockpt(fd); /* unlock slave */ unlockpt(fd); /* Unlock slave */
slave_name = ptsname(fd); /* get name of slave */ slave_name = ptsname(fd); /* Get name of slave */
if ((fd = open(slave_name, O_RDWR | O_NOCTTY)) < 0) /* open slave */ if ((fd = open(slave_name, O_RDWR | O_NOCTTY)) < 0) /* Open slave */
goto open_err; goto open_err;
ioctl(fd, TCGETS, &tty->tio); ioctl(fd, TCGETS, &tty->tio);
@@ -470,34 +506,42 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
res = res =
telnet_open(tty->tn, ttynvt_param.raw ? TN_MODE_RAW : TN_MODE_TELNET); telnet_open(tty->tn, ttynvt_param.raw ? TN_MODE_RAW : TN_MODE_TELNET);
if (res != 0) if (res < 0)
{ {
errno = res; errno = EPROTO;
goto open_err; goto open_err;
} }
if ((res = pthread_mutex_init(&tty->tty_lock, NULL)) || if ((res = pthread_mutex_init(&tty->tty_lock, NULL) < 0) ||
(res = pthread_mutex_init(&tty->poll_lock, NULL)) || (res = pthread_mutex_init(&tty->poll_lock, NULL) < 0) ||
(res = pthread_mutex_init(&tty->ioctl_lock, NULL) < 0) ||
(res = pthread_mutex_init(&tty->read_lock, NULL) < 0) ||
(res = pthread_mutex_init(&tty->write_lock, NULL) < 0) ||
(res = pthread_create(&tty->ptid_poll, NULL, &_read_net, tty))) (res = pthread_create(&tty->ptid_poll, NULL, &_read_net, tty)))
{ {
errno = res; errno = res;
goto open_err; goto open_err;
} }
info->fh = (uintptr_t) tty; nvt_log(LOG_INFO, "Connected to server: %s\n", ttynvt_param.server);
out:
info->fh = (uintptr_t) tty_cli;
info->nonseekable = 1; info->nonseekable = 1;
info->direct_io = 1; info->direct_io = 1;
nvt_log(LOG_INFO, "Connected to server: %s\n", ttynvt_param.server);
fuse_reply_open(req, info); fuse_reply_open(req, info);
pthread_mutex_unlock(&access_lock);
return; return;
open_err: open_err:
for (n = 0; n < 3; n++) for (n = 0; n < 3; n++)
_fd_close(tty->fds[n].fd); _fd_close(tty->fds[n].fd);
free(tty_cli);
_ttynvt_ctx_destroy(tty); _ttynvt_ctx_destroy(tty);
ptty = NULL;
fuse_reply_err(req, errno); fuse_reply_err(req, errno);
@@ -511,11 +555,14 @@ static void
ttynvt_read(fuse_req_t req, size_t size, off_t off, ttynvt_read(fuse_req_t req, size_t size, off_t off,
struct fuse_file_info *info) struct fuse_file_info *info)
{ {
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh; ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
ttynvt_t *tty = tty_cli->tty;
char buf[TMP_BUF_SIZE]; char buf[TMP_BUF_SIZE];
int res, nr; int res, nr;
DBG2("%s\n", __func__); pthread_mutex_lock(&tty->read_lock);
DBG2("%s (%d)\n", __func__, tty_cli->id);
if (tty->error) if (tty->error)
{ {
@@ -529,7 +576,7 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
else else
fuse_reply_err(req, EBADFD); fuse_reply_err(req, EBADFD);
return; goto out;
} }
if (size > TMP_BUF_SIZE) if (size > TMP_BUF_SIZE)
@@ -556,36 +603,42 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
{ {
DBG2("%s: error: %m\n", __func__); DBG2("%s: error: %m\n", __func__);
fuse_reply_err(req, errno); fuse_reply_err(req, errno);
return; goto out;
} }
DBG2("%s: fd=%d: sz=%u/%u: '%.*s'\n", __func__, DBG2("%s (%d): fd=%d: sz=%u/%u: '%.*s'\n", __func__,
tty->fds[FD_SLAVE].fd, res, (int)size, res, buf); tty_cli->id, tty->fds[FD_SLAVE].fd, res, (int)size, res, buf);
fuse_reply_buf(req, buf, res); fuse_reply_buf(req, buf, res);
out:
pthread_mutex_unlock(&tty->read_lock);
} }
static void static void
ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off, ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
struct fuse_file_info *info) struct fuse_file_info *info)
{ {
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh; ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
ttynvt_t *tty = tty_cli->tty;
int res; int res;
DBG2("%s: fd=%d: sz=%u: '%.*s'\n", __func__, pthread_mutex_lock(&tty->write_lock);
tty->fds[FD_SLAVE].fd, (int)size, (int)size, data);
DBG2("%s (%d): fd=%d: sz=%u: '%.*s'\n", __func__,
tty_cli->id, tty->fds[FD_SLAVE].fd, (int)size, (int)size, data);
if (size == 0) if (size == 0)
{ {
fuse_reply_write(req, 0); fuse_reply_write(req, 0);
return; goto out;
} }
if (tty->error) if (tty->error)
{ {
DBG2("%s: tty->error=%d\n", __func__, tty->error); DBG2("%s: tty->error=%d\n", __func__, tty->error);
fuse_reply_err(req, EPIPE); fuse_reply_err(req, EPIPE);
return; goto out;
} }
res = write(tty->fds[FD_SLAVE].fd, data, size); res = write(tty->fds[FD_SLAVE].fd, data, size);
@@ -594,7 +647,7 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
DBG2("%s: error: %m\n", __func__); DBG2("%s: error: %m\n", __func__);
tty->error = 1; tty->error = 1;
fuse_reply_err(req, errno); fuse_reply_err(req, errno);
return; goto out;
} }
else if (res != (int)size) else if (res != (int)size)
{ {
@@ -603,6 +656,9 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
} }
fuse_reply_write(req, res); fuse_reply_write(req, res);
out:
pthread_mutex_unlock(&tty->write_lock);
} }
static int baudrate(speed_t speed) static int baudrate(speed_t speed)
@@ -691,12 +747,10 @@ static unsigned int _tio_parity(const struct termios2 *tio)
{ {
unsigned int par; unsigned int par;
if (!(tio->c_cflag & PARENB)) if ((tio->c_cflag & PARENB) && (tio->c_iflag & IGNPAR))
{
par = 1; par = 1;
}
#ifdef CMSPAR #ifdef CMSPAR
else if (tio->c_iflag & CMSPAR) else if ((tio->c_cflag & PARENB) && (tio->c_iflag & CMSPAR))
{ {
if (tio->c_cflag & PARODD) if (tio->c_cflag & PARODD)
par = 4; par = 4;
@@ -704,14 +758,10 @@ static unsigned int _tio_parity(const struct termios2 *tio)
par = 5; par = 5;
} }
#endif #endif
else if (tio->c_cflag & PARODD) else if (tio->c_cflag & PARENB)
{
par = 2;
}
else
{
par = 3; par = 3;
} else
par = 1;
return par; return par;
} }
@@ -786,7 +836,8 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
struct fuse_file_info *info, unsigned int flags, struct fuse_file_info *info, unsigned int flags,
const void *in_buf, size_t in_bufsz, size_t out_bufsz) const void *in_buf, size_t in_bufsz, size_t out_bufsz)
{ {
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh; ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
ttynvt_t *tty = tty_cli->tty;
struct termios2 *tio = &tty->tio; struct termios2 *tio = &tty->tio;
struct winsize ws; struct winsize ws;
unsigned int tiocm, baud; unsigned int tiocm, baud;
@@ -796,14 +847,16 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
uint8_t byte; uint8_t byte;
uint32_t byte4; uint32_t byte4;
DBG("%s: cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__, pthread_mutex_lock(&tty->ioctl_lock);
_ioctl_name(cmd), arg, in_buf,
DBG("%s (%d): cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__,
tty_cli->id, _ioctl_name(cmd), arg, in_buf,
(unsigned int)in_bufsz, (unsigned int)out_bufsz); (unsigned int)in_bufsz, (unsigned int)out_bufsz);
if (ttynvt_param.raw) if (ttynvt_param.raw)
{ {
fuse_reply_ioctl(req, 0, 0, 0); fuse_reply_ioctl(req, 0, 0, 0);
return; goto out;
} }
switch (cmd) switch (cmd)
@@ -824,12 +877,8 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
goto do_tcset; goto do_tcset;
do_tcset: do_tcset:
/* BEWARE of termios/termios2 buf structure difference! */ /* BEWARE of termios/termios2 buf structure difference! */
if (baud != 0)
{
byte4 = htonl(baud); byte4 = htonl(baud);
telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &byte4, 4); telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &byte4, 4);
}
byte = _tio_csize(tio); byte = _tio_csize(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &byte, 1); telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &byte, 1);
@@ -998,7 +1047,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
if (res < 0) if (res < 0)
{ {
fuse_reply_err(req, errno); fuse_reply_err(req, errno);
return; goto out;
} }
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize)); fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
break; break;
@@ -1009,7 +1058,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
if (res < 0) if (res < 0)
{ {
fuse_reply_err(req, errno); fuse_reply_err(req, errno);
return; goto out;
} }
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize)); fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
break; break;
@@ -1070,17 +1119,21 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
} }
break; break;
} }
out:
pthread_mutex_unlock(&tty->ioctl_lock);
} }
static void static void
ttynvt_poll(fuse_req_t req, struct fuse_file_info *info, ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
struct fuse_pollhandle *ph) struct fuse_pollhandle *ph)
{ {
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh; ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
ttynvt_t *tty = tty_cli->tty;
int revents = 0; int revents = 0;
DBG2("%s: tty->pollin=%d tty->error/epipe=%d/%d\n", DBG2("%s (%d): tty->pollin=%d tty->error/epipe=%d/%d\n",
__func__, tty->pollin, tty->error, tty->epipe); __func__, tty_cli->id, tty->pollin, tty->error, tty->epipe);
_update_notify(tty, ph); _update_notify(tty, ph);
@@ -1107,33 +1160,57 @@ ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info) static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
{ {
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh; ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
ttynvt_t *tty = tty_cli->tty;
ttynvt_cli_t *tmp, *prev;
DBG("%s\n", __func__); DBG("%s\n", __func__);
pthread_mutex_lock(&access_lock);
for (tmp = tty->cli, prev = NULL; tmp; prev = tmp, tmp = tmp->next)
if (tmp == tty_cli)
{
nvt_log(LOG_INFO, "Client release (%d)\n", tmp->id);
if (prev)
prev->next = tmp->next;
else
tty->cli = tmp->next;
free(tmp);
break;
}
if (tty->cli)
goto out;
tty->rel_pending = 1; tty->rel_pending = 1;
pthread_cancel(tty->ptid_poll); pthread_cancel(tty->ptid_poll);
pthread_join(tty->ptid_poll, NULL); pthread_join(tty->ptid_poll, NULL);
pthread_mutex_destroy(&tty->tty_lock); pthread_mutex_destroy(&tty->tty_lock);
pthread_mutex_destroy(&tty->poll_lock); pthread_mutex_destroy(&tty->poll_lock);
pthread_mutex_destroy(&tty->ioctl_lock);
pthread_mutex_destroy(&tty->read_lock);
pthread_mutex_destroy(&tty->write_lock);
if (ttynvt_param.close_delay_us)
_ttynvt_sleep_us(ttynvt_param.close_delay_us);
_fd_close(tty->fds[FD_NET].fd); _fd_close(tty->fds[FD_NET].fd);
_fd_close(tty->fds[FD_MASTER].fd); _fd_close(tty->fds[FD_MASTER].fd);
_fd_close(tty->fds[FD_SLAVE].fd); _fd_close(tty->fds[FD_SLAVE].fd);
if (ttynvt_param.close_delay_us)
_ttynvt_sleep_us(ttynvt_param.close_delay_us);
if (tty->ph) if (tty->ph)
fuse_pollhandle_destroy(tty->ph); fuse_pollhandle_destroy(tty->ph);
_ttynvt_ctx_destroy(tty); _ttynvt_ctx_destroy(tty);
ptty = NULL;
fuse_reply_err(req, 0);
nvt_log(LOG_INFO, "Disconnected from server: %s\n", ttynvt_param.server); nvt_log(LOG_INFO, "Disconnected from server: %s\n", ttynvt_param.server);
out:
fuse_reply_err(req, 0);
pthread_mutex_unlock(&access_lock); pthread_mutex_unlock(&access_lock);
} }
@@ -1182,9 +1259,7 @@ static void print_help(void)
"\t-q\t\tBe quiet (suppress informational messages)\n" "\t-q\t\tBe quiet (suppress informational messages)\n"
"\t-r\t\tDisable telnet processing\n" "\t-r\t\tDisable telnet processing\n"
"\t-s delay \t\tDelay at close in ms (use u suffix for us)\n" "\t-s delay \t\tDelay at close in ms (use u suffix for us)\n"
"\t-S server, --server=host:port\n" "\t-S server, --server=host:port\n");
"\t\t\t[ipv6addr]:port (connect via IPv6)\n"
"\t\t\tunix:/path/to.socket (connect via UNIX domain socket)\n");
} }
static int static int
-48
View File
@@ -1,48 +0,0 @@
# Unit test makefile
#
.NOTPARALLEL:
noinst_PROGRAMS = $(GTESTS)
GTEST_LIBS = -lgtest -lstdc++
GTESTS = test_misc test_nvttest
AM_CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter
AM_CFLAGS += $(CFLAGS_ASAN)
AM_CXXFLAGS = $(AM_CFLAGS)
AM_CPPFLAGS = -I $(top_builddir)
AM_CPPFLAGS += -D TOP_SRC_DIR='"$(top_srcdir)"' -D TOP_BLD_DIR='"$(top_builddir)"'
AM_CPPFLAGS += -D BUILD_TEST=1
LIBS += $(GTEST_LIBS)
TEST_COMMON = test.cpp test.h
test_misc_SOURCES = $(TEST_COMMON) test_misc.cpp
test_misc_LDADD = $(LIBS)
test_nvttest_SOURCES = $(TEST_COMMON) test_nvttest.cpp
test_nvttest_LDADD = $(LIBS)
TESTS_RUN = $(addprefix run-, $(GTESTS))
VG_PROG = valgrind --leak-check=full
all-local: run
.PHONY: run $(TESTS_RUN)
run: $(TESTS_RUN)
$(TESTS_RUN): run-%: %
# $(TEST_ENV) ./.libs/$* $(RUN_OPTS)
$(TEST_ENV) ./$* $(RUN_OPTS)
TESTS_RUN_VG = $(addprefix run-vg-, $(GTESTS))
.PHONY: run-vg $(TESTS_RUN_VG)
run-vg: $(TESTS_RUN_VG)
$(TESTS_RUN_VG): run-vg-%: %
# $(TEST_ENV) $(VG_PROG) ./.libs/$* $(RUN_OPTS)
$(TEST_ENV) $(VG_PROG) ./$* $(RUN_OPTS)
-70
View File
@@ -1,70 +0,0 @@
/*
* General test stuff
*
* - The main() function
* - Some printing functions
*/
#include "test.h"
int debug = 0;
static bool ttyout = false;
int main(int argc, char **argv)
{
const char *s;
ttyout = isatty(STDOUT_FILENO);
::testing::InitGoogleTest(&argc, argv);
for (argc--, argv++; argc > 0; argc--, argv++)
{
s = argv[0];
if (*s++ != '-')
break;
again:
switch (*s++)
{
case 'd':
debug++;
goto again;
}
}
return RUN_ALL_TESTS();
}
#include <stdarg.h>
void _pr_text(const char *col, const char *fmt, va_list args)
{
char fmtx[1024];
if (ttyout)
snprintf(fmtx, sizeof(fmtx), "%s[ ] - %s%s\n",
col, fmt, COL_RST);
else
snprintf(fmtx, sizeof(fmtx), "[ ] - %s\n", fmt);
fmt = fmtx;
vprintf(fmt, args);
}
void pr_text(const char *col, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
_pr_text(col, fmt, args);
va_end(args);
}
void pr_info(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
_pr_text(COL_YEL, fmt, args);
va_end(args);
}
-25
View File
@@ -1,25 +0,0 @@
#ifndef TEST_H
#define TEST_H 1
#include <gtest/gtest.h>
#include "config.h"
#define D(...) do{ if (debug) printf(__VA_ARGS__); }while(0)
#define D2(...) do{ if (debug > 1) printf(__VA_ARGS__); }while(0)
extern int debug;
#define COL_RST "\x1B[0m"
#define COL_RED "\x1B[31m"
#define COL_GRN "\x1B[32m"
#define COL_YEL "\x1B[33m"
#define COL_BLU "\x1B[34m"
#define COL_MAG "\x1B[35m"
#define COL_CYN "\x1B[36m"
#define COL_WHT "\x1B[37m"
void pr_text(const char *col, const char *fmt, ...);
void pr_info(const char *fmt, ...);
#endif /* TEST_H */
-6
View File
@@ -1,6 +0,0 @@
#include "test.h"
TEST(Misc, dummy)
{
EXPECT_EQ(1, 1);
}
-216
View File
@@ -1,216 +0,0 @@
#include "test.h"
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define NVT_NAME "ttyNVT99"
#define NVT_DEV "/dev/" NVT_NAME
#define NVT_SRV TOP_BLD_DIR "/nvtsrv/nvtsrv"
#define NVT_TEST TOP_BLD_DIR "/nvttest/nvttest"
#define NVT_OPTS "-D" NVT_NAME
#define LAUNCH_NVTTEST(...) proc_launch(NVT_TEST, NVT_OPTS, __VA_ARGS__)
pid_t proc_spawn(char **argv)
{
pid_t pid;
char buf[1024];
for (int i = 0, len = 0; argv[i]; i++)
len += snprintf(buf + len, sizeof(buf) - len, "%s ", argv[i]);
pr_info("Launching: %s", buf);
pid = fork();
if (pid > 0)
{
pr_info("... ok, pid = %d", pid);
return pid;
}
// Child
execv(argv[0], argv);
pr_info("Launch failed: %m");
EXPECT_EQ(1, 0);
exit(1);
}
void proc_waitfor(const char *name, pid_t pid)
{
int err, wstatus;
pr_info("Wait for %s (pid %d) to finish", name, pid);
wstatus = 0;
err = waitpid(pid, &wstatus, 0);
if (err > 0)
pr_info("... ok, status = %x", wstatus);
else
pr_info("... error: %m");
EXPECT_GT(err, 0);
EXPECT_EQ(WEXITSTATUS(wstatus), 0);
}
static pid_t proc_launch(const char *prog, ...)
{
char *argv[16], *arg;
int i;
pid_t pid;
va_list args;
va_start(args, prog);
arg = (char *)prog;
i = 0;
argv[i++] = arg;
for (; arg != NULL;)
{
arg = va_arg(args, char *);
argv[i++] = arg;
}
va_end(args);
pid = proc_spawn(argv);
return pid;
}
bool ttynvt_check()
{
struct stat st;
bool ok;
ok = stat(NVT_DEV, &st) == 0 && S_ISCHR(st.st_mode);
if (!ok)
{
fprintf(stderr, "\n%s"
"************************************************************\n"
"*** Invalid device file: '%s'\n"
"*** Ensure that ttynvt is launched correcly, e.g.:\n"
"# ttynvt -d -E -S localhost:1234 -n %s\n"
"*** ttynvt must run as root, and the device and server names\n"
"*** must be as shown above.\n"
"************************************************************\n"
"%s\n", COL_CYN, NVT_DEV, NVT_NAME, COL_RST);
}
return ok;
}
TEST(Test1, t1)
{
int err;
pid_t pid_srv, pid;
ASSERT_TRUE(ttynvt_check());
pid_srv = proc_launch(NVT_SRV, "-de", "localhost:1234", NULL);
ASSERT_GT(pid_srv, 0);
usleep(100000);
pid = LAUNCH_NVTTEST(NULL);
ASSERT_GT(pid, 0);
proc_waitfor("nvttest", pid);
pr_info("Terminate nvtsrv");
err = kill(pid_srv, SIGTERM);
EXPECT_EQ(err, 0);
proc_waitfor("nvtsrv", pid_srv);
}
/* Using fixture */
/**INDENT-OFF**/
class Test2:public::testing::Test {
pid_t pid_srv;
protected:
Test2() { }
virtual ~Test2() { }
virtual void SetUp() {
// Ensure ttynvt is running
ASSERT_TRUE(ttynvt_check());
// NB! Quiet nvtsrv
pid_srv = proc_launch(NVT_SRV, "-qe", "localhost:1234", NULL);
ASSERT_GT(pid_srv, 0);
usleep(100000);
}
virtual void TearDown() {
int err;
pr_info("Terminate nvtsrv");
err = kill(pid_srv, SIGTERM);
EXPECT_EQ(err, 0);
proc_waitfor("nvtsrv", pid_srv);
}
};
/**INDENT-ON**/
TEST_F(Test2, t1)
{
pid_t pid;
pid = LAUNCH_NVTTEST(NULL);
ASSERT_GT(pid, 0);
proc_waitfor("nvttest", pid);
}
// Multiple threads
TEST_F(Test2, t2)
{
pid_t pid;
// NB! Quiet nvttest
pid = LAUNCH_NVTTEST("-t5", "-q", NULL);
ASSERT_GT(pid, 0);
proc_waitfor("nvttest", pid);
}
// Multiple loops
TEST_F(Test2, t3)
{
pid_t pid;
// NB! Quiet nvttest
pid = LAUNCH_NVTTEST("-n5", "-q", NULL);
ASSERT_GT(pid, 0);
proc_waitfor("nvttest", pid);
}
// Multiple threads and loops
TEST_F(Test2, t4)
{
pid_t pid;
// NB! Quiet nvttest
pid = LAUNCH_NVTTEST("-n5", "-t5", "-q", NULL);
ASSERT_GT(pid, 0);
proc_waitfor("nvttest", pid);
}