First public release of ovh-ttyrec (v1.1.5.0)

Signed-off-by: Stéphane Lesimple <stephane.lesimple@corp.ovh.com>
This commit is contained in:
Stéphane Lesimple
2019-05-09 12:38:27 +02:00
parent 482b3a75d7
commit 7ac1b27cd0
29 changed files with 4136 additions and 739 deletions

13
AUTHORS Normal file
View File

@@ -0,0 +1,13 @@
# This is the official list of ovh-ttyrec authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files
# and it lists the copyright holders only.
# Names should be added to this file as one of
# Organization's name
# Individual's name <submission email address>
# Individual's name <submission email address> <email2> <emailN>
# See CONTRIBUTORS for the meaning of multiple email addresses.
# Please keep the list sorted.
OVH SAS

78
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,78 @@
# Contributing to ovh-ttyrec
This project accepts contributions. In order to contribute, you should
pay attention to a few things:
1. your code must follow the coding style rules
2. your code must be unit-tested
3. your code must be documented
4. your work must be signed (see below)
5. you may contribute through GitHub Pull Requests
# Coding and documentation Style
Please follow the coding style you'll find in the main ttyrec.c file.
Ensure you `make style` before comitting.
# Submitting Modifications
The contributions should be submitted through Github Pull Requests
and follow the DCO which is defined below.
Note that we want to keep compatibility with ttyrec classic, and as
ovh-ttyrec needs to be extremely stable (as it's used in critical
environment), deep modifications will probably get rejected.
For this project, stability will always be preferred over fancy.
# Licensing for new files
ovh-ttyrec is licensed under a Modified 3-Clause BSD license. Anything
contributed to ovh-ttyrec must be released under this license.
When introducing a new file into the project, please make sure it has a
copyright header making clear under which license it's being released.
# Developer Certificate of Origin (DCO)
To improve tracking of contributions to this project we will use a
process modeled on the modified DCO 1.1 and use a "sign-off" procedure
on patches that are being emailed around or contributed in any other
way.
The sign-off is a simple line at the end of the explanation for the
patch, which certifies that you wrote it or otherwise have the right
to pass it on as an open-source patch. The rules are pretty simple:
if you can certify the below:
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I have
the right to submit it under the open source license indicated in
the file; or
(b) The contribution is based upon previous work that, to the best of
my knowledge, is covered under an appropriate open source License
and I have the right under that license to submit that work with
modifications, whether created in whole or in part by me, under
the same open source license (unless I am permitted to submit
under a different license), as indicated in the file; or
(c) The contribution was provided directly to me by some other person
who certified (a), (b) or (c) and I have not modified it.
(d) The contribution is made free of any other party's intellectual
property claims or rights.
(e) I understand and agree that this project and the contribution are
public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
then you just add a line saying
Signed-off-by: Random J Developer <random@example.org>
using your real name (sorry, no pseudonyms or anonymous contributions.)

14
CONTRIBUTORS Normal file
View File

@@ -0,0 +1,14 @@
# This is the official list of people who can contribute
# (and typically have contributed) code to the <PROJECT> repository.
#
# Names should be added to this file only after verifying that
# the individual or the individual's organization has agreed to
# the appropriate CONTRIBUTING.md file.
#
# Names should be added to this file like so:
# Individual's name <submission email address>
# Individual's name <submission email address>
#
# Please keep the list sorted.
#
Stéphane Lesimple <stephane.lesimple@corp.ovh.com>

27
LICENSE Normal file
View File

@@ -0,0 +1,27 @@
Copyright 2001-2019, OVH SAS.
All rights reserved.
Modified 3-Clause BSD
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of OVH SAS 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 OVH SAS 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 OVH SAS AND 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.

12
MAINTAINERS Normal file
View File

@@ -0,0 +1,12 @@
# This is the official list of the project maintainers.
# This is mostly useful for contributors that want to push
# significant pull requests or for project management issues.
#
#
# Names should be added to this file like so:
# Individual's name <submission email address>
# Individual's name <submission email address>
#
# Please keep the list sorted.
#
Stéphane Lesimple <stephane.lesimple@corp.ovh.com>

View File

