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