diff --git a/src/ttynvt.c b/src/ttynvt.c index c164a22..8914874 100644 --- a/src/ttynvt.c +++ b/src/ttynvt.c @@ -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); }