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
+129 -42
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,7 +155,6 @@ 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)
@@ -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);
@@ -478,26 +514,34 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
if ((res = pthread_mutex_init(&tty->tty_lock, NULL) < 0) || if ((res = pthread_mutex_init(&tty->tty_lock, NULL) < 0) ||
(res = pthread_mutex_init(&tty->poll_lock, NULL) < 0) || (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)
@@ -780,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;
@@ -790,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)
@@ -988,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;
@@ -999,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;
@@ -1060,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);
@@ -1097,16 +1160,38 @@ 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) if (ttynvt_param.close_delay_us)
_ttynvt_sleep_us(ttynvt_param.close_delay_us); _ttynvt_sleep_us(ttynvt_param.close_delay_us);
@@ -1119,11 +1204,13 @@ static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
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);
} }