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

Cyrill Gorcunov gorcunov at openvz.org
Tue Apr 12 15:08:39 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         | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 130 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..42d218858923 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,113 @@ const struct fdtype_ops tty_dump_ops = {
 	.dump	= dump_one_tty,
 };
 
+int tty_post_dump(int status)
+{
+	struct tty_dump_info *dinfo, *peer, *n;
+	const int fmask = O_RDWR | O_NONBLOCK;
+	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 (fcntl(dinfo->lfd, F_SETFL, fmask)) {
+			pr_perror("Can't change to r/w,nonblocking (%#x)\n", dinfo->id);
+			break;
+		}
+
+		size = read(dinfo->lfd, rbuf, rbuf_size);
+		if (size == 0) {
+			continue;
+		} else if (size < 0) {
+			pr_perror("Can't read data from tty (%#x)\n", dinfo->id);
+			break;
+		} else if (size == rbuf_size) {
+			/*
+			 * XXX Should we allocate bigger buffers?
+			 */
+			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;
+				fcntl(peer->lfd, F_SETFL, fmask);
+				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