diff options
Diffstat (limited to '')
-rw-r--r-- | ptee.c | 171 |
1 files changed, 77 insertions, 94 deletions
diff --git a/ptee.c b/ptee.c index e04fd2a..6a9a16b 100644 --- a/ptee.c +++ b/ptee.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Causal Agent June <june@causal.agency> +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -16,7 +16,8 @@ #include <err.h> #include <poll.h> -#include <pwd.h> +#include <stdbool.h> +#include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/wait.h> @@ -32,102 +33,84 @@ #include <util.h> #endif -static struct { - int pty; - int input; - int local; - int remote; -} fd = { -1, STDIN_FILENO, STDERR_FILENO, STDOUT_FILENO }; +typedef unsigned char byte; static struct termios saveTerm; static void restoreTerm(void) { - tcsetattr(fd.local, TCSADRAIN, &saveTerm); + tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm); } int main(int argc, char *argv[]) { - int error; - - if (isatty(fd.remote)) { - errx(EX_USAGE, "stdout is not redirected"); - } - - if (argc > 1) { - argv++; - } else { - uid_t uid = getuid(); - struct passwd *user = getpwuid(uid); - if (!user) err(EX_OSFILE, "/etc/passwd"); - argv[0] = user->pw_shell; - } - - error = tcgetattr(fd.local, &saveTerm); - if (error) err(EX_IOERR, "tcgetattr"); - atexit(restoreTerm); - - struct termios raw; - cfmakeraw(&raw); - error = tcsetattr(fd.local, TCSADRAIN, &raw); - if (error) err(EX_IOERR, "tcsetattr"); - - struct winsize window; - error = ioctl(fd.local, TIOCGWINSZ, &window); - if (error) err(EX_IOERR, "TIOCGWINSZ"); - - pid_t pid = forkpty(&fd.pty, NULL, NULL, &window); - if (pid < 0) err(EX_OSERR, "forkpty"); - - if (!pid) { - execvp(argv[0], argv); - err(EX_NOINPUT, "%s", argv[0]); - } - - ssize_t size = write(fd.remote, &window, sizeof(window)); - if (size < 0) err(EX_IOERR, "write(%d)", fd.remote); - - char buf[4096]; - ssize_t totalSize = 0; - struct pollfd fds[2] = { - { .fd = fd.input, .events = POLLIN }, - { .fd = fd.pty, .events = POLLIN }, - }; - while (0 < poll(fds, 2, -1)) { - if (fds[0].revents) { - ssize_t size = read(fd.input, buf, sizeof(buf)); - if (size < 0) err(EX_IOERR, "read(%d)", fd.input); - - size = write(fd.pty, buf, size); - if (size < 0) err(EX_IOERR, "write(%d)", fd.pty); - } - - if (fds[1].revents) { - ssize_t readSize = read(fd.pty, buf, sizeof(buf)); - if (readSize < 0) err(EX_IOERR, "read(%d)", fd.pty); - - ssize_t writeSize = write(fd.local, buf, readSize); - if (writeSize < 0) err(EX_IOERR, "write(%d)", fd.local); - - writeSize = write(fd.remote, buf, readSize); - if (writeSize < 0) err(EX_IOERR, "write(%d)", fd.remote); - - if ((totalSize += readSize) >= 1024 * 1024) { - totalSize = 0; - - struct winsize redraw = window; - redraw.ws_row = 1; - redraw.ws_col = 1; - - error = ioctl(fd.pty, TIOCSWINSZ, &redraw); - if (error) err(EX_IOERR, "TIOCSWINSZ"); - - error = ioctl(fd.pty, TIOCSWINSZ, &window); - if (error) err(EX_IOERR, "TIOCSWINSZ"); - } - } - - int status; - pid_t dead = waitpid(pid, &status, WNOHANG); - if (dead < 0) err(EX_OSERR, "waitpid(%d)", pid); - if (dead) return WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE; - } - err(EX_IOERR, "poll"); + if (argc < 2) return EX_USAGE; + if (isatty(STDOUT_FILENO)) errx(EX_USAGE, "stdout is not redirected"); + + int error = tcgetattr(STDIN_FILENO, &saveTerm); + if (error) err(EX_IOERR, "tcgetattr"); + atexit(restoreTerm); + + struct termios raw = saveTerm; + cfmakeraw(&raw); + error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw); + if (error) err(EX_IOERR, "tcsetattr"); + + struct winsize window; + error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window); + if (error) err(EX_IOERR, "ioctl"); + + int pty; + pid_t pid = forkpty(&pty, NULL, NULL, &window); + if (pid < 0) err(EX_OSERR, "forkpty"); + + if (!pid) { + execvp(argv[1], &argv[1]); + err(EX_NOINPUT, "%s", argv[1]); + } + + bool stop = false; + + byte buf[4096]; + struct pollfd fds[2] = { + { .events = POLLIN, .fd = STDIN_FILENO }, + { .events = POLLIN, .fd = pty }, + }; + while (0 < poll(fds, 2, -1)) { + if (fds[0].revents & POLLIN) { + ssize_t rlen = read(STDIN_FILENO, buf, sizeof(buf)); + if (rlen < 0) err(EX_IOERR, "read"); + + if (rlen == 1 && buf[0] == CTRL('S')) { + stop ^= true; + continue; + } + + if (rlen == 1 && buf[0] == CTRL('Q')) { + char dump[] = "\x1B[10i"; + ssize_t wlen = write(STDOUT_FILENO, dump, sizeof(dump) - 1); + if (wlen < 0) err(EX_IOERR, "write"); + continue; + } + + ssize_t wlen = write(pty, buf, rlen); + if (wlen < 0) err(EX_IOERR, "write"); + } + + if (fds[1].revents & POLLIN) { + ssize_t rlen = read(pty, buf, sizeof(buf)); + if (rlen < 0) err(EX_IOERR, "read"); + + ssize_t wlen = write(STDIN_FILENO, buf, rlen); + if (wlen < 0) err(EX_IOERR, "write"); + + if (!stop) { + wlen = write(STDOUT_FILENO, buf, rlen); + if (wlen < 0) err(EX_IOERR, "write"); + } + } + + int status; + pid_t dead = waitpid(pid, &status, WNOHANG); + if (dead < 0) err(EX_OSERR, "waitpid"); + if (dead) return WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE; + } + err(EX_IOERR, "poll"); } |