@@ -1,46 +0,0 @@
CC = gcc
CFLAGS = -O2 -DSVR4 -D_GNU_SOURCE -Wall -std=gnu99 -D_FORTIFY_SOURCE=1 -Wno-unused-result
VERSION = 1.0
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
BIN = $(DESTDIR)/usr/bin
all: $(TARGET)
patch:
quilt push -a
touch $@
ttyrec: patch ttyrec.o io.o
$(CC) $(CFLAGS) -o ttyrec ttyrec.o io.o
ttyplay: patch ttyplay.o io.o
$(CC) $(CFLAGS) -o ttyplay ttyplay.o io.o
ttytime: patch ttytime.o io.o
$(CC) $(CFLAGS) -o ttytime ttytime.o io.o
clean:
rm -f *.o $(TARGET) ttyrecord *~
quilt pop -a || true
rm -f patch
install:
install -d -m 0755 -o root -g root $(BIN)
install -m 0755 -o root -g root ttyrec $(BIN)
install -m 0755 -o root -g root ttyplay $(BIN)
install -m 0755 -o root -g root ttytime $(BIN)
install -m 0755 -o root -g root helper/telnetpassfile $(BIN)
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)

47
Makefile.in Normal file
View File

@@ -0,0 +1,47 @@
CC = %CC%
CFLAGS = -O2 -D_GNU_SOURCE -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -std=gnu99 %WARNINGS% -Werror
LIBS = %LIBS%
BINARIES = ttyrec ttyplay ttytime
DOCS = docs/ttyrec.1
BIN = $(DESTDIR)/usr/bin
all: prog doc
deb:
dpkg-buildpackage -b -rfakeroot -us -uc
prog: $(BINARIES)
doc: $(DOCS)
ttyrec.o: ttyrec.c configure.h
$(CC) $(CFLAGS) -c ttyrec.c
ttyrec: ttyrec.o io.o
$(CC) $(CFLAGS) -pthread -o ttyrec ttyrec.o io.o $(LIBS)
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
docs/ttyrec.1: ttyrec
help2man -N --help-option=-h --version-option=-V --no-discard-stderr ./ttyrec > docs/ttyrec.1
clean:
rm -f *.o $(BINARIES) $(DOCS) ttyrecord *~ Makefile configure.h
style:
uncrustify -c uncrustify.cfg -l C --no-backup *.h *.c
dist:
tar cvzf ttyrec.tar.gz *.c *.h docs/ debian/ configure Makefile.in uncrustify.cfg
install:
install -d -m 0755 -o root -g root $(BIN)
install -m 0755 -o root -g root ttyrec $(BIN)
install -m 0755 -o root -g root ttyplay $(BIN)
install -m 0755 -o root -g root ttytime $(BIN)

27
README
View File

@@ -1,27 +0,0 @@
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>

93
README.md Normal file
View File

