#define _POSIX_C_SOURCE 200112L #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #define writeout(buff, size) if(write(STDOUT_FILENO, buff, size) < 0) goto fail #define put(str) writeout(str, strlen(str)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define BUFF_SIZE 1024 int main(int argc, char *argv[]) { if(argc < 2) { put("usage: ./this program"); exit(EXIT_FAILURE); } struct termios parentTermios; if(tcgetattr(STDIN_FILENO, &parentTermios) < 0) goto fail;; cfmakeraw(&parentTermios); //TODO: should restore terminal state if(tcsetattr(STDOUT_FILENO, TCSANOW, &parentTermios) < 0) goto fail; int fdChild; pid_t pid = forkpty(&fdChild, NULL, NULL, NULL); //FIXME: non-standard //TODO: read all input and terminate on SIGCHILD? if(pid < 0) goto fail; else if(pid == 0) /* child */ { //if(setsid() < 0) //goto fail; //if(ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0) //goto fail; //TODO: set raw mode //TODO: set echo off //TODO: set session leader. forkpty(2) does that //TODO: set controlling terminal. forkpty(2) does that //raw mode => echo off //this should be done to the parent terminal put("I'm child! "); for(char i='a'; i<='z'; i++) writeout(&i, 1); put("\n"); execvp(argv[1], argv + 1); goto fail; } else /* parent */ { prctl(PR_SET_NAME, "parent"); char downBuff[BUFF_SIZE], upBuff[BUFF_SIZE]; size_t downRead = 0, downWrite = 0; size_t upRead = 0, upWrite = 0; int nfds = 0; nfds = MAX(nfds, STDOUT_FILENO); nfds = MAX(nfds, STDIN_FILENO); nfds = MAX(nfds, fdChild); fd_set readSet, writeSet; loop: FD_ZERO(&readSet); FD_ZERO(&writeSet); if(downRead < BUFF_SIZE) FD_SET(STDIN_FILENO, &readSet); if(downWrite < downRead) FD_SET(fdChild, &writeSet); if(upRead < BUFF_SIZE) FD_SET(fdChild, &readSet); if(upWrite < upRead) FD_SET(STDOUT_FILENO, &writeSet); if(select(nfds + 1, &readSet, &writeSet, NULL, NULL) < 0) goto fail; ssize_t nbytes; if(FD_ISSET(STDIN_FILENO, &readSet)) { assert(BUFF_SIZE - downRead > 0); nbytes = read(STDIN_FILENO, downBuff + downRead, BUFF_SIZE - downRead); if(nbytes < 0) goto fail; else if(nbytes == 0) goto eof; downRead += nbytes; assert(downRead <= BUFF_SIZE); } /* mangle downBuff as you wish */ if(FD_ISSET(fdChild, &writeSet)) { assert(downRead - downWrite > 0); nbytes = write(fdChild, downBuff + downWrite, downRead - downWrite); if(nbytes < 0) goto fail; else if(nbytes == 0) goto eof; downWrite += nbytes; assert(downWrite <= BUFF_SIZE); } if(downWrite == downRead) downWrite = downRead = 0; if(FD_ISSET(fdChild, &readSet)) { assert(BUFF_SIZE - upRead > 0); nbytes = read(fdChild, upBuff + upRead, BUFF_SIZE - upRead); if(nbytes < 0) goto fail; else if(nbytes == 0) goto eof; upRead += nbytes; assert(upRead <= BUFF_SIZE); } /* mangle upBuff as you wish */ for(size_t i=0; i 0); nbytes = write(STDOUT_FILENO, upBuff + upWrite, upRead - upWrite); if(nbytes < 0) goto fail; else if(nbytes == 0) goto eof; upWrite += nbytes; assert(upWrite <= BUFF_SIZE); } if(upWrite == upRead) upWrite = upRead = 0; goto loop; } return 0; eof: fprintf(stderr, "end of file\n"); exit(EXIT_SUCCESS); fail: perror(NULL); exit(EXIT_FAILURE); }