[CRIU] [PATCH 2/3] tty: Write unread pty buffers on post dump stage

Cyrill Gorcunov gorcunov at openvz.org
Wed Apr 13 00:25:43 PDT 2016


When unread data present on peers we currently simply ignore
it but actually we can try to fetch it in non(that)destructive
way.

For this we collect tty peers into a separate list, currently
only _paired_ ptys are supported, which means that both ends
must belong the containers. Once they are collected at the
post dump stage we read the data from peers and write it
into the image. In case if some error happened we write
the data back into appropriate peer.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 criu/cr-dump.c     |   2 +
 criu/include/tty.h |   1 +
 criu/tty.c         | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 146 insertions(+), 2 deletions(-)

diff --git a/criu/cr-dump.c b/criu/cr-dump.c
index 5ac9fd041e4e..754db6d93478 100644
--- a/criu/cr-dump.c
+++ b/criu/cr-dump.c
@@ -1545,6 +1545,8 @@ static int cr_dump_finish(int ret)
 {
 	int post_dump_ret = 0;
 
+	tty_post_dump(ret);
+
 	if (disconnect_from_page_server())
 		ret = -1;
 
diff --git a/criu/include/tty.h b/criu/include/tty.h
index 48f743eb293b..0db3b764e585 100644
--- a/criu/include/tty.h
+++ b/criu/include/tty.h
@@ -18,6 +18,7 @@ static inline int is_tty(dev_t rdev, dev_t dev)
 	return get_tty_driver(rdev, dev) != NULL;
 }
 
+extern int tty_post_dump(int ret);
 extern int dump_verify_tty_sids(void);
 extern struct collect_image_info tty_info_cinfo;
 extern struct collect_image_info tty_cinfo;
diff --git a/criu/tty.c b/criu/tty.c
index 68d7ba3d133b..c2bab09cf118 100644
--- a/criu/tty.c
+++ b/criu/tty.c
@@ -104,6 +104,12 @@ struct tty_dump_info {
 	pid_t				pgrp;
 	int				fd;
 	struct tty_driver		*driver;
+
+	int				index;
+	int				lfd;
+	struct tty_dump_info		*link;
+	void				*data;
+	size_t				size;
 };
 
 static LIST_HEAD(all_tty_info_entries);
@@ -1518,7 +1524,6 @@ int dump_verify_tty_sids(void)
 				}
 			}
 		}
-		xfree(dinfo);
 	}
 
 	return ret;
@@ -1551,7 +1556,7 @@ static int dump_tty_info(int lfd, u32 id, const struct fd_parms *p, struct tty_d
 	if (!pti)
 		return -1;
 
-	dinfo = xmalloc(sizeof(*dinfo));
+	dinfo = xzalloc(sizeof(*dinfo));
 	if (!dinfo)
 		return -1;
 
@@ -1561,6 +1566,19 @@ static int dump_tty_info(int lfd, u32 id, const struct fd_parms *p, struct tty_d
 	dinfo->fd		= p->fd;
 	dinfo->driver		= driver;
 
+	if (is_pty(driver)) {
+		dinfo->lfd = dup(lfd);
+		if (dinfo->lfd < 0) {
+			pr_perror("Can't dup local fd on %x", id);
+			xfree(dinfo);
+			return -1;
+		}
+		dinfo->index	= index;
+	} else {
+		dinfo->index	= -1;
+		dinfo->lfd	= -1;
+	}
+
 	list_add_tail(&dinfo->list, &all_ttys);
 
 	info.id			= id;
@@ -1698,6 +1716,129 @@ const struct fdtype_ops tty_dump_ops = {
 	.dump	= dump_one_tty,
 };
 
+static int tty_unblock_lfd(struct tty_dump_info *dinfo)
+{
+	static const int fmask = O_RDWR | O_NONBLOCK;
+
+	if ((fcntl(dinfo->lfd, F_GETFL, 0) & fmask) != fmask) {
+		if (fcntl(dinfo->lfd, F_SETFL, fmask)) {
+			pr_perror("Can't change mode to %o on (%#x)\n",
+				  fmask, dinfo->id);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int tty_post_dump(int status)
+{
+	struct tty_dump_info *dinfo, *peer, *n;
+	LIST_HEAD(all_ptys);
+	int ret = 0;
+
+	const size_t rbuf_size = 32768;
+	ssize_t size;
+	char *rbuf;
+
+	rbuf = xmalloc(rbuf_size);
+	if (!rbuf) {
+		pr_err("Can't allocate buffer for data\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * Link PTY peers, and move one of linked
+	 * into separate list.
+	 */
+	list_for_each_entry_safe(dinfo, n, &all_ttys, list) {
+		if (!is_pty(dinfo->driver) || dinfo->link)
+			continue;
+		peer = dinfo;
+		list_for_each_entry_continue(peer, &all_ttys, list) {
+			if (!is_pty(peer->driver) || peer->link)
+				continue;
+
+			if (peer->index == dinfo->index) {
+				dinfo->link = peer;
+				peer->link = dinfo;
+				pr_debug("Link PTYs (%#x)\n", dinfo->id);
+				list_move(&dinfo->list, &all_ptys);
+				break;
+			}
+		}
+	}
+
+	list_for_each_entry(dinfo, &all_ptys, list) {
+		TtyDataEntry e = TTY_DATA_ENTRY__INIT;
+
+		if (tty_unblock_lfd(dinfo))
+			break;
+
+		size = read(dinfo->lfd, rbuf, rbuf_size);
+		if (size == 0) {
+			continue;
+		} else if (size < 0) {
+			/* No queued data */
+			if (errno == EAGAIN)
+				continue;
+			pr_perror("Can't read data from tty (%#x) errno %d size %d\n",
+				  dinfo->id, errno, (int)size);
+			break;
+		} else if (size == rbuf_size) {
+			/*
+			 * FIXME We don't expect unread data to exceed
+			 * 12K but it may change in future.
+			 */
+			pr_err("The queued data is unexpectedly big\n");
+			break;
+		}
+
+		dinfo->data	= xmemdup(rbuf, size);
+		dinfo->size	= size;
+
+		if (!dinfo->data) {
+			pr_err("No free memory to keep queued data\n");
+			dinfo->data = rbuf;
+			rbuf = NULL;
+			ret = -ENOMEM;
+			break;
+		}
+
+		e.tty_id	= dinfo->id;
+		e.data.data	= (void *)rbuf;
+		e.data.len	= size;
+
+		ret = pb_write_one(img_from_set(glob_imgset, CR_FD_TTY_DATA), &e, PB_TTY_DATA);
+		if (ret)
+			break;
+	}
+
+	if (ret || opts.final_state == TASK_ALIVE) {
+		list_for_each_entry(dinfo, &all_ptys, list) {
+			if (dinfo->data) {
+				peer = dinfo->link;
+				tty_unblock_lfd(peer);
+				if (write(peer->lfd, dinfo->data, dinfo->size) != dinfo->size)
+					pr_perror("Can't writedata data back to tty (%#x)\n", peer->id);
+			}
+		}
+	}
+
+	list_for_each_entry_safe(dinfo, n, &all_ttys, list) {
+		close_safe(&dinfo->lfd);
+		xfree(dinfo->data);
+	}
+
+	list_for_each_entry_safe(dinfo, n, &all_ptys, list) {
+		close_safe(&dinfo->lfd);
+		xfree(dinfo->data);
+		xfree(dinfo);
+	}
+
+	xfree(rbuf);
+	return ret;
+}
+
 int tty_prep_fds(void)
 {
 	if (!opts.shell_job)
-- 
2.5.5



More information about the CRIU mailing list