]> diplodocus.org Git - nmh/blob - test/runpty.c
e8db35f2ebdf2451fa0f3e0841da4cde59e2a69a
[nmh] / test / runpty.c
1 /*
2 * runpty.c - Run a process under a pseudo-tty
3 *
4 * This code is Copyright (c) 2017, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdarg.h>
13 #include <fcntl.h>
14 #include <termios.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <sys/select.h>
19 #include <time.h>
20 #include <errno.h>
21
22 #define COMMAND_TIMEOUT 30
23
24 static void die(const char *fmt, ...);
25
26 int
27 main(int argc, char *argv[])
28 {
29 int master_in, master_out, slave, cc, status;
30 time_t starttime, now;
31 const char *slavename;
32 pid_t child;
33 unsigned char readbuf[1024];
34 FILE *output;
35
36 if (argc < 3) {
37 die("%s: too few arguments\n"
38 "usage: %s output-filename command [arguments...]\n",
39 *argv, *argv);
40 }
41
42 if ((master_in = posix_openpt(O_RDWR | O_NOCTTY)) < 0) {
43 die("Unable to open master pseudo-tty: %s\n", strerror(errno));
44 }
45
46 if ((master_out = posix_openpt(O_RDWR | O_NOCTTY)) < 0) {
47 die("Unable to open master pseudo-tty: %s\n", strerror(errno));
48 }
49
50 if (grantpt(master_in) < 0) {
51 die("Unable to grant permissions to master pty: %s\n",
52 strerror(errno));
53 }
54
55 if (grantpt(master_out) < 0) {
56 die("Unable to grant permissions to master pty: %s\n",
57 strerror(errno));
58 }
59
60 if (unlockpt(master_in) < 0) {
61 die("Unable to unlock master pty: %s\n", strerror(errno));
62 }
63
64 if (unlockpt(master_out) < 0) {
65 die("Unable to unlock master pty: %s\n", strerror(errno));
66 }
67
68 child = fork();
69
70 /*
71 * Start the child process if we are in the child; open the two
72 * slave pseudo-ttys and close the masters after we are done with them.
73 */
74
75 if (child == 0) {
76 if (!(slavename = ptsname(master_in))) {
77 die("Unable to determine name of slave pty: %s\n",
78 strerror(errno));
79 }
80
81 if ((slave = open(slavename, O_RDWR)) < 0) {
82 die("Unable to open slave pty \"%s\": %s\n", slavename,
83 strerror(errno));
84 }
85
86 dup2(slave, STDIN_FILENO);
87 close(slave);
88 close(master_in);
89
90 if (!(slavename = ptsname(master_out))) {
91 die("Unable to determine name of slave pty: %s\n",
92 strerror(errno));
93 }
94
95 if ((slave = open(slavename, O_RDWR | O_NOCTTY)) < 0) {
96 die("Unable to open slave pty \"%s\": %s\n", slavename,
97 strerror(errno));
98 }
99
100 dup2(slave, STDOUT_FILENO);
101 dup2(slave, STDERR_FILENO);
102 close(slave);
103 close(master_out);
104
105 execvp(argv[2], argv + 2);
106
107 die("execvp(%s) failed: %s\n", argv[2], strerror(errno));
108
109 } else if (child < 0) {
110 die("fork() failed: %s\n", strerror(errno));
111 }
112
113 if (!(output = fopen(argv[1], "w"))) {
114 die("Unable to open \"%s\" for output: %s\n", argv[1],
115 strerror(errno));
116 }
117
118 starttime = time(NULL);
119
120 for (;;) {
121 fd_set readfds;
122 struct timeval tv;
123
124 FD_ZERO(&readfds);
125
126 FD_SET(master_out, &readfds);
127
128 /*
129 * After we get our first bit of data, close the master pty
130 * connected to standard input on our slave; that will generate
131 * an EOF.
132 */
133
134 tv.tv_sec = starttime + COMMAND_TIMEOUT - time(NULL);
135 if (tv.tv_sec < 0)
136 tv.tv_sec = 0;
137 tv.tv_usec = 0;
138
139 cc = select(master_out + 1, &readfds, NULL, NULL, &tv);
140
141 if (cc < 0) {
142 die("select() failed: %s\n", strerror(errno));
143 }
144
145 if (cc > 0 && FD_ISSET(master_out, &readfds)) {
146 cc = read(master_out, readbuf, sizeof(readbuf));
147
148 if (cc <= 0)
149 break;
150
151 fwrite(readbuf, 1, cc, output);
152
153 if (master_in != -1) {
154 close(master_in);
155 master_in = -1;
156 }
157 }
158
159 now = time(NULL);
160 if (now >= starttime + COMMAND_TIMEOUT) {
161 fprintf(stderr, "Command execution timed out: %ld to %ld: %d\n",
162 starttime, now, cc);
163 break;
164 }
165 }
166
167 if (master_in != -1)
168 close(master_in);
169 close(master_out);
170 fclose(output);
171
172 waitpid(child, &status, 0);
173
174 exit(0);
175 }
176
177 static void
178 die(const char *fmt, ...)
179 {
180 va_list args;
181
182 va_start(args, fmt);
183 vfprintf(stderr, fmt, args);
184 va_end(args);
185
186 exit(1);
187 }