]> git.proxmox.com Git - systemd.git/blame - src/shared/ptyfwd.c
Imported Upstream version 217
[systemd.git] / src / shared / ptyfwd.c
CommitLineData
60f067b4
JS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010-2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <sys/epoll.h>
23#include <sys/signalfd.h>
24#include <sys/ioctl.h>
25#include <limits.h>
26#include <termios.h>
27
28#include "util.h"
29#include "ptyfwd.h"
30
31#define ESCAPE_USEC USEC_PER_SEC
32
33static bool look_for_escape(usec_t *timestamp, unsigned *counter, const char *buffer, size_t n) {
34 const char *p;
35
36 assert(timestamp);
37 assert(counter);
38 assert(buffer);
39 assert(n > 0);
40
41 for (p = buffer; p < buffer + n; p++) {
42
43 /* Check for ^] */
44 if (*p == 0x1D) {
45 usec_t nw = now(CLOCK_MONOTONIC);
46
47 if (*counter == 0 || nw > *timestamp + USEC_PER_SEC) {
48 *timestamp = nw;
49 *counter = 1;
50 } else {
51 (*counter)++;
52
53 if (*counter >= 3)
54 return true;
55 }
56 } else {
57 *timestamp = 0;
58 *counter = 0;
59 }
60 }
61
62 return false;
63}
64
65static int process_pty_loop(int master, sigset_t *mask, pid_t kill_pid, int signo) {
66 char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
67 size_t in_buffer_full = 0, out_buffer_full = 0;
68 struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev;
69 bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false;
70 bool stdin_hangup = false, stdout_hangup = false, master_hangup = false;
71 bool tried_orderly_shutdown = false, process_signalfd = false, quit = false;
72 usec_t escape_timestamp = 0;
73 unsigned escape_counter = 0;
74 _cleanup_close_ int ep = -1, signal_fd = -1;
75
76 assert(master >= 0);
77 assert(mask);
78 assert(kill_pid == 0 || kill_pid > 1);
79 assert(signo >= 0 && signo < _NSIG);
80
81 fd_nonblock(STDIN_FILENO, true);
82 fd_nonblock(STDOUT_FILENO, true);
83 fd_nonblock(master, true);
84
85 signal_fd = signalfd(-1, mask, SFD_NONBLOCK|SFD_CLOEXEC);
86 if (signal_fd < 0) {
87 log_error("signalfd(): %m");
88 return -errno;
89 }
90
91 ep = epoll_create1(EPOLL_CLOEXEC);
92 if (ep < 0) {
93 log_error("Failed to create epoll: %m");
94 return -errno;
95 }
96
97 /* We read from STDIN only if this is actually a TTY,
98 * otherwise we assume non-interactivity. */
99 if (isatty(STDIN_FILENO)) {
100 zero(stdin_ev);
101 stdin_ev.events = EPOLLIN|EPOLLET;
102 stdin_ev.data.fd = STDIN_FILENO;
103
104 if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0) {
105 log_error("Failed to register STDIN in epoll: %m");
106 return -errno;
107 }
108 }
109
110 zero(stdout_ev);
111 stdout_ev.events = EPOLLOUT|EPOLLET;
112 stdout_ev.data.fd = STDOUT_FILENO;
113
114 zero(master_ev);
115 master_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
116 master_ev.data.fd = master;
117
118 zero(signal_ev);
119 signal_ev.events = EPOLLIN;
120 signal_ev.data.fd = signal_fd;
121
122 if (epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0) {
123 if (errno != EPERM) {
124 log_error("Failed to register stdout in epoll: %m");
125 return -errno;
126 }
127
128 /* stdout without epoll support. Likely redirected to regular file. */
129 stdout_writable = true;
130 }
131
132 if (epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 ||
133 epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) {
134 log_error("Failed to register fds in epoll: %m");
135 return -errno;
136 }
137
138 for (;;) {
139 struct epoll_event ev[16];
140 ssize_t k;
141 int i, nfds;
142
143 nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), quit ? 0 : -1);
144 if (nfds < 0) {
145
146 if (errno == EINTR || errno == EAGAIN)
147 continue;
148
149 log_error("epoll_wait(): %m");
150 return -errno;
151 }
152
153 if (nfds == 0)
154 return 0;
155
156 for (i = 0; i < nfds; i++) {
157 if (ev[i].data.fd == STDIN_FILENO) {
158
159 if (ev[i].events & (EPOLLIN|EPOLLHUP))
160 stdin_readable = true;
161
162 } else if (ev[i].data.fd == STDOUT_FILENO) {
163
164 if (ev[i].events & (EPOLLOUT|EPOLLHUP))
165 stdout_writable = true;
166
167 } else if (ev[i].data.fd == master) {
168
169 if (ev[i].events & (EPOLLIN|EPOLLHUP))
170 master_readable = true;
171
172 if (ev[i].events & (EPOLLOUT|EPOLLHUP))
173 master_writable = true;
174
175 } else if (ev[i].data.fd == signal_fd)
176 process_signalfd = true;
177 }
178
179 while ((stdin_readable && in_buffer_full <= 0) ||
180 (master_writable && in_buffer_full > 0) ||
181 (master_readable && out_buffer_full <= 0) ||
182 (stdout_writable && out_buffer_full > 0)) {
183
184 if (stdin_readable && in_buffer_full < LINE_MAX) {
185
186 k = read(STDIN_FILENO, in_buffer + in_buffer_full, LINE_MAX - in_buffer_full);
187 if (k < 0) {
188
189 if (errno == EAGAIN)
190 stdin_readable = false;
191 else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
192 stdin_readable = false;
193 stdin_hangup = true;
194 epoll_ctl(ep, EPOLL_CTL_DEL, STDIN_FILENO, NULL);
195 } else {
196 log_error("read(): %m");
197 return -errno;
198 }
199 } else {
200 /* Check if ^] has been
201 * pressed three times within
202 * one second. If we get this
203 * we quite immediately. */
204 if (look_for_escape(&escape_timestamp, &escape_counter, in_buffer + in_buffer_full, k))
205 return !quit;
206
207 in_buffer_full += (size_t) k;
208 }
209 }
210
211 if (master_writable && in_buffer_full > 0) {
212
213 k = write(master, in_buffer, in_buffer_full);
214 if (k < 0) {
215
216 if (errno == EAGAIN || errno == EIO)
217 master_writable = false;
218 else if (errno == EPIPE || errno == ECONNRESET) {
219 master_writable = master_readable = false;
220 master_hangup = true;
221 epoll_ctl(ep, EPOLL_CTL_DEL, master, NULL);
222 } else {
223 log_error("write(): %m");
224 return -errno;
225 }
226
227 } else {
228 assert(in_buffer_full >= (size_t) k);
229 memmove(in_buffer, in_buffer + k, in_buffer_full - k);
230 in_buffer_full -= k;
231 }
232 }
233
234 if (master_readable && out_buffer_full < LINE_MAX) {
235
236 k = read(master, out_buffer + out_buffer_full, LINE_MAX - out_buffer_full);
237 if (k < 0) {
238
239 /* Note that EIO on the master
240 * device might be cause by
241 * vhangup() or temporary
242 * closing of everything on
243 * the other side, we treat it
244 * like EAGAIN here and try
245 * again. */
246
247 if (errno == EAGAIN || errno == EIO)
248 master_readable = false;
249 else if (errno == EPIPE || errno == ECONNRESET) {
250 master_readable = master_writable = false;
251 master_hangup = true;
252 epoll_ctl(ep, EPOLL_CTL_DEL, master, NULL);
253 } else {
254 log_error("read(): %m");
255 return -errno;
256 }
257 } else
258 out_buffer_full += (size_t) k;
259 }
260
261 if (stdout_writable && out_buffer_full > 0) {
262
263 k = write(STDOUT_FILENO, out_buffer, out_buffer_full);
264 if (k < 0) {
265
266 if (errno == EAGAIN)
267 stdout_writable = false;
268 else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
269 stdout_writable = false;
270 stdout_hangup = true;
271 epoll_ctl(ep, EPOLL_CTL_DEL, STDOUT_FILENO, NULL);
272 } else {
273 log_error("write(): %m");
274 return -errno;
275 }
276
277 } else {
278 assert(out_buffer_full >= (size_t) k);
279 memmove(out_buffer, out_buffer + k, out_buffer_full - k);
280 out_buffer_full -= k;
281 }
282 }
283
284 }
285
286 if (process_signalfd) {
287 struct signalfd_siginfo sfsi;
288 ssize_t n;
289
290 n = read(signal_fd, &sfsi, sizeof(sfsi));
291 if (n != sizeof(sfsi)) {
292
293 if (n >= 0) {
294 log_error("Failed to read from signalfd: invalid block size");
295 return -EIO;
296 }
297
298 if (errno != EINTR && errno != EAGAIN) {
299 log_error("Failed to read from signalfd: %m");
300 return -errno;
301 }
302 } else {
303
304 if (sfsi.ssi_signo == SIGWINCH) {
305 struct winsize ws;
306
307 /* The window size changed, let's forward that. */
308 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
309 ioctl(master, TIOCSWINSZ, &ws);
310
311 } else if (sfsi.ssi_signo == SIGTERM && kill_pid > 0 && signo > 0 && !tried_orderly_shutdown) {
312
313 if (kill(kill_pid, signo) < 0)
314 quit = true;
315 else {
316 log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination.");
317
318 /* This only works for systemd... */
319 tried_orderly_shutdown = true;
320 }
321
322 } else
323 /* Signals that where
324 * delivered via signalfd that
325 * we didn't know are a reason
326 * for us to quit */
327 quit = true;
328 }
329 }
330
331 if (stdin_hangup || stdout_hangup || master_hangup) {
332 /* Exit the loop if any side hung up and if
333 * there's nothing more to write or nothing we
334 * could write. */
335
336 if ((out_buffer_full <= 0 || stdout_hangup) &&
337 (in_buffer_full <= 0 || master_hangup))
338 return !quit;
339 }
340 }
341}
342
343int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) {
344 struct termios saved_stdin_attr, raw_stdin_attr;
345 struct termios saved_stdout_attr, raw_stdout_attr;
346 bool saved_stdin = false;
347 bool saved_stdout = false;
348 struct winsize ws;
349 int r;
350
351 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
352 ioctl(master, TIOCSWINSZ, &ws);
353
354 if (tcgetattr(STDIN_FILENO, &saved_stdin_attr) >= 0) {
355 saved_stdin = true;
356
357 raw_stdin_attr = saved_stdin_attr;
358 cfmakeraw(&raw_stdin_attr);
359 raw_stdin_attr.c_oflag = saved_stdin_attr.c_oflag;
360 tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr);
361 }
362 if (tcgetattr(STDOUT_FILENO, &saved_stdout_attr) >= 0) {
363 saved_stdout = true;
364
365 raw_stdout_attr = saved_stdout_attr;
366 cfmakeraw(&raw_stdout_attr);
367 raw_stdout_attr.c_iflag = saved_stdout_attr.c_iflag;
368 raw_stdout_attr.c_lflag = saved_stdout_attr.c_lflag;
369 tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr);
370 }
371
372 r = process_pty_loop(master, mask, kill_pid, signo);
373
374 if (saved_stdout)
375 tcsetattr(STDOUT_FILENO, TCSANOW, &saved_stdout_attr);
376 if (saved_stdin)
377 tcsetattr(STDIN_FILENO, TCSANOW, &saved_stdin_attr);
378
379 /* STDIN/STDOUT should not be nonblocking normally, so let's
380 * unconditionally reset it */
381 fd_nonblock(STDIN_FILENO, false);
382 fd_nonblock(STDOUT_FILENO, false);
383
384 return r;
385
386}