1 /* Run program with its first three file descriptors attached to a tty.
3 Copyright 2014 Free Software Foundation, Inc.
5 This file is part of GNU tar.
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.
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.
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/>.
20 #define _XOPEN_SOURCE 600
25 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
36 #include <sys/ioctl.h>
52 # define DEBUG(c) fprintf (stderr, "%s\n", c)
66 #define shut(fildes) \
69 DEBUG (("closing " #fildes)); \
75 #define bufinit(buffer,all) \
78 (buffer).avail = (buffer).written = 0; \
79 (buffer).ts = time (NULL); \
85 #define bufisempty(buffer) ((buffer).avail == (buffer).written)
86 #define bufavail(buffer) (BUF_SIZE - (buffer).avail)
88 #define bufread(buffer,fildes,tty) \
91 int r = read (fildes, (buffer).buf + (buffer).avail, \
92 BUF_SIZE - (buffer).avail); \
93 (buffer).ts = time (NULL); \
98 if (tty && errno == EIO) \
102 fprintf (stderr, "%s:%d: reading from %s: %s", \
103 __FILE__,__LINE__,#fildes, strerror (errno)); \
110 (buffer).avail += r; \
114 #define bufwrite(buffer,fildes) \
117 int r = write (fildes, (buffer).buf + (buffer).written, \
118 (buffer).avail - (buffer).written); \
119 (buffer).ts = time (NULL); \
122 if (errno == EINTR) \
128 perror ("writing"); \
135 (buffer).written += r; \
140 tr (struct buffer *bp)
144 for (i = j = bp->written; i < bp->avail;)
146 if (bp->buf[i] == '\r')
156 if (bp->buf[i] != '\n')
159 bp->buf[j++] = bp->buf[i++];
171 DEBUG (("child exited"));
181 if (tcgetattr (fd, &to))
183 perror ("tcgetattr");
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))
191 perror ("tcsetattr");
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"
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",
209 "Report bugs and suggestions to <bug-tar@gnu.org>.",
218 for (i = 0; usage_text[i]; i++)
220 fputs (usage_text[i], stderr);
221 fputc ('\n', stderr);
226 main (int argc, char **argv)
232 struct buffer ibuf, obuf;
234 char *infile = NULL, *outfile = NULL;
235 int outflags = O_TRUNC;
240 while ((i = getopt (argc, argv, "ai:o:t:h")) != EOF)
245 outflags &= ~O_TRUNC;
257 timeout = atoi (optarg);
280 in = open (infile, O_RDONLY);
290 out = open (outfile, O_RDWR|O_CREAT|outflags, 0666);
298 master = posix_openpt (O_RDWR);
301 perror ("posix_openpty");
305 if (grantpt (master))
311 if (unlockpt (master))
317 signal (SIGCHLD, sigchld);
328 slave = open (ptsname (master), O_RDWR);
336 for (i = 0; i < 3; i++)
341 if (dup (slave) != i)
348 for (i = sysconf (_SC_OPEN_MAX) - 1; i > 2; --i)
353 ioctl (0, TIOCSCTTY, 1);
355 execvp (argv[0], argv);
379 FD_SET (master, &rdset);
381 FD_SET (master, &wrset);
394 if (select (maxfd + 1, &rdset, &wrset, NULL, NULL) < 0)
404 time_t now = time (NULL);
405 if (now - ibuf.ts > timeout || now - obuf.ts > timeout)
407 fprintf (stderr, "ttyemu: I/O timeout\n");
414 if (bufavail (ibuf) && FD_ISSET (in, &rdset))
415 bufread (ibuf, in, 0);
417 else if (master == -1)
420 if (master >= 0 && FD_ISSET (master, &wrset))
422 if (!bufisempty (ibuf))
423 bufwrite (ibuf, master);
424 else if (in == -1 && eot)
426 DEBUG (("sent EOT"));
427 if (write (master, &eot, 1) <= 0)
436 if (master >= 0 && bufavail (obuf) && FD_ISSET (master, &rdset))
437 bufread (obuf, master, 1);
439 if (bufisempty (obuf))
444 bufwrite (obuf, out);
447 if (bufisempty (ibuf))
451 if (WIFEXITED (status))
452 return WEXITSTATUS (status);
454 if (WIFSIGNALED (status))
455 fprintf (stderr, "ttyemu: child process %s failed on signal %d\n",
456 argv[0], WTERMSIG (status));
458 fprintf (stderr, "ttyemu: child process %s failed\n", argv[0]);