[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