vtmitm

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

vtmitm.c (4094B)


      1#define _XOPEN_SOURCE 600
      2
      3#include <sys/stat.h>
      4#include <sys/types.h>
      5#include <sys/select.h>
      6#include <sys/wait.h>
      7#include <sys/ioctl.h>
      8#include <fcntl.h>
      9#include <unistd.h>
     10#include <termios.h>
     11#include <signal.h>
     12#include <errno.h>
     13#include <stdbool.h>
     14#include <stdarg.h>
     15#include <string.h>
     16#include <stdio.h>
     17#include <stdlib.h>
     18
     19struct termios oldterm;
     20int master, slave;
     21int pipeout;
     22pid_t child;
     23
     24bool encode_resize;
     25
     26void
     27usage(void)
     28{
     29	fprintf(stderr, "Usage: vtmitm [--encode-resize] -- CMD [ARG]..\n");
     30	exit(1);
     31}
     32
     33void
     34err(const char *fmtstr, ...)
     35{
     36	va_list ap;
     37
     38	va_start(ap, fmtstr);
     39	vfprintf(stderr, fmtstr, ap);
     40	fprintf(stderr, ": %s\r\n", strerror(errno));
     41	va_end(ap);
     42
     43	exit(1);
     44}
     45
     46int
     47writeall(int fd, char *buffer, size_t size)
     48{
     49	ssize_t nbytes;
     50
     51	while (size) {
     52		nbytes = write(fd, buffer, size);
     53		if (nbytes <= 0) return 1;
     54		buffer += nbytes;
     55		size -= nbytes;
     56	}
     57
     58	return 0;
     59}
     60
     61int
     62fwd(int in, int out, int out2)
     63{
     64	static char buffer[8194];
     65	ssize_t got, i, last;
     66
     67	do {
     68		got = read(in, buffer, sizeof(buffer));
     69		if (got < 0) return 1;
     70
     71		if (writeall(out, buffer, got))
     72			return 1;
     73
     74		if (out2 >= 0) {
     75			if (writeall(out2, buffer, got))
     76				return 1;
     77		}
     78	} while (got == sizeof(buffer));
     79
     80	return 0;
     81}
     82
     83void
     84mitm(int pts, int pipe)
     85{
     86	fd_set fds;
     87
     88	while (1) {
     89		FD_ZERO(&fds);
     90		FD_SET(0, &fds);
     91		FD_SET(pts, &fds);
     92
     93		if (select(pts + 1, &fds, 0, 0, 0) == -1) {
     94			if (errno == EINTR) continue;
     95			return;
     96		}
     97
     98		if (FD_ISSET(0, &fds)) { /* stdin -> ptty */
     99			if (fwd(0, pts, -1)) return;
    100		}
    101
    102		if (FD_ISSET(pts, &fds)) { /* ptty -> stdout + pipe */
    103			if (fwd(pts, 1, pipe)) return;
    104		}
    105	}
    106}
    107
    108void
    109rawterm(struct termios *term)
    110{
    111	term->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP);
    112	term->c_iflag &= ~(INLCR | IGNCR | ICRNL | IXON);
    113	term->c_oflag &= ~OPOST;
    114	term->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    115	term->c_cflag &= ~(CSIZE | PARENB);
    116	term->c_cflag |= CS8;
    117}
    118
    119void
    120sigwinch(int sig)
    121{
    122	char buf[64];
    123	struct winsize wsize;
    124
    125	if (ioctl(0, TIOCGWINSZ, &wsize) != -1)
    126		ioctl(slave, TIOCSWINSZ, &wsize);
    127	kill(child, SIGWINCH);
    128	signal(SIGWINCH, sigwinch);
    129
    130	/* transmit new size info in form of xterm resize escape sequence */
    131	snprintf(buf, sizeof(buf), "\x1b[8;%u;%ut", wsize.ws_row, wsize.ws_col);
    132	write(pipeout, buf, sizeof(buf));
    133}
    134
    135void
    136sigchld(int sig)
    137{
    138	int exitcode;
    139
    140	wait(&exitcode);
    141	exit(exitcode);
    142}
    143
    144void
    145cleanup(void)
    146{
    147	tcsetattr(0, TCSANOW, &oldterm);
    148}
    149
    150int
    151main(int argc, char *const *argv)
    152{
    153	struct termios newterm;
    154	struct winsize wsize;
    155	char *const *arg, *const *cmd;
    156	int exitcode;
    157
    158	cmd = NULL;
    159	encode_resize = false;
    160	for (arg = argv + 1; *arg; arg++) {
    161		if (!strcmp(*arg, "--encode-resize")) {
    162			encode_resize = true;
    163		} else if (!strcmp(*arg, "--")) {
    164			cmd = arg + 1;
    165			break;
    166		} else {
    167			usage();
    168		}
    169	}
    170
    171	if (!cmd) usage();
    172
    173	pipeout = dup(1);
    174	if (!freopen("/dev/tty", "w", stdout))
    175		err("main: open /dev/tty");
    176	dup2(fileno(stdout), 1);
    177
    178	master = posix_openpt(O_RDWR);
    179	if (master == -1) err("main: posix_openpt");
    180	if (grantpt(master)) err("main: grantpt");
    181	if (unlockpt(master)) err("main: unlockpt");
    182
    183	slave = open(ptsname(master), O_RDWR);
    184	if (slave == -1) err("main: open pts");
    185
    186	signal(SIGCHLD, sigchld);
    187
    188	child = fork();
    189	if (child < 0) err("main: fork");
    190
    191	if (child == 0) {
    192		close(master);
    193
    194		signal(SIGCHLD, SIG_DFL);
    195
    196		if (setsid() == -1)
    197			err("slave: setsid");
    198
    199		if (ioctl(slave, TIOCSCTTY, 0) == -1)
    200			err("slave: ioctl TIOCSCTTY");
    201
    202		if (dup2(slave, 0) == -1)
    203			err("slave: dup2 stdin");
    204		if (dup2(slave, 1) == -1)
    205			err("slave: dup2 stdout");
    206		if (dup2(slave, 2) == -1)
    207			err("slave: dup2 stderr");
    208		close(slave);
    209
    210		execvp(*cmd, cmd);
    211		err("slave: execvp");
    212	} else {
    213		if (encode_resize) {
    214			signal(SIGWINCH, sigwinch);
    215			raise(SIGWINCH);
    216		}
    217
    218		if (tcgetattr(0, &oldterm))
    219			err("master: tcgetattr");
    220		newterm = oldterm;
    221		rawterm(&newterm);
    222		if (tcsetattr(0, TCSANOW, &newterm))
    223			err("master: tcsetattr");
    224		atexit(cleanup);
    225
    226		mitm(master, pipeout);
    227
    228		waitpid(child, &exitcode, 0);
    229		close(master);
    230	}
    231
    232	return exitcode;
    233}