@@ -0,0 +1,93 @@
ovh-ttyrec
==========
`ttyrec` is a terminal (tty) recorder, it comes with `ttyplay`, which is a tty player.
The original ttyrec is Copyright (c) 2000 Satoru Takabayashi.
The original ttyrec is based on the `script` program, Copyright (c) 1980 Regents of the University of California.
ovh-ttyrec is based (and compatible with) the original ttyrec, and can be used as a drop-in replacement. It is licensed under the 3-clause BSD license (see LICENSE file).
Efforts have been made to ensure the code is portable. It is known to work under at least:
- Linux
- FreeBSD
- NetBSD
- OpenBSD
- MacOS
- OpenSolaris distributions (such as OmniOS)
It should work under any POSIX OS that support either `openpty()` or the `grantpt()`/`unlockpt() `mechanisms.
## features
- Drop-in replacement of the classic ttyrec, additional features don't break compatibility
- The code is portable and OS features that can be used are detected at compile time
- Supports ttyrec output file rotation without interrupting the session
- Supports locking the session after a keyboard input timeout, optionally displaying a custom message
- Supports terminating the session after a keyboard input timeout
- Supports manually locking or terminating the session via "cheatcodes" (specific keystrokes)
- Supports a no-tty mode, relying on pipes instead of pseudottys, while still recording stdout/stderr
- Automatically detects whether to use pseudottys or pipes, also overridable from command-line
- Supports reporting the number of bytes that were output to the terminal on session exit
## compilation
To compile the binaries and build the man pages, just run:
$ ./configure && make
If you don't want to build the manpages, or you don't have the required `help2man` tool, you can run `make prog` instead.
Installation:
$ make install
Note that installation is not needed to test the binaries: you can just call `./ttyrec` from the build folder.
## build a .deb package
If you want to build a .deb (Debian/Ubuntu) package, just run:
$ ./configure && make deb
## usage
The simplest usage is just calling the binary, it'll execute the users' shell and record the session until exit:
$ ttyrec
To replay this session:
$ ttyplay ./ttyrecord
Run some shell commands:
$ ttyrec -f cmds.ttyrec -- sh -c 'for i in a b c; do echo $i; done'
Connect to a remote machine interactively, lock the session after 1 minute of inactivity, and kill it after 5 minutes of inactivity:
$ ttyrec -t 60 -k 300 -- ssh remoteserver
Execute a local script remotely with the default remote shell:
$ cat script.sh | ttyrec -- ssh remoteserver
Record a screen session:
$ ttyrec screen
Usage information:
$ ttyrec -h
## version scheme
We follow the version format `A.B.C.D`. The following rules apply:
- A is incremented when the file format of ttyrec changes, as long as A=1, the format is compatible with the original ttyrec (and original ttyplay)
- B is incremented for a breaking change in the way ttyrec can be called (a command-line option was removed for example), which means in that case, other programs or scripts using ttyrec should be checked for compatibility
- C is incremented for any non-hotfix change that stays backwards compatible (a new feature that can be enabled with a new command-line option for example)
- D is incremented for a quickfix/hotfix, or a change in the build system, docs, etc.
When a digit is incremented, all the "lower" ones go back to zero, i.e. if we are at version 4.7.1.5, and we implement a breaking change, the version number becomes 4.8.0.0.

149
configure vendored Executable file
View File

