rawtty

run program under raw tty
git clone https://logand.com/git/rawtty.git/
Log | Files | Refs

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 }