[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