vtwrap

Fake ptty allocator
git clone https://git.sinitax.com/sinitax/vtwrap
Log | Files | Refs | LICENSE | sfeed.txt

vtwrap.c (2962B)


      1#define _XOPEN_SOURCE 600
      2#define _DEFAULT_SOURCE
      3
      4#include <sys/select.h>
      5#include <sys/wait.h>
      6#include <sys/epoll.h>
      7#include <fcntl.h>
      8#include <termios.h>
      9#include <unistd.h>
     10#include <pty.h>
     11#include <errno.h>
     12
     13#include <err.h>
     14#include <stdio.h>
     15#include <string.h>
     16#include <stdbool.h>
     17#include <stdlib.h>
     18
     19static char buf[BUFSIZ] = { 0 };
     20
     21static int
     22writeall(int fd, char *buffer, size_t size)
     23{
     24	ssize_t nbytes;
     25
     26	while (size) {
     27		nbytes = write(fd, buffer, size);
     28		if (nbytes <= 0) return 1;
     29		buffer += nbytes;
     30		size -= nbytes;
     31	}
     32
     33	return 0;
     34}
     35
     36static int
     37fwd(int in, int out)
     38{
     39	static char *buffer = NULL;
     40	ssize_t got, i, last;
     41
     42	if (!buffer) {
     43		buffer = malloc(BUFSIZ);
     44		if (!buffer) err(1, "malloc");
     45	}
     46
     47	do {
     48		got = read(in, buffer, sizeof(buffer));
     49		if (got < 0) return 1;
     50
     51		if (writeall(out, buffer, got))
     52			return 1;
     53	} while (got == sizeof(buffer));
     54
     55	return 0;
     56}
     57
     58static void
     59mitm(int pts)
     60{
     61	fd_set fds;
     62
     63	while (1) {
     64		FD_ZERO(&fds);
     65		FD_SET(0, &fds);
     66		FD_SET(pts, &fds);
     67
     68		if (select(pts + 1, &fds, 0, 0, 0) == -1) {
     69			if (errno == EINTR) continue;
     70			return;
     71		}
     72
     73		if (FD_ISSET(0, &fds)) { /* stdin -> ptty */
     74			if (fwd(0, pts)) return;
     75		}
     76
     77		if (FD_ISSET(pts, &fds)) { /* ptty -> stdout + pipe */
     78			if (fwd(pts, 1)) return;
     79		}
     80	}
     81}
     82
     83static void
     84usage(int rc)
     85{
     86
     87	fprintf(stderr, "Usage: apty [-r ROWS] [-c COLS] [-h] [--] CMD\n");
     88	exit(rc);
     89}
     90
     91int
     92main(int argc, char **argv)
     93{
     94	char **arg, **cmd;
     95	struct termios termios = {0};
     96	struct winsize winsize = {0};
     97	int exitcode;
     98	int master, tty;
     99	pid_t child;
    100
    101	if (argc <= 1) usage(1);
    102
    103	winsize.ws_col = 120;
    104	winsize.ws_row = 80;
    105	cfmakeraw(&termios);
    106
    107	if (isatty(STDIN_FILENO)) {
    108		if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize))
    109			err(1, "tcgetwinsize");
    110
    111		if (tcgetattr(STDIN_FILENO, &termios))
    112			err(1, "tcgettattr");
    113		termios.c_lflag &= ~ICANON;
    114		if (tcsetattr(STDIN_FILENO, TCSANOW, &termios))
    115			err(1, "tcsetattr");
    116	}
    117
    118	for (arg = argv + 1; *arg; arg++) {
    119		if (!strcmp(*arg, "-t") || !strcmp(*arg, "--tty")) {
    120			if (!isatty(STDIN_FILENO)) {
    121				tty = open("/dev/tty", O_RDONLY);
    122				if (tty < 0) err(1, "open /dev/tty");
    123
    124				if (tcgetattr(tty, &termios))
    125					err(1, "tcgetattr tty");
    126
    127				if (ioctl(tty, TIOCGWINSZ, &winsize))
    128					err(1, "tcgetwinsize tty");
    129
    130				close(tty);
    131			}
    132		} else if (!strcmp(*arg, "-c") || !strcmp(*arg, "--cols")) {
    133			winsize.ws_col = atoi(*++arg);
    134		} else if (!strcmp(*arg, "-r") || !strcmp(*arg, "--rows")) {
    135			winsize.ws_row = atoi(*++arg);
    136		} else if (!strcmp(*arg, "-h") || !strcmp(*arg, "--help")) {
    137			usage(0);
    138		} else if (!strcmp(*arg, "--")) {
    139			arg++;
    140			break;
    141		} else {
    142			break;
    143		}
    144	}
    145
    146	if (!*arg) errx(1, "missing command");
    147	cmd = arg;
    148
    149	child = forkpty(&master, NULL, &termios, &winsize);
    150	if (child < 0) err(1, "fork");
    151
    152	if (child == 0) {
    153		execvp(*cmd, cmd);
    154		err(1, "execvp %s", *cmd);
    155	} else {
    156		mitm(master);
    157
    158		waitpid(child, &exitcode, 0);
    159		close(master);
    160	}
    161
    162	return exitcode;
    163}