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.
This commit is contained in:
+115
-31
@@ -55,6 +55,9 @@ static struct ttynvt_param {
|
||||
typedef struct {
|
||||
pthread_mutex_t tty_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_read;
|
||||
|
||||
@@ -80,8 +83,18 @@ typedef struct {
|
||||
int epipe;
|
||||
|
||||
tn_ctx_t *tn;
|
||||
|
||||
int seq;
|
||||
struct ttynvt_cli_s *cli;
|
||||
} 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;
|
||||
|
||||
@@ -406,7 +419,8 @@ static void _ttynvt_ctx_destroy(ttynvt_t * tty)
|
||||
|
||||
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;
|
||||
struct termios ios;
|
||||
int fd;
|
||||
@@ -415,14 +429,35 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
|
||||
|
||||
pthread_mutex_lock(&access_lock);
|
||||
|
||||
nvt_log(LOG_INFO, "Connecting to server: %s\n", ttynvt_param.server);
|
||||
|
||||
tty = _ttynvt_ctx_create();
|
||||
if (!tty)
|
||||
tty_cli = calloc(1, sizeof(ttynvt_cli_t));
|
||||
if (!tty_cli)
|
||||
{
|
||||
fuse_reply_err(req, ENOMEM);
|
||||
return;
|
||||
}
|
||||
if (tty == NULL)
|
||||
tty = _ttynvt_ctx_create();
|
||||
if (!tty)
|
||||
{
|
||||
fuse_reply_err(req, ENOMEM);
|
||||
free(tty_cli);
|
||||
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);
|
||||
if (fd < 0)
|
||||
@@ -478,25 +513,32 @@ 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_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)))
|
||||
{
|
||||
errno = res;
|
||||
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->direct_io = 1;
|
||||
|
||||
nvt_log(LOG_INFO, "Connected to server: %s\n", ttynvt_param.server);
|
||||
|
||||
fuse_reply_open(req, info);
|
||||
|
||||
pthread_mutex_unlock(&access_lock);
|
||||
return;
|
||||
|
||||
open_err:
|
||||
for (n = 0; n < 3; n++)
|
||||
_fd_close(tty->fds[n].fd);
|
||||
|
||||
free(tty_cli);
|
||||
_ttynvt_ctx_destroy(tty);
|
||||
|
||||
fuse_reply_err(req, errno);
|
||||
@@ -511,10 +553,13 @@ static void
|
||||
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;
|
||||
ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
|
||||
ttynvt_t *tty = tty_cli->tty;
|
||||
char buf[TMP_BUF_SIZE];
|
||||
int res, nr;
|
||||
|
||||
pthread_mutex_lock(&tty->read_lock);
|
||||
|
||||
DBG2("%s\n", __func__);
|
||||
|
||||
if (tty->error)
|
||||
@@ -529,7 +574,7 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
|
||||
else
|
||||
fuse_reply_err(req, EBADFD);
|
||||
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (size > TMP_BUF_SIZE)
|
||||
@@ -556,36 +601,42 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
|
||||
{
|
||||
DBG2("%s: error: %m\n", __func__);
|
||||
fuse_reply_err(req, errno);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
DBG2("%s: fd=%d: sz=%u/%u: '%.*s'\n", __func__,
|
||||
tty->fds[FD_SLAVE].fd, res, (int)size, res, buf);
|
||||
DBG2("%s: cli=%d fd=%d: sz=%u/%u: '%.*s'\n", __func__,
|
||||
tty_cli->id, tty->fds[FD_SLAVE].fd, res, (int)size, res, buf);
|
||||
|
||||
fuse_reply_buf(req, buf, res);
|
||||
|
||||
out:
|
||||
pthread_mutex_unlock(&tty->read_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
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;
|
||||
ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
|
||||
ttynvt_t *tty = tty_cli->tty;
|
||||
int res;
|
||||
|
||||
DBG2("%s: fd=%d: sz=%u: '%.*s'\n", __func__,
|
||||
tty->fds[FD_SLAVE].fd, (int)size, (int)size, data);
|
||||
pthread_mutex_lock(&tty->write_lock);
|
||||
|
||||
DBG2("%s: cli=%d fd=%d: sz=%u: '%.*s'\n", __func__,
|
||||
tty_cli->id, tty->fds[FD_SLAVE].fd, (int)size, (int)size, data);
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
fuse_reply_write(req, 0);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (tty->error)
|
||||
{
|
||||
DBG2("%s: tty->error=%d\n", __func__, tty->error);
|
||||
fuse_reply_err(req, EPIPE);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = write(tty->fds[FD_SLAVE].fd, data, size);
|
||||
@@ -594,7 +645,7 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
|
||||
DBG2("%s: error: %m\n", __func__);
|
||||
tty->error = 1;
|
||||
fuse_reply_err(req, errno);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
else if (res != (int)size)
|
||||
{
|
||||
@@ -603,6 +654,9 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
|
||||
}
|
||||
|
||||
fuse_reply_write(req, res);
|
||||
|
||||
out:
|
||||
pthread_mutex_unlock(&tty->write_lock);
|
||||
}
|
||||
|
||||
static int baudrate(speed_t speed)
|
||||
@@ -780,7 +834,8 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
||||
struct fuse_file_info *info, unsigned int flags,
|
||||
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 winsize ws;
|
||||
unsigned int tiocm, baud;
|
||||
@@ -790,14 +845,16 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
||||
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,
|
||||
pthread_mutex_lock(&tty->ioctl_lock);
|
||||
|
||||
DBG("%s: cli=%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);
|
||||
|
||||
if (ttynvt_param.raw)
|
||||
{
|
||||
fuse_reply_ioctl(req, 0, 0, 0);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
@@ -988,7 +1045,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
||||
if (res < 0)
|
||||
{
|
||||
fuse_reply_err(req, errno);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
|
||||
break;
|
||||
@@ -999,7 +1056,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
||||
if (res < 0)
|
||||
{
|
||||
fuse_reply_err(req, errno);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
|
||||
break;
|
||||
@@ -1060,17 +1117,21 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
pthread_mutex_unlock(&tty->ioctl_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
|
||||
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;
|
||||
|
||||
DBG2("%s: tty->pollin=%d tty->error/epipe=%d/%d\n",
|
||||
__func__, tty->pollin, tty->error, tty->epipe);
|
||||
DBG2("%s: cli=%d, tty->pollin=%d tty->error/epipe=%d/%d\n",
|
||||
__func__, tty_cli->id, tty->pollin, tty->error, tty->epipe);
|
||||
|
||||
_update_notify(tty, ph);
|
||||
|
||||
@@ -1097,16 +1158,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)
|
||||
{
|
||||
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__);
|
||||
|
||||
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;
|
||||
|
||||
pthread_cancel(tty->ptid_poll);
|
||||
pthread_join(tty->ptid_poll, NULL);
|
||||
pthread_mutex_destroy(&tty->tty_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);
|
||||
@@ -1120,10 +1203,11 @@ static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
|
||||
|
||||
_ttynvt_ctx_destroy(tty);
|
||||
|
||||
fuse_reply_err(req, 0);
|
||||
|
||||
nvt_log(LOG_INFO, "Disconnected from server: %s\n", ttynvt_param.server);
|
||||
|
||||
out:
|
||||
fuse_reply_err(req, 0);
|
||||
|
||||
pthread_mutex_unlock(&access_lock);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user