rawtty.c (4435B)
1 /* 2 3 rawtty -- run program under raw tty 4 5 Copyright (C) 2013 Tomas Hlavaty <tom at logand.com> 6 7 Permission is hereby granted, free of charge, to any person 8 obtaining a copy of this software and associated documentation files 9 (the "Software"), to deal in the Software without restriction, 10 including without limitation the rights to use, copy, modify, merge, 11 publish, distribute, sublicense, and/or sell copies of the Software, 12 and to permit persons to whom the Software is furnished to do so, 13 subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be 16 included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 22 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 23 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 SOFTWARE. 26 27 */ 28 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <signal.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <termios.h> 35 #include <unistd.h> 36 //#include <stdio.h> 37 38 #define BLEN 1024 39 40 static int childpipe[2]; 41 42 static struct termios old; 43 static int selfpipe[2]; 44 45 static int die(char *msg) { 46 write(STDERR_FILENO, "error: ", 7); 47 write(STDERR_FILENO, msg, strlen(msg)); 48 write(STDERR_FILENO, "\n", 1); 49 exit(1); 50 return 1; 51 } 52 53 static void cleanup(void) { 54 tcsetattr(STDIN_FILENO, TCSAFLUSH, &old); 55 } 56 57 static void setup(void) { 58 isatty(STDIN_FILENO) || die("not on a tty"); 59 tcgetattr(STDIN_FILENO, &old) < 0 && die("can't query tty"); 60 atexit(cleanup) && die("can't clean up"); 61 struct termios raw = old; 62 raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); 63 raw.c_oflag &= ~(OPOST); 64 raw.c_cflag |= (CS8); 65 raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); 66 raw.c_cc[VMIN] = 0; 67 raw.c_cc[VTIME] = 0; 68 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0 && die("can't set raw mode"); 69 } 70 71 static void handler(int signo) { 72 unsigned char n = signo; 73 write(selfpipe[1], &n, 1); 74 //fprintf(stderr, "handler %d\n", signo); 75 } 76 77 static void nonblocking(int fd) { 78 int x = fcntl(fd, F_GETFL); 79 x < 0 && die("F_GETFL"); 80 fcntl(fd, F_SETFL, x | O_NONBLOCK) < 0 && die("O_NONBLOCK"); 81 } 82 83 static void parent() { 84 close(childpipe[0]) == -1 && die("parent can't close read end"); 85 dup2(childpipe[1], STDOUT_FILENO); 86 close(childpipe[1]) == -1 && die("parent can't close write end"); 87 nonblocking(STDOUT_FILENO); 88 pipe(selfpipe) == -1 && die("can't create self pipe"); 89 nonblocking(selfpipe[0]); 90 nonblocking(selfpipe[1]); 91 signal(SIGCHLD, handler); 92 fd_set rfds; 93 fd_set wfds; 94 char buf[BLEN]; 95 int abuf = 0, zbuf = 0; 96 for(;;) { 97 FD_ZERO(&rfds); 98 FD_ZERO(&wfds); 99 if(0 < zbuf) FD_SET(STDOUT_FILENO, &wfds); 100 else FD_SET(STDIN_FILENO, &rfds); 101 FD_SET(selfpipe[0], &rfds); 102 switch(select(FD_SETSIZE, &rfds, &wfds, NULL, NULL)) { 103 case -1: EINTR == errno || die("can't select"); break; 104 case 0: die("no data"); 105 default: 106 if(FD_ISSET(STDIN_FILENO, &rfds)) { 107 zbuf = read(STDIN_FILENO, buf, BLEN); 108 zbuf < 1 && die("can't read"); 109 //if(4 == *buf) die("^d"); 110 //if(3 == *buf) die("^c"); 111 } 112 else if(FD_ISSET(STDOUT_FILENO, &wfds)) { 113 int n = write(STDOUT_FILENO, buf, zbuf - abuf); 114 n < 1 && die("can't write"); 115 abuf += n; 116 if(zbuf <= abuf) abuf = zbuf = 0; 117 } 118 else if(FD_ISSET(selfpipe[0], &rfds)) { 119 unsigned char n; 120 while(0 < read(selfpipe[0], &n, 1)); 121 //fprintf(stderr, "sig %d \n", (int) n); 122 exit(0); 123 } 124 else die("unhandled select case"); 125 } 126 } 127 //int stat; 128 //wait(&stat); 129 } 130 131 static void child(char *argv[]) { 132 close(childpipe[1]) == -1 && die("child can't close write end"); 133 dup2(childpipe[0], STDIN_FILENO); 134 close(childpipe[0]) == -1 && die("child can't close read end"); 135 execvp(argv[1], &argv[1]); 136 die("can't execute"); 137 } 138 139 int main(int argc, char *argv[]) { 140 0 < argc || die("usage: rawtty program [arg...]"); 141 setup(); 142 pipe(childpipe) == -1 && die("can't create child pipe"); 143 switch(fork()) { 144 case -1: die("can't fork"); 145 case 0: child(argv); 146 default: parent(); 147 } 148 return 0; 149 }