]> diplodocus.org Git - nmh/blobdiff - test/runpty.c
Normalize some #include paths.
[nmh] / test / runpty.c
index e5de14e0c05f31aaa774de75f20e569154347c86..661d8753c343175cd5dabf7d2fc6620eacd5f4a7 100644 (file)
@@ -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
  *
  * 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 <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <stdarg.h>
 #include <fcntl.h>
 #include <termios.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <termios.h>
 #include <unistd.h>
 
 #define COMMAND_TIMEOUT 30
 
 
 #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
 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) {
     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) {
     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"))) {
     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);
     }
 
     starttime = time(NULL);
@@ -103,68 +62,127 @@ main(int argc, char *argv[])
 
        FD_ZERO(&readfds);
 
 
        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;
 
        tv.tv_usec = 0;
 
-       cc = select(master + 1, &readfds, NULL, NULL, &tv);
+       cc = select(master_out + 1, &readfds, NULL, NULL, &tv);
 
        if (cc < 0) {
 
        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)
                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;
        }
     }
 
            break;
        }
     }
 
-    close(master);
+    if (master_in != -1)
+       close(master_in);
+    close(master_out);
     fclose(output);
 
     waitpid(child, &status, 0);
 
     exit(0);
 }
     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);
+}