ttyrex

ttyrec with more options
git clone https://a3nm.net/git/ttyrex/
Log | Files | Refs | README

ttyrec.c (12693B)


      1 /*
      2  * Copyright (c) 1980 Regents of the University of California.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. All advertising materials mentioning features or use of this software
     14  *    must display the following acknowledgement:
     15  *	This product includes software developed by the University of
     16  *	California, Berkeley and its contributors.
     17  * 4. Neither the name of the University nor the names of its contributors
     18  *    may be used to endorse or promote products derived from this software
     19  *    without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
     35  * - added Native Language Support
     36  */
     37 
     38 /* 2000-12-27 Satoru Takabayashi <satoru@namazu.org>
     39  * - modify `script' to create `ttyrec'.
     40  */
     41 
     42 /* 2012-10-03 Antoine Amarilli <a3nm@a3nm.net>
     43  * - add -f and -l
     44  */
     45 
     46 /*
     47  * script
     48  */
     49 #include <sys/types.h>
     50 #include <sys/stat.h>
     51 #include <termios.h>
     52 #include <sys/ioctl.h>
     53 #include <sys/time.h>
     54 #include <sys/file.h>
     55 #include <sys/signal.h>
     56 #include <stdio.h>
     57 #include <time.h>
     58 #include <unistd.h>
     59 #include <string.h>
     60 #include <stdlib.h>
     61 
     62 #if defined(SVR4)
     63 #include <fcntl.h>
     64 #include <stropts.h>
     65 #endif /* SVR4 */
     66 
     67 #include <sys/time.h>
     68 #include "ttyrec.h"
     69 #include "io.h"
     70 
     71 #define HAVE_inet_aton
     72 #define HAVE_scsi_h
     73 #define HAVE_kd_h
     74 
     75 #define _(FOO) FOO
     76 
     77 #ifdef HAVE_openpty
     78 #include <libutil.h>
     79 #endif
     80 
     81 #if defined(SVR4) && !defined(CDEL)
     82 #if defined(_POSIX_VDISABLE)
     83 #define CDEL _POSIX_VDISABLE
     84 #elif defined(CDISABLE)
     85 #define CDEL CDISABLE
     86 #else /* not _POSIX_VISIBLE && not CDISABLE */
     87 #define CDEL 255
     88 #endif /* not _POSIX_VISIBLE && not CDISABLE */
     89 #endif /* SVR4 && ! CDEL */
     90 
     91 void done(void);
     92 void fail(void);
     93 void fixtty(void);
     94 void getmaster(void);
     95 void getslave(void);
     96 void doinput(void);
     97 void dooutput(void);
     98 void doshell(const char*);
     99 void spawnshell();
    100 void dowrites(Header, int, char*);
    101 
    102 char	*shell;
    103 FILE	*fscript;
    104 int	master;
    105 int	slave;
    106 int	child;
    107 int	subchild;
    108 char	*fname;
    109 
    110 struct	termios tt;
    111 struct	winsize win;
    112 int	lb;
    113 int	l;
    114 #if !defined(SVR4)
    115 #ifndef HAVE_openpty
    116 char	line[] = "/dev/ptyXX";
    117 #endif
    118 #endif /* !SVR4 */
    119 int	aflg;
    120 int	uflg;
    121 int	force;
    122 int	limit;
    123 char *command = NULL;
    124 
    125 static void
    126 resize(int dummy) {
    127 	/* transmit window change information to the child */
    128 	(void) ioctl(0, TIOCGWINSZ, (char *)&win);
    129 	(void) ioctl(master, TIOCSWINSZ, (char *)&win);
    130 }
    131 
    132 int
    133 main(argc, argv)
    134 	int argc;
    135 	char *argv[];
    136 {
    137 	struct sigaction sa;
    138 	extern int optind;
    139 	int ch;
    140 	void finish();
    141 	char *getenv();
    142 
    143 	while ((ch = getopt(argc, argv, "auefl:h?")) != EOF)
    144 		switch((char)ch) {
    145 		case 'a':
    146 			aflg++;
    147 			break;
    148 		case 'u':
    149 		        uflg++;
    150 			break;
    151 		case 'e':
    152 			command = strdup(optarg);
    153 			break;
    154 		case 'l':
    155 			limit = atoi(optarg);
    156 			break;
    157 		case 'f':
    158                         force = 1;
    159                         break;
    160 		case 'h':
    161 		case '?':
    162 		default:
    163 			fprintf(stderr, _("usage: ttyrec [-u] [-e command] [-a] [-f] [-l limit] [file]\n"));
    164 			exit(1);
    165 		}
    166 	argc -= optind;
    167 	argv += optind;
    168 
    169 	if (argc > 0)
    170 		fname = argv[0];
    171 	else
    172 		fname = "ttyrecord";
    173 
    174 	shell = getenv("SHELL");
    175 	if (shell == NULL)
    176 		shell = "/bin/sh";
    177 
    178 	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
    179 		if (!force) perror(fname);
    180                 fail();
    181 	}
    182         setbuf(fscript, NULL);
    183 
    184         getmaster();
    185         fixtty();
    186 
    187 	sigemptyset(&sa.sa_mask);
    188 	sa.sa_flags = 0;
    189 	sa.sa_handler = finish;
    190 	sigaction(SIGCHLD, &sa, NULL);
    191         child = fork();
    192         if (child < 0) {
    193                 if (!force) perror("fork");
    194                 fail();
    195         }
    196         if (child == 0) {
    197                 subchild = child = fork();
    198                 if (child < 0) {
    199                         if (!force) perror("fork");
    200                         fail();
    201                 }
    202                 if (child) {
    203                         sa.sa_flags = SA_RESTART;
    204                         sigaction(SIGCHLD, &sa, NULL);
    205                         dooutput();
    206                 } else
    207                         doshell(command);
    208         }
    209         sa.sa_handler = resize;
    210         sa.sa_flags = SA_RESTART;
    211         sigaction(SIGWINCH, &sa, NULL);
    212 	doinput();
    213 
    214 	return 0;
    215 }
    216 
    217 void
    218 doinput()
    219 {
    220 	register int cc;
    221 	char ibuf[BUFSIZ];
    222 
    223 	(void) fclose(fscript);
    224 #ifdef HAVE_openpty
    225 	(void) close(slave);
    226 #endif
    227 	while ((cc = read(0, ibuf, BUFSIZ)) > 0)
    228 		(void) write(master, ibuf, cc);
    229         usleep(10); // TODO ugly way to avoid a race condition
    230 	done();
    231 }
    232 
    233 #include <sys/wait.h>
    234 
    235 void
    236 finish()
    237 {
    238 #if defined(SVR4)
    239 	int status;
    240 #else /* !SVR4 */
    241 	union wait status;
    242 #endif /* !SVR4 */
    243 	register int pid;
    244 
    245 	while ((pid = wait3((int *)&status, WNOHANG, 0)) > 0)
    246 		if (pid == child)
    247 			break;
    248 }
    249 
    250 struct linebuf {
    251     char str[BUFSIZ + 1]; /* + 1 for an additional NULL character.*/
    252     int len;
    253 };
    254 
    255 
    256 void
    257 check_line (const char *line)
    258 {
    259     static int uuencode_mode = 0;
    260     static FILE *uudecode;
    261 
    262     if (uuencode_mode == 1) {
    263 	fprintf(uudecode, "%s", line);
    264 	if (strcmp(line, "end\n") == 0) {
    265 	    pclose(uudecode);
    266 	    uuencode_mode = 0;
    267 	}
    268     } else {
    269 	int dummy; char dummy2[BUFSIZ];
    270 	if (sscanf(line, "begin %o %s", &dummy, dummy2) == 2) {
    271 	    /* 
    272 	     * uuencode line found! 
    273 	     */
    274 	    uudecode = popen("uudecode", "w");
    275 	    fprintf(uudecode, "%s", line);
    276 	    uuencode_mode = 1;
    277 	}
    278     }
    279 }
    280 
    281 static void
    282 uu_check_output(const char *str, int len)
    283 {
    284     static struct linebuf lbuf = {"", 0};
    285     int i;
    286 
    287     for (i = 0; i < len; i++) {
    288 	if (lbuf.len < BUFSIZ) {
    289 	    lbuf.str[lbuf.len] = str[i];
    290 	    if (lbuf.str[lbuf.len] == '\r') {
    291 		lbuf.str[lbuf.len] = '\n';
    292 	    }
    293 	    lbuf.len++;
    294 	    if (lbuf.str[lbuf.len - 1] == '\n') {
    295 		if (lbuf.len > 1) { /* skip a blank line. */
    296 		    lbuf.str[lbuf.len] = '\0';
    297 		    check_line(lbuf.str);
    298 		}
    299 		lbuf.len = 0;
    300 	    }
    301 	} else {/* buffer overflow */
    302 	    lbuf.len = 0;
    303 	}
    304     }
    305 }
    306 
    307 static int
    308 check_output(char *str, int len)
    309 {
    310     char *p;
    311 
    312     /* If we see query string, remove it */
    313     /* ESC [ > 0 c : Send Device Attributes */
    314     if (len >= 5 && (p = strstr(str, "\e[>0c")) != NULL) {
    315 	if (len == 5)
    316 	    return 0;
    317 	memmove(p, p+5, len-5+1-(p-str));
    318 	return len-5;
    319     }
    320 
    321     return len;
    322 }
    323 
    324 void
    325 dowrites(Header h, int cc, char *obuf) {
    326 		if ((cc = check_output(obuf, cc))) {
    327 			(void) write_header(fscript, &h);
    328 			(void) fwrite(obuf, 1, cc, fscript);
    329 		}
    330 }
    331 
    332 void
    333 dooutput()
    334 {
    335 	int cc;
    336         int lcc = 0;
    337 	char obuf[BUFSIZ];
    338 	char lobuf[BUFSIZ];
    339         time_t lastsec = 0;
    340         int readsec = 0;
    341         Header h;
    342         Header lh;
    343 
    344 	setbuf(stdout, NULL);
    345 	(void) close(0);
    346 #ifdef HAVE_openpty
    347 	(void) close(slave);
    348 #endif
    349 	for (;;) {
    350 
    351 		cc = read(master, obuf, BUFSIZ);
    352 		if (cc <= 0) {
    353                         if (lcc) {
    354                           // do the last chunk of ignored data
    355                           dowrites(lh, lcc, lobuf);
    356                           lcc = 0;
    357                         }
    358 			break;
    359                 }
    360 		if (uflg)
    361 		    uu_check_output(obuf, cc);
    362 		h.len = cc;
    363 		gettimeofday(&h.tv, NULL);
    364 		(void) write(1, obuf, cc);
    365                 if (limit) {
    366                   if (h.tv.tv_sec == lastsec) {
    367                     if (readsec >= limit) {
    368                       // TODO: avoid this by cycling between two buffers
    369                       strncpy(lobuf, obuf, BUFSIZ);
    370                       lcc = cc;
    371                       lh = h;
    372                       continue;
    373                     }
    374                     readsec += cc;
    375                   } else {
    376                     if (lcc) {
    377                       // do the last chunk of ignored data
    378                       dowrites(lh, lcc, lobuf);
    379                       lcc = 0;
    380                     }
    381                     lastsec = h.tv.tv_sec;
    382                     readsec = cc;
    383                   }
    384                 }
    385                 dowrites(h, cc, obuf);
    386 	}
    387 	done();
    388 }
    389 
    390 void
    391 doshell(const char* command)
    392 {
    393 	/***
    394 	int t;
    395 
    396 	t = open(_PATH_TTY, O_RDWR);
    397 	if (t >= 0) {
    398 		(void) ioctl(t, TIOCNOTTY, (char *)0);
    399 		(void) close(t);
    400 	}
    401 	***/
    402 	getslave();
    403 	(void) close(master);
    404 	(void) fclose(fscript);
    405 	(void) dup2(slave, 0);
    406 	(void) dup2(slave, 1);
    407 	(void) dup2(slave, 2);
    408 	(void) close(slave);
    409 
    410         spawnshell();
    411 	perror(shell);
    412 	fail();
    413 }
    414 
    415 void
    416 fixtty()
    417 {
    418 	struct termios rtt;
    419 
    420 	rtt = tt;
    421 #if defined(SVR4)
    422 #if !defined(XCASE)
    423 #define XCASE 0
    424 #endif
    425 	rtt.c_iflag = 0;
    426 	rtt.c_lflag &= ~(ISIG|ICANON|XCASE|ECHO|ECHOE|ECHOK|ECHONL);
    427 	rtt.c_oflag = OPOST;
    428 	rtt.c_cc[VINTR] = CDEL;
    429 	rtt.c_cc[VQUIT] = CDEL;
    430 	rtt.c_cc[VERASE] = CDEL;
    431 	rtt.c_cc[VKILL] = CDEL;
    432 	rtt.c_cc[VEOF] = 1;
    433 	rtt.c_cc[VEOL] = 0;
    434 #else /* !SVR4 */
    435 	cfmakeraw(&rtt);
    436 	rtt.c_lflag &= ~ECHO;
    437 #endif /* !SVR4 */
    438 	(void) tcsetattr(0, TCSAFLUSH, &rtt);
    439 }
    440 
    441 void
    442 fail()
    443 {
    444         if (force) {
    445                 spawnshell();
    446                 perror(shell);
    447         }
    448 	(void) kill(0, SIGTERM);
    449 	done();
    450 }
    451 
    452 void
    453 done()
    454 {
    455 	if (subchild) {
    456 		(void) fclose(fscript);
    457 		(void) close(master);
    458 	} else {
    459 		(void) tcsetattr(0, TCSAFLUSH, &tt);
    460 	}
    461 	exit(0);
    462 }
    463 
    464 void
    465 getmaster()
    466 {
    467 #if defined(SVR4)
    468 	(void) tcgetattr(0, &tt);
    469 	(void) ioctl(0, TIOCGWINSZ, (char *)&win);
    470 	if ((master = open("/dev/ptmx", O_RDWR)) < 0) {
    471 		if (!force) perror("open(\"/dev/ptmx\", O_RDWR)");
    472 		fail();
    473 	}
    474 #else /* !SVR4 */
    475 #ifdef HAVE_openpty
    476 	(void) tcgetattr(0, &tt);
    477 	(void) ioctl(0, TIOCGWINSZ, (char *)&win);
    478 	if (openpty(&master, &slave, NULL, &tt, &win) < 0) {
    479 		fprintf(stderr, _("openpty failed\n"));
    480 		fail();
    481 	}
    482 #else
    483 #ifdef HAVE_getpt
    484 	if ((master = getpt()) < 0) {
    485 		if (!force) perror("getpt()");
    486 		fail();
    487 	}
    488 #else
    489 	char *pty, *bank, *cp;
    490 	struct stat stb;
    491 
    492 	pty = &line[strlen("/dev/ptyp")];
    493 	for (bank = "pqrs"; *bank; bank++) {
    494 		line[strlen("/dev/pty")] = *bank;
    495 		*pty = '0';
    496 		if (stat(line, &stb) < 0)
    497 			break;
    498 		for (cp = "0123456789abcdef"; *cp; cp++) {
    499 			*pty = *cp;
    500 			master = open(line, O_RDWR);
    501 			if (master >= 0) {
    502 				char *tp = &line[strlen("/dev/")];
    503 				int ok;
    504 
    505 				/* verify slave side is usable */
    506 				*tp = 't';
    507 				ok = access(line, R_OK|W_OK) == 0;
    508 				*tp = 'p';
    509 				if (ok) {
    510 					(void) tcgetattr(0, &tt);
    511 				    	(void) ioctl(0, TIOCGWINSZ, 
    512 						(char *)&win);
    513 					return;
    514 				}
    515 				(void) close(master);
    516 			}
    517 		}
    518 	}
    519 	fprintf(stderr, _("Out of pty's\n"));
    520 	fail();
    521 #endif /* not HAVE_getpt */
    522 #endif /* not HAVE_openpty */
    523 #endif /* !SVR4 */
    524 }
    525 
    526 void
    527 getslave()
    528 {
    529 #if defined(SVR4)
    530 	(void) setsid();
    531 	grantpt( master);
    532 	unlockpt(master);
    533 	if ((slave = open((const char *)ptsname(master), O_RDWR)) < 0) {
    534 		if (!force) perror("open(fd, O_RDWR)");
    535 		fail();
    536 	}
    537 	if (isastream(slave)) {
    538 		if (ioctl(slave, I_PUSH, "ptem") < 0) {
    539 			if (!force) perror("ioctl(fd, I_PUSH, ptem)");
    540 			fail();
    541 		}
    542 		if (ioctl(slave, I_PUSH, "ldterm") < 0) {
    543 			if (!force) perror("ioctl(fd, I_PUSH, ldterm)");
    544 			fail();
    545 		}
    546 #ifndef _HPUX_SOURCE
    547 		if (ioctl(slave, I_PUSH, "ttcompat") < 0) {
    548 			if (!force) perror("ioctl(fd, I_PUSH, ttcompat)");
    549 			fail();
    550 		}
    551 #endif
    552 	}
    553 	(void) tcsetattr(slave, TCSAFLUSH, &tt);
    554 	(void) ioctl(slave, TIOCSWINSZ, (char *)&win);
    555 	(void) ioctl(slave, TIOCSCTTY, 0);
    556 #else /* !SVR4 */
    557 #ifndef HAVE_openpty
    558 	line[strlen("/dev/")] = 't';
    559 	slave = open(line, O_RDWR);
    560 	if (slave < 0) {
    561 		if (!force) perror(line);
    562 		fail();
    563 	}
    564 	(void) tcsetattr(slave, TCSAFLUSH, &tt);
    565 	(void) ioctl(slave, TIOCSWINSZ, (char *)&win);
    566 #endif
    567 	(void) setsid();
    568 	(void) ioctl(slave, TIOCSCTTY, 0);
    569 #endif /* SVR4 */
    570 }
    571 
    572 void
    573 spawnshell()
    574 {
    575         force = 0;
    576 
    577 	if (!command) {
    578 		execl(shell, strrchr(shell, '/') + 1, "-i", NULL);
    579 	} else {
    580 		execl(shell, strrchr(shell, '/') + 1, "-c", command, NULL);
    581 	}
    582 }