-/*
- * 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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#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_in, master_out, slave, cc, 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_in = posix_openpt(O_RDWR | O_NOCTTY)) < 0) {
- fprintf(stderr, "Unable to open master pseudo-tty: %s\n",
- strerror(errno));
- exit(1);
+ die("%s: too few arguments\n"
+ "usage: %s output-filename command [arguments...]\n",
+ *argv, *argv);
}
- if ((master_out = posix_openpt(O_RDWR | O_NOCTTY)) < 0) {
- fprintf(stderr, "Unable to open master pseudo-tty: %s\n",
- strerror(errno));
- exit(1);
- }
-
- if (grantpt(master_in) < 0) {
- fprintf(stderr, "Unable to grant permissions to master pty: %s\n",
- strerror(errno));
- exit(1);
- }
+ master_in = open_master_pty("input");
+ master_out = open_master_pty("output");
- if (grantpt(master_out) < 0) {
- fprintf(stderr, "Unable to grant permissions to master pty: %s\n",
- strerror(errno));
- exit(1);
+ if ((child = fork()) == -1) {
+ die("fork() failed: %s\n", strerror(errno));
}
-
- if (unlockpt(master_in) < 0) {
- fprintf(stderr, "Unable to unlock master pty: %s\n", strerror(errno));
- exit(1);
- }
-
- if (unlockpt(master_out) < 0) {
- fprintf(stderr, "Unable to unlock master pty: %s\n", strerror(errno));
- exit(1);
- }
-
- child = fork();
-
- /*
- * Start the child process if we are in the child; open the two
- * slave pseudo-ttys and close the masters after we are done with them.
- */
-
if (child == 0) {
- if (!(slavename = ptsname(master_in))) {
- fprintf(stderr, "Unable to determine name of slave pty: %s\n",
- strerror(errno));
- exit(1);
- }
-
- if ((slave = open(slavename, O_RDWR)) < 0) {
- fprintf(stderr, "Unable to open slave pty \"%s\": %s\n", slavename,
- strerror(errno));
- exit(1);
- }
-
- dup2(slave, STDIN_FILENO);
- close(slave);
- close(master_in);
-
- if (!(slavename = ptsname(master_out))) {
- fprintf(stderr, "Unable to determine name of slave pty: %s\n",
- strerror(errno));
- exit(1);
- }
-
- if ((slave = open(slavename, O_RDWR | O_NOCTTY)) < 0) {
- fprintf(stderr, "Unable to open slave pty \"%s\": %s\n", slavename,
- strerror(errno));
- exit(1);
- }
-
- dup2(slave, STDOUT_FILENO);
- dup2(slave, STDERR_FILENO);
- close(slave);
- close(master_out);
-
- 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. */
}
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);
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_out, &readfds)) {
}
}
- 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;
}
}
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);
+}