[CRIU] [RFC] tty: Add external ttys handling

Cyrill Gorcunov gorcunov at openvz.org
Thu Oct 2 11:44:17 PDT 2014


Some types of ttys (such as /dev/console) should be c/r'ed in
simle manner -- just opent them by path on restore, for this
sake that named external TTYs are introduced.

For example, for /dev/console one simply need to pass an option

	--ext-tty-map 5:1

where 5 represent major number of console and 1 is a minor, and
the criu will simply re-open it on restore.

This should be enough for the start.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---

Guys, please take a look. This does trick for me with /dev/console
but comments are highly appreciated.

 crtools.c          |  10 +-
 files.c            |  10 ++
 include/tty.h      |  14 +++
 protobuf/tty.proto |   6 +
 tty.c              | 356 ++++++++++++++++++++++++++++++++++++++++++++++-------
 5 files changed, 353 insertions(+), 43 deletions(-)

diff --git a/crtools.c b/crtools.c
index b8f73060222f..b515423cd998 100644
--- a/crtools.c
+++ b/crtools.c
@@ -28,6 +28,7 @@
 #include "files.h"
 #include "sk-inet.h"
 #include "net.h"
+#include "tty.h"
 #include "version.h"
 #include "page-xfer.h"
 #include "tty.h"
@@ -133,7 +134,7 @@ int main(int argc, char *argv[], char *envp[])
 	int log_level = LOG_UNSET;
 	char *imgs_dir = ".";
 	char *work_dir = NULL;
-	static const char short_opts[] = "dSsRf:F:t:p:hcD:o:n:v::xVr:jlW:L:M:";
+	static const char short_opts[] = "dSsRf:F:t:p:hcD:o:n:v::xVr:jlW:L:M:T:";
 	static struct option long_opts[] = {
 		{ "tree", required_argument, 0, 't' },
 		{ "pid", required_argument, 0, 'p' },
@@ -174,6 +175,7 @@ int main(int argc, char *argv[], char *envp[])
 		{ "cpu-cap", required_argument, 0, 1057},
 		{ "force-irmap", no_argument, 0, 1058},
 		{ "ext-mount-map", required_argument, 0, 'M'},
+		{ "ext-tty-map", required_argument, 0, 'T'},
 		{ "exec-cmd", no_argument, 0, 1059},
 		{ "manage-cgroups", no_argument, 0, 1060},
 		{ "cgroup-root", required_argument, 0, 1061},
@@ -395,6 +397,10 @@ int main(int argc, char *argv[], char *envp[])
 					return 1;
 			}
 			break;
+		case 'T':
+			if (tty_add_ext(optarg))
+				return 1;
+			break;
 		case 'V':
 			pr_msg("Version: %s\n", CRIU_VERSION);
 			if (strcmp(CRIU_GITID, "0"))
@@ -576,6 +582,8 @@ usage:
 "  --force-irmap         force resolving names for inotify/fsnotify watches\n"
 "  -M|--ext-mount-map KEY:VALUE\n"
 "                        add external mount mapping\n"
+"  -T|--ext-tty-map KEY:VALUE\n"
+"                        add external tty mapping\n"
 "  --manage-cgroups      dump or restore cgroups the process is in\n"
 "  --cgroup-root [controller:]/newroot\n"
 "                        change the root cgroup the controller will be\n"
diff --git a/files.c b/files.c
index 42425a25eab7..944773eedeb9 100644
--- a/files.c
+++ b/files.c
@@ -33,6 +33,7 @@
 #include "signalfd.h"
 #include "namespaces.h"
 #include "tun.h"
+#include "tty.h"
 #include "timerfd.h"
 #include "imgset.h"
 #include "fs-magic.h"
@@ -286,11 +287,20 @@ static int dump_chrdev(struct fd_parms *p, int lfd, struct cr_img *img)
 	default: {
 		char more[32];
 
+		/*
+		 * Ext mapping for tty devices.
+		 */
+		if (lookup_ext_tty(maj, minor(p->stat.st_rdev))) {
+			ops = &tty_dump_ops;
+			goto found;
+		}
+
 		sprintf(more, "%d:%d", maj, minor(p->stat.st_rdev));
 		return dump_unsupp_fd(p, lfd, img, "chr", more);
 	}
 	}
 
+found:
 	return do_dump_gen_file(p, lfd, ops, img);
 }
 
