ptyfwd.c revision eaf73b061604c028aa28f960870a9b46aab2f76a
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010-2013 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/signalfd.h>
#include <limits.h>
#include <termios.h>
#include "util.h"
#include "ptyfwd.h"
#define ESCAPE_USEC USEC_PER_SEC
const char *p;
assert(n > 0);
/* Check for ^] */
if (*p == 0x1D) {
*counter = 1;
} else {
(*counter)++;
if (*counter >= 3)
return true;
}
} else {
*timestamp = 0;
*counter = 0;
}
}
return false;
}
bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false;
usec_t escape_timestamp = 0;
unsigned escape_counter = 0;
if (signal_fd < 0) {
log_error("signalfd(): %m");
return -errno;
}
if (ep < 0) {
log_error("Failed to create epoll: %m");
return -errno;
}
/* We read from STDIN only if this is actually a TTY,
* otherwise we assume non-interactivity. */
if (isatty(STDIN_FILENO)) {
log_error("Failed to register STDIN in epoll: %m");
return -errno;
}
}
log_error("Failed to register stdout in epoll: %m");
return -errno;
}
/* stdout without epoll support. Likely redirected to regular file. */
stdout_writable = true;
}
log_error("Failed to register fds in epoll: %m");
return -errno;
}
for (;;) {
ssize_t k;
int i, nfds;
if (nfds < 0) {
continue;
log_error("epoll_wait(): %m");
return -errno;
}
if (nfds == 0)
return 0;
for (i = 0; i < nfds; i++) {
stdin_readable = true;
stdout_writable = true;
master_readable = true;
master_writable = true;
process_signalfd = true;
}
while ((stdin_readable && in_buffer_full <= 0) ||
(master_writable && in_buffer_full > 0) ||
(master_readable && out_buffer_full <= 0) ||
(stdout_writable && out_buffer_full > 0)) {
if (k < 0) {
stdin_readable = false;
stdin_readable = false;
stdin_hangup = true;
} else {
log_error("read(): %m");
return -errno;
}
} else {
/* Check if ^] has been
* pressed three times within
* one second. If we get this
* we quite immediately. */
return !quit;
in_buffer_full += (size_t) k;
}
}
if (master_writable && in_buffer_full > 0) {
if (k < 0) {
master_writable = false;
master_writable = master_readable = false;
master_hangup = true;
} else {
log_error("write(): %m");
return -errno;
}
} else {
in_buffer_full -= k;
}
}
if (k < 0) {
/* Note that EIO on the master
* device might be cause by
* vhangup() or temporary
* closing of everything on
* the other side, we treat it
* like EAGAIN here and try
* again. */
master_readable = false;
master_readable = master_writable = false;
master_hangup = true;
} else {
log_error("read(): %m");
return -errno;
}
} else
out_buffer_full += (size_t) k;
}
if (stdout_writable && out_buffer_full > 0) {
if (k < 0) {
stdout_writable = false;
stdout_writable = false;
stdout_hangup = true;
} else {
log_error("write(): %m");
return -errno;
}
} else {
out_buffer_full -= k;
}
}
}
if (process_signalfd) {
struct signalfd_siginfo sfsi;
ssize_t n;
if (n != sizeof(sfsi)) {
if (n >= 0) {
log_error("Failed to read from signalfd: invalid block size");
return -EIO;
}
log_error("Failed to read from signalfd: %m");
return -errno;
}
} else {
/* The window size changed, let's forward that. */
quit = true;
else {
log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination.");
/* This only works for systemd... */
tried_orderly_shutdown = true;
}
} else
/* Signals that where
* delivered via signalfd that
* we didn't know are a reason
* for us to quit */
quit = true;
}
}
/* Exit the loop if any side hung up and if
* there's nothing more to write or nothing we
* could write. */
if ((out_buffer_full <= 0 || stdout_hangup) &&
(in_buffer_full <= 0 || master_hangup))
return !quit;
}
}
}
struct termios saved_attr;
bool saved = false;
int r;
saved = true;
}
if (saved)
return r;
}