[CRIU] Re: [PATCH 2/2] tty: Add checkpoint/restore for ttys
Andrew Vagin
avagin at parallels.com
Thu Jun 21 06:46:56 EDT 2012
Looks like that it should be dead-locked if slave and master will be
opened in one process and a descriptor number of slave will be less than
a number of master.
On Fri, Jun 15, 2012 at 01:32:46PM +0400, Cyrill Gorcunov wrote:
> At moment it handles unix98 ttys only. At least
> that's what being tested with 'screen' session.
>
> The base idea is to save/restore tty parameters with
> TCGETS/TCSETS ioctls.
>
> Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
> ---
> Makefile | 1 +
> cr-dump.c | 7 +-
> cr-restore.c | 4 +
> cr-show.c | 33 ++++
> crtools.c | 1 +
> include/crtools.h | 2 +
> include/image.h | 25 +++
> include/tty.h | 15 ++
> tty.c | 502 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 9 files changed, 588 insertions(+), 2 deletions(-)
> create mode 100644 include/tty.h
> create mode 100644 tty.c
>
> diff --git a/Makefile b/Makefile
> index 44b6854..c4e5a58 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -55,6 +55,7 @@ OBJS += eventfd.o
> OBJS += eventpoll.o
> OBJS += mount.o
> OBJS += inotify.o
> +OBJS += tty.o
>
> DEPS := $(patsubst %.o,%.d,$(OBJS))
>
> diff --git a/cr-dump.c b/cr-dump.c
> index 47f2667..555c125 100644
> --- a/cr-dump.c
> +++ b/cr-dump.c
> @@ -41,6 +41,7 @@
> #include "eventfd.h"
> #include "eventpoll.h"
> #include "inotify.h"
> +#include "tty.h"
>
> #ifndef CONFIG_X86_64
> # error No x86-32 support yet
> @@ -443,12 +444,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 9a17cd3..1c44716 100644
> --- a/cr-restore.c
> +++ b/cr-restore.c
> @@ -45,6 +45,7 @@
> #include "shmem.h"
> #include "mount.h"
> #include "inotify.h"
> +#include "tty.h"
>
> static struct task_entries *task_entries;
>
> @@ -290,6 +291,9 @@ static int prepare_shared(void)
> if (collect_pipes())
> return -1;
>
> + if (collect_tty())
> + return -1;
> +
> if (collect_inet_sockets())
> return -1;
>
> diff --git a/cr-show.c b/cr-show.c
> index 27de63a..296257a 100644
> --- a/cr-show.c
> +++ b/cr-show.c
> @@ -62,6 +62,7 @@ static char *fdtype2s(u8 type)
> [FDINFO_REG] = "reg",
> [FDINFO_INETSK] = "isk",
> [FDINFO_PIPE] = "pipe",
> + [FDINFO_TTY] = "tty",
> [FDINFO_UNIXSK] = "usk",
> [FDINFO_EVENTFD] = "efd",
> [FDINFO_EVENTPOLL] = "epl",
> @@ -227,6 +228,38 @@ out:
> pr_img_tail(CR_FD_PIPES);
> }
>
> +void show_tty(int fd, struct cr_options *o)
> +{
> + struct tty_file_entry e;
> + int ret;
> +
> + pr_img_head(CR_FD_TTY);
> +
> + while (1) {
> + ret = read_img_eof(fd, &e);
> + if (ret <= 0)
> + goto out;
> + pr_msg("id: 0x%-8x flags: 0x%-8x rdev: 0x%-16lx index 0x%-8x ",
> + e.id, e.flags, e.rdev, e.index);
> + show_fown_cont(&e.fown);
> +
> + if (e.len) {
> + int ret = read(fd, local_buf, e.len);
> +
> + if (ret != e.len) {
> + pr_perror("Can't read %d bytes", e.len);
> + goto out;
> + }
> + local_buf[e.len] = 0;
> + pr_msg(" --> %s", local_buf);
> + }
> + pr_msg("\n");
> + }
> +
> +out:
> + pr_img_tail(CR_FD_TTY);
> +}
> +
> void show_fs(int fd_fs, struct cr_options *o)
> {
> struct fs_entry fe;
> diff --git a/crtools.c b/crtools.c
> index fc0e010..536ec57 100644
> --- a/crtools.c
> +++ b/crtools.c
> @@ -60,6 +60,7 @@ struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX] = {
> FD_ENTRY(VMAS, "vmas-%d", show_vmas),
> FD_ENTRY(PIPES, "pipes", show_pipes),
> FD_ENTRY(PIPES_DATA, "pipes-data", show_pipes_data),
> + FD_ENTRY(TTY, "tty", show_tty),
> FD_ENTRY(PSTREE, "pstree", show_pstree),
> FD_ENTRY(SIGACT, "sigacts-%d", show_sigacts),
> FD_ENTRY(UNIXSK, "unixsk", show_unixsk),
> diff --git a/include/crtools.h b/include/crtools.h
> index 798d196..557f0f1 100644
> --- a/include/crtools.h
> +++ b/include/crtools.h
> @@ -54,6 +54,7 @@ enum {
> CR_FD_UNIXSK,
> CR_FD_PIPES,
> CR_FD_PIPES_DATA,
> + CR_FD_TTY,
> CR_FD_REMAP_FPATH,
> CR_FD_EVENTFD,
> CR_FD_EVENTPOLL,
> @@ -113,6 +114,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(fown_t *fown);
> void show_eventfds(int fd, struct cr_options *o);
> +void show_tty(int fd, struct cr_options *o);
>
> extern void print_data(unsigned long addr, unsigned char *data, size_t size);
> extern struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX];
> diff --git a/include/image.h b/include/image.h
> index eed7389..0c7b2fd 100644
> --- a/include/image.h
> +++ b/include/image.h
> @@ -40,11 +40,13 @@
> #define INOTIFY_MAGIC 0x48424431 /* Volgograd */
> #define INOTIFY_WD_MAGIC 0x54562009 /* Svetlogorsk (Rauschen) */
> #define MOUNTPOINTS_MAGIC 0x55563928 /* Petushki */
> +#define TTY_MAGIC 0x59433025 /* Pushkin */
>
> enum fd_types {
> FDINFO_UND,
> FDINFO_REG,
> FDINFO_PIPE,
> + FDINFO_TTY,
> FDINFO_INETSK,
> FDINFO_UNIXSK,
> FDINFO_EVENTFD,
> @@ -154,6 +156,29 @@ enum {
> PIPE_TYPE_MAX,
> };
>
> +typedef struct {
> + u16 c_iflag;
> + u16 c_oflag;
> + u16 c_cflag;
> + u16 c_lflag;
> + u8 c_line;
> + u8 c_cc[19];
> + u16 c_ispeed;
> + u16 c_ospeed;
> +} __packed term2_t;
> +
> +struct tty_file_entry {
> + u32 id;
> + u16 flags;
> + u16 len;
> + u64 pos;
> + u64 rdev;
> + u32 index;
> + fown_t fown;
> + term2_t term2;
> + u8 name[0];
> +} __packed;
> +
> struct pipe_entry {
> u32 id;
> u32 pipe_id;
> diff --git a/include/tty.h b/include/tty.h
> new file mode 100644
> index 0000000..9fa6055
> --- /dev/null
> +++ b/include/tty.h
> @@ -0,0 +1,15 @@
> +#ifndef CR_TTY_H__
> +#define CR_TTY_H__
> +
> +#include "files.h"
> +#include "crtools.h"
> +
> +#define PTMX_PATH "/dev/ptmx"
> +#ifndef PTMX_MINOR
> +# define PTMX_MINOR 2
> +#endif
> +
> +extern int dump_tty(struct fd_parms *p, int lfd, const struct cr_fdset *set);
> +extern int collect_tty(void);
> +
> +#endif /* CR_TTY_H__ */
> diff --git a/tty.c b/tty.c
> new file mode 100644
> index 0000000..5ed3fdc
> --- /dev/null
> +++ b/tty.c
> @@ -0,0 +1,502 @@
> +#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 "tty.h"
> +
> +struct tty_file_info;
> +
> +struct pty_unix98_priv {
> + struct tty_file_info *slave; /* pointer to slave info if there */
> + bool fake_ptmx; /* fake ptmx needed to create slave */
> + bool slave_here; /* slave in the same ns as a master */
> +};
> +
> +struct tty_private {
> + union {
> + struct pty_unix98_priv unix98;
> + };
> +};
> +
> +struct tty_file_info {
> + struct list_head list;
> + struct file_desc d;
> + struct tty_file_entry tfe;
> + struct tty_private priv;
> +
> + char *path;
> + int major;
> + int minor;
> + bool create;
> +};
> +
> +static LIST_HEAD(all_ttys);
> +
> +static void from_termio(term2_t *d, struct termio *s)
> +{
> + BUILD_BUG_ON(sizeof(d->c_cc) < sizeof(s->c_cc));
> +
> + memzero(d, sizeof(*d));
> + 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_termio(struct termio *d, term2_t *s)
> +{
> + memzero(d, sizeof(*d));
> + 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);
> +}
> +
> +#define PTMX_INDEX_ANY -1
> +
> +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 || index == PTMX_INDEX_ANY) {
> + ret = fds[i];
> + fds[i] = -1;
> + break;
> + }
> +
> +
> + /*
> + * 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_pts(struct tty_file_info *info)
> +{
> + struct fdinfo_list_entry *fle;
> + struct file_desc *info_desc;
> + char path[64];
> + int tmp, fd;
> +
> + info_desc = find_file_desc_raw(FDINFO_TTY, info->tfe.id);
> + fle = file_master(info_desc);
> + pr_info("\tWaiting tty fd %d\n", fle->fe.fd);
> +
> + tmp = recv_fd(fle->fe.fd);
> + close(fle->fe.fd);
> + if (tmp < 0) {
> + pr_err("Can't get fd %d\n", tmp);
> + return -1;
> + }
> +
> + snprintf(path, sizeof(path), "/proc/self/fd/%d", tmp);
> + fd = open(path, info->tfe.flags);
> + close(tmp);
> +
> + return fd;
> +}
> +
> +static int tty_loopup_open_pts(struct tty_file_info *info)
> +{
> + int slave_fd, master_fd = -1;
> +
> + if (!info->create)
> + return receive_tty_pts(info);
> +
> + slave_fd = open(info->path, info->tfe.flags);
> + if (slave_fd >= 0)
> + goto out;
> +
> + if (info->priv.unix98.fake_ptmx) {
> +
> + /* If we faile here we won't connect slave anyway */
> + master_fd = tty_open_ptmx_index(info->tfe.flags | O_RDWR, info->tfe.index);
> + if (master_fd >= 0)
> + unlock_pty_master(master_fd);
> + else
> + pr_err("Can't open fake ptmx for %s (%d)\n",
> + info->path, info->tfe.index);
> + }
> +
> + slave_fd = open(info->path, info->tfe.flags);
> + if (slave_fd < 0) {
> + pr_perror("Can't open %s\n", info->path);
> + goto out;
> + }
> +
> +out:
> + close_safe(&master_fd);
> + return slave_fd;
> +}
> +
> +static int tty_open_ptmx(struct tty_file_info *info)
> +{
> + struct tty_file_info *slave = info->priv.unix98.slave;
> + int master_fd = -1;
> + int slave_fd = -1;
> + int sock = -1;
> + int ret = -1;
> +
> + if (info->minor != PTMX_MINOR || strcmp(info->path, PTMX_PATH)) {
> + pr_err("Unexpected tty path %s (%d %d)\n",
> + info->path, info->minor, PTMX_MINOR);
> + return -1;
> + }
> +
> + if (slave) {
> + struct fdinfo_list_entry *fle;
> + struct file_desc *slave_desc;
> + int index;
> +
> + if (info->priv.unix98.fake_ptmx)
> + index = -1;
> + else
> + index = slave->tfe.index;
> +
> + master_fd = tty_open_ptmx_index(info->tfe.flags, index);
> + if (master_fd < 0)
> + goto err;
> +
> + if (unlock_pty_master(master_fd))
> + goto err;
> +
> + sock = socket(PF_UNIX, SOCK_DGRAM, 0);
> + if (sock < 0) {
> + pr_perror("Can't create socket");
> + goto err;
> + }
> +
> + slave_fd = open(slave->path, slave->tfe.flags);
> + if (slave_fd < 0) {
> + pr_perror("Can't open slave %s", slave->path);
> + goto err;
> + }
> +
> + slave_desc = find_file_desc_raw(FDINFO_TTY, slave->tfe.id);
> + fle = file_master(slave_desc);
> +
> + list_for_each_entry(fle, &slave_desc->fd_info_head, desc_list) {
> + if (fle->pid == getpid())
> + continue;
> + if (send_fd_to_peer(slave_fd, fle, sock)) {
> + pr_perror("Can't send file descriptor");
> + goto err;
> + }
> + }
> +
> + ret = master_fd, master_fd = -1;
> + } else
> + ret = tty_open_ptmx_index(info->tfe.flags, info->tfe.index);
> +
> +err:
> + close_safe(&master_fd);
> + close_safe(&slave_fd);
> + close_safe(&sock);
> + return ret;
> +}
> +
> +static int tty_lookup_open(struct tty_file_info *info)
> +{
> + switch (info->major) {
> + case UNIX98_PTY_SLAVE_MAJOR:
> + return tty_loopup_open_pts(info);
> + case TTYAUX_MAJOR:
> + return tty_open_ptmx(info);
> + }
> +
> + BUG_ON(1);
> + return -1;
> +}
> +
> +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 termio t;
> +
> + tmp = tty_lookup_open(info);
> + if (tmp < 0) {
> + pr_perror("Can't open tty id %#08x [%s]",
> + info->tfe.id, info->path);
> + return -1;
> + }
> +
> + 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_termio(&t, &info->tfe.term2);
> + if (ioctl(tmp, TCSETS, &t) < 0) {
> + pr_perror("Can't set tty params on %d [%s]",
> + info->tfe.id, info->path);
> + goto err;
> + }
> +
> + ret = tmp, tmp = -1;
> +err:
> + close_safe(&tmp);
> + return ret;
> +}
> +
> +static int want_transport(struct fdinfo_entry *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 = FDINFO_TTY,
> + .open = tty_open,
> + .want_transport = want_transport,
> +};
> +
> +static int tty_handle_priv(struct list_head *all_ttys_head)
> +{
> + struct tty_file_info *info;
> +
> + list_for_each_entry(info, all_ttys_head, list) {
> + struct tty_file_info *entry;
> + int master;
> +
> + /*
> + * At moment we're only interested in UNIX98_PTY_SLAVE_MAJOR
> + * devices since they require to open ptmx with appropriate
> + * index.
> + */
> + if (info->major == TTYAUX_MAJOR ||
> + info->major != UNIX98_PTY_SLAVE_MAJOR)
> + continue;
> +
> + /*
> + * Setup the ptmx index here, the kernel
> + * doesn't provide any other interface :/
> + */
> + if (sscanf(info->path, "/dev/pts/%d", &master) != 1) {
> + pr_err("Unexpected format on path %s\n", info->path);
> + return -1;
> + }
> + info->tfe.index = master;
> +
> + /*
> + * By default we assume the fake ptmx
> + * connection is needed, until contrary
> + * proved.
> + */
> + info->priv.unix98.fake_ptmx = true;
> +
> + /* Finally connect slave to a master device */
> + list_for_each_entry(entry, all_ttys_head, list) {
> + if (entry->major != TTYAUX_MAJOR ||
> + entry->tfe.index != master)
> + continue;
> +
> + entry->priv.unix98.fake_ptmx = false;
> + entry->priv.unix98.slave = info;
> + info->create = false;
> + break;
> + }
> + }
> +
> + 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) {
> + int len;
> +
> + info = xzalloc(sizeof(*info));
> + if (!info) {
> + ret = -1;
> + break;
> + }
> +
> + ret = read_img_eof(fd, &info->tfe);
> + if (ret <= 0)
> + break;
> +
> + len = info->tfe.len;
> + info->path = xmalloc(len + 1);
> + if (!info->path) {
> + ret = -1;
> + break;
> + }
> +
> + ret = read_img_buf(fd, info->path, len);
> + if (ret < 0)
> + break;
> +
> + info->path[len] = '\0';
> + info->create = true;
> + info->major = major(info->tfe.rdev);
> + info->minor = minor(info->tfe.rdev);
> +
> + pr_info("Collected tty [%s] ID %#x\n", info->path, info->tfe.id);
> +
> + /* Partial ordering is needed for search speedup */
> + switch (info->major) {
> + case TTYAUX_MAJOR:
> + list_add(&info->list, &all_ttys);
> + break;
> + case UNIX98_PTY_SLAVE_MAJOR:
> + list_add_tail(&info->list, &all_ttys);
> + break;
> + default:
> + pr_err("Unsupported tty type %s (major %d)\n",
> + info->path, info->major);
> + ret = -1;
> + goto out;
> + }
> + }
> +
> + ret = tty_handle_priv(&all_ttys);
> +
> + if (!ret) {
> + list_for_each_entry(info, &all_ttys, list)
> + file_desc_add(&info->d, info->tfe.id, &tty_desc_ops);
> + info = NULL;
> + }
> +
> +out:
> + if (info)
> + xfree(info->path);
> + xfree(info);
> + close(fd);
> + return ret;
> +}
> +
> +static int dump_one_tty(int lfd, u32 id, const struct fd_parms *p)
> +{
> + struct tty_file_entry e = { };
> + struct termio t;
> + char *path;
> + int fd, len;
> +
> + pr_info("Dumping tty %d with id %#x\n", lfd, id);
> +
> + path = read_proc_selffd_link(lfd, &len);
> + if (!path)
> + return -1;
> +
> + if (ioctl(lfd, TCGETS, &t) < 0) {
> + pr_perror("Can't get tty params on %d [%s]", p->fd, path);
> + return -1;
> + }
> +
> + from_termio(&e.term2, &t);
> +
> + e.id = id;
> + e.flags = p->flags;
> + e.len = len;
> + e.pos = p->pos;
> + e.fown = p->fown;
> + e.rdev = p->stat.st_rdev;
> +
> + /*
> + * This ioctl is implemented for unix98 ptys,
> + * so simply ignore error code if not implemented.
> + */
> + if (ioctl(lfd, TIOCGPTN, &e.index))
> + e.index = 0;
> +
> + fd = fdset_fd(glob_fdset, CR_FD_TTY);
> + if (write_img(fd, &e))
> + return -1;
> +
> + pr_info("Dumping path for tty %d fd via self %d [%s]\n", p->fd, lfd, path);
> + if (write_img_buf(fd, path, e.len))
> + return -1;
> +
> + return 0;
> +}
> +
> +static const struct fdtype_ops tty_ops = {
> + .type = FDINFO_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