diff --git a/include/tty.h b/include/tty.h
index d2c31e298a34..f7ce4eab4b75 100644
--- a/include/tty.h
+++ b/include/tty.h
@@ -3,6 +3,17 @@
 
 #include "files.h"
 
+typedef struct {
+	struct {
+		unsigned int from;
+		unsigned int to;
+	} major;
+	struct {
+		unsigned int from;
+		unsigned int to;
+	} minor;
+} ext_tty_range_t;
+
 /* Kernel's limit */
 #define TERMIOS_NCC	19
 
@@ -26,4 +37,7 @@ extern void tty_fini_fds(void);
 
 #define OPT_SHELL_JOB	"shell-job"
 
+extern ext_tty_range_t *lookup_ext_tty(unsigned int major, unsigned int minor);
+extern int tty_add_ext(char *opt);
+
 #endif /* __CR_TTY_H__ */
diff --git a/protobuf/tty.proto b/protobuf/tty.proto
index 215bff35f212..4e842503c3e1 100644
--- a/protobuf/tty.proto
+++ b/protobuf/tty.proto
@@ -24,9 +24,14 @@ message tty_pty_entry {
 	optional uint32			mnt_id		= 2;
 }
 
+message tty_ext_entry {
+	required string			path		= 1;
+};
+
 enum TtyType {
 	UNKNOWN		= 0;
 	PTY		= 1;
+	EXT		= 2;
 }
 
 message tty_info_entry {
@@ -57,6 +62,7 @@ message tty_info_entry {
 	 * TTY type.
 	 */
 	optional tty_pty_entry		pty		= 12;
+	optional tty_ext_entry		ext		= 13;
 };
 
 message tty_file_entry {
diff --git a/tty.c b/tty.c
index c9aa70bc4087..edb7e57e408c 100644
--- a/tty.c
+++ b/tty.c
@@ -104,7 +104,8 @@ struct tty_dump_info {
 };
 
 static LIST_HEAD(all_tty_info_entries);
-static LIST_HEAD(all_ttys);
+static LIST_HEAD(all_ptys);
+static LIST_HEAD(all_ext);
 
 /*
  * Usually an application has not that many ttys opened.
@@ -119,11 +120,118 @@ static DECLARE_BITMAP(tty_bitmap, (MAX_TTYS << 1));
 static DECLARE_BITMAP(tty_active_pairs, (MAX_TTYS << 1));
 
 /*
+ * External ttys tracking.
+ */
+static ext_tty_range_t ext_tty_ranges[64];
+static size_t nr_ext_tty_ranges;
+static unsigned int ext_tty_ids = (MAX_TTYS << 1) + 2;
+
+/*
  * /dev/ptmx is a shared resource between all tasks
  * so we need to serialize access to it.
  */
 static mutex_t *tty_mutex;
 
+/*
+ * Expect "a[-b]:c[-d],e[-f]:g[-h]" format here
+ */
+int tty_add_ext(char *optarg)
+{
+	char *opts, *tok;
+
+#define __push_ext_tty_different_pair(__which, __from, __to)			\
+	do {									\
+		ext_tty_ranges[nr_ext_tty_ranges].__which.from = __from;	\
+		ext_tty_ranges[nr_ext_tty_ranges].__which.to = __to;		\
+	} while (0)
+
+#define __push_ext_tty_same_pair(__which, __num)				\
+	do {									\
+		ext_tty_ranges[nr_ext_tty_ranges].__which.from =		\
+		ext_tty_ranges[nr_ext_tty_ranges].__which.to = __num;		\
+	} while (0)
+
+	opts = xstrdup(optarg);
+	if (!opts)
+		return -1;
+	if (strchr(opts, ','))
+		tok = strtok(opts, ",");
+	else
+		tok = opts;
+
+	do {
+		char *d = strchr(tok, ':');
+		char *m;
+
+		if (nr_ext_tty_ranges == ARRAY_SIZE(ext_tty_ranges) - 1)
+			goto Eoverflow;
+
+		if (!d || d == tok || d == (tok + strlen(tok) - 1))
+			goto Esyntax;
+
+		*d = '\0';
+		m = strchr(tok, '-');
+		if (m) {
+			if (m == tok || m == (tok + strlen(tok) - 1))
+				goto Esyntax;
+			*m = '\0';
+			__push_ext_tty_different_pair(major, atoi(tok),  atoi(m + 1));
+		} else {
+			__push_ext_tty_same_pair(major, atoi(tok));
+		}
+
+		tok = d + 1;
+		m = strchr(tok, '-');
+		if (m) {
+			if (m == tok || m == (tok + strlen(tok) - 1))
+				goto Esyntax;
+			*m = '\0';
+			__push_ext_tty_different_pair(minor, atoi(tok),  atoi(m + 1));
+		} else {
+			__push_ext_tty_same_pair(minor, atoi(tok));
+		}
+		nr_ext_tty_ranges++;
+	} while ((tok = strtok(NULL, ",")));
+
+	xfree(opts);
+	return 0;
+
+Esyntax:
+	pr_err("Unknown format `%s' selected (expected 'a[-b]:c[-d],e[-f]:g[-h],...')\n", optarg);
+	goto err;
+Eoverflow:
+	pr_err("Too many external tty maps are selected, "
+	       "only %li is availabe\n", ARRAY_SIZE(ext_tty_ranges));
+	goto err;
+err:
+	xfree(opts);
+	return -1;
+#undef __push_ext_tty_different_pair
+#undef __push_ext_tty_same_pair
+
+}
+
+ext_tty_range_t *lookup_ext_tty(unsigned int major, unsigned int minor)
+{
+	size_t i;
+
+	pr_debug("lookup ext %d:%d\n", major, minor);
+	for (i = 0; i < nr_ext_tty_ranges; i++) {
+		pr_debug("\t%d-%d:%d-%d\n",
+			 ext_tty_ranges[i].major.from,
+			 ext_tty_ranges[i].major.to,
+			 ext_tty_ranges[i].minor.from,
+			 ext_tty_ranges[i].minor.to);
+		if (major < ext_tty_ranges[i].major.from	||
+		    major > ext_tty_ranges[i].major.to		||
+		    minor < ext_tty_ranges[i].minor.from	||
+		    minor > ext_tty_ranges[i].minor.to)
+			continue;
+		return &ext_tty_ranges[i];
+	}
+	return NULL;
+}
+
 int prepare_shared_tty(void)
 {
 	tty_mutex = shmalloc(sizeof(*tty_mutex));
@@ -455,9 +563,13 @@ static bool tty_has_active_pair(struct tty_info *info)
 
 static void tty_show_pty_info(char *prefix, struct tty_info *info)
 {
-	pr_info("%s type %s id %#x index %d (master %d sid %d pgrp %d inherit %d)\n",
-		prefix, tty_type(info->major, info->minor), info->tfe->id, info->tie->pty->index,
-		tty_is_master(info), info->tie->sid, info->tie->pgrp, info->inherit);
+	if (info->tie->type == TTY_TYPE__PTY)
+		pr_info("%s type %s id %#x index %d (master %d sid %d pgrp %d inherit %d)\n",
+			prefix, tty_type(info->major, info->minor), info->tfe->id, info->tie->pty->index,
+			tty_is_master(info), info->tie->sid, info->tie->pgrp, info->inherit);
+	else
+		pr_info("%s type ext id %#x (path %s)\n",
+			prefix, info->tfe->id, info->tie->ext->path);
 }
 
 static int restore_tty_params(int fd, struct tty_info *info)
@@ -691,6 +803,27 @@ err:
 	return -1;
 }
 
+static int tty_open_ext(struct file_desc *d, struct tty_info *info)
+{
+	int ret = -1, fd;
+
+	fd = open(info->tie->ext->path, info->tfe->flags | O_NOCTTY);
+	if (fd < 0) {
+		pr_perror("Can't open ext tty %s for fd %d",
+			  info->tie->ext->path, fd);
+		return -1;
+	}
+
+	if (restore_tty_params(fd, info))
+		goto err;
+
+	ret = fd;
+	fd = -1;
+err:
+	close_safe(&fd);
+	return ret;
+}
+
 static int tty_open(struct file_desc *d)
 {
 	struct tty_info *info = container_of(d, struct tty_info, d);
@@ -700,11 +833,14 @@ static int tty_open(struct file_desc *d)
 	if (!info->create)
 		return receive_tty(info);
 
-	if (!tty_is_master(info))
-		return pty_open_unpaired_slave(d, info);
-
-	return pty_open_ptmx(info);
-
+	if (info->tie->type == TTY_TYPE__PTY) {
+		if (!tty_is_master(info))
+			return pty_open_unpaired_slave(d, info);
+		return pty_open_ptmx(info);
+	} else if (info->tie->type == TTY_TYPE__EXT)
+		return tty_open_ext(d, info);
+	BUG();
+	return -1;
 }
 
 static int tty_transport(FdinfoEntry *fe, struct file_desc *d)
@@ -716,6 +852,7 @@ static int tty_transport(FdinfoEntry *fe, struct file_desc *d)
 static void tty_collect_fd(struct file_desc *d, struct fdinfo_list_entry *fle,
 		struct rst_info *ri)
 {
+	struct tty_info *info = container_of(d, struct tty_info, d);
 	struct list_head *tgt;
 
 	/*
@@ -723,7 +860,7 @@ static void tty_collect_fd(struct file_desc *d, struct fdinfo_list_entry *fle,
 	 * opened before them
 	 */
 
-	if (tty_is_master(container_of(d, struct tty_info, d)))
+	if (tty_is_master(info) || info->tie->type == TTY_TYPE__EXT)
 		tgt = &ri->fds;
 	else
 		tgt = &ri->tty_slaves;
@@ -838,7 +975,7 @@ static int tty_setup_orphan_slavery(void)
 {
 	struct tty_info *info, *peer, *m;
 
-	list_for_each_entry(info, &all_ttys, list) {
+	list_for_each_entry(info, &all_ptys, list) {
 		struct fdinfo_list_entry *a, *b;
 		bool has_leader = false;
 
@@ -880,12 +1017,12 @@ int tty_setup_slavery(void)
 {
 	struct tty_info *info, *peer, *m;
 
-	list_for_each_entry(info, &all_ttys, list) {
+	list_for_each_entry(info, &all_ptys, list) {
 		if (tty_find_restoring_task(info))
 			return -1;
 
 		peer = info;
-		list_for_each_entry_safe_continue(peer, m, &all_ttys, list) {
+		list_for_each_entry_safe_continue(peer, m, &all_ptys, list) {
 			if (peer->tie->pty->index != info->tie->pty->index)
 				continue;
 
@@ -900,7 +1037,7 @@ int tty_setup_slavery(void)
 	/*
 	 * Print out information about peers.
 	 */
-	list_for_each_entry(info, &all_ttys, list) {
+	list_for_each_entry(info, &all_ptys, list) {
 		tty_show_pty_info("head", info);
 		list_for_each_entry(peer, &info->sibling, sibling)
 			tty_show_pty_info("    `- sibling", peer);
@@ -951,8 +1088,20 @@ static int verify_info(struct tty_info *info)
 	    verify_termios(info->tfe->id, info->tie->termios))
 		return -1;
 
-	if (info->tie->termios && info->tfe->tty_info_id > (MAX_TTYS << 1))
-		return -1;
+	if (info->tie->type == TTY_TYPE__PTY) {
+		if (info->tie->termios) {
+			if (info->tfe->tty_info_id > (MAX_TTYS << 1)) {
+				pr_err("Index %d is too big on %x\n",
+				       info->tfe->tty_info_id, info->tfe->id);
+				return -1;
+			}
+		}
+	} else if (info->tie->type == TTY_TYPE__EXT) {
+		if (!info->tie->ext->path) {
+			pr_err("Corrupted ext peer %x\n", info->tfe->id);
+			return -1;
+		}
+	}
 
 	return 0;
 }
@@ -975,18 +1124,27 @@ static int collect_one_tty_info_entry(void *obj, ProtobufCMessage *msg)
 
 	info->tie = pb_msg(msg, TtyInfoEntry);
 
-	if (info->tie->type != TTY_TYPE__PTY) {
-		pr_err("Unexpected TTY type %d (id %x)\n",
+	switch (info->tie->type) {
+	case TTY_TYPE__PTY:
+		if (!info->tie->pty) {
+			pr_err("No PTY data found (id %x), corrupted image?\n",
+			       info->tie->id);
+			return -1;
+		}
+		break;
+	case TTY_TYPE__EXT:
+		if (!info->tie->ext) {
+			pr_err("No EXT data found (id %x), corrupted image?\n",
+			       info->tie->id);
+			return -1;
+		}
+		break;
+	default:
+		pr_err("Unknown type %d (id %x), corrupted image?\n",
 		       info->tie->type, info->tie->id);
 		return -1;
 	}
 
-	if (!info->tie->pty) {
-		pr_err("No PTY data found (id %x), corrupted image?\n",
-		       info->tie->id);
-		return -1;
-	}
-
 	INIT_LIST_HEAD(&info->list);
 	list_add(&info->list, &all_tty_info_entries);
 
@@ -1017,25 +1175,34 @@ static int collect_one_tty(void *obj, ProtobufCMessage *msg)
 	INIT_LIST_HEAD(&info->sibling);
 	info->major = major(info->tie->rdev);
 	info->minor = minor(info->tie->rdev);
-	info->create = tty_is_master(info);
 	info->inherit = false;
 
 	if (verify_info(info))
 		return -1;
 
-	/*
-	 * The tty peers which have no @termios are hung up,
-	 * so don't mark them as active, we create them with
-	 * faked master and they are rather a rudiment which
-	 * can't be used. Most likely they appear if a user has
-	 * dumped program when it was closing a peer.
-	 */
-	if (info->tie->termios)
-		tty_test_and_set(info->tfe->tty_info_id, tty_active_pairs);
+	switch (info->tie->type) {
+	case TTY_TYPE__PTY:
+		info->create = tty_is_master(info);
+		list_add(&info->list, &all_ptys);
+		/*
+		 * The tty peers which have no @termios are hung up,
+		 * so don't mark them as active, we create them with
+		 * faked master and they are rather a rudiment which
+		 * can't be used. Most likely they appear if a user has
+		 * dumped program when it was closing a peer.
+		 */
+		if (info->tie->termios)
+			tty_test_and_set(info->tfe->tty_info_id, tty_active_pairs);
+		break;
+	case TTY_TYPE__EXT:
+		info->create = true;
+		list_add(&info->list, &all_ext);
+		break;
+	default:
+		BUG();
+	}
 
 	pr_info("Collected tty ID %#x\n", info->tfe->id);
-
-	list_add(&info->list, &all_ttys);
 	return file_desc_add(&info->d, info->tfe->id, &tty_desc_ops);
 }
 
@@ -1071,7 +1238,7 @@ int dump_verify_tty_sids(void)
 	 * In this case we simply zap sid/pgid and inherit
 	 * the peer from the current terminal on restore.
 	 */
-	list_for_each_entry_safe(dinfo, n, &all_ttys, list) {
+	list_for_each_entry_safe(dinfo, n, &all_ptys, list) {
 		if (!ret && dinfo->sid) {
 			struct pstree_item *item = find_first_sid(dinfo->sid);
 
@@ -1137,7 +1304,7 @@ static int dump_pty_info(int lfd, u32 id, const struct fd_parms *p, const struct
 	dinfo->major		= major;
 	dinfo->minor		= minor;
 
-	list_add_tail(&dinfo->list, &all_ttys);
+	list_add_tail(&dinfo->list, &all_ptys);
 
 	info.id			= id;
 	info.type		= TTY_TYPE__PTY;
@@ -1270,10 +1437,110 @@ static int dump_one_pty(int lfd, u32 id, const struct fd_parms *p, int major, in
 	return ret;
 }
 
+static int dump_ext_tty(int lfd, u32 id, const struct fd_parms *p, int major, int minor)
+{
+	TtyFileEntry e			= TTY_FILE_ENTRY__INIT;
+	TtyInfoEntry info		= TTY_INFO_ENTRY__INIT;
+	TermiosEntry termios		= TERMIOS_ENTRY__INIT;
+	TermiosEntry termios_locked	= TERMIOS_ENTRY__INIT;
+	WinsizeEntry winsize		= WINSIZE_ENTRY__INIT;
+	TtyExtEntry ety			= TTY_EXT_ENTRY__INIT;
+
+	struct termios t;
+	struct winsize w;
+
+	ext_tty_range_t *ext;
+	char path[PATH_MAX];
+	char link[32];
+	int len;
+
+	int ret = -1;
+
+	ext = lookup_ext_tty(major, minor);
+	if (!ext)
+		return -1;
+
+	pr_debug("External tty (%d:%d) matched (%u-%u|%u-%u)\n",
+		 major, minor, ext->major.from, ext->major.to,
+		 ext->minor.from, ext->minor.to);
+
+	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';
+
+	e.id		= id;
+	e.tty_info_id	= ext_tty_ids++;
+	e.flags		= p->flags;
+	e.fown		= (FownEntry *)&p->fown;
+
+	/*
+	 * FIXME Merge with dump_pty_info()
+	 */
+	BUILD_BUG_ON(ARRAY_SIZE(t.c_cc) < TERMIOS_NCC);
+	BUILD_BUG_ON(sizeof(termios.c_cc) != sizeof(void *));
+	BUILD_BUG_ON((sizeof(termios.c_cc) * TERMIOS_NCC) < sizeof(t.c_cc));
+
+	info.id			= e.tty_info_id;
+	info.type		= TTY_TYPE__EXT;
+	info.sid		= 0;
+	info.pgrp		= 0;
+	info.rdev		= p->stat.st_rdev;
+	info.termios		= &termios;
+	info.termios_locked	= &termios_locked;
+	info.winsize		= &winsize;
+	info.ext		= &ety;
+
+	ety.path		= path;
+
+	termios.n_c_cc		= TERMIOS_NCC;
+	termios.c_cc		= xmalloc(pb_repeated_size(&termios, c_cc));
+
+	termios_locked.n_c_cc	= TERMIOS_NCC;
+	termios_locked.c_cc	= xmalloc(pb_repeated_size(&termios_locked, c_cc));
+
+	if (!termios.c_cc || !termios_locked.c_cc)
+		goto out;
+
+	memzero(&t, sizeof(t));
+	if (ioctl(lfd, TCGETS, &t) < 0) {
+		pr_perror("Can't get tty params on %x", id);
+		goto out;
+	}
+	termios_copy(&termios, &t);
+
+	memzero(&t, sizeof(t));
+	if (ioctl(lfd, TIOCGLCKTRMIOS, &t) < 0) {
+		pr_perror("Can't get tty locked params on %x", id);
+		goto out;
+	}
+	termios_copy(&termios_locked, &t);
+
+	memzero(&w, sizeof(w));
+	if (ioctl(lfd, TIOCGWINSZ, &w) < 0) {
+		pr_perror("Can't get tty window params on %x", id);
+		goto out;
+	}
+	winsize_copy(&winsize, &w);
+
+	ret = pb_write_one(img_from_set(glob_imgset, CR_FD_TTY_INFO), &info, PB_TTY_INFO);
+
+	if (!ret)
+		ret = pb_write_one(img_from_set(glob_imgset, CR_FD_TTY_FILES), &e, PB_TTY_FILE);
+out:
+	xfree(termios.c_cc);
+	xfree(termios_locked.c_cc);
+	return ret;
+}
+
 static int dump_one_tty(int lfd, u32 id, const struct fd_parms *p)
 {
 	int major = major(p->stat.st_rdev);
 	int minor = minor(p->stat.st_rdev);
+	int ret = -1;
 
 	pr_info("Dumping tty (%d:%d) %d with id %#x\n",
 		major, minor, lfd, id);
@@ -1281,17 +1548,22 @@ static int dump_one_tty(int lfd, u32 id, const struct fd_parms *p)
 	switch (major) {
 	case TTYAUX_MAJOR:
 		if (minor == 0 || minor == 2)
-			return dump_one_pty(lfd, id, p, major, minor);
+			ret = dump_one_pty(lfd, id, p, major, minor);
+		else
+			ret = dump_ext_tty(lfd, id, p, major, minor);
 		break;
 	case UNIX98_PTY_MASTER_MAJOR ... (UNIX98_PTY_MASTER_MAJOR + UNIX98_PTY_MAJOR_COUNT - 1):
 	case UNIX98_PTY_SLAVE_MAJOR:
-		return dump_one_pty(lfd, id, p, major, minor);
+		ret = dump_one_pty(lfd, id, p, major, minor);
+		break;
 	default:
+		ret = dump_ext_tty(lfd, id, p, major, minor);
 		break;
 	}
 
-	pr_err("Unsupported tty (%d:%d) device\n", major, minor);
-	return -1;
+	if (ret)
+		pr_err("Unsupported tty (%d:%d) device\n", major, minor);
+	return ret;
 }
 
 const struct fdtype_ops tty_dump_ops = {
-- 
1.9.3



More information about the CRIU mailing list