ptty.c (2734B)
1 #include <sys/wait.h> 2 3 #include <errno.h> 4 #include <inttypes.h> 5 #include <limits.h> 6 #include <poll.h> 7 #include <stdarg.h> 8 #include <stdbool.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <termios.h> 13 #include <unistd.h> 14 15 #if defined(__linux) 16 #include <pty.h> 17 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 18 #include <util.h> 19 #elif defined(__FreeBSD__) || defined(__DragonFly__) 20 #include <libutil.h> 21 #endif 22 23 void 24 die(const char *fmt, ...) 25 { 26 va_list ap; 27 va_start(ap, fmt); 28 vfprintf(stderr, fmt, ap); 29 va_end(ap); 30 31 if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 32 fputc(' ', stderr); 33 perror(NULL); 34 } else { 35 fputc('\n', stderr); 36 } 37 38 exit(EXIT_FAILURE); 39 } 40 41 void 42 usage(void) 43 { 44 fputs("ptty [-C] [-c cols] [-r rows] cmd\n", stderr); 45 exit(EXIT_FAILURE); 46 } 47 48 int 49 main(int argc, char *argv[]) 50 { 51 struct winsize ws = {.ws_row = 25, .ws_col = 80, 0, 0}; 52 int ch; 53 bool closeflag = false; 54 55 while ((ch = getopt(argc, argv, "c:r:Ch")) != -1) { 56 switch (ch) { 57 case 'c': /* cols */ 58 ws.ws_col = strtoimax(optarg, NULL, 10); 59 if (errno != 0) 60 die("strtoimax: %s", optarg); 61 break; 62 case 'r': /* lines */ 63 ws.ws_row = strtoimax(optarg, NULL, 10); 64 if (errno != 0) 65 die("strtoimax: %s", optarg); 66 break; 67 case 'C': 68 closeflag = true; 69 break; 70 case 'h': 71 default: 72 usage(); 73 } 74 } 75 argc -= optind; 76 argv += optind; 77 78 if (argc < 1) 79 usage(); 80 81 int mfd; 82 pid_t child = forkpty(&mfd, NULL, NULL, &ws); 83 switch (child) { 84 case -1: 85 die("forkpty"); 86 case 0: /* child */ 87 execvp(argv[0], argv); 88 die("exec"); 89 } 90 91 /* parent */ 92 93 if (closeflag && close(mfd) == -1) 94 die("close:"); 95 96 int pfds = 2; 97 struct pollfd pfd[2] = { 98 { STDIN_FILENO, POLLIN, 0}, 99 { mfd, POLLIN, 0} 100 }; 101 102 for (;;) { 103 char buf[BUFSIZ]; 104 ssize_t n; 105 int r; 106 107 if ((r = poll(pfd, pfds, -1)) == -1) 108 die("poll:"); 109 110 if (pfd[0].revents & POLLIN) { 111 if ((n = read(STDIN_FILENO, buf, sizeof buf)) == -1) 112 die("read:"); 113 if (n == 0) { 114 pfd[0].fd = -1; 115 if (close(mfd) == -1) 116 die("close:"); 117 break; 118 } 119 if (write(mfd, buf, n) == -1) 120 die("write:"); 121 } 122 123 if (pfd[1].revents & POLLIN) { 124 if ((n = read(mfd, buf, sizeof(buf)-1)) == -1) 125 die("read:"); 126 127 if (n == 0) break; 128 129 buf[n] = '\0'; 130 131 /* handle cursor position request */ 132 if (strcmp("\033[6n", buf) == 0) { 133 dprintf(mfd, "\033[25;1R"); 134 continue; 135 } 136 137 if (write(STDOUT_FILENO, buf, n) == -1) 138 die("write:"); 139 } 140 141 if (pfd[0].revents & POLLHUP) { 142 pfd[0].fd = -1; 143 if (close(mfd) == -1) 144 die("close:"); 145 break; 146 } 147 if (pfd[1].revents & POLLHUP) 148 break; 149 } 150 151 int status; 152 if (waitpid(child, &status, 0) != child) 153 die("waitpid:"); 154 155 return WEXITSTATUS(status); 156 }