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