ttyrex

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

commit 73b825713a0dd7be02a387423b032f940a5cf15f
Author: Antoine Amarilli <a3nm@a3nm.net>
Date:   Wed,  3 Oct 2012 00:24:13 +0200

initial

Diffstat:
Makefile | 31+++++++++++++++++++++++++++++++
README | 27+++++++++++++++++++++++++++
io.c | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
io.h | 13+++++++++++++
ttyplay.1 | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ttyplay.c | 312+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ttyrec.1 | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ttyrec.c | 483+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ttyrec.h | 12++++++++++++
ttytime.1 | 27+++++++++++++++++++++++++++
ttytime.c | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 1274 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,31 @@ +CC = gcc +CFLAGS = -O2 +VERSION = 1.0.8 + +TARGET = ttyrec ttyplay ttytime + +DIST = ttyrec.c ttyplay.c ttyrec.h io.c io.h ttytime.c\ + README Makefile ttyrec.1 ttyplay.1 ttytime.1 + +all: $(TARGET) + +ttyrec: ttyrec.o io.o + $(CC) $(CFLAGS) -o ttyrec ttyrec.o io.o + +ttyplay: ttyplay.o io.o + $(CC) $(CFLAGS) -o ttyplay ttyplay.o io.o + +ttytime: ttytime.o io.o + $(CC) $(CFLAGS) -o ttytime ttytime.o io.o + +clean: + rm -f *.o $(TARGET) ttyrecord *~ + +dist: + rm -rf ttyrec-$(VERSION) + rm -f ttyrec-$(VERSION).tar.gz + + mkdir ttyrec-$(VERSION) + cp $(DIST) ttyrec-$(VERSION) + tar zcf ttyrec-$(VERSION).tar.gz ttyrec-$(VERSION) + rm -rf ttyrec-$(VERSION) diff --git a/README b/README @@ -0,0 +1,27 @@ +ttyrec is a tty recorder. ttyplay is a tty player. + +Installation: + + % make + +or if your system is SVR4 system (Solaris etc.), + + % make CFLAGS=-DSVR4 + +or if your system supports getpt(3), + + % make CFLAGS=-DHAVE_getpt + +HAVE_getpt is required if your linux system uses devfs. + + +Usage: + + % ttyrec + (In the excuted shell, do whatever you want and exit) + + % ttyplay ttyrecord + +Have fun! + +-- Satoru Takabayashi <satoru@namazu.org> diff --git a/io.c b/io.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2000 Satoru Takabayashi <satoru@namazu.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#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); + } +} + +int +read_header (FILE *fp, Header *h) +{ + int buf[3]; + + if (fread(buf, sizeof(int), 3, fp) == 0) { + return 0; + } + + h->tv.tv_sec = convert_to_little_endian(buf[0]); + h->tv.tv_usec = convert_to_little_endian(buf[1]); + h->len = convert_to_little_endian(buf[2]); + + return 1; +} + +int +write_header (FILE *fp, Header *h) +{ + int 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) { + return 0; + } + + return 1; +} + +static char *progname = ""; +void +set_progname (const char *name) +{ + progname = strdup(name); +} + +FILE * +efopen (const char *path, const char *mode) +{ + FILE *fp = fopen(path, mode); + if (fp == NULL) { + fprintf(stderr, "%s: %s: %s\n", progname, path, strerror(errno)); + exit(EXIT_FAILURE); + } + return fp; +} + +int +edup (int oldfd) +{ + int fd = dup(oldfd); + if (fd == -1) { + fprintf(stderr, "%s: dup failed: %s\n", progname, strerror(errno)); + exit(EXIT_FAILURE); + } + return fd; +} + +int +edup2 (int oldfd, int newfd) +{ + int fd = dup2(oldfd, newfd); + if (fd == -1) { + fprintf(stderr, "%s: dup2 failed: %s\n", progname, strerror(errno)); + exit(EXIT_FAILURE); + } + return fd; +} + +FILE * +efdopen (int fd, const char *mode) +{ + FILE *fp = fdopen(fd, mode); + if (fp == NULL) { + fprintf(stderr, "%s: fdopen failed: %s\n", progname, strerror(errno)); + exit(EXIT_FAILURE); + } +} diff --git a/io.h b/io.h @@ -0,0 +1,13 @@ +#ifndef __TTYREC_IO_H__ +#define __TTYREC_IO_H__ + +#include "ttyrec.h" + +int read_header (FILE *fp, Header *h); +int write_header (FILE *fp, Header *h); +FILE* efopen (const char *path, const char *mode); +int edup (int oldfd); +int edup2 (int oldfd, int newfd); +FILE* efdopen (int fd, const char *mode); + +#endif diff --git a/ttyplay.1 b/ttyplay.1 @@ -0,0 +1,62 @@ +.\" +.\" This manual page is written by NAKANO Takeo <nakano@webmasters.gr.jp> +.\" +.TH TTYPLAY 1 +.SH NAME +ttyplay \- player of the tty session recorded by ttyrec +.SH SYNOPSIS +.br +.B ttyplay +.I [\-s SPEED] [\-n] [\-p] file +.br +.SH DESCRIPTION +.B Ttyplay +plays the tty session in +.IR file , +which was recorded previously by +.BR ttyrec (1). +.PP +When +.B \-p +option is given, +.B ttyplay +output the +.I file +as it grows. +It means that you can see the "live" shell session +running by another user. +.PP +If you hit any key during playback, it will go right to the next +character typed. This is handy when examining sessions where a user +spends a lot of time at a prompt. +.PP +Additionally, there are some special keys defined: +.TP +.BI + " or " f + double the speed of playback. +.TP +.BI \- " or " s + halve the speed of playback. +.TP +.BI 1 +set playback to speed 1.0 again. + +.SH OPTIONS +.TP +.BI \-s " SPEED" +multiple the playing speed by +.I SPEED +(default is 1). +.TP +.B \-n +no wait mode. +Ignore the timing information in +.IR file . +.TP +.B \-p +peek another person's tty session. +.SH "SEE ALSO" +.BR script (1), +.BR ttyrec (1), +.BR ttytime (1) + diff --git a/ttyplay.c b/ttyplay.c @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2000 Satoru Takabayashi <satoru@namazu.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <unistd.h> +#include <termios.h> +#include <sys/time.h> +#include <string.h> + +#include "ttyrec.h" +#include "io.h" + +typedef double (*WaitFunc) (struct timeval prev, + struct timeval cur, + double speed); +typedef int (*ReadFunc) (FILE *fp, Header *h, char **buf); +typedef void (*WriteFunc) (char *buf, int len); +typedef void (*ProcessFunc) (FILE *fp, double speed, + ReadFunc read_func, WaitFunc wait_func); + +struct timeval +timeval_diff (struct timeval tv1, struct timeval tv2) +{ + struct timeval diff; + + diff.tv_sec = tv2.tv_sec - tv1.tv_sec; + diff.tv_usec = tv2.tv_usec - tv1.tv_usec; + if (diff.tv_usec < 0) { + diff.tv_sec--; + diff.tv_usec += 1000000; + } + + return diff; +} + +struct timeval +timeval_div (struct timeval tv1, double n) +{ + double x = ((double)tv1.tv_sec + (double)tv1.tv_usec / 1000000.0) / n; + struct timeval div; + + div.tv_sec = (int)x; + div.tv_usec = (x - (int)x) * 1000000; + + return div; +} + +double +ttywait (struct timeval prev, struct timeval cur, double speed) +{ + static struct timeval drift = {0, 0}; + struct timeval start; + struct timeval diff = timeval_diff(prev, cur); + fd_set readfs; + + gettimeofday(&start, NULL); + + assert(speed != 0); + diff = timeval_diff(drift, timeval_div(diff, speed)); + if (diff.tv_sec < 0) { + diff.tv_sec = diff.tv_usec = 0; + } + + FD_SET(STDIN_FILENO, &readfs); + /* + * We use select() for sleeping with subsecond precision. + * select() is also used to wait user's input from a keyboard. + * + * Save "diff" since select(2) may overwrite it to {0, 0}. + */ + struct timeval orig_diff = diff; + select(1, &readfs, NULL, NULL, &diff); + diff = orig_diff; /* Restore the original diff value. */ + if (FD_ISSET(0, &readfs)) { /* a user hits a character? */ + char c; + read(STDIN_FILENO, &c, 1); /* drain the character */ + switch (c) { + case '+': + case 'f': + speed *= 2; + break; + case '-': + case 's': + speed /= 2; + break; + case '1': + speed = 1.0; + break; + } + drift.tv_sec = drift.tv_usec = 0; + } else { + struct timeval stop; + gettimeofday(&stop, NULL); + /* Hack to accumulate the drift */ + if (diff.tv_sec == 0 && diff.tv_usec == 0) { + diff = timeval_diff(drift, diff); // diff = 0 - drift. + } + drift = timeval_diff(diff, timeval_diff(start, stop)); + } + return speed; +} + +double +ttynowait (struct timeval prev, struct timeval cur, double speed) +{ + /* do nothing */ + return 0; /* Speed isn't important. */ +} + +int +ttyread (FILE *fp, Header *h, char **buf) +{ + if (read_header(fp, h) == 0) { + return 0; + } + + *buf = malloc(h->len); + if (*buf == NULL) { + perror("malloc"); + } + + if (fread(*buf, 1, h->len, fp) == 0) { + perror("fread"); + } + return 1; +} + +int +ttypread (FILE *fp, Header *h, char **buf) +{ + /* + * Read persistently just like tail -f. + */ + while (ttyread(fp, h, buf) == 0) { + struct timeval w = {0, 250000}; + select(0, NULL, NULL, NULL, &w); + clearerr(fp); + } + return 1; +} + +void +ttywrite (char *buf, int len) +{ + fwrite(buf, 1, len, stdout); +} + +void +ttynowrite (char *buf, int len) +{ + /* do nothing */ +} + +void +ttyplay (FILE *fp, double speed, ReadFunc read_func, + WriteFunc write_func, WaitFunc wait_func) +{ + int first_time = 1; + struct timeval prev; + + setbuf(stdout, NULL); + setbuf(fp, NULL); + + while (1) { + char *buf; + Header h; + + if (read_func(fp, &h, &buf) == 0) { + break; + } + + if (!first_time) { + speed = wait_func(prev, h.tv, speed); + } + first_time = 0; + + write_func(buf, h.len); + prev = h.tv; + free(buf); + } +} + +void +ttyskipall (FILE *fp) +{ + /* + * Skip all records. + */ + ttyplay(fp, 0, ttyread, ttynowrite, ttynowait); +} + +void ttyplayback (FILE *fp, double speed, + ReadFunc read_func, WaitFunc wait_func) +{ + ttyplay(fp, speed, ttyread, ttywrite, wait_func); +} + +void ttypeek (FILE *fp, double speed, + ReadFunc read_func, WaitFunc wait_func) +{ + ttyskipall(fp); + ttyplay(fp, speed, ttypread, ttywrite, ttynowait); +} + + +void +usage (void) +{ + printf("Usage: ttyplay [OPTION] [FILE]\n"); + printf(" -s SPEED Set speed to SPEED [1.0]\n"); + printf(" -n No wait mode\n"); + printf(" -p Peek another person's ttyrecord\n"); + exit(EXIT_FAILURE); +} + +/* + * We do some tricks so that select(2) properly works on + * STDIN_FILENO in ttywait(). + */ +FILE * +input_from_stdin (void) +{ + FILE *fp; + int fd = edup(STDIN_FILENO); + edup2(STDOUT_FILENO, STDIN_FILENO); + return efdopen(fd, "r"); +} + +int +main (int argc, char **argv) +{ + double speed = 1.0; + ReadFunc read_func = ttyread; + WaitFunc wait_func = ttywait; + ProcessFunc process = ttyplayback; + FILE *input = NULL; + struct termios old, new; + + set_progname(argv[0]); + while (1) { + int ch = getopt(argc, argv, "s:np"); + if (ch == EOF) { + break; + } + switch (ch) { + case 's': + if (optarg == NULL) { + perror("-s option requires an argument"); + exit(EXIT_FAILURE); + } + sscanf(optarg, "%lf", &speed); + break; + case 'n': + wait_func = ttynowait; + break; + case 'p': + process = ttypeek; + break; + default: + usage(); + } + } + + if (optind < argc) { + input = efopen(argv[optind], "r"); + } else { + input = input_from_stdin(); + } + assert(input != NULL); + + tcgetattr(0, &old); /* Get current terminal state */ + new = old; /* Make a copy */ + new.c_lflag &= ~(ICANON | ECHO | ECHONL); /* unbuffered, no echo */ + tcsetattr(0, TCSANOW, &new); /* Make it current */ + + process(input, speed, read_func, wait_func); + tcsetattr(0, TCSANOW, &old); /* Return terminal state */ + + return 0; +} diff --git a/ttyrec.1 b/ttyrec.1 @@ -0,0 +1,76 @@ +.\" +.\" This manual page is written by NAKANO Takeo <nakano@webmasters.gr.jp> +.\" +.TH TTYREC 1 +.SH NAME +ttyrec \- a tty recorder +.SH SYNOPSIS +.br +.B ttyrec +.I "[\-a][\-u] [file]" +.br +.SH DESCRIPTION +.B Ttyrec +is a tty recorder. +It is a derivative of +.BR script (1) +command for recording timing information with microsecond accuracy as well. +It can record emacs -nw, vi, lynx, or any programs running on tty. +.PP +.B Ttyrec +invokes a shell and records the session until the shell exits. +Recorded data can be played back with +.BR ttyplay (1). +If the argument +.I file +is given, the session will be recorded in that file. +Otherwise, +.I ttyrecord +is used as default. +.SH OPTIONS +.TP +.B \-a +Append the output to +.I file +or +.IR ttyrecord , +rather than overwriting it. +.TP +.B \-u +With this option, +.B ttyrec +automatically calls +.BR uudecode (1) +and saves its output when uuencoded data appear on the session. +It allow you to transfer files from remote host. +You can call +.B ttyrec +with this option, login to the remote host +and invoke +.BR uuencode (1) +on it for the file you want to transfer. +.TP +.BI \-e " command" +Invoke +.I command +when ttyrec starts. + + +.SH ENVIRONMENT +.TP +.I SHELL +If the variable +.I SHELL +exists, the shell forked by +.B ttyrec +will be that shell. +If it's not set, +the Bourne shell is assumed. +(Most shells set this variable automatically). +.SH "SEE ALSO" +.BR script (1), +.BR ttyplay (1), +.BR ttytime (1), +.BR uuencode (1), +.BR uudecode (1) + diff --git a/ttyrec.c b/ttyrec.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org> + * - added Native Language Support + */ + +/* 2000-12-27 Satoru Takabayashi <satoru@namazu.org> + * - modify `script' to create `ttyrec'. + */ + +/* + * script + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/file.h> +#include <sys/signal.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +#if defined(SVR4) +#include <fcntl.h> +#include <stropts.h> +#endif /* SVR4 */ + +#include <sys/time.h> +#include "ttyrec.h" +#include "io.h" + +#define HAVE_inet_aton +#define HAVE_scsi_h +#define HAVE_kd_h + +#define _(FOO) FOO + +#ifdef HAVE_openpty +#include <libutil.h> +#endif + +#if defined(SVR4) && !defined(CDEL) +#if defined(_POSIX_VDISABLE) +#define CDEL _POSIX_VDISABLE +#elif defined(CDISABLE) +#define CDEL CDISABLE +#else /* not _POSIX_VISIBLE && not CDISABLE */ +#define CDEL 255 +#endif /* not _POSIX_VISIBLE && not CDISABLE */ +#endif /* SVR4 && ! CDEL */ + +void done(void); +void fail(void); +void fixtty(void); +void getmaster(void); +void getslave(void); +void doinput(void); +void dooutput(void); +void doshell(const char*); + +char *shell; +FILE *fscript; +int master; +int slave; +int child; +int subchild; +char *fname; + +struct termios tt; +struct winsize win; +int lb; +int l; +#if !defined(SVR4) +#ifndef HAVE_openpty +char line[] = "/dev/ptyXX"; +#endif +#endif /* !SVR4 */ +int aflg; +int uflg; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + int ch; + void finish(); + char *getenv(); + char *command = NULL; + + while ((ch = getopt(argc, argv, "aue:h?")) != EOF) + switch((char)ch) { + case 'a': + aflg++; + break; + case 'u': + uflg++; + break; + case 'e': + command = strdup(optarg); + break; + case 'h': + case '?': + default: + fprintf(stderr, _("usage: ttyrec [-u] [-e command] [-a] [file]\n")); + exit(1); + } + argc -= optind; + argv += optind; + + if (argc > 0) + 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); + } + doinput(); + + return 0; +} + +void +doinput() +{ + register int cc; + char ibuf[BUFSIZ]; + + (void) fclose(fscript); +#ifdef HAVE_openpty + (void) close(slave); +#endif + while ((cc = read(0, ibuf, BUFSIZ)) > 0) + (void) write(master, ibuf, cc); + done(); +} + +#include <sys/wait.h> + +void +finish() +{ +#if defined(SVR4) + int status; +#else /* !SVR4 */ + 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(); +} + +struct linebuf { + char str[BUFSIZ + 1]; /* + 1 for an additional NULL character.*/ + int len; +}; + + +void +check_line (const char *line) +{ + static int uuencode_mode = 0; + static FILE *uudecode; + + if (uuencode_mode == 1) { + fprintf(uudecode, "%s", line); + if (strcmp(line, "end\n") == 0) { + pclose(uudecode); + uuencode_mode = 0; + } + } else { + int dummy; char dummy2[BUFSIZ]; + if (sscanf(line, "begin %o %s", &dummy, dummy2) == 2) { + /* + * uuencode line found! + */ + uudecode = popen("uudecode", "w"); + fprintf(uudecode, "%s", line); + uuencode_mode = 1; + } + } +} + +void +check_output(const char *str, int len) +{ + static struct linebuf lbuf = {"", 0}; + int i; + + for (i = 0; i < len; i++) { + if (lbuf.len < BUFSIZ) { + lbuf.str[lbuf.len] = str[i]; + if (lbuf.str[lbuf.len] == '\r') { + lbuf.str[lbuf.len] = '\n'; + } + lbuf.len++; + if (lbuf.str[lbuf.len - 1] == '\n') { + if (lbuf.len > 1) { /* skip a blank line. */ + lbuf.str[lbuf.len] = '\0'; + check_line(lbuf.str); + } + lbuf.len = 0; + } + } else {/* buffer overflow */ + lbuf.len = 0; + } + } +} + +void +dooutput() +{ + int cc; + char obuf[BUFSIZ]; + + setbuf(stdout, NULL); + (void) close(0); +#ifdef HAVE_openpty + (void) close(slave); +#endif + for (;;) { + Header h; + + cc = read(master, obuf, BUFSIZ); + if (cc <= 0) + break; + if (uflg) + 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); + } + done(); +} + +void +doshell(const char* command) +{ + /*** + int t; + + t = open(_PATH_TTY, O_RDWR); + if (t >= 0) { + (void) ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + ***/ + getslave(); + (void) close(master); + (void) fclose(fscript); + (void) dup2(slave, 0); + (void) dup2(slave, 1); + (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); + } + perror(shell); + fail(); +} + +void +fixtty() +{ + struct termios rtt; + + rtt = tt; +#if defined(SVR4) + rtt.c_iflag = 0; + rtt.c_lflag &= ~(ISIG|ICANON|XCASE|ECHO|ECHOE|ECHOK|ECHONL); + rtt.c_oflag = OPOST; + rtt.c_cc[VINTR] = CDEL; + rtt.c_cc[VQUIT] = CDEL; + rtt.c_cc[VERASE] = CDEL; + rtt.c_cc[VKILL] = CDEL; + rtt.c_cc[VEOF] = 1; + rtt.c_cc[VEOL] = 0; +#else /* !SVR4 */ + cfmakeraw(&rtt); + rtt.c_lflag &= ~ECHO; +#endif /* !SVR4 */ + (void) tcsetattr(0, TCSAFLUSH, &rtt); +} + +void +fail() +{ + + (void) kill(0, SIGTERM); + done(); +} + +void +done() +{ + if (subchild) { + (void) fclose(fscript); + (void) close(master); + } else { + (void) tcsetattr(0, TCSAFLUSH, &tt); + } + exit(0); +} + +void +getmaster() +{ +#if defined(SVR4) + (void) tcgetattr(0, &tt); + (void) ioctl(0, TIOCGWINSZ, (char *)&win); + if ((master = open("/dev/ptmx", O_RDWR)) < 0) { + perror("open(\"/dev/ptmx\", O_RDWR)"); + fail(); + } +#else /* !SVR4 */ +#ifdef HAVE_openpty + (void) tcgetattr(0, &tt); + (void) ioctl(0, TIOCGWINSZ, (char *)&win); + if (openpty(&master, &slave, NULL, &tt, &win) < 0) { + fprintf(stderr, _("openpty failed\n")); + fail(); + } +#else +#ifdef HAVE_getpt + if ((master = getpt()) < 0) { + perror("getpt()"); + fail(); + } +#else + char *pty, *bank, *cp; + struct stat stb; + + pty = &line[strlen("/dev/ptyp")]; + for (bank = "pqrs"; *bank; bank++) { + line[strlen("/dev/pty")] = *bank; + *pty = '0'; + if (stat(line, &stb) < 0) + break; + for (cp = "0123456789abcdef"; *cp; cp++) { + *pty = *cp; + master = open(line, O_RDWR); + if (master >= 0) { + char *tp = &line[strlen("/dev/")]; + int ok; + + /* verify slave side is usable */ + *tp = 't'; + ok = access(line, R_OK|W_OK) == 0; + *tp = 'p'; + if (ok) { + (void) tcgetattr(0, &tt); + (void) ioctl(0, TIOCGWINSZ, + (char *)&win); + return; + } + (void) close(master); + } + } + } + fprintf(stderr, _("Out of pty's\n")); + fail(); +#endif /* not HAVE_getpt */ +#endif /* not HAVE_openpty */ +#endif /* !SVR4 */ +} + +void +getslave() +{ +#if defined(SVR4) + (void) setsid(); + grantpt( master); + unlockpt(master); + if ((slave = open((const char *)ptsname(master), O_RDWR)) < 0) { + perror("open(fd, O_RDWR)"); + fail(); + } + if (isastream(slave)) { + if (ioctl(slave, I_PUSH, "ptem") < 0) { + perror("ioctl(fd, I_PUSH, ptem)"); + fail(); + } + if (ioctl(slave, I_PUSH, "ldterm") < 0) { + perror("ioctl(fd, I_PUSH, ldterm)"); + fail(); + } +#ifndef _HPUX_SOURCE + if (ioctl(slave, I_PUSH, "ttcompat") < 0) { + perror("ioctl(fd, I_PUSH, ttcompat)"); + fail(); + } +#endif + (void) ioctl(0, TIOCGWINSZ, (char *)&win); + } +#else /* !SVR4 */ +#ifndef HAVE_openpty + line[strlen("/dev/")] = 't'; + slave = open(line, O_RDWR); + if (slave < 0) { + perror(line); + fail(); + } + (void) tcsetattr(slave, TCSAFLUSH, &tt); + (void) ioctl(slave, TIOCSWINSZ, (char *)&win); +#endif + (void) setsid(); + (void) ioctl(slave, TIOCSCTTY, 0); +#endif /* SVR4 */ +} diff --git a/ttyrec.h b/ttyrec.h @@ -0,0 +1,12 @@ +#ifndef __TTYREC_H__ +#define __TTYREC_H__ + +#include "sys/time.h" + +typedef struct header { + struct timeval tv; + int len; +} Header; + + +#endif diff --git a/ttytime.1 b/ttytime.1 @@ -0,0 +1,27 @@ +.\" +.\" This manual page is written by NAKANO Takeo <nakano@webmasters.gr.jp> +.\" +.TH TTYTIME 1 +.SH NAME +ttytime \- print the time of the recorded session data by ttyrec(1) +.SH SYNOPSIS +.br +.B ttytime +.I file... +.SH DESCRIPTION +.B Ttytime +tells you the time of recorded data in seconds. +For example: +.sp +.RS +.nf +% ttytime *.tty + 173 foo.tty + 1832 bar.tty +.fi +.RE +.SH "SEE ALSO" +.BR script (1), +.BR ttyrec (1), +.BR ttyplay (1) + diff --git a/ttytime.c b/ttytime.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "io.h" +#include "ttyrec.h" + +int +calc_time (const char *filename) +{ + Header start, end; + FILE *fp = efopen(filename, "r"); + + read_header(fp, &start); + fseek(fp, start.len, SEEK_CUR); + while (1) { + Header h; + if (read_header(fp, &h) == 0) { + break; + } + end = h; + fseek(fp, h.len, SEEK_CUR); + } + return end.tv.tv_sec - start.tv.tv_sec; +} + +int +main (int argc, char **argv) +{ + int i; + set_progname(argv[0]); + for (i = 1; i < argc; i++) { + char *filename = argv[i]; + printf("%7d %s\n", calc_time(filename), filename); + } + return 0; +}