X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/d11f8c6ba92fc28c3ea4d76676ff7d5148c36d7d..ff168106b44fdfbaed7d7cf08c54a72ee1fe2c0a:/test/runpty.c?ds=inline diff --git a/test/runpty.c b/test/runpty.c index e5de14e0..661d8753 100644 --- a/test/runpty.c +++ b/test/runpty.c @@ -1,5 +1,4 @@ -/* - * runpty.c - Run a process under a pseudo-tty +/* runpty.c - Run a process under a pseudo-tty * * This code is Copyright (c) 2017, by the authors of nmh. See the * COPYRIGHT file in the root directory of the nmh distribution for @@ -9,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -20,79 +20,38 @@ #define COMMAND_TIMEOUT 30 +static void run_command(char *argv[], int master_in, int master_out); +static int open_master_pty(const char *desc); +static void die(const char *fmt, ...); + int main(int argc, char *argv[]) { - int master, slave, cc, sendeof = 0, status; - time_t starttime; - const char *slavename; + int master_in, master_out, cc, status; + time_t starttime, now; pid_t child; unsigned char readbuf[1024]; FILE *output; if (argc < 3) { - fprintf(stderr, "Usage: %s output-filename command [arguments ...]\n", - argv[0]); - exit(1); - } - - if ((master = posix_openpt(O_RDWR | O_NOCTTY)) < 0) { - fprintf(stderr, "Unable to open master pseudo-tty: %s\n", - strerror(errno)); - exit(1); - } - - if (grantpt(master) < 0) { - fprintf(stderr, "Unable to grant permissions to master pty: %s\n", - strerror(errno)); - exit(1); - } - - if (unlockpt(master) < 0) { - fprintf(stderr, "Unable to unlock master pty: %s\n", strerror(errno)); - exit(1); + die("%s: too few arguments\n" + "usage: %s output-filename command [arguments...]\n", + *argv, *argv); } - if (!(slavename = ptsname(master))) { - fprintf(stderr, "Unable to determine name of slave pty: %s\n", - strerror(errno)); - exit(1); - } + master_in = open_master_pty("input"); + master_out = open_master_pty("output"); - if ((slave = open(slavename, O_RDWR | O_NOCTTY)) < 0) { - fprintf(stderr, "Unable to open slave pty \"%s\": %s\n", slavename, - strerror(errno)); - exit(1); + if ((child = fork()) == -1) { + die("fork() failed: %s\n", strerror(errno)); } - - child = fork(); - - /* - * Start the child process if we are in the child; close the master - * as we are supposed to only use the slave ptys here. - */ - if (child == 0) { - close(master); - dup2(slave, STDIN_FILENO); - dup2(slave, STDOUT_FILENO); - dup2(slave, STDERR_FILENO); - close(slave); - - execvp(argv[2], argv + 2); - - fprintf(stderr, "execvp(%s) failed: %s\n", argv[2], strerror(errno)); - } else if (child < 0) { - fprintf(stderr, "fork() failed: %s\n", strerror(errno)); - exit(1); + run_command(argv + 2, master_in, master_out); /* Does not return. */ } - close(slave); - if (!(output = fopen(argv[1], "w"))) { - fprintf(stderr, "Unable to open \"%s\" for output: %s\n", argv[1], - strerror(errno)); - exit(1); + die("Unable to open \"%s\" for output: %s\n", argv[1], + strerror(errno)); } starttime = time(NULL); @@ -103,68 +62,127 @@ main(int argc, char *argv[]) FD_ZERO(&readfds); - FD_SET(master, &readfds); + FD_SET(master_out, &readfds); /* - * Okay, what's going on here? - * - * We want to send the EOF character (to simulate a "end of file", - * as if we redirected stdin to /dev/null). We can't just close - * master, because we won't be able to get any data back. So, - * after we get SOME data, set sendeof, and that will cause us to - * send the EOF character after the first select call. If we are - * doing sendeof, set the timeout to 1 second; otherwise, set the - * timeout to COMMAND_TIMEOUT seconds remaining. + * After we get our first bit of data, close the master pty + * connected to standard input on our slave; that will generate + * an EOF. */ - if (sendeof) { - tv.tv_sec = 1; - } else { - tv.tv_sec = starttime + COMMAND_TIMEOUT - time(NULL); - if (tv.tv_sec < 0) - tv.tv_sec = 0; - } + tv.tv_sec = starttime + COMMAND_TIMEOUT - time(NULL); + if (tv.tv_sec < 0) + tv.tv_sec = 0; tv.tv_usec = 0; - cc = select(master + 1, &readfds, NULL, NULL, &tv); + cc = select(master_out + 1, &readfds, NULL, NULL, &tv); if (cc < 0) { - fprintf(stderr, "select() failed: %s\n", strerror(errno)); - exit(1); + die("select() failed: %s\n", strerror(errno)); } - if (cc > 0 && FD_ISSET(master, &readfds)) { - cc = read(master, readbuf, sizeof(readbuf)); + if (cc > 0 && FD_ISSET(master_out, &readfds)) { + cc = read(master_out, readbuf, sizeof(readbuf)); if (cc <= 0) break; fwrite(readbuf, 1, cc, output); - } - - if (cc == 0 && sendeof) { - struct termios rtty; - if (tcgetattr(master, &rtty) < 0) { - fprintf(stderr, "tcgetattr() failed: %s\n", strerror(errno)); - exit(1); + if (master_in != -1) { + close(master_in); + master_in = -1; } + } - if (rtty.c_lflag & ICANON) - write(master, &rtty.c_cc[VEOF], 1); - } else if (!sendeof) - sendeof = 1; - - if (time(NULL) > starttime + COMMAND_TIMEOUT) { - fprintf(stderr, "Command execution timed out\n"); + now = time(NULL); + if (now >= starttime + COMMAND_TIMEOUT) { + fprintf(stderr, "Command execution timed out: %ld to %ld: %d\n", + starttime, now, cc); break; } } - close(master); + if (master_in != -1) + close(master_in); + close(master_out); fclose(output); waitpid(child, &status, 0); exit(0); } + +static void +run_command(char *argv[], int master_in, int master_out) +{ + const char *slavename; + int slave; + + /* Open the two slave pseudo-ttys and close the masters after we are + * done with them. */ + + if (!(slavename = ptsname(master_in))) { + die("Unable to determine name of slave pty: %s\n", + strerror(errno)); + } + + if ((slave = open(slavename, O_RDWR)) == -1) { + die("Unable to open slave pty \"%s\": %s\n", slavename, + strerror(errno)); + } + + dup2(slave, STDIN_FILENO); + close(slave); + close(master_in); + + if (!(slavename = ptsname(master_out))) { + die("Unable to determine name of slave pty: %s\n", + strerror(errno)); + } + + if ((slave = open(slavename, O_RDWR | O_NOCTTY)) < 0) { + die("Unable to open slave pty \"%s\": %s\n", slavename, + strerror(errno)); + } + + dup2(slave, STDOUT_FILENO); + dup2(slave, STDERR_FILENO); + close(slave); + close(master_out); + + execvp(*argv, argv); + + die("execvp(%s) failed: %s\n", *argv, strerror(errno)); +} + +static int +open_master_pty(const char *desc) +{ + int fd; + + if ((fd = posix_openpt(O_RDWR | O_NOCTTY)) == -1) { + die("Unable to open master %s pseudo-tty: %s\n", desc, strerror(errno)); + } + if (grantpt(fd) == -1) { + die("Unable to grant permissions to master %s pty: %s\n", desc, + strerror(errno)); + } + if (unlockpt(fd) == -1) { + die("Unable to unlock master %s pty: %s\n", desc, strerror(errno)); + } + + return fd; +} + +static void +die(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + exit(1); +}