vtmitm

Vtty output duplicator
git clone https://git.sinitax.com/sinitax/vtmitm
Log | Files | Refs | README | sfeed.txt

commit 6e966daedecfa8fd07f6362d842917225031856f
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 20 Feb 2023 01:25:29 +0100

Initial version

Diffstat:
A.gitignore | 1+
AMakefile | 17+++++++++++++++++
AREADME | 4++++
Avtmitm.c | 233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 255 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +vtmitm diff --git a/Makefile b/Makefile @@ -0,0 +1,17 @@ +PREFIX ?= /usr/local +BINDIR ?= /bin + +all: vtmitm + +clean: + rm -f vtmitm + +vtmitm: vtmitm.c + +install: + install -m755 vtmitm -t "$(DESTDIR)$(PREFIX)$(BINDIR)" + +uninstall: + rm -f "$(DESTDIR)$(PREFIX)$(BINDIR)/vtmitm" + +.PHONY: all clean install uninstall diff --git a/README b/README @@ -0,0 +1,4 @@ +vtmitm +====== + +Stream terminal output by duplicating it as a vtty man-in-the-middle diff --git a/vtmitm.c b/vtmitm.c @@ -0,0 +1,233 @@ +#define _XOPEN_SOURCE 600 + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <signal.h> +#include <errno.h> +#include <stdbool.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +struct termios oldterm; +int master, slave; +int pipeout; +pid_t child; + +bool encode_resize; + +void +usage(void) +{ + fprintf(stderr, "Usage: vtmitm [--encode-resize] -- CMD [ARG]..\n"); + exit(1); +} + +void +err(const char *fmtstr, ...) +{ + va_list ap; + + va_start(ap, fmtstr); + vfprintf(stderr, fmtstr, ap); + fprintf(stderr, ": %s\r\n", strerror(errno)); + va_end(ap); + + exit(1); +} + +int +writeall(int fd, char *buffer, size_t size) +{ + ssize_t nbytes; + + while (size) { + nbytes = write(fd, buffer, size); + if (nbytes <= 0) return 1; + buffer += nbytes; + size -= nbytes; + } + + return 0; +} + +int +fwd(int in, int out, int out2) +{ + static char buffer[8194]; + ssize_t got, i, last; + + do { + got = read(in, buffer, sizeof(buffer)); + if (got < 0) return 1; + + if (writeall(out, buffer, got)) + return 1; + + if (out2 >= 0) { + if (writeall(out2, buffer, got)) + return 1; + } + } while (got == sizeof(buffer)); + + return 0; +} + +void +mitm(int pts, int pipe) +{ + fd_set fds; + + while (1) { + FD_ZERO(&fds); + FD_SET(0, &fds); + FD_SET(pts, &fds); + + if (select(pts + 1, &fds, 0, 0, 0) == -1) { + if (errno == EINTR) continue; + return; + } + + if (FD_ISSET(0, &fds)) { /* stdin -> ptty */ + if (fwd(0, pts, -1)) return; + } + + if (FD_ISSET(pts, &fds)) { /* ptty -> stdout + pipe */ + if (fwd(pts, 1, pipe)) return; + } + } +} + +void +rawterm(struct termios *term) +{ + term->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP); + term->c_iflag &= ~(INLCR | IGNCR | ICRNL | IXON); + term->c_oflag &= ~OPOST; + term->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + term->c_cflag &= ~(CSIZE | PARENB); + term->c_cflag |= CS8; +} + +void +sigwinch(int sig) +{ + char buf[64]; + struct winsize wsize; + + if (ioctl(0, TIOCGWINSZ, &wsize) != -1) + ioctl(slave, TIOCSWINSZ, &wsize); + kill(child, SIGWINCH); + signal(SIGWINCH, sigwinch); + + /* transmit new size info in form of xterm resize escape sequence */ + snprintf(buf, sizeof(buf), "\x1b[8;%u;%ut", wsize.ws_row, wsize.ws_col); + write(pipeout, buf, sizeof(buf)); +} + +void +sigchld(int sig) +{ + int exitcode; + + wait(&exitcode); + exit(exitcode); +} + +void +cleanup(void) +{ + tcsetattr(0, TCSANOW, &oldterm); +} + +int +main(int argc, char *const *argv) +{ + struct termios newterm; + struct winsize wsize; + char *const *arg, *const *cmd; + int exitcode; + + cmd = NULL; + encode_resize = false; + for (arg = argv + 1; *arg; arg++) { + if (!strcmp(*arg, "--encode-resize")) { + encode_resize = true; + } else if (!strcmp(*arg, "--")) { + cmd = arg + 1; + break; + } else { + usage(); + } + } + + if (!cmd) usage(); + + pipeout = dup(1); + if (!freopen("/dev/tty", "w", stdout)) + err("main: open /dev/tty"); + dup2(fileno(stdout), 1); + + master = posix_openpt(O_RDWR); + if (master == -1) err("main: posix_openpt"); + if (grantpt(master)) err("main: grantpt"); + if (unlockpt(master)) err("main: unlockpt"); + + slave = open(ptsname(master), O_RDWR); + if (slave == -1) err("main: open pts"); + + signal(SIGCHLD, sigchld); + + child = fork(); + if (child < 0) err("main: fork"); + + if (child == 0) { + close(master); + + signal(SIGCHLD, SIG_DFL); + + if (setsid() == -1) + err("slave: setsid"); + + if (ioctl(slave, TIOCSCTTY, 0) == -1) + err("slave: ioctl TIOCSCTTY"); + + if (dup2(slave, 0) == -1) + err("slave: dup2 stdin"); + if (dup2(slave, 1) == -1) + err("slave: dup2 stdout"); + if (dup2(slave, 2) == -1) + err("slave: dup2 stderr"); + close(slave); + + execv(*cmd, cmd + 1); + err("slave: execv"); + } else { + if (encode_resize) { + signal(SIGWINCH, sigwinch); + raise(SIGWINCH); + } + + if (tcgetattr(0, &oldterm)) + err("master: tcgetattr"); + newterm = oldterm; + rawterm(&newterm); + if (tcsetattr(0, TCSANOW, &newterm)) + err("master: tcsetattr"); + atexit(cleanup); + + mitm(master, pipeout); + + waitpid(child, &exitcode, 0); + close(master); + } + + return exitcode; +}