[CRIU] [RFC] tty: Add external ttys handling
Pavel Emelyanov
xemul at parallels.com
Fri Oct 3 01:45:48 PDT 2014
On 10/02/2014 10:44 PM, Cyrill Gorcunov wrote:
> 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.
Why do we need an option for that? What's missing in existing tty.c?
> 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 = {
>
More information about the CRIU
mailing list