summary refs log tree commit diff
path: root/ptee.c
diff options
context:
space:
mode:
Diffstat (limited to 'ptee.c')
-rw-r--r--ptee.c171
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");
 }