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