@@ -0,0 +1,149 @@
#! /bin/sh
curdir=$(dirname $0)
echo '#ifndef CONFIGURE_H' >"$curdir/configure.h"
echo '#define CONFIGURE_H' >>"$curdir/configure.h"
printf "%b" "Looking for compiler... "
[ -z "$CC" ] && CC=gcc
which $CC >/dev/null 2>&1 || CC=clang
which $CC >/dev/null 2>&1 || CC=cc
echo "$CC"
LIBS=''
DEFINES_STR='uses:'
WARNINGS=''
srcfile=$(mktemp)
mv $srcfile $srcfile.c
trap "rm -f $srcfile.c" INT HUP EXIT
printf "%b" "Looking for isastream()... "
cat >$srcfile.c <<EOF
#include <stropts.h>
int main(void) { return isastream(0); }
EOF
if $CC $srcfile.c -o /dev/null >/dev/null 2>&1; then
echo "yes"
echo '#define HAVE_isastream' >>"$curdir/configure.h"
DEFINES_STR="$DEFINES_STR isastream"
else
echo "no"
fi
printf "%b" "Looking for cfmakeraw()... "
cat >$srcfile.c <<EOF
#include <termios.h>
#include <unistd.h>
int main(void) { cfmakeraw(0); return 0; }
EOF
if $CC $srcfile.c -o /dev/null >/dev/null 2>&1; then
echo "yes"
echo '#define HAVE_cfmakeraw' >>"$curdir/configure.h"
DEFINES_STR="$DEFINES_STR cfmakeraw"
else
echo "no"
fi
printf "%b" "Looking for getpt()... "
cat >$srcfile.c <<EOF
#include <stdlib.h>
int main(void) { return getpt(); }
EOF
if $CC $srcfile.c -o /dev/null >/dev/null 2>&1; then
echo "yes"
echo '#define HAVE_getpt' >>"$curdir/configure.h"
DEFINES_STR="$DEFINES_STR getpt"
else
echo "no"
fi
printf "%b" "Looking for posix_openpt()... "
cat >$srcfile.c <<EOF
#include <stdlib.h>
#include <fcntl.h>
int main(void) { return posix_openpt(0); }
EOF
if $CC $srcfile.c -o /dev/null >/dev/null 2>&1; then
echo "yes"
echo '#define HAVE_posix_openpt' >>"$curdir/configure.h"
DEFINES_STR="$DEFINES_STR posix_openpt"
else
echo "no"
fi
printf "%b" "Looking for grantpt()... "
cat >$srcfile.c <<EOF
#include <stdlib.h>
int main(void) { (void)ptsname(0); return grantpt(0) + unlockpt(0); }
EOF
if $CC $srcfile.c -o /dev/null >/dev/null 2>&1; then
echo "yes"
echo '#define HAVE_grantpt' >>"$curdir/configure.h"
DEFINES_STR="$DEFINES_STR grantpt"
else
echo "no"
fi
printf "%b" "Looking for openpty()... "
cat >$srcfile.c <<EOF
#include <pty.h>
int main(void) { return openpty(0, 0, 0, 0, 0); }
EOF
if $CC $srcfile.c -lutil -o /dev/null >/dev/null 2>&1; then
echo "yes (pty.h)"
LIBS="$LIBS -lutil"
echo '#define HAVE_openpty' >>"$curdir/configure.h"
echo '#define HAVE_openpty_pty_h' >>"$curdir/configure.h"
DEFINES_STR="$DEFINES_STR openpty[pty.h]"
else
cat >$srcfile.c <<EOF
#include <util.h>
int main(void) { return openpty(0, 0, 0, 0, 0); }
EOF
if $CC $srcfile.c -lutil -o /dev/null >/dev/null 2>&1; then
echo "yes (util.h)"
LIBS="$LIBS -lutil"
echo '#define HAVE_openpty' >>"$curdir/configure.h"
echo '#define HAVE_openpty_util_h' >>"$curdir/configure.h"
DEFINES_STR="$DEFINES_STR openpty[util.h]"
else
cat >$srcfile.c <<EOF
#include <libutil.h>
int main(void) { return openpty(0, 0, 0, 0, 0); }
EOF
if $CC $srcfile.c -lutil -o /dev/null >/dev/null 2>&1; then
echo "yes (libutil.h)"
LIBS="$LIBS -lutil"
echo '#define HAVE_openpty' >>"$curdir/configure.h"
echo '#define HAVE_openpty_libutil_h' >>"$curdir/configure.h"
DEFINES_STR="$DEFINES_STR openpty[libutil.h]"
else
echo "no"
fi
fi
fi
echo "Checking for supported compiler options..."
for w in -Wall -Wextra -pedantic -Wno-unused-result -Wbad-function-cast -Wmissing-declarations \
-Wmissing-prototypes -Wnested-externs -Wold-style-definition -Wstrict-prototypes \
-Wpointer-sign -Wmissing-parameter-type -Wold-style-declaration
do
echo 'int main(void) { return 0; }' >$srcfile.c
if [ $($CC $srcfile.c $w -o /dev/null 2>&1 | wc -l) = 0 ]; then
echo "... OK $w"
WARNINGS="$WARNINGS $w"
else
echo "... unsupported $w"
fi
done
sed "s/%CC%/$CC/g;s/%LIBS%/$LIBS/g;s/%WARNINGS%/$WARNINGS/" $(dirname $0)/Makefile.in > $(dirname $0)/Makefile
cat >>"$curdir/configure.h" <<EOF
#define DEFINES_STR "$DEFINES_STR"
#endif
EOF
echo
echo "You may run make now"

7
debian/README vendored Normal file
View File

@@ -0,0 +1,7 @@
The Debian Package ovh-ttyrec
-----------------------------
Records terminal sessions.
See included README.md for more information.
-- Stéphane Lesimple <stephane.lesimple@corp.ovh.com> Thu, 09 May 2019 12:56:27 +0200

8
debian/README.source vendored Normal file
View File

@@ -0,0 +1,8 @@
ovh-ttyrec for Debian
---------------------
See the included README.md file for more information.

6
debian/changelog vendored Normal file
View File

@@ -0,0 +1,6 @@
ovh-ttyrec (1.1.5.0) master; urgency=medium
* First public release
* Add -c option to enable cheatcodes, as they're now disabled by default
-- Stéphane Lesimple (deb packages) <stephane.lesimple@corp.ovh.com> Thu, 09 May 2019 12:55:21 +0200

1
debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
7

23
debian/control vendored Normal file
View File

