[CRIU] [PATCH 3/4] tty: Add checkpoint/restore for unix terminals

Cyrill Gorcunov gorcunov at openvz.org
Mon Aug 13 03:59:33 EDT 2012


The main idea of TTY restore (the dumping is straight forward
and has nothing special)

 - All ttys are splitted into three groups of lists
   - master ptys
   - slave ptys associated with masters
   - standalone ptys, for example there might
     be slave ptys where master link is closed

 - Once groups are formed we figure out who should
   open and send ptys to the tasks which are waiting
   for them.

The main problem I've faced now is the case where
SID for master is set by say task 1 then this master
dup'ed in task 2 and then task 1 closes original
master thus task 2 need somehow to restore SID but
the kernel doesn't allow us to do that from task 2
since task 2 is not a session leader.

Moreover we could restore SID from task 1 but in this
scenario task 2 doesn't even have file desciptor for
master pty.

So at moment I'm not sure how to resolve such situation,
and this prevents "screen" session from restore.

Btw, this problem is addressed in test pty02.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 Makefile              |    1 +
 cr-dump.c             |    7 +-
 cr-restore.c          |    9 +-
 cr-show.c             |    6 +
 image.c               |    1 +
 include/crtools.h     |    2 +
 include/image.h       |    1 +
 include/protobuf.h    |    1 +
 include/tty.h         |   19 ++
 protobuf.c            |    2 +
 protobuf/Makefile     |    1 +
 protobuf/fdinfo.proto |    1 +
 protobuf/tty.proto    |   37 +++
 tty.c                 |  693 +++++++++++++++++++++++++++++++++++++++++++++++++
 14 files changed, 778 insertions(+), 3 deletions(-)
 create mode 100644 include/tty.h
 create mode 100644 protobuf/tty.proto
 create mode 100644 tty.c

diff --git a/Makefile b/Makefile
index 2fcedd0..8da6d19 100644
--- a/Makefile
+++ b/Makefile
@@ -63,6 +63,7 @@ OBJS		+= inotify.o
 OBJS		+= signalfd.o
 OBJS		+= pstree.o
 OBJS		+= protobuf.o
+OBJS		+= tty.o
 
 PROTOBUF-LIB	:= protobuf/protobuf-lib.o
 
diff --git a/cr-dump.c b/cr-dump.c
index b5c57fe..ffcacdc 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -47,6 +47,7 @@
 #include "signalfd.h"
 #include "pstree.h"
 #include "mount.h"
+#include "tty.h"
 
 #include "protobuf.h"
 #include "protobuf/fdinfo.pb-c.h"
@@ -248,12 +249,14 @@ static int dump_chrdev(struct fd_parms *p, int lfd, const struct cr_fdset *set)
 	switch (maj) {
 	case MEM_MAJOR:
 		return dump_reg_file(p, lfd, set);
-	case TTY_MAJOR:
+	case TTYAUX_MAJOR:
+	case UNIX98_PTY_MASTER_MAJOR ... (UNIX98_PTY_MASTER_MAJOR + UNIX98_PTY_MAJOR_COUNT - 1):
 	case UNIX98_PTY_SLAVE_MAJOR:
 		if (p->fd < 3) {
 			pr_info("... Skipping tty ... %d\n", p->fd);
 			return 0;
-		}
+		} else
+			return dump_tty(p, lfd, set);
 	}
 
 	return dump_unsupp_fd(p);
diff --git a/cr-restore.c b/cr-restore.c
index 4f31d74..437ae61 100644
--- a/cr-restore.c
+++ b/cr-restore.c
@@ -51,6 +51,7 @@
 #include "inotify.h"
 #include "pstree.h"
 #include "net.h"
+#include "tty.h"
 
 #include "protobuf.h"
 #include "protobuf/sa.pb-c.h"
@@ -98,6 +99,9 @@ static int prepare_shared(void)
 	if (collect_fifo())
 		return -1;
 
+	if (collect_tty())
+		return -1;
+
 	if (collect_inet_sockets())
 		return -1;
 
@@ -133,7 +137,10 @@ static int prepare_shared(void)
 	}
 
 	mark_pipe_master();
