123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- /* Run program with its first three file descriptors attached to a tty.
- Copyright 2014, 2016 Free Software Foundation, Inc.
- This file is part of GNU tar.
- GNU tar is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- GNU tar is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #define _XOPEN_SOURCE 600
- #include <config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <signal.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <sys/select.h>
- #include <termios.h>
- #include <sys/ioctl.h>
- #ifndef TCSASOFT
- # define TCSASOFT 0
- #endif
- #define C_EOT 4
- #define EX_OK 0
- #define EX_USAGE 125
- #define EX_ERR 126
- #define EX_EXEC 127
- #define BUF_SIZE 1024
- #if 0
- # define DEBUG(c) fprintf (stderr, "%s\n", c)
- #else
- # define DEBUG(c)
- #endif
- struct buffer
- {
- char buf[BUF_SIZE];
- int avail;
- int written;
- int cr;
- time_t ts;
- };
- #define shut(fildes) \
- do \
- { \
- DEBUG (("closing " #fildes)); \
- close(fildes); \
- fildes = -1; \
- } \
- while(0)
- #define bufinit(buffer,all) \
- do \
- { \
- (buffer).avail = (buffer).written = 0; \
- (buffer).ts = time (NULL); \
- if (all) \
- (buffer).cr = 0; \
- } \
- while(0)
- #define bufisempty(buffer) ((buffer).avail == (buffer).written)
- #define bufavail(buffer) (BUF_SIZE - (buffer).avail)
- #define bufread(buffer,fildes,tty) \
- do \
- { \
- int r = read (fildes, (buffer).buf + (buffer).avail, \
- BUF_SIZE - (buffer).avail); \
- (buffer).ts = time (NULL); \
- if (r < 0) \
- { \
- if (errno == EINTR) \
- continue; \
- if (tty && errno == EIO) \
- shut (fildes); \
- else \
- { \
- fprintf (stderr, "%s:%d: reading from %s: %s", \
- __FILE__,__LINE__,#fildes, strerror (errno)); \
- exit (EX_ERR); \
- } \
- } \
- else if (r == 0) \
- shut (fildes); \
- else \
- (buffer).avail += r; \
- } \
- while(0)
- #define bufwrite(buffer,fildes) \
- do \
- { \
- int r = write (fildes, (buffer).buf + (buffer).written, \
- (buffer).avail - (buffer).written); \
- (buffer).ts = time (NULL); \
- if (r < 0) \
- { \
- if (errno == EINTR) \
- continue; \
- if (stop) \
- shut (fildes); \
- else \
- { \
- perror ("writing"); \
- exit (EX_ERR); \
- } \
- } \
- else if (r == 0) \
- /*shut (fildes)*/; \
- else \
- (buffer).written += r; \
- } \
- while(0)
- void
- tr (struct buffer *bp)
- {
- int i, j;
- for (i = j = bp->written; i < bp->avail;)
- {
- if (bp->buf[i] == '\r')
- {
- bp->cr = 1;
- i++;
- }
- else
- {
- if (bp->cr)
- {
- bp->cr = 0;
- if (bp->buf[i] != '\n')
- bp->buf[j++] = '\r';
- }
- bp->buf[j++] = bp->buf[i++];
- }
- }
- bp->avail = j;
- }
- int stop;
- int status;
- void
- sigchld (int sig)
- {
- DEBUG (("child exited"));
- wait (&status);
- stop = 1;
- }
- void
- noecho (int fd)
- {
- struct termios to;
- if (tcgetattr (fd, &to))
- {
- perror ("tcgetattr");
- exit (EX_ERR);
- }
- to.c_lflag |= ICANON;
- to.c_lflag &= ~(ECHO | ISIG);
- to.c_cc[VEOF] = C_EOT;
- if (tcsetattr (fd, TCSAFLUSH | TCSASOFT, &to))
- {
- perror ("tcsetattr");
- exit (EX_ERR);
- }
- }
- char *usage_text[] = {
- "usage: ttyemu [-ah] [-i INFILE] [-o OUTFILE] [-t TIMEOUT] PROGRAM [ARGS...]",
- "ttyemu runs PROGRAM with its first three file descriptors connected to a"
- " terminal",
- "",
- "Options are:",
- "",
- " -a append output to OUTFILE, instead of overwriting it",
- " -i INFILE read input from INFILE",
- " -o OUTFILE write output to OUTFILE",
- " -t TIMEOUT set I/O timeout",
- " -h print this help summary",
- "",
- "Report bugs and suggestions to <[email protected]>.",
- NULL
- };
- static void
- usage (void)
- {
- int i;
-
- for (i = 0; usage_text[i]; i++)
- {
- fputs (usage_text[i], stderr);
- fputc ('\n', stderr);
- }
- }
- int
- main (int argc, char **argv)
- {
- int i;
- int master, slave;
- pid_t pid;
- fd_set rdset, wrset;
- struct buffer ibuf, obuf;
- int in = 0, out = 1;
- char *infile = NULL, *outfile = NULL;
- int outflags = O_TRUNC;
- int maxfd;
- int eot = C_EOT;
- int timeout = 0;
-
- while ((i = getopt (argc, argv, "ai:o:t:h")) != EOF)
- {
- switch (i)
- {
- case 'a':
- outflags &= ~O_TRUNC;
- break;
-
- case 'i':
- infile = optarg;
- break;
-
- case 'o':
- outfile = optarg;
- break;
- case 't':
- timeout = atoi (optarg);
- break;
-
- case 'h':
- usage ();
- return EX_OK;
-
- default:
- return EX_USAGE;
- }
- }
-
- argc -= optind;
- argv += optind;
- if (argc == 0)
- {
- usage ();
- return EX_USAGE;
- }
- if (infile)
- {
- in = open (infile, O_RDONLY);
- if (in == -1)
- {
- perror (infile);
- return EX_ERR;
- }
- }
- if (outfile)
- {
- out = open (outfile, O_RDWR|O_CREAT|outflags, 0666);
- if (out == -1)
- {
- perror (outfile);
- return EX_ERR;
- }
- }
-
- master = posix_openpt (O_RDWR);
- if (master == -1)
- {
- perror ("posix_openpty");
- return EX_ERR;
- }
- if (grantpt (master))
- {
- perror ("grantpt");
- return EX_ERR;
- }
- if (unlockpt (master))
- {
- perror ("unlockpt");
- return EX_ERR;
- }
-
- signal (SIGCHLD, sigchld);
- pid = fork ();
- if (pid == -1)
- {
- perror ("fork");
- return EX_ERR;
- }
- if (pid == 0)
- {
- slave = open (ptsname (master), O_RDWR);
- if (slave < 0)
- {
- perror ("open");
- return EX_ERR;
- }
- noecho (slave);
- for (i = 0; i < 3; i++)
- {
- if (slave != i)
- {
- close (i);
- if (dup (slave) != i)
- {
- perror ("dup");
- _exit (EX_EXEC);
- }
- }
- }
- for (i = sysconf (_SC_OPEN_MAX) - 1; i > 2; --i)
- close (i);
- setsid ();
- #ifdef TIOCSCTTY
- ioctl (0, TIOCSCTTY, 1);
- #endif
- execvp (argv[0], argv);
- perror (argv[0]);
- _exit (EX_EXEC);
- }
- sleep (1);
- bufinit (ibuf, 1);
- bufinit (obuf, 1);
- while (1)
- {
- FD_ZERO (&rdset);
- FD_ZERO (&wrset);
-
- maxfd = 0;
- if (in != -1)
- {
- FD_SET (in, &rdset);
- if (in > maxfd)
- maxfd = in;
- }
- if (master != -1)
- {
- FD_SET (master, &rdset);
- if (!stop)
- FD_SET (master, &wrset);
- if (master > maxfd)
- maxfd = master;
- }
- if (maxfd == 0)
- {
- if (stop)
- break;
- pause ();
- continue;
- }
-
- if (select (maxfd + 1, &rdset, &wrset, NULL, NULL) < 0)
- {
- if (errno == EINTR)
- continue;
- perror ("select");
- return EX_ERR;
- }
- if (timeout)
- {
- time_t now = time (NULL);
- if (now - ibuf.ts > timeout || now - obuf.ts > timeout)
- {
- fprintf (stderr, "ttyemu: I/O timeout\n");
- return EX_ERR;
- }
- }
-
- if (in >= 0)
- {
- if (bufavail (ibuf) && FD_ISSET (in, &rdset))
- bufread (ibuf, in, 0);
- }
- else if (master == -1)
- break;
- if (master >= 0 && FD_ISSET (master, &wrset))
- {
- if (!bufisempty (ibuf))
- bufwrite (ibuf, master);
- else if (in == -1 && eot)
- {
- DEBUG (("sent EOT"));
- if (write (master, &eot, 1) <= 0)
- {
- perror ("write");
- return EX_ERR;
- }
- eot = 0;
- }
- }
- if (master >= 0 && bufavail (obuf) && FD_ISSET (master, &rdset))
- bufread (obuf, master, 1);
- if (bufisempty (obuf))
- bufinit (obuf, 0);
- else
- {
- tr (&obuf);
- bufwrite (obuf, out);
- }
-
- if (bufisempty (ibuf))
- bufinit (ibuf, 0);
- }
- if (WIFEXITED (status))
- return WEXITSTATUS (status);
- if (WIFSIGNALED (status))
- fprintf (stderr, "ttyemu: child process %s failed on signal %d\n",
- argv[0], WTERMSIG (status));
- else
- fprintf (stderr, "ttyemu: child process %s failed\n", argv[0]);
- return EX_EXEC;
- }
|