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

Andrey Wagin avagin at gmail.com
Mon Aug 13 13:10:55 EDT 2012


2012/8/13 Cyrill Gorcunov <gorcunov at openvz.org>:
> 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.

* a controll terminal can be gotten by ioctl(fd, TIOCSCTTY, ...);
* it can be send across an Unix socket.

I see only one special case, when a session leader should get this
descriptor across unix socket for restoring a control terminal.
But it may be solved by two ways: a session leader always creates a
controll terminal or a free descriptor is used for unix socket. I
prefere the first way.

>
> 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