-	ret = resolve_unix_peers();
+
+	ret = tty_handle_priv();
+	if (!ret)
+		ret = resolve_unix_peers();
 
 	if (!ret) {
 		show_saved_shmems();
diff --git a/cr-show.c b/cr-show.c
index dd9bd8c..4f97137 100644
--- a/cr-show.c
+++ b/cr-show.c
@@ -41,6 +41,7 @@
 #include "protobuf/vma.pb-c.h"
 #include "protobuf/creds.pb-c.h"
 #include "protobuf/core.pb-c.h"
+#include "protobuf/tty.pb-c.h"
 
 #define DEF_PAGES_PER_LINE	6
 
@@ -113,6 +114,11 @@ void show_fifo(int fd, struct cr_options *o)
 	pb_show_plain(fd, PB_FIFO);
 }
 
+void show_tty(int fd, struct cr_options *o)
+{
+	pb_show_plain(fd, PB_TTY);
+}
+
 void show_fs(int fd_fs, struct cr_options *o)
 {
 	pb_show_vertical(fd_fs, PB_FS);
diff --git a/image.c b/image.c
index 456cbce..822b5fd 100644
--- a/image.c
+++ b/image.c
@@ -128,6 +128,7 @@ struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX] = {
 	FD_ENTRY(IFADDR,	"ifaddr-%d",	 show_raw_image),
 	FD_ENTRY(ROUTE,		"route-%d",	 show_raw_image),
 	FD_ENTRY(TMPFS,		"tmpfs-%d.tar.gz", show_raw_image),
+	FD_ENTRY(TTY,		"tty",		 show_tty),
 };
 
 static struct cr_fdset *alloc_cr_fdset(int nr)
