1 package main
2
3 import (
4 "errors"
5 "fmt"
6 "io"
7 "os"
8 "os/signal"
9 "sync"
10
11 "github.com/containerd/console"
12 "github.com/opencontainers/runc/libcontainer"
13 "github.com/opencontainers/runc/libcontainer/utils"
14 )
15
16 type tty struct {
17 epoller *console.Epoller
18 console *console.EpollConsole
19 hostConsole console.Console
20 closers []io.Closer
21 postStart []io.Closer
22 wg sync.WaitGroup
23 consoleC chan error
24 }
25
26 func (t *tty) copyIO(w io.Writer, r io.ReadCloser) {
27 defer t.wg.Done()
28 _, _ = io.Copy(w, r)
29 _ = r.Close()
30 }
31
32
33
34 func setupProcessPipes(p *libcontainer.Process, rootuid, rootgid int) (*tty, error) {
35 i, err := p.InitializeIO(rootuid, rootgid)
36 if err != nil {
37 return nil, err
38 }
39 t := &tty{
40 closers: []io.Closer{
41 i.Stdin,
42 i.Stdout,
43 i.Stderr,
44 },
45 }
46
47 for _, cc := range []interface{}{
48 p.Stdin,
49 p.Stdout,
50 p.Stderr,
51 } {
52 if c, ok := cc.(io.Closer); ok {
53 t.postStart = append(t.postStart, c)
54 }
55 }
56 go func() {
57 _, _ = io.Copy(i.Stdin, os.Stdin)
58 _ = i.Stdin.Close()
59 }()
60 t.wg.Add(2)
61 go t.copyIO(os.Stdout, i.Stdout)
62 go t.copyIO(os.Stderr, i.Stderr)
63 return t, nil
64 }
65
66 func inheritStdio(process *libcontainer.Process) {
67 process.Stdin = os.Stdin
68 process.Stdout = os.Stdout
69 process.Stderr = os.Stderr
70 }
71
72 func (t *tty) initHostConsole() error {
73
74
75 for _, s := range []*os.File{os.Stderr, os.Stdout, os.Stdin} {
76 c, err := console.ConsoleFromFile(s)
77 if err == nil {
78 t.hostConsole = c
79 return nil
80 }
81 if errors.Is(err, console.ErrNotAConsole) {
82 continue
83 }
84
85 return fmt.Errorf("unable to get console: %w", err)
86 }
87
88
89 tty, err := os.Open("/dev/tty")
90 if err != nil {
91 return err
92 }
93 c, err := console.ConsoleFromFile(tty)
94 if err != nil {
95 return fmt.Errorf("unable to get console: %w", err)
96 }
97
98 t.hostConsole = c
99 return nil
100 }
101
102 func (t *tty) recvtty(socket *os.File) (Err error) {
103 f, err := utils.RecvFd(socket)
104 if err != nil {
105 return err
106 }
107 cons, err := console.ConsoleFromFile(f)
108 if err != nil {
109 return err
110 }
111 err = console.ClearONLCR(cons.Fd())
112 if err != nil {
113 return err
114 }
115 epoller, err := console.NewEpoller()
116 if err != nil {
117 return err
118 }
119 epollConsole, err := epoller.Add(cons)
120 if err != nil {
121 return err
122 }
123 defer func() {
124 if Err != nil {
125 _ = epollConsole.Close()
126 }
127 }()
128 go func() { _ = epoller.Wait() }()
129 go func() { _, _ = io.Copy(epollConsole, os.Stdin) }()
130 t.wg.Add(1)
131 go t.copyIO(os.Stdout, epollConsole)
132
133
134 if err := t.hostConsole.SetRaw(); err != nil {
135 return fmt.Errorf("failed to set the terminal from the stdin: %w", err)
136 }
137 go handleInterrupt(t.hostConsole)
138
139 t.epoller = epoller
140 t.console = epollConsole
141 t.closers = []io.Closer{epollConsole}
142 return nil
143 }
144
145 func handleInterrupt(c console.Console) {
146 sigchan := make(chan os.Signal, 1)
147 signal.Notify(sigchan, os.Interrupt)
148 <-sigchan
149 _ = c.Reset()
150 os.Exit(0)
151 }
152
153 func (t *tty) waitConsole() error {
154 if t.consoleC != nil {
155 return <-t.consoleC
156 }
157 return nil
158 }
159
160
161
162 func (t *tty) ClosePostStart() {
163 for _, c := range t.postStart {
164 _ = c.Close()
165 }
166 }
167
168
169
170 func (t *tty) Close() {
171
172 for _, c := range t.postStart {
173 _ = c.Close()
174 }
175
176
177 if t.console != nil && t.epoller != nil {
178 _ = t.console.Shutdown(t.epoller.CloseConsole)
179 }
180 t.wg.Wait()
181 for _, c := range t.closers {
182 _ = c.Close()
183 }
184 if t.hostConsole != nil {
185 _ = t.hostConsole.Reset()
186 }
187 }
188
189 func (t *tty) resize() error {
190 if t.console == nil || t.hostConsole == nil {
191 return nil
192 }
193 return t.console.ResizeFrom(t.hostConsole)
194 }
195
View as plain text