+#define BUFFER_SIZE 1024
+
+static int process_pty(int master, sigset_t *mask) {
+ char in_buffer[BUFFER_SIZE], out_buffer[BUFFER_SIZE];
+ size_t in_buffer_full = 0, out_buffer_full = 0;
+ struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev;
+ bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false;
+ int ep = -1, signal_fd = -1, r;
+
+ fd_nonblock(STDIN_FILENO, 1);
+ fd_nonblock(STDOUT_FILENO, 1);
+ fd_nonblock(master, 1);
+
+ if ((signal_fd = signalfd(-1, mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
+ log_error("signalfd(): %m");
+ r = -errno;
+ goto finish;
+ }
+
+ if ((ep = epoll_create1(EPOLL_CLOEXEC)) < 0) {
+ log_error("Failed to create epoll: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ zero(stdin_ev);
+ stdin_ev.events = EPOLLIN|EPOLLET;
+ stdin_ev.data.fd = STDIN_FILENO;
+
+ zero(stdout_ev);
+ stdout_ev.events = EPOLLOUT|EPOLLET;
+ stdout_ev.data.fd = STDOUT_FILENO;
+
+ zero(master_ev);
+ master_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
+ master_ev.data.fd = master;
+
+ zero(signal_ev);
+ signal_ev.events = EPOLLIN;
+ signal_ev.data.fd = signal_fd;
+
+ if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 ||
+ epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 ||
+ epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 ||
+ epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) {
+ log_error("Failed to regiser fds in epoll: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ for (;;) {
+ struct epoll_event ev[16];
+ ssize_t k;
+ int i, nfds;
+
+ if ((nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1)) < 0) {
+
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+
+ log_error("epoll_wait(): %m");
+ r = -errno;
+ goto finish;
+ }
+
+ assert(nfds >= 1);
+
+ for (i = 0; i < nfds; i++) {
+ if (ev[i].data.fd == STDIN_FILENO) {
+
+ if (ev[i].events & (EPOLLIN|EPOLLHUP))
+ stdin_readable = true;
+
+ } else if (ev[i].data.fd == STDOUT_FILENO) {
+
+ if (ev[i].events & (EPOLLOUT|EPOLLHUP))
+ stdout_writable = true;
+
+ } else if (ev[i].data.fd == master) {
+
+ if (ev[i].events & (EPOLLIN|EPOLLHUP))
+ master_readable = true;
+
+ if (ev[i].events & (EPOLLOUT|EPOLLHUP))
+ master_writable = true;
+
+ } else if (ev[i].data.fd == signal_fd) {
+ struct signalfd_siginfo sfsi;
+ ssize_t n;
+
+ if ((n = read(signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
+
+ if (n >= 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ if (errno != EINTR && errno != EAGAIN) {
+ r = -errno;
+ goto finish;
+ }
+ } else {
+
+ if (sfsi.ssi_signo == SIGWINCH) {
+ struct winsize ws;
+
+ /* The window size changed, let's forward that. */
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
+ ioctl(master, TIOCSWINSZ, &ws);
+ } else {
+ r = -EINTR;
+ goto finish;
+ }
+ }
+ }
+ }
+
+ 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 (stdin_readable && in_buffer_full < BUFFER_SIZE) {
+
+ if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, BUFFER_SIZE - in_buffer_full)) < 0) {
+
+ if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+ stdin_readable = false;
+ else {
+ log_error("read(): %m");
+ goto finish;
+ }
+ } else
+ in_buffer_full += (size_t) k;
+ }
+
+ if (master_writable && in_buffer_full > 0) {
+
+ if ((k = write(master, in_buffer, in_buffer_full)) < 0) {
+
+ if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+ master_writable = false;
+ else {
+ log_error("write(): %m");
+ goto finish;
+ }
+
+ } else {
+ assert(in_buffer_full >= (size_t) k);
+ memmove(in_buffer, in_buffer + k, in_buffer_full - k);
+ in_buffer_full -= k;
+ }
+ }
+
+ if (master_readable && out_buffer_full < BUFFER_SIZE) {
+
+ if ((k = read(master, out_buffer + out_buffer_full, BUFFER_SIZE - out_buffer_full)) < 0) {
+
+ if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+ master_readable = false;
+ else {
+ log_error("read(): %m");
+ goto finish;
+ }
+ } else
+ out_buffer_full += (size_t) k;
+ }
+
+ if (stdout_writable && out_buffer_full > 0) {
+
+ if ((k = write(STDOUT_FILENO, out_buffer, out_buffer_full)) < 0) {
+
+ if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+ stdout_writable = false;
+ else {
+ log_error("write(): %m");
+ goto finish;
+ }
+
+ } else {
+ assert(out_buffer_full >= (size_t) k);
+ memmove(out_buffer, out_buffer + k, out_buffer_full - k);
+ out_buffer_full -= k;
+ }
+ }
+ }
+ }
+
+finish:
+ if (ep >= 0)
+ close_nointr_nofail(ep);
+
+ if (signal_fd >= 0)
+ close_nointr_nofail(signal_fd);
+
+ return r;
+}