diff --git a/include/crtools.h b/include/crtools.h
index 702196b..d5e739a 100644
--- a/include/crtools.h
+++ b/include/crtools.h
@@ -66,6 +66,7 @@ enum {
 	CR_FD_PIPES_DATA,
 	CR_FD_FIFO,
 	CR_FD_FIFO_DATA,
+	CR_FD_TTY,
 	CR_FD_REMAP_FPATH,
 	CR_FD_EVENTFD,
 	CR_FD_EVENTPOLL,
@@ -131,6 +132,7 @@ void show_remap_files(int fd, struct cr_options *o);
 void show_ghost_file(int fd, struct cr_options *o);
 void show_fown_cont(void *p);
 void show_eventfds(int fd, struct cr_options *o);
+void show_tty(int fd, struct cr_options *o);
 
 int check_img_inventory(void);
 int write_img_inventory(void);
diff --git a/include/image.h b/include/image.h
index 983d359..d8397ba 100644
--- a/include/image.h
+++ b/include/image.h
@@ -63,6 +63,7 @@
 #define IFADDR_MAGIC		RAW_IMAGE_MAGIC
 #define ROUTE_MAGIC		RAW_IMAGE_MAGIC
 #define TMPFS_MAGIC		RAW_IMAGE_MAGIC
+#define TTY_MAGIC		0x59433025 /* Pushkin */
 
 #define PAGE_IMAGE_SIZE	4096
 #define PAGE_RSS	1
diff --git a/include/protobuf.h b/include/protobuf.h
index fca883e..c148de5 100644
--- a/include/protobuf.h
+++ b/include/protobuf.h
@@ -41,6 +41,7 @@ enum {
 	PB_SIGNALFD,
 	PB_INOTIFY,
 	PB_INOTIFY_WD,
+	PB_TTY,
 
 	PB_MAX
 };
diff --git a/include/tty.h b/include/tty.h
new file mode 100644
index 0000000..2a9580e
--- /dev/null
+++ b/include/tty.h
@@ -0,0 +1,19 @@
+#ifndef CR_TTY_H__
+#define CR_TTY_H__
+
+#include "files.h"
+#include "crtools.h"
+
+#define TERM2_NCC	19
+
+#define PTMX_PATH	"/dev/ptmx"
+#ifndef PTMX_MINOR
+# define PTMX_MINOR 2
+#endif
+#define PTS_FMT		"/dev/pts/%d"
+
+extern int dump_tty(struct fd_parms *p, int lfd, const struct cr_fdset *set);
+extern int collect_tty(void);
+extern int tty_handle_priv(void);
+
+#endif /* CR_TTY_H__ */
diff --git a/protobuf.c b/protobuf.c
index 1835a05..97e059b 100644
--- a/protobuf.c
+++ b/protobuf.c
@@ -44,6 +44,7 @@
 #include "protobuf/mnt.pb-c.h"
 #include "protobuf/netdev.pb-c.h"
 #include "protobuf/tcp-stream.pb-c.h"
+#include "protobuf/tty.pb-c.h"
 
 typedef size_t (*pb_getpksize_t)(void *obj);
 typedef size_t (*pb_pack_t)(void *obj, void *where);
@@ -118,6 +119,7 @@ void cr_pb_init(void)
 	CR_PB_DESC(MOUNTPOINTS,		Mnt,		mnt);
 	CR_PB_DESC(NETDEV,		NetDevice,	net_device);
 	CR_PB_DESC(PACKETSK,		PacketSock,	packet_sock);
+	CR_PB_DESC(TTY,			TtyFile,	tty_file);
 }
 
 /*
diff --git a/protobuf/Makefile b/protobuf/Makefile
index 901fbf8..3e60c0e 100644
--- a/protobuf/Makefile
+++ b/protobuf/Makefile
@@ -55,6 +55,7 @@ PROTO_FILES	+= creds.proto
 PROTO_FILES	+= vma.proto
 PROTO_FILES	+= core.proto
 PROTO_FILES	+= netdev.proto
+PROTO_FILES	+= tty.proto
 
 HDRS	:= $(patsubst %.proto,%.pb-c.h,$(PROTO_FILES))
 SRCS	:= $(patsubst %.proto,%.pb-c.c,$(PROTO_FILES))
diff --git a/protobuf/fdinfo.proto b/protobuf/fdinfo.proto
index 5ad78de..59cbcf8 100644
--- a/protobuf/fdinfo.proto
+++ b/protobuf/fdinfo.proto
@@ -10,6 +10,7 @@ enum fd_types {
 	INOTIFY		= 8;
 	SIGNALFD	= 9;
 	PACKETSK	= 10;
+	TTY		= 11;
 }
 
 message fdinfo_entry {
diff --git a/protobuf/tty.proto b/protobuf/tty.proto
new file mode 100644
index 0000000..047da64
--- /dev/null
+++ b/protobuf/tty.proto
@@ -0,0 +1,37 @@
+import "fown.proto";
+
+message term2_entry {
+	required uint32			c_iflag		= 1;
+	required uint32			c_oflag		= 2;
+	required uint32			c_cflag		= 3;
+	required uint32			c_lflag		= 4;
+	required uint32			c_line		= 5;
+	required uint32			c_ispeed	= 6;
+	required uint32			c_ospeed	= 7;
+
+	repeated uint32			c_cc		= 8;
+}
+
+message tty_unix98_entry {
+	required uint32			index		= 1;
+}
+
+message tty_file_entry {
+	required uint32			id		=  1;
+	required uint32			flags		=  2;
+	required uint32			sid		=  3;
+	required uint32			prgp		=  4;
+	required uint64			pos		=  5;
+	required uint64			rdev		=  6;
+	required fown_entry		fown		=  7;
+	required term2_entry		term2		=  8;
+
+	enum TtyType {
+		UNKNOWN		= 0;
+		UNIX98		= 1;
+	}
+
+	required TtyType		type		=  9;
+
+	optional tty_unix98_entry	unix98		= 10;
+}
diff --git a/tty.c b/tty.c
new file mode 100644
index 0000000..c80a86a
--- /dev/null
+++ b/tty.c
@@ -0,0 +1,693 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <linux/major.h>
+
+#include "compiler.h"
+#include "types.h"
+
+#include "syscall.h"
+#include "files.h"
+#include "crtools.h"
+#include "image.h"
+#include "util.h"
+#include "log.h"
+#include "list.h"
+#include "util-net.h"
+#include "proc_parse.h"
+
+#include "protobuf.h"
+#include "protobuf/tty.pb-c.h"
+
+#include "tty.h"
+
+#undef	LOG_PREFIX
+#define LOG_PREFIX "TTY: "
+
+struct tty_file_info;
+
+struct tty_private {
+  union {
+	struct {
+		struct tty_file_info	*peer;		/* pointer to peer info if there */
+		struct list_head	list;
+
+		bool			fake_ptmx;	/* fake ptmx needed to create peer */
+		bool			send_peer;
+		char			path[64];
+	} unix98;
+  };
+};
+
+struct tty_file_info {
+	struct list_head		list;
+	struct file_desc		d;
+	TtyFileEntry			*tfe;
+	struct tty_private		priv;
+
+	int				major;
+	int				minor;
+	bool				create;
+};
+
+static LIST_HEAD(all_ttys);
+
+static void from_termios(Term2Entry *d, struct termios *s)
+{
+	BUG_ON(pb_repeated_size(d, c_cc) < sizeof(s->c_cc));
+
+	memcpy(d->c_cc, s->c_cc, sizeof(s->c_cc));
+
+	ASSIGN_MEMBER(d, s, c_iflag);
+	ASSIGN_MEMBER(d, s, c_oflag);
+	ASSIGN_MEMBER(d, s, c_cflag);
+	ASSIGN_MEMBER(d, s, c_lflag);
+	ASSIGN_MEMBER(d, s, c_line);
+}
+
+static void to_termios(struct termios *d, Term2Entry *s)
+{
+	BUG_ON(s->n_c_cc != TERM2_NCC);
+
+	memcpy(d->c_cc, s->c_cc, sizeof(d->c_cc));
+
+	ASSIGN_MEMBER(d, s, c_iflag);
+	ASSIGN_MEMBER(d, s, c_oflag);
+	ASSIGN_MEMBER(d, s, c_cflag);
+	ASSIGN_MEMBER(d, s, c_lflag);
+	ASSIGN_MEMBER(d, s, c_line);
+}
+
+static int tty_open_ptmx_index(int flags, int index)
+{
+	int fds[32], i, ret = -1, cur_idx;
+
+	memset(fds, 0xff, sizeof(fds));
+
+	for (i = 0; i < ARRAY_SIZE(fds); i++) {
+		fds[i] = open(PTMX_PATH, flags);
+		if (fds[i] < 0) {
+			pr_perror("Can't open %s", PTMX_PATH);
+			break;
+		}
+
+		if (ioctl(fds[i], TIOCGPTN, &cur_idx)) {
+			pr_perror("Can't obtain current index on %s", PTMX_PATH);
+			break;
+		}
+
+		/*
+		 * Index match or any index requested.
+		 */
+		if (cur_idx == index) {
+			ret = fds[i];
+			fds[i] = -1;
+			break;
+		}
+
+		pr_debug("ptmx opened with index %d\n", cur_idx);
+
+		/*
+		 * Maybe indices are already borrowed by
+		 * someone else, so no need to continue.
+		 */
+		if (cur_idx < index && (index - cur_idx) < ARRAY_SIZE(fds))
+			continue;
+		break;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(fds); i++) {
+		if (fds[i] >= 0)
+			close(fds[i]);
+	}
+
+	if (ret < 0)
+		pr_err("Unable to open %s with specified index %d\n",
+		       PTMX_PATH, index);
+
+	return ret;
+}
+
+static int unlock_pty_master(int master)
+{
+	const int lock = 0;
+
+	if (ioctl(master, TIOCSPTLCK, &lock)) {
+		pr_err("Unable to unlock pty master device %d\n", master);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int receive_tty(struct tty_file_info *info)
+{
+	struct fdinfo_list_entry *fle;
+	struct file_desc *info_desc;
+	int fd;
+
+	info_desc = find_file_desc_raw(FD_TYPES__TTY, info->tfe->id);
+	fle = file_master(info_desc);
+	pr_info("\tWaiting tty fd %d (pid %d)\n",
+		fle->fe->fd, fle->pid);
+
+	fd = recv_fd(fle->fe->fd);
+	close(fle->fe->fd);
+	if (fd < 0) {
+		pr_err("Can't get fd %d\n", fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int tty_open_ptmx(struct tty_file_info *info)
+{
+	int master_fd = -1, sock = -1, ret = -1;
+	struct tty_file_info *slave;
+	int slave_fd = -1;
+
+	/* If we fail here we won't connect peer anyway */
+	master_fd = tty_open_ptmx_index(info->tfe->flags | O_RDWR,
+					info->tfe->unix98->index);
+	if (master_fd >= 0) {
+		unlock_pty_master(master_fd);
+	} else {
+		pr_err("Can't open ptmx for %s (%d)\n",
+		       info->priv.unix98.path, info->tfe->unix98->index);
+		goto err;
+	}
+
+	if (list_empty(&info->priv.unix98.list))
+		goto out;
+
+	sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (sock < 0) {
+		pr_perror("Can't create socket");
+		goto err;
+	}
+
+	/*
+	 * Once master opened, try to open all slaves connected
+	 * and send them out to receivers.
+	 */
+	list_for_each_entry(slave, &info->priv.unix98.list, priv.unix98.list) {
+		struct file_desc *slave_desc;
+		struct fdinfo_list_entry *fle;
+
+		/* This slave is special */
+		if (slave->create)
+			continue;
+
+		slave_fd = open(slave->priv.unix98.path, slave->tfe->flags);
+		if (slave_fd < 0) {
+			pr_perror("Can't open %s", slave->priv.unix98.path);
+			goto err;
+		}
+
+		slave_desc = find_file_desc_raw(FD_TYPES__TTY, slave->tfe->id);
+		fle = file_master(slave_desc);
+
+		pr_debug("send slave %#x fd %d connected on %s (pid %d)\n",
+			 slave->tfe->id, slave_fd, slave->priv.unix98.path, fle->pid);
+
+		list_for_each_entry(fle, &slave_desc->fd_info_head, desc_list) {
+			if (send_fd_to_peer(slave_fd, fle, sock)) {
+				pr_perror("Can't send file descriptor");
+				goto err;
+			}
+		}
+
+		close_safe(&slave_fd);
+	}
+
+out:
+	ret = master_fd, master_fd = -1;
+err:
+	close_safe(&slave_fd);
+	close_safe(&sock);
+	close_safe(&master_fd);
+	return ret;
+}
+
+static int tty_open_pts(struct tty_file_info *info)
+{
+	int slave_fd = -1, master_fd = -1, ret = -1, sock = -1;
+
+	if (info->priv.unix98.fake_ptmx) {
+		slave_fd = open(info->priv.unix98.path, info->tfe->flags);
+		if (slave_fd >= 0)
+			return slave_fd;
+
+		master_fd = tty_open_ptmx(info);
+		if (master_fd < 0)
+			return -1;
+	} else if (info->priv.unix98.send_peer) {
+		struct tty_file_info *master = info->priv.unix98.peer;
+		struct file_desc *master_desc;
+		struct fdinfo_list_entry *fle;
+
+		master_fd = tty_open_ptmx(master);
+		if (master_fd < 0)
+			return -1;
+
+		sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+		if (sock < 0) {
+			pr_perror("Can't create socket");
+			goto out;
+		}
+
+		master_desc = find_file_desc_raw(FD_TYPES__TTY, master->tfe->id);
+		fle = file_master(master_desc);
+
+		list_for_each_entry(fle, &master_desc->fd_info_head, desc_list) {
+			if (send_fd_to_peer(master_fd, fle, sock)) {
+				pr_perror("Can't send file descriptor");
+				goto out;
+			}
+		}
+	}
+
+	slave_fd = open(info->priv.unix98.path, info->tfe->flags);
+	if (slave_fd < 0) {
+		pr_perror("Can't open %s\n", info->priv.unix98.path);
+	}
+
+	ret = slave_fd, slave_fd = -1;
+out:
+	close_safe(&sock);
+	close_safe(&slave_fd);
+	close_safe(&master_fd);
+	return ret;
+}
+
+static int tty_lookup_open(struct tty_file_info *info)
+{
+	pr_debug("open for index %d create %d send peer %d fake %d (path %s)\n",
+		 info->tfe->unix98->index,
+		 !!info->create,
+		 !!info->priv.unix98.send_peer,
+		 !!info->priv.unix98.fake_ptmx,
+		 info->priv.unix98.path);
+
+	if (!info->create)
+		return receive_tty(info);
+
+	switch (info->major) {
+	case UNIX98_PTY_SLAVE_MAJOR:
+		return tty_open_pts(info);
+	case TTYAUX_MAJOR:
+		return tty_open_ptmx(info);
+	}
+
+	BUG_ON(1);
+	return -1;
+}
+
+static int tty_get_sid(int fd)
+{
+	int sid, ret;
+
+	ret = ioctl(fd, TIOCGSID, &sid);
+	if (ret < 0) {
+		if (errno != ENOTTY) {
+			pr_perror("Can't get sid on %d", fd);
+			return -1;
+		}
+		sid = 0;
+	}
+	return sid;
+}
+
+static int tty_open(struct file_desc *d)
+{
+	struct tty_file_info *info = container_of(d, struct tty_file_info, d);
+	int tmp, ret = -1;
+	struct termios t = { };
+
+	tmp = tty_lookup_open(info);
+	if (tmp < 0) {
+		pr_perror("Can't open tty id %#08x [%s]",
+			  info->tfe->id, info->priv.unix98.path);
+		return -1;
+	}
+
+	if (info->tfe->sid) {
+		/* If we're having same sid already -- nothing to do */
+		if (tty_get_sid(tmp) != info->tfe->sid) {
+			if (ioctl(tmp, TIOCSCTTY, 1)) {
+				pr_perror("Can't set session %d by %d tty on %d [%s]",
+					  (int)info->tfe->sid, (int)getpid(),
+					  info->tfe->id, info->priv.unix98.path);
+				goto err;
+			}
+			pr_debug("Restore session %d by %d tty on %d [%s]\n",
+				 (int)info->tfe->sid, (int)getpid(),
+				 info->tfe->id, info->priv.unix98.path);
+		} else
+			pr_debug("Skip restoring session %d by %d tty on %d [%s]\n",
+				 (int)info->tfe->sid, (int)getpid(),
+				 info->tfe->id, info->priv.unix98.path);
+	}
+
+	if (info->tfe->prgp) {
+		if (ioctl(tmp, TIOCSPGRP, &info->tfe->prgp)) {
+			pr_perror("Can't set tty group on %d [%s]",
+				  info->tfe->id, info->priv.unix98.path);
+			goto err;
+		}
+		pr_debug("Restore tty group on %d [%s]\n",
+			 info->tfe->id, info->priv.unix98.path);
+	}
+
+	if (rst_file_params(tmp, info->tfe->fown, info->tfe->flags)) {
+		pr_perror("Can't restore params on tfe %#08x",
+			  info->tfe->id);
+		goto err;
+	}
+
+	to_termios(&t, info->tfe->term2);
+	if (ioctl(tmp, TCSETS, &t) < 0) {
+		pr_perror("Can't set tty params on %d [%s]",
+			  info->tfe->id, info->priv.unix98.path);
+		goto err;
+	}
+
+	ret = tmp, tmp = -1;
+err:
+	close_safe(&tmp);
+	return ret;
+}
+
+static int want_transport(FdinfoEntry *fe, struct file_desc *d)
+{
+	struct tty_file_info *info = container_of(d, struct tty_file_info, d);
+	return info->create == false;
+}
+
+static struct file_desc_ops tty_desc_ops = {
+	.type		= FD_TYPES__TTY,
+	.open		= tty_open,
+	.want_transport	= want_transport,
+};
+
+int tty_handle_priv(void)
+{
+	struct tty_file_info *info, *master, *tmp, *slave;
+	struct fdinfo_list_entry *slave_fle, *master_fle, *this;
+	LIST_HEAD(masters);
+	LIST_HEAD(slaves);
+	LIST_HEAD(standalone_slaves);
+
+	/*
+	 * Range masters and slaves and init anything needed.
+	 */
+	list_for_each_entry_safe(info, tmp, &all_ttys, list) {
+		if (info->major == TTYAUX_MAJOR) {
+			strncpy(info->priv.unix98.path,
+				PTMX_PATH, sizeof(info->priv.unix98.path));
+			INIT_LIST_HEAD(&info->priv.unix98.list);
+			list_move(&info->list, &masters);
+		} else if (info->major == UNIX98_PTY_SLAVE_MAJOR) {
+			snprintf(info->priv.unix98.path,
+				 sizeof(info->priv.unix98.path),
+				 PTS_FMT, info->tfe->unix98->index);
+			INIT_LIST_HEAD(&info->priv.unix98.list);
+			list_move(&info->list, &slaves);
+		}
+	}
+
+
+	/*
+	 * Chain slaves on masters
+	 */
+	list_for_each_entry_safe(slave, tmp, &slaves, list) {
+		bool added = false;
+		slave->priv.unix98.fake_ptmx = true;
+		slave->priv.unix98.send_peer = false;
+
+		list_for_each_entry(master, &masters, list) {
+			if (slave->tfe->unix98->index == master->tfe->unix98->index) {
+				slave->priv.unix98.peer = master;
+				slave->priv.unix98.fake_ptmx = false;
+
+				list_add(&slave->priv.unix98.list,
+					 &master->priv.unix98.list);
+				added = true;
+				break;
+			}
+		}
+
+		if (!added)
+			list_move(&slave->list, &standalone_slaves);
+	}
+
+	/*
+	 * Try to resolve the case where slaves are
+	 * created earlier than masters, thus we need
+	 * to create masters as well but send them out
+	 * via SCM to a waiting side.
+	 */
+	list_for_each_entry(master, &masters, list) {
+		bool spec = false;
+		master_fle = file_master(&master->d);
+
+		if (list_empty(&master->priv.unix98.list))
+			continue;
+
+		INIT_LIST_HEAD(&slaves);
+		this = NULL;
+
+		list_for_each_entry_safe(slave, tmp, &master->priv.unix98.list, priv.unix98.list) {
+			slave_fle = file_master(&slave->d);
+			slave->create = false;
+
+			if (slave_fle->pid < master_fle->pid ||
+			    (slave_fle->pid == master_fle->pid &&
+			     slave_fle->fe->fd < master_fle->fe->fd)) {
+				if (unlikely(!this)) {
+					this = slave_fle;
+					list_move(&slave->priv.unix98.list, &slaves);
+				} else
+					if (slave_fle->fe->fd < this->fe->fd)
+						list_move(&slave->priv.unix98.list, &slaves);
+					else
+						list_move_tail(&slave->priv.unix98.list, &slaves);
+				spec = true;
+			} else
+				list_move_tail(&slave->priv.unix98.list, &slaves);
+		}
+
+		if (spec) {
+			slave = list_first_entry(&slaves, struct tty_file_info, priv.unix98.list);
+			slave->create = true;
+			slave->priv.unix98.send_peer = true;
+			master->create = false;
+		}
+
+		list_splice(&slaves, &master->priv.unix98.list);
+	}
+
+	list_for_each_entry(master, &masters, list) {
+		master_fle = file_master(&master->d);
+
+		pr_info("master %#x index %d fd %d pid %d (create %d sid %d pgrp %d)\n",
+			master->tfe->id, master->tfe->unix98->index,
+			master_fle->fe->fd, master_fle->pid, !!master->create,
+			!!master->tfe->sid, !!master->tfe->prgp);
+
+		list_for_each_entry(slave, &master->priv.unix98.list, priv.unix98.list) {
+			slave_fle = file_master(&slave->d);
+
+				pr_info("    `- slave %#x index %d fd %d pid %d (create %d send %d sid %d pgrp %d)\n",
+					slave->tfe->id, slave->tfe->unix98->index,
+					slave_fle->fe->fd, slave_fle->pid, !!slave->create,
+					!!slave->priv.unix98.send_peer,
+					!!slave->tfe->sid, !!slave->tfe->prgp);
+		}
+	}
+
+	list_for_each_entry(slave, &standalone_slaves, list) {
+		slave_fle = file_master(&slave->d);
+
+		pr_info("standalone slave %#x index %d fd %d pid %d (create %d sid %d pgrp %d)\n",
+			slave->tfe->id, slave->tfe->unix98->index,
+			slave_fle->fe->fd, slave_fle->pid, !!slave->create,
+			!!slave->tfe->sid, !!slave->tfe->prgp);
+	}
+
+	return 0;
+}
+
+int collect_tty(void)
+{
+	struct tty_file_info *info = NULL;
+	int fd, ret = -1;
+
+	fd = open_image_ro(CR_FD_TTY);
+	if (fd < 0)
+		return -1;
+
+	while (1) {
+		info = xzalloc(sizeof(*info));
+		if (!info) {
+			ret = -1;
+			break;
+		}
+
+		ret = pb_read_one_eof(fd, &info->tfe, PB_TTY);
+		if (ret <= 0)
+			break;
+
+		if (info->tfe->type != TTY_FILE_ENTRY__TTY_TYPE__UNIX98) {
+			pr_err("Unsuported tty type %d\n", info->tfe->type);
+			ret = -1;
+			goto out_free;
+		}
+
+		info->create = true;
+		info->major = major(info->tfe->rdev);
+		info->minor = minor(info->tfe->rdev);
+
+		/* Partial ordering is needed for search speedup */
+		switch (info->major) {
+		case TTYAUX_MAJOR:
+		case UNIX98_PTY_SLAVE_MAJOR:
+			break;
+		default:
+			pr_err("Unsupported tty type (major %d)\n", info->major);
+			ret = -1;
+			goto out_free;
+		}
+
+		pr_info("Collected tty ID %#x\n", info->tfe->id);
+
+		list_add(&info->list, &all_ttys);
+		file_desc_add(&info->d, info->tfe->id, &tty_desc_ops);
+	}
+
+out:
+	xfree(info);
+	close(fd);
+	return ret;
+
+out_free:
+	tty_file_entry__free_unpacked(info->tfe, NULL);
+	goto out;
+}
+
+static int parse_index(int lfd, int rdev, const struct fd_parms *p)
+{
+	int major = major(rdev);
+	int index = -1;
+
+	switch (major) {
+	case TTYAUX_MAJOR:
+		if (ioctl(lfd, TIOCGPTN, &index)) {
+			pr_perror("Can't obtain ptmx index\n");
+			return -1;
+		}
+		break;
+
+	case UNIX98_PTY_SLAVE_MAJOR: {
+		char path[PATH_MAX];
+		char link[64];
+		int len;
+
+		snprintf(link, sizeof(link), "/proc/self/fd/%d", lfd);
+		len = readlink(link, path, sizeof(path) - 1);
+		if (len < 0) {
+			pr_perror("Can't readlink %s", link);
+			return -1;
+		}
+		path[len] = '\0';
+
+		if (sscanf(path, PTS_FMT, &index) != 1) {
+			pr_err("Unexpected format on path %s\n", path);
+			return -1;
+		}
+		break;
+	}
+	}
+
+	return index;
+}
+
+static int dump_one_tty(int lfd, u32 id, const struct fd_parms *p)
+{
+	TtyFileEntry e = TTY_FILE_ENTRY__INIT;
+	Term2Entry term2 = TERM2_ENTRY__INIT;
+	TtyUnix98Entry unix98 = TTY_UNIX98_ENTRY__INIT;
+	struct termios t = { };
+	int ret;
+
+	pr_info("Dumping tty %d with id %#x\n", lfd, id);
+
+	e.id		= id;
+	e.flags		= p->flags;
+	e.pos		= p->pos;
+	e.rdev		= p->stat.st_rdev;
+	e.fown		= (FownEntry *)&p->fown;
+	e.term2		= &term2;
+	e.unix98	= &unix98;
+	e.type		= TTY_FILE_ENTRY__TTY_TYPE__UNIX98;
+
+	ret = parse_index(lfd, e.rdev, p);
+	if (ret < 0) {
+		pr_perror("Can't get tty index on %d", p->fd);
+		return -1;
+	}
+	unix98.index = ret;
+
+	ret = tty_get_sid(lfd);
+	if (ret < 0)
+		return -1;
+	e.sid = ret;
+
+	ret = ioctl(lfd, TIOCGPGRP, &e.prgp);
+	if (ret < 0) {
+		if (errno != ENOTTY) {
+			pr_perror("Can't get pgrp on %d", p->fd);
+			return -1;
+		} else
+			e.prgp = 0;
+	}
+
+	if (ioctl(lfd, TCGETS, &t) < 0) {
+		pr_perror("Can't get tty params on %d", p->fd);
+		return -1;
+	}
+
+
+	term2.n_c_cc	= TERM2_NCC;
+	term2.c_cc	= xmalloc(pb_repeated_size(&term2, c_cc));
+	if (!term2.c_cc)
+		return -1;
+
+	from_termios(&term2, &t);
+	ret = pb_write_one(fdset_fd(glob_fdset, CR_FD_TTY), &e, PB_TTY);
+
+	xfree(term2.c_cc);
+
+	return ret;
+}
+
+static const struct fdtype_ops tty_ops = {
+	.type		= FD_TYPES__TTY,
+	.make_gen_id	= make_gen_id,
+	.dump		= dump_one_tty,
+};
+
+int dump_tty(struct fd_parms *p, int lfd, const struct cr_fdset *set)
+{
+	return do_dump_gen_file(p, lfd, &tty_ops, set);
+}
-- 
1.7.7.6



More information about the CRIU mailing list