#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "eat.h" static void do_output(int fd); static void do_input(int fd); #define BUF_SIZE 4096 static char buf[BUF_SIZE]; /* Display output from the child process */ void do_output(int fd) { int tmp; errno = 0; while ((tmp = read(fd, buf, BUF_SIZE)) > 0) { fwrite(buf, 1, tmp, stdout); } fflush(stdout); if (errno != EAGAIN && tmp < 0) { if (errno != EIO) perror("read"); exit(EXIT_FAILURE); } } /* Feed input to the child process */ void do_input(int fd) { int tmp; while ((tmp = read(STDIN_FILENO, buf, BUF_SIZE)) > 0) { if (write(fd, buf, tmp) < 0) { perror("write"); break; } fdatasync(fd); } if (errno != EAGAIN && tmp < 0) { perror("read"); exit(EXIT_FAILURE); } } /* struct cleanup_t { pid_t pid; int pt_fd; struct termios flags; }; static void sigchld_handler(int sig); static void exit_handler(int status, struct cleanup_t* cleanup); #define BUF_SIZE 4096 char buf[BUF_SIZE]; */ #if 0 int pt; int child_pid; fd_set r_set, x_set; struct winsize ws = {60, 132, 8, 8}; struct termios flags; struct cleanup_t cleanup; int tmp; /* Mishandle command line arguments */ if (argc == 1) { fprintf(stderr, "Usage: eat [options] [--] command args ...\n"); fprintf(stderr, "options: none yet, hehehe!\n"); return EXIT_FAILURE; } /* Fork and run child process in a pt */ child_pid = forkpty(&pt, NULL, NULL, &ws); if (child_pid == 0) { execvp(argv[1], argv + 1); /* Something Bad must have happened... */ perror("execvp"); return EXIT_FAILURE; } cleanup.pid = child_pid; cleanup.pt_fd = pt; /* Arrange for stuff to be cleaned up on exit */ on_exit((void (*)(int, void*))exit_handler, &cleanup); /* Change terminal mode for stdin */ tcgetattr(0, &flags); cleanup.flags = flags; /* flags.c_iflag &= ~(ECHO | ICANON | ISIG | BRKINT | ICRNL); flags.c_oflag &= ~OPOST; flags.c_cc[VTIME] = 0; flags.c_cc[VMIN] = 1; if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &flags) < 0) { perror("tcsetattr"); return EXIT_FAILURE; } */ /* Enable non-blocking mode for the pt and stdin */ fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) & O_NONBLOCK); fcntl(pt, F_SETFL, fcntl(pt, F_GETFL, 0) & O_NONBLOCK); /* Set up our SIGCHLD handler */ if (signal(SIGCHLD, sigchld_handler) == SIG_ERR) { perror("signal"); return EXIT_FAILURE; } /* Main loop */ FD_ZERO(&r_set); FD_ZERO(&x_set); while (1) { /* Wait for data from the child process or from the user */ FD_SET(pt, &r_set); FD_SET(STDIN_FILENO, &r_set); FD_SET(pt, &x_set); if (select(FD_SETSIZE, &r_set, NULL, &x_set, NULL) < 0) { perror("select"); return EXIT_FAILURE; } /* Display data from the child process */ if (FD_ISSET(pt, &r_set)) do_output(pt); /* Feed user data to the child process */ if (FD_ISSET(STDIN_FILENO, &r_set)) do_input(pt); /* Something... */ if (FD_ISSET(pt, &x_set)) { /* Will this ever happen? */ printf("exception\n"); break; } } return EXIT_SUCCESS; } /* We get signal. */ void sigchld_handler(int sig) { int status; if (wait(&status) == -1) { perror("wait"); exit(EXIT_FAILURE); } else if (WIFEXITED(status)) { fprintf(stderr, "Child exited with status %i\n", WEXITSTATUS(status)); exit(EXIT_SUCCESS); } else if (WIFSIGNALED(status)) { fprintf(stderr, "Child signaled to death with %i\n", WTERMSIG(status)); exit(EXIT_SUCCESS); } else if (WIFSTOPPED(status)) { fprintf(stderr, "Child stopped by signal %i\n", WSTOPSIG(status)); } else { fprintf(stderr, "Hmm...\n"); exit(EXIT_FAILURE); } } /* Clean up some stuff */ void exit_handler(int status, struct cleanup_t* cleanup) { tcsetattr(STDIN_FILENO, TCSANOW, &cleanup->flags); do_output(cleanup->pt_fd); /* Display the final output */ signal(SIGCHLD, SIG_IGN); /* We don't want an infinite loop... */ kill(cleanup->pid, SIGTERM); /* ...when we kill the child process */ close(cleanup->pt_fd); } #endif