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}