@@ -0,0 +1,23 @@
Source: ovh-ttyrec
Section: ovh
Priority: extra
Maintainer: Stéphane Lesimple <stephane.lesimple@corp.ovh.com>
Build-Depends: debhelper (>= 7.0.50~)
Standards-Version: 3.8.4
Package: ovh-ttyrec
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Conflicts: ttyrec
Description: Extended (but compatible) fork of ttyrec
ttyrec is a terminal (tty) recorder, it comes with ttyplay, which is a tty player.
Some features ov ovh-ttyrec follow:
- Drop-in replacement of the classic ttyrec, additional features don't break compatibility
- The code is portable and OS features that can be used are detected at compile time
- Supports ttyrec output file rotation without interrupting the session
- Supports locking the session after a keyboard input timeout, optionally displaying a custom message
- Supports terminating the session after a keyboard input timeout
- Supports manually locking or terminating the session via "cheatcodes" (specific keystrokes)
- Supports a no-tty mode, relying on pipes instead of pseudottys, while still recording stdout/stderr
- Automatically detects whether to use pseudottys or pipes, also overridable from command-line
- Supports reporting the number of bytes that were output to the terminal on session exit

20
debian/copyright vendored Normal file
View File

@@ -0,0 +1,20 @@
This work was packaged for Debian by:
Stéphane Lesimple <stephane.lesimple@corp.ovh.com> on Fri, 13 Jul 2012 15:16:31 +0200
Upstream Author(s):
Stéphane Lesimple <stephane.lesimple@corp.ovh.com>
Copyright:
Copyright 2001-2019, OVH SAS.
License:
See included LICENSE file.
The Debian packaging is:
Under the same terms as the included LICENSE file.

1
debian/links vendored Normal file
View File

@@ -0,0 +1 @@
usr/bin/ttyrec usr/bin/ovh-ttyrec

3
debian/manpages vendored Normal file
View File

@@ -0,0 +1,3 @@
docs/ttyplay.1
docs/ttyrec.1
docs/ttytime.1

13
debian/rules vendored Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
%:
dh $@

142
io.c
View File

