diff -Naur ttyrec-1.0.8-orig/io.c ttyrex-ttyrex/io.c --- ttyrec-1.0.8-orig/io.c 2006-06-11 17:52:50.000000000 +0200 +++ ttyrex-ttyrex/io.c 2012-10-03 01:12:39.000000000 +0200 @@ -33,6 +33,8 @@ #include <assert.h> #include <errno.h> +#include <endian.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -40,50 +42,28 @@ #include "ttyrec.h" -#define SWAP_ENDIAN(val) ((unsigned int) ( \ - (((unsigned int) (val) & (unsigned int) 0x000000ffU) << 24) | \ - (((unsigned int) (val) & (unsigned int) 0x0000ff00U) << 8) | \ - (((unsigned int) (val) & (unsigned int) 0x00ff0000U) >> 8) | \ - (((unsigned int) (val) & (unsigned int) 0xff000000U) >> 24))) - -static int -is_little_endian () -{ - static int retval = -1; - - if (retval == -1) { - int n = 1; - char *p = (char *)&n; - char x[] = {1, 0, 0, 0}; - - assert(sizeof(int) == 4); - - if (memcmp(p, x, 4) == 0) { - retval = 1; - } else { - retval = 0; - } - } - - return retval; -} - -static int -convert_to_little_endian (int x) -{ - if (is_little_endian()) { - return x; - } else { - return SWAP_ENDIAN(x); - } +#define SWAP_ENDIAN(val) ((uint32_t) ( \ + (((uint32_t) (val) & (uint32_t) 0x000000ffU) << 24) | \ + (((uint32_t) (val) & (uint32_t) 0x0000ff00U) << 8) | \ + (((uint32_t) (val) & (uint32_t) 0x00ff0000U) >> 8) | \ + (((uint32_t) (val) & (uint32_t) 0xff000000U) >> 24))) + +static uint32_t +convert_to_little_endian (uint32_t x) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + return x; +#else + return SWAP_ENDIAN(x); +#endif } int read_header (FILE *fp, Header *h) { - int buf[3]; + uint32_t buf[3]; - if (fread(buf, sizeof(int), 3, fp) == 0) { + if (fread(buf, sizeof(uint32_t), 3, fp) == 0) { return 0; } @@ -97,13 +77,13 @@ int write_header (FILE *fp, Header *h) { - int buf[3]; + uint32_t buf[3]; buf[0] = convert_to_little_endian(h->tv.tv_sec); buf[1] = convert_to_little_endian(h->tv.tv_usec); buf[2] = convert_to_little_endian(h->len); - if (fwrite(buf, sizeof(int), 3, fp) == 0) { + if (fwrite(buf, sizeof(uint32_t), 3, fp) == 0) { return 0; } @@ -158,4 +138,5 @@ fprintf(stderr, "%s: fdopen failed: %s\n", progname, strerror(errno)); exit(EXIT_FAILURE); } + return fp; } diff -Naur ttyrec-1.0.8-orig/io.h ttyrex-ttyrex/io.h --- ttyrec-1.0.8-orig/io.h 2006-06-11 17:52:50.000000000 +0200 +++ ttyrex-ttyrex/io.h 2012-10-03 01:12:39.000000000 +0200 @@ -9,5 +9,6 @@ int edup (int oldfd); int edup2 (int oldfd, int newfd); FILE* efdopen (int fd, const char *mode); +void set_progname (const char *name); #endif diff -Naur ttyrec-1.0.8-orig/Makefile ttyrex-ttyrex/Makefile --- ttyrec-1.0.8-orig/Makefile 2006-06-11 17:52:50.000000000 +0200 +++ ttyrex-ttyrex/Makefile 2012-10-03 01:12:39.000000000 +0200 @@ -1,7 +1,10 @@ CC = gcc -CFLAGS = -O2 +DEFS= -DSVR4 -D_GNU_SOURCE +CFLAGS = -O2 -g -fomit-frame-pointer -Wall $(DEFS) VERSION = 1.0.8 +DESTDIR= + TARGET = ttyrec ttyplay ttytime DIST = ttyrec.c ttyplay.c ttyrec.h io.c io.h ttytime.c\ @@ -18,6 +21,9 @@ ttytime: ttytime.o io.o $(CC) $(CFLAGS) -o ttytime ttytime.o io.o +install: + install ttyrec ttyplay ttytime $(DESTDIR)/usr/bin + clean: rm -f *.o $(TARGET) ttyrecord *~ diff -Naur ttyrec-1.0.8-orig/ttyplay.1 ttyrex-ttyrex/ttyplay.1 --- ttyrec-1.0.8-orig/ttyplay.1 2006-06-11 17:52:50.000000000 +0200 +++ ttyrex-ttyrex/ttyplay.1 2012-10-03 01:12:39.000000000 +0200 @@ -38,6 +38,9 @@ .BI \- " or " s halve the speed of playback. .TP +.BI 0 +set playback speed to 0, pausing playback. +.TP .BI 1 set playback to speed 1.0 again. diff -Naur ttyrec-1.0.8-orig/ttyplay.c ttyrex-ttyrex/ttyplay.c --- ttyrec-1.0.8-orig/ttyplay.c 2006-06-11 17:52:50.000000000 +0200 +++ ttyrex-ttyrex/ttyplay.c 2012-10-03 01:12:39.000000000 +0200 @@ -83,16 +83,21 @@ static struct timeval drift = {0, 0}; struct timeval start; struct timeval diff = timeval_diff(prev, cur); + struct timeval *diffp = &diff; fd_set readfs; gettimeofday(&start, NULL); - assert(speed != 0); - diff = timeval_diff(drift, timeval_div(diff, speed)); + if (speed == 0.0) + diffp = NULL; + else + diff = timeval_diff(drift, timeval_div(diff, speed)); + if (diff.tv_sec < 0) { diff.tv_sec = diff.tv_usec = 0; } + FD_ZERO(&readfs); FD_SET(STDIN_FILENO, &readfs); /* * We use select() for sleeping with subsecond precision. @@ -101,9 +106,10 @@ * Save "diff" since select(2) may overwrite it to {0, 0}. */ struct timeval orig_diff = diff; - select(1, &readfs, NULL, NULL, &diff); + int r; + r = select(1, &readfs, NULL, NULL, diffp); /* skip if a user hits any key */ diff = orig_diff; /* Restore the original diff value. */ - if (FD_ISSET(0, &readfs)) { /* a user hits a character? */ + if (r > 0 && FD_ISSET(0, &readfs)) { /* a user hits a character? */ char c; read(STDIN_FILENO, &c, 1); /* drain the character */ switch (c) { @@ -118,6 +124,9 @@ case '1': speed = 1.0; break; + case '0': + speed = 0.0; + break; } drift.tv_sec = drift.tv_usec = 0; } else { @@ -136,14 +145,21 @@ ttynowait (struct timeval prev, struct timeval cur, double speed) { /* do nothing */ - return 0; /* Speed isn't important. */ + return 1.0; /* Speed isn't important. */ } int ttyread (FILE *fp, Header *h, char **buf) { + fpos_t pos; + int can_seek=0; + if (fgetpos(fp, &pos) == 0) { + can_seek=1; + } + clearerr(fp); + if (read_header(fp, h) == 0) { - return 0; + goto err; } *buf = malloc(h->len); @@ -152,9 +168,21 @@ } if (fread(*buf, 1, h->len, fp) == 0) { - perror("fread"); + goto err; } return 1; + +err: + if (ferror(fp)) { + perror("fread"); + } + else { + /* Short read. Seek back to before header, to set up for retry. */ + if (can_seek) { + fsetpos(fp, &pos); + } + } + return 0; } int @@ -202,7 +230,9 @@ } if (!first_time) { - speed = wait_func(prev, h.tv, speed); + do { + speed = wait_func(prev, h.tv, speed); + } while (speed == 0.0); } first_time = 0; @@ -252,7 +282,6 @@ FILE * input_from_stdin (void) { - FILE *fp; int fd = edup(STDIN_FILENO); edup2(STDOUT_FILENO, STDIN_FILENO); return efdopen(fd, "r"); diff -Naur ttyrec-1.0.8-orig/ttyrec.1 ttyrex-ttyrex/ttyrec.1 --- ttyrec-1.0.8-orig/ttyrec.1 2006-06-11 17:52:50.000000000 +0200 +++ ttyrex-ttyrex/ttyrec.1 2012-10-03 01:12:39.000000000 +0200 @@ -7,7 +7,7 @@ .SH SYNOPSIS .br .B ttyrec -.I "[\-a][\-u] [file]" +.I "[\-a] [\-f] [\-u] [\-e command] [\-l limit] [file]" .br .SH DESCRIPTION .B Ttyrec @@ -36,6 +36,9 @@ .IR ttyrecord , rather than overwriting it. .TP +.B \-f +Instead of failing, try hard to just run a shell without recording. +.TP .B \-u With this option, .B ttyrec @@ -54,8 +57,9 @@ Invoke .I command when ttyrec starts. - - +.TP +.BI \-l " limit" +Only log the first limit bytes of each second. .SH ENVIRONMENT .TP .I SHELL diff -Naur ttyrec-1.0.8-orig/ttyrec.c ttyrex-ttyrex/ttyrec.c --- ttyrec-1.0.8-orig/ttyrec.c 2006-06-11 17:52:50.000000000 +0200 +++ ttyrex-ttyrex/ttyrec.c 2012-10-03 01:12:39.000000000 +0200 @@ -39,6 +39,10 @@ * - modify `script' to create `ttyrec'. */ +/* 2012-10-03 Antoine Amarilli <a3nm@a3nm.net> + * - add -f and -l + */ + /* * script */ @@ -92,6 +96,8 @@ void doinput(void); void dooutput(void); void doshell(const char*); +void spawnshell(); +void dowrites(Header, int, char*); char *shell; FILE *fscript; @@ -112,19 +118,29 @@ #endif /* !SVR4 */ int aflg; int uflg; +int force; +int limit; +char *command = NULL; + +static void +resize(int dummy) { + /* transmit window change information to the child */ + (void) ioctl(0, TIOCGWINSZ, (char *)&win); + (void) ioctl(master, TIOCSWINSZ, (char *)&win); +} int main(argc, argv) int argc; char *argv[]; { + struct sigaction sa; extern int optind; int ch; void finish(); char *getenv(); - char *command = NULL; - while ((ch = getopt(argc, argv, "aue:h?")) != EOF) + while ((ch = getopt(argc, argv, "auefl:h?")) != EOF) switch((char)ch) { case 'a': aflg++; @@ -135,10 +151,16 @@ case 'e': command = strdup(optarg); break; + case 'l': + limit = atoi(optarg); + break; + case 'f': + force = 1; + break; case 'h': case '?': default: - fprintf(stderr, _("usage: ttyrec [-u] [-e command] [-a] [file]\n")); + fprintf(stderr, _("usage: ttyrec [-u] [-e command] [-a] [-f] [-l limit] [file]\n")); exit(1); } argc -= optind; @@ -148,36 +170,45 @@ fname = argv[0]; else fname = "ttyrecord"; - if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) { - perror(fname); - fail(); - } - setbuf(fscript, NULL); shell = getenv("SHELL"); if (shell == NULL) shell = "/bin/sh"; - getmaster(); - fixtty(); - - (void) signal(SIGCHLD, finish); - child = fork(); - if (child < 0) { - perror("fork"); - fail(); - } - if (child == 0) { - subchild = child = fork(); - if (child < 0) { - perror("fork"); - fail(); - } - if (child) - dooutput(); - else - doshell(command); + if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) { + if (!force) perror(fname); + fail(); } + setbuf(fscript, NULL); + + getmaster(); + fixtty(); + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = finish; + sigaction(SIGCHLD, &sa, NULL); + child = fork(); + if (child < 0) { + if (!force) perror("fork"); + fail(); + } + if (child == 0) { + subchild = child = fork(); + if (child < 0) { + if (!force) perror("fork"); + fail(); + } + if (child) { + sa.sa_flags = SA_RESTART; + sigaction(SIGCHLD, &sa, NULL); + dooutput(); + } else + doshell(command); + } + sa.sa_handler = resize; + sa.sa_flags = SA_RESTART; + sigaction(SIGWINCH, &sa, NULL); doinput(); return 0; @@ -209,14 +240,10 @@ union wait status; #endif /* !SVR4 */ register int pid; - register int die = 0; while ((pid = wait3((int *)&status, WNOHANG, 0)) > 0) if (pid == child) - die = 1; - - if (die) - done(); + break; } struct linebuf { @@ -250,8 +277,8 @@ } } -void -check_output(const char *str, int len) +static void +uu_check_output(const char *str, int len) { static struct linebuf lbuf = {"", 0}; int i; @@ -276,11 +303,42 @@ } } +static int +check_output(char *str, int len) +{ + char *p; + + /* If we see query string, remove it */ + /* ESC [ > 0 c : Send Device Attributes */ + if (len >= 5 && (p = strstr(str, "\e[>0c")) != NULL) { + if (len == 5) + return 0; + memmove(p, p+5, len-5+1-(p-str)); + return len-5; + } + + return len; +} + +void +dowrites(Header h, int cc, char *obuf) { + if ((cc = check_output(obuf, cc))) { + (void) write_header(fscript, &h); + (void) fwrite(obuf, 1, cc, fscript); + } +} + void dooutput() { int cc; + int lcc = 0; char obuf[BUFSIZ]; + char lobuf[BUFSIZ]; + time_t lastsec = 0; + int readsec = 0; + Header h; + Header lh; setbuf(stdout, NULL); (void) close(0); @@ -288,18 +346,42 @@ (void) close(slave); #endif for (;;) { - Header h; cc = read(master, obuf, BUFSIZ); - if (cc <= 0) + if (cc <= 0) { + if (lcc) { + // do the last chunk of ignored data + dowrites(lh, lcc, lobuf); + lcc = 0; + } break; + } if (uflg) - check_output(obuf, cc); + uu_check_output(obuf, cc); h.len = cc; gettimeofday(&h.tv, NULL); (void) write(1, obuf, cc); - (void) write_header(fscript, &h); - (void) fwrite(obuf, 1, cc, fscript); + if (limit) { + if (h.tv.tv_sec == lastsec) { + if (readsec >= limit) { + // TODO: avoid this by cycling between two buffers + strncpy(lobuf, obuf, BUFSIZ); + lcc = cc; + lh = h; + continue; + } + readsec += cc; + } else { + if (lcc) { + // do the last chunk of ignored data + dowrites(lh, lcc, lobuf); + lcc = 0; + } + lastsec = h.tv.tv_sec; + readsec = cc; + } + } + dowrites(h, cc, obuf); } done(); } @@ -324,11 +406,7 @@ (void) dup2(slave, 2); (void) close(slave); - if (!command) { - execl(shell, strrchr(shell, '/') + 1, "-i", 0); - } else { - execl(shell, strrchr(shell, '/') + 1, "-c", command, 0); - } + spawnshell(); perror(shell); fail(); } @@ -340,6 +418,9 @@ rtt = tt; #if defined(SVR4) +#if !defined(XCASE) +#define XCASE 0 +#endif rtt.c_iflag = 0; rtt.c_lflag &= ~(ISIG|ICANON|XCASE|ECHO|ECHOE|ECHOK|ECHONL); rtt.c_oflag = OPOST; @@ -359,7 +440,10 @@ void fail() { - + if (force) { + spawnshell(); + perror(shell); + } (void) kill(0, SIGTERM); done(); } @@ -383,7 +467,7 @@ (void) tcgetattr(0, &tt); (void) ioctl(0, TIOCGWINSZ, (char *)&win); if ((master = open("/dev/ptmx", O_RDWR)) < 0) { - perror("open(\"/dev/ptmx\", O_RDWR)"); + if (!force) perror("open(\"/dev/ptmx\", O_RDWR)"); fail(); } #else /* !SVR4 */ @@ -397,7 +481,7 @@ #else #ifdef HAVE_getpt if ((master = getpt()) < 0) { - perror("getpt()"); + if (!force) perror("getpt()"); fail(); } #else @@ -446,32 +530,34 @@ grantpt( master); unlockpt(master); if ((slave = open((const char *)ptsname(master), O_RDWR)) < 0) { - perror("open(fd, O_RDWR)"); + if (!force) perror("open(fd, O_RDWR)"); fail(); } if (isastream(slave)) { if (ioctl(slave, I_PUSH, "ptem") < 0) { - perror("ioctl(fd, I_PUSH, ptem)"); + if (!force) perror("ioctl(fd, I_PUSH, ptem)"); fail(); } if (ioctl(slave, I_PUSH, "ldterm") < 0) { - perror("ioctl(fd, I_PUSH, ldterm)"); + if (!force) perror("ioctl(fd, I_PUSH, ldterm)"); fail(); } #ifndef _HPUX_SOURCE if (ioctl(slave, I_PUSH, "ttcompat") < 0) { - perror("ioctl(fd, I_PUSH, ttcompat)"); + if (!force) perror("ioctl(fd, I_PUSH, ttcompat)"); fail(); } #endif - (void) ioctl(0, TIOCGWINSZ, (char *)&win); } + (void) tcsetattr(slave, TCSAFLUSH, &tt); + (void) ioctl(slave, TIOCSWINSZ, (char *)&win); + (void) ioctl(slave, TIOCSCTTY, 0); #else /* !SVR4 */ #ifndef HAVE_openpty line[strlen("/dev/")] = 't'; slave = open(line, O_RDWR); if (slave < 0) { - perror(line); + if (!force) perror(line); fail(); } (void) tcsetattr(slave, TCSAFLUSH, &tt); @@ -481,3 +567,15 @@ (void) ioctl(slave, TIOCSCTTY, 0); #endif /* SVR4 */ } + +void +spawnshell() +{ + force = 0; + + if (!command) { + execl(shell, strrchr(shell, '/') + 1, "-i", NULL); + } else { + execl(shell, strrchr(shell, '/') + 1, "-c", command, NULL); + } +}