summaryrefslogtreecommitdiff
path: root/contrib/python/podman/libs/_containers_attach.py
blob: 54e6a009e05a3a3d0ac3bf5b42380d56999b213b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
"""Exported method Container.attach()."""

import fcntl
import os
import select
import signal
import socket
import struct
import sys
import termios
import tty

CONMON_BUFSZ = 8192


class Mixin:
    """Publish attach() for inclusion in Container class."""

    def attach(self, eot=4, stdin=None, stdout=None):
        """Attach to container's PID1 stdin and stdout.

        stderr is ignored.
        """
        if stdin is None:
            stdin = sys.stdin.fileno()

        if stdout is None:
            stdout = sys.stdout.fileno()

        with self._client() as podman:
            attach = podman.GetAttachSockets(self._id)

        # This is the UDS where all the IO goes
        io_socket = attach['sockets']['io_socket']
        assert len(io_socket) <= 107,\
            'Path length for sockets too long. {} > 107'.format(
                len(io_socket)
            )

        # This is the control socket where resizing events are sent to conmon
        ctl_socket = attach['sockets']['control_socket']

        def resize_handler(signum, frame):
            """Send the new window size to conmon.

            The method arguments are not used.
            """
            packed = fcntl.ioctl(stdout, termios.TIOCGWINSZ,
                                 struct.pack('HHHH', 0, 0, 0, 0))
            rows, cols, _, _ = struct.unpack('HHHH', packed)

            with open(ctl_socket, 'w') as skt:
                # send conmon window resize message
                skt.write('1 {} {}\n'.format(rows, cols))

        def log_handler(signum, frame):
            """Send command to reopen log to conmon.

            The method arguments are not used.
            """
            with open(ctl_socket, 'w') as skt:
                # send conmon reopen log message
                skt.write('2\n')

        try:
            # save off the old settings for terminal
            original_attr = termios.tcgetattr(stdout)
            tty.setraw(stdin)

            # initialize containers window size
            resize_handler(None, sys._getframe(0))

            # catch any resizing events and send the resize info
            # to the control fifo "socket"
            signal.signal(signal.SIGWINCH, resize_handler)
        except termios.error:
            original_attr = None

        try:
            # Prepare socket for communicating with conmon/container
            with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as skt:
                skt.connect(io_socket)
                skt.sendall(b'\n')

                sources = [skt, stdin]
                while sources:
                    readable, _, _ = select.select(sources, [], [])
                    for r in readable:
                        if r is skt:
                            data = r.recv(CONMON_BUFSZ)
                            if not data:
                                sources.remove(skt)

                            # Remove source marker when writing
                            os.write(stdout, data[1:])
                        elif r is stdin:
                            data = os.read(stdin, CONMON_BUFSZ)
                            if not data:
                                sources.remove(stdin)

                            skt.sendall(data)

                            if eot in data:
                                sources.clear()
                                break
                        else:
                            raise ValueError('Unknown source in select')
        finally:
            if original_attr:
                termios.tcsetattr(stdout, termios.TCSADRAIN, original_attr)
                signal.signal(signal.SIGWINCH, signal.SIG_DFL)