@@ -1,4 +1,10 @@
/*
/* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
* Copyright 2001-2019 The ovh-ttyrec Authors. All rights reserved.
*
* This work is based on the original ttyrec, whose license text
* can be found below unmodified.
*
* Copyright (c) 2000 Satoru Takabayashi <satoru@namazu.org>
* All rights reserved.
*
@@ -40,51 +46,69 @@
#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)))
#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 ()
int read_header(FILE *fp, Header *h);
int write_header(FILE *fp, Header *h);
void set_progname(const char *name);
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);
static int is_little_endian(void)
{
static int retval = -1;
if (retval == -1) {
int n = 1;
char *p = (char *)&n;
char x[] = {1, 0, 0, 0};
if (retval == -1)
{
int n = 1;
char *p = (char *)&n;
char x[] = { 1, 0, 0, 0 };
assert(sizeof(int) == 4);
assert(sizeof(int) == 4);
if (memcmp(p, x, 4) == 0) {
retval = 1;
} else {
retval = 0;
}
if (memcmp(p, x, 4) == 0)
{
retval = 1;
}
else
{
retval = 0;
}
}
return retval;
}
static int
convert_to_little_endian (int x)
static int convert_to_little_endian(int x)
{
if (is_little_endian()) {
return x;
} else {
return SWAP_ENDIAN(x);
if (is_little_endian())
{
return x;
}
else
{
return SWAP_ENDIAN(x);
}
}
int
read_header (FILE *fp, Header *h)
int read_header(FILE *fp, Header *h)
{
int buf[3];
if (fread(buf, sizeof(int), 3, fp) == 0) {
return 0;
if (fread(buf, sizeof(int), 3, fp) == 0)
{
return 0;
}
h->tv.tv_sec = convert_to_little_endian(buf[0]);
@@ -94,8 +118,8 @@ read_header (FILE *fp, Header *h)
return 1;
}
int
write_header (FILE *fp, Header *h)
int write_header(FILE *fp, Header *h)
{
int buf[3];
@@ -103,59 +127,69 @@ write_header (FILE *fp, Header *h)
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;
if (fwrite(buf, sizeof(int), 3, fp) == 0)
{
return 0;
}
return 1;
}
static char *progname = "";
void
set_progname (const char *name)
void set_progname(const char *name)
{
progname = strdup(name);
}
FILE *
efopen (const char *path, const char *mode)
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);
if (fp == NULL)
{
fprintf(stderr, "%s: %s: %s\n", progname, path, strerror(errno));
exit(EXIT_FAILURE);
}
return fp;
}
int
edup (int oldfd)
int edup(int oldfd)
{
int fd = dup(oldfd);
if (fd == -1) {
fprintf(stderr, "%s: dup failed: %s\n", progname, strerror(errno));
exit(EXIT_FAILURE);
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 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);
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 *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);
if (fp == NULL)
{
fprintf(stderr, "%s: fdopen failed: %s\n", progname, strerror(errno));
exit(EXIT_FAILURE);
}
return fp;
}

13
io.h
View File

@@ -3,11 +3,12 @@
#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);
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);
void set_progname(const char *name);
#endif

328
ttyplay.c
View File

@@ -1,4 +1,11 @@
/*
// vim: noai:ts=4:sw=4:expandtab:
/* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
* Copyright 2001-2019 The ovh-ttyrec Authors. All rights reserved.
*
* This work is based on the original ttyrec, whose license text
* can be found below unmodified.
*
* Copyright (c) 2000 Satoru Takabayashi <satoru@namazu.org>
* All rights reserved.
*
@@ -12,8 +19,8 @@
* 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.
* 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.
@@ -42,178 +49,217 @@
#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);
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 timeval_diff(struct timeval tv1, struct timeval tv2);
struct timeval timeval_div(struct timeval tv1, double n);
double ttywait(struct timeval prev, struct timeval cur, double speed);
double ttynowait(struct timeval prev, struct timeval cur, double speed);
int ttyread(FILE *fp, Header *h, char **buf);
int ttypread(FILE *fp, Header *h, char **buf);
void ttywrite(char *buf, int len);
void ttynowrite(char *buf, int len);
void ttyplay(FILE *fp, double speed, ReadFunc read_func, WriteFunc write_func, WaitFunc wait_func);
void ttyskipall(FILE *fp);
void ttyplayback(FILE *fp, double speed, ReadFunc read_func, WaitFunc wait_func);
void ttypeek(FILE *fp, double speed, ReadFunc read_func, WaitFunc wait_func);
void usage(void);
FILE *input_from_stdin(void);
struct timeval timeval_diff(struct timeval tv1, struct timeval tv2)
{
struct timeval diff;
diff.tv_sec = tv2.tv_sec - tv1.tv_sec;
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;
if (diff.tv_usec < 0)
{
diff.tv_sec--;
diff.tv_usec += 1000000;
}
return diff;
}
struct timeval
timeval_div (struct timeval tv1, double n)
struct timeval timeval_div(struct timeval tv1, double n)
{
double x = ((double)tv1.tv_sec + (double)tv1.tv_usec / 1000000.0) / 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)
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;
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;
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}.
* 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? */
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;
if (read(STDIN_FILENO, &c, 1) == 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) {
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));
drift = timeval_diff(diff, timeval_diff(start, stop));
}
return speed;
}
double
ttynowait (struct timeval prev, struct timeval cur, double speed)
double ttynowait(struct timeval prev, struct timeval cur, double speed)
{
/* do nothing */
(void)prev;
(void)cur;
(void)speed;
return 0; /* Speed isn't important. */
}
int
ttyread (FILE *fp, Header *h, char **buf)
int ttyread(FILE *fp, Header *h, char **buf)
{
if (read_header(fp, h) == 0) {
return 0;
if (read_header(fp, h) == 0)
{
return 0;
}
*buf = malloc(h->len);
if (*buf == NULL) {
perror("malloc");
if (*buf == NULL)
{
perror("malloc");
}
if (fread(*buf, 1, h->len, fp) == 0) {
perror("fread");
if (fread(*buf, 1, h->len, fp) == 0)
{
perror("fread");
}
return 1;
}
int
ttypread (FILE *fp, Header *h, char **buf)
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);
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)
void ttywrite(char *buf, int len)
{
fwrite(buf, 1, len, stdout);
}
void
ttynowrite (char *buf, int len)
void ttynowrite(char *buf, int len)
{
/* do nothing */
(void)buf;
(void)len;
}
void
ttyplay (FILE *fp, double speed, ReadFunc read_func,
WriteFunc write_func, WaitFunc wait_func)
void ttyplay(FILE *fp, double speed, ReadFunc read_func, WriteFunc write_func, WaitFunc wait_func)
{
int first_time = 1;
int first_time = 1;
struct timeval prev;
setbuf(stdout, NULL);
setbuf(fp, NULL);
while (1) {
char *buf;
Header h;
while (1)
{
char *buf;
Header h;
if (read_func(fp, &h, &buf) == 0) {
break;
}
if (read_func(fp, &h, &buf) == 0)
{
break;
}
if (!first_time) {
speed = wait_func(prev, h.tv, speed);
}
first_time = 0;
if (!first_time)
{
speed = wait_func(prev, h.tv, speed);
}
first_time = 0;
write_func(buf, h.len);
prev = h.tv;
free(buf);
write_func(buf, h.len);
prev = h.tv;
free(buf);
}
}
void
ttyskipall (FILE *fp)
void ttyskipall(FILE *fp)
{
/*
* Skip all records.
@@ -221,22 +267,24 @@ ttyskipall (FILE *fp)
ttyplay(fp, 0, ttyread, ttynowrite, ttynowait);
}
void ttyplayback (FILE *fp, double speed,
ReadFunc read_func, WaitFunc wait_func)
void ttyplayback(FILE *fp, double speed, ReadFunc read_func, WaitFunc wait_func)
{
(void)read_func;
ttyplay(fp, speed, ttyread, ttywrite, wait_func);
}
void ttypeek (FILE *fp, double speed,
ReadFunc read_func, WaitFunc wait_func)
void ttypeek(FILE *fp, double speed, ReadFunc read_func, WaitFunc wait_func)
{
(void)read_func;
(void)wait_func;
ttyskipall(fp);
ttyplay(fp, speed, ttypread, ttywrite, ttynowait);
}
void
usage (void)
void usage(void)
{
printf("Usage: ttyplay [OPTION] [FILE]\n");
printf(" -s SPEED Set speed to SPEED [1.0]\n");
@@ -245,68 +293,78 @@ usage (void)
exit(EXIT_FAILURE);
}
/*
* We do some tricks so that select(2) properly works on
* STDIN_FILENO in ttywait().
*/
FILE *
input_from_stdin (void)
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)
int main(int argc, char **argv)
{
double speed = 1.0;
ReadFunc read_func = ttyread;
WaitFunc wait_func = ttywait;
ProcessFunc process = ttyplayback;
FILE *input = NULL;
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) {
while (1)
{
int ch = getopt(argc, argv, "s:np");
if (ch == EOF) {
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();
}
}
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 {
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 */
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 */
tcsetattr(0, TCSANOW, &new); /* Make it current */
process(input, speed, read_func, wait_func);
tcsetattr(0, TCSANOW, &old); /* Return terminal state */
tcsetattr(0, TCSANOW, &old); /* Return terminal state */
return 0;
}

View File

@@ -1,76 +0,0 @@
.\"
.\" 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)

1909
ttyrec.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,12 @@
#ifndef __TTYREC_H__
#define __TTYREC_H__
#include "sys/time.h"
#include <sys/time.h>
typedef struct header {
typedef struct header
{
struct timeval tv;
int len;
int len;
} Header;

View File

@@ -1,4 +1,10 @@
/*
/* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
* Copyright 2001-2019 The ovh-ttyrec Authors. All rights reserved.
*
* This work is based on the original ttyrec, whose license text
* can be found below unmodified.
*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved.
*
@@ -38,33 +44,39 @@
#include "io.h"
#include "ttyrec.h"
int
calc_time (const char *filename)
int calc_time(const char *filename);
int calc_time(const char *filename)
{
Header start, end;
FILE *fp = efopen(filename, "r");
FILE *fp = efopen(filename, "r");
read_header(fp, &start);
end.tv.tv_sec = start.tv.tv_sec; // to avoid can-be-uninit warning
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);
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 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);
for (i = 1; i < argc; i++)
{
char *filename = argv[i];
printf("%7d %s\n", calc_time(filename), filename);
}
return 0;
}

1768
uncrustify.cfg Normal file

File diff suppressed because it is too large Load Diff