]> Dogcows Code - chaz/tar/blob - tests/ttyemu.c
Don't build ttyemu and run tty I/O test if grantpt is not available.
[chaz/tar] / tests / ttyemu.c
1 /* Run program with its first three file descriptors attached to a tty.
2
3 Copyright 2014 Free Software Foundation, Inc.
4
5 This file is part of GNU tar.
6
7 GNU tar is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 GNU tar is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20 #define _XOPEN_SOURCE 600
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <string.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
35 #include <termios.h>
36 #include <sys/ioctl.h>
37
38 #ifndef TCSASOFT
39 # define TCSASOFT 0
40 #endif
41
42 #define C_EOT 4
43
44 #define EX_OK 0
45 #define EX_USAGE 125
46 #define EX_ERR 126
47 #define EX_EXEC 127
48
49 #define BUF_SIZE 1024
50
51 #if 0
52 # define DEBUG(c) fprintf (stderr, "%s\n", c)
53 #else
54 # define DEBUG(c)
55 #endif
56
57 struct buffer
58 {
59 char buf[BUF_SIZE];
60 int avail;
61 int written;
62 int cr;
63 time_t ts;
64 };
65
66 #define shut(fildes) \
67 do \
68 { \
69 DEBUG (("closing " #fildes)); \
70 close(fildes); \
71 fildes = -1; \
72 } \
73 while(0)
74
75 #define bufinit(buffer,all) \
76 do \
77 { \
78 (buffer).avail = (buffer).written = 0; \
79 (buffer).ts = time (NULL); \
80 if (all) \
81 (buffer).cr = 0; \
82 } \
83 while(0)
84
85 #define bufisempty(buffer) ((buffer).avail == (buffer).written)
86 #define bufavail(buffer) (BUF_SIZE - (buffer).avail)
87
88 #define bufread(buffer,fildes,tty) \
89 do \
90 { \
91 int r = read (fildes, (buffer).buf + (buffer).avail, \
92 BUF_SIZE - (buffer).avail); \
93 (buffer).ts = time (NULL); \
94 if (r < 0) \
95 { \
96 if (errno == EINTR) \
97 continue; \
98 if (tty && errno == EIO) \
99 shut (fildes); \
100 else \
101 { \
102 fprintf (stderr, "%s:%d: reading from %s: %s", \
103 __FILE__,__LINE__,#fildes, strerror (errno)); \
104 exit (EX_ERR); \
105 } \
106 } \
107 else if (r == 0) \
108 shut (fildes); \
109 else \
110 (buffer).avail += r; \
111 } \
112 while(0)
113
114 #define bufwrite(buffer,fildes) \
115 do \
116 { \
117 int r = write (fildes, (buffer).buf + (buffer).written, \
118 (buffer).avail - (buffer).written); \
119 (buffer).ts = time (NULL); \
120 if (r < 0) \
121 { \
122 if (errno == EINTR) \
123 continue; \
124 if (stop) \
125 shut (fildes); \
126 else \
127 { \
128 perror ("writing"); \
129 exit (EX_ERR); \
130 } \
131 } \
132 else if (r == 0) \
133 /*shut (fildes)*/; \
134 else \
135 (buffer).written += r; \
136 } \
137 while(0)
138
139 void
140 tr (struct buffer *bp)
141 {
142 int i, j;
143
144 for (i = j = bp->written; i < bp->avail;)
145 {
146 if (bp->buf[i] == '\r')
147 {
148 bp->cr = 1;
149 i++;
150 }
151 else
152 {
153 if (bp->cr)
154 {
155 bp->cr = 0;
156 if (bp->buf[i] != '\n')
157 bp->buf[j++] = '\r';
158 }
159 bp->buf[j++] = bp->buf[i++];
160 }
161 }
162 bp->avail = j;
163 }
164
165 int stop;
166 int status;
167
168 void
169 sigchld (int sig)
170 {
171 DEBUG (("child exited"));
172 wait (&status);
173 stop = 1;
174 }
175
176 void
177 noecho (int fd)
178 {
179 struct termios to;
180
181 if (tcgetattr (fd, &to))
182 {
183 perror ("tcgetattr");
184 exit (EX_ERR);
185 }
186 to.c_lflag |= ICANON;
187 to.c_lflag &= ~(ECHO | ISIG);
188 to.c_cc[VEOF] = C_EOT;
189 if (tcsetattr (fd, TCSAFLUSH | TCSASOFT, &to))
190 {
191 perror ("tcsetattr");
192 exit (EX_ERR);
193 }
194 }
195
196 char *usage_text[] = {
197 "usage: ttyemu [-ah] [-i INFILE] [-o OUTFILE] [-t TIMEOUT] PROGRAM [ARGS...]",
198 "ttyemu runs PROGRAM with its first three file descriptors connected to a"
199 " terminal",
200 "",
201 "Options are:",
202 "",
203 " -a append output to OUTFILE, instead of overwriting it",
204 " -i INFILE read input from INFILE",
205 " -o OUTFILE write output to OUTFILE",
206 " -t TIMEOUT set I/O timeout",
207 " -h print this help summary",
208 "",
209 "Report bugs and suggestions to <bug-tar@gnu.org>.",
210 NULL
211 };
212
213 static void
214 usage (void)
215 {
216 int i;
217
218 for (i = 0; usage_text[i]; i++)
219 {
220 fputs (usage_text[i], stderr);
221 fputc ('\n', stderr);
222 }
223 }
224
225 int
226 main (int argc, char **argv)
227 {
228 int i;
229 int master, slave;
230 pid_t pid;
231 fd_set rdset, wrset;
232 struct buffer ibuf, obuf;
233 int in = 0, out = 1;
234 char *infile = NULL, *outfile = NULL;
235 int outflags = O_TRUNC;
236 int maxfd;
237 int eot = C_EOT;
238 int timeout = 0;
239
240 while ((i = getopt (argc, argv, "ai:o:t:h")) != EOF)
241 {
242 switch (i)
243 {
244 case 'a':
245 outflags &= ~O_TRUNC;
246 break;
247
248 case 'i':
249 infile = optarg;
250 break;
251
252 case 'o':
253 outfile = optarg;
254 break;
255
256 case 't':
257 timeout = atoi (optarg);
258 break;
259
260 case 'h':
261 usage ();
262 return EX_OK;
263
264 default:
265 return EX_USAGE;
266 }
267 }
268
269 argc -= optind;
270 argv += optind;
271
272 if (argc == 0)
273 {
274 usage ();
275 return EX_USAGE;
276 }
277
278 if (infile)
279 {
280 in = open (infile, O_RDONLY);
281 if (in == -1)
282 {
283 perror (infile);
284 return EX_ERR;
285 }
286 }
287
288 if (outfile)
289 {
290 out = open (outfile, O_RDWR|O_CREAT|outflags, 0666);
291 if (out == -1)
292 {
293 perror (outfile);
294 return EX_ERR;
295 }
296 }
297
298 master = posix_openpt (O_RDWR);
299 if (master == -1)
300 {
301 perror ("posix_openpty");
302 return EX_ERR;
303 }
304
305 if (grantpt (master))
306 {
307 perror ("grantpt");
308 return EX_ERR;
309 }
310
311 if (unlockpt (master))
312 {
313 perror ("unlockpt");
314 return EX_ERR;
315 }
316
317 signal (SIGCHLD, sigchld);
318
319 pid = fork ();
320 if (pid == -1)
321 {
322 perror ("fork");
323 return EX_ERR;
324 }
325
326 if (pid == 0)
327 {
328 slave = open (ptsname (master), O_RDWR);
329 if (slave < 0)
330 {
331 perror ("open");
332 return EX_ERR;
333 }
334
335 noecho (slave);
336 for (i = 0; i < 3; i++)
337 {
338 if (slave != i)
339 {
340 close (i);
341 if (dup (slave) != i)
342 {
343 perror ("dup");
344 _exit (EX_EXEC);
345 }
346 }
347 }
348 for (i = sysconf (_SC_OPEN_MAX) - 1; i > 2; --i)
349 close (i);
350
351 setsid ();
352 #ifdef TIOCSCTTY
353 ioctl (0, TIOCSCTTY, 1);
354 #endif
355 execvp (argv[0], argv);
356 perror (argv[0]);
357 _exit (EX_EXEC);
358 }
359 sleep (1);
360
361 bufinit (ibuf, 1);
362 bufinit (obuf, 1);
363 while (1)
364 {
365 FD_ZERO (&rdset);
366 FD_ZERO (&wrset);
367
368 maxfd = 0;
369
370 if (in != -1)
371 {
372 FD_SET (in, &rdset);
373 if (in > maxfd)
374 maxfd = in;
375 }
376
377 if (master != -1)
378 {
379 FD_SET (master, &rdset);
380 if (!stop)
381 FD_SET (master, &wrset);
382 if (master > maxfd)
383 maxfd = master;
384 }
385
386 if (maxfd == 0)
387 {
388 if (stop)
389 break;
390 pause ();
391 continue;
392 }
393
394 if (select (maxfd + 1, &rdset, &wrset, NULL, NULL) < 0)
395 {
396 if (errno == EINTR)
397 continue;
398 perror ("select");
399 return EX_ERR;
400 }
401
402 if (timeout)
403 {
404 time_t now = time (NULL);
405 if (now - ibuf.ts > timeout || now - obuf.ts > timeout)
406 {
407 fprintf (stderr, "ttyemu: I/O timeout\n");
408 return EX_ERR;
409 }
410 }
411
412 if (in >= 0)
413 {
414 if (bufavail (ibuf) && FD_ISSET (in, &rdset))
415 bufread (ibuf, in, 0);
416 }
417 else if (master == -1)
418 break;
419
420 if (master >= 0 && FD_ISSET (master, &wrset))
421 {
422 if (!bufisempty (ibuf))
423 bufwrite (ibuf, master);
424 else if (in == -1 && eot)
425 {
426 DEBUG (("sent EOT"));
427 if (write (master, &eot, 1) <= 0)
428 {
429 perror ("write");
430 return EX_ERR;
431 }
432 eot = 0;
433 }
434 }
435
436 if (master >= 0 && bufavail (obuf) && FD_ISSET (master, &rdset))
437 bufread (obuf, master, 1);
438
439 if (bufisempty (obuf))
440 bufinit (obuf, 0);
441 else
442 {
443 tr (&obuf);
444 bufwrite (obuf, out);
445 }
446
447 if (bufisempty (ibuf))
448 bufinit (ibuf, 0);
449 }
450
451 if (WIFEXITED (status))
452 return WEXITSTATUS (status);
453
454 if (WIFSIGNALED (status))
455 fprintf (stderr, "ttyemu: child process %s failed on signal %d\n",
456 argv[0], WTERMSIG (status));
457 else
458 fprintf (stderr, "ttyemu: child process %s failed\n", argv[0]);
459 return EX_EXEC;
460 }
This page took 0.048181 seconds and 4 git commands to generate.