bin/114465: [patch]: script(1): add really cool -d, -p & -r options from NetBSD

Ighighi ighighi at gmail.com
Mon Jul 9 23:50:04 UTC 2007


>Number:         114465
>Category:       bin
>Synopsis:       [patch]: script(1): add really cool -d, -p & -r options from NetBSD
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jul 09 23:50:03 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator:     Ighighi
>Release:        6.2-STABLE
>Organization:
>Environment:
FreeBSD orion 6.2-STABLE FreeBSD 6.2-STABLE #0: Fri Jul  6 04:35:46 VET 2007     root at orion:/usr/obj/usr/src/sys/CUSTOM  i386

>Description:
Inspired by this BSDNews article:
http://www.feyrer.de/NetBSD/bx/blosxom.cgi/nb_20070707_1417.html

Using latest 1.12 version of src/usr.bin/script/script.c available at
http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/usr.bin/script/script.c?rev=1.12


>How-To-Repeat:

>Fix:
Download this patch and apply with
patch -d /usr < /path/to/patch
cd /usr/src/usr.bin/script
make clean obj depend && make && make install


Patch attached with submission follows:

#
# (!c) 2007 by Ighighi
#
# This patch to script(1) adds support for the -d, -p & -r options from NetBSD
# http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/usr.bin/script/script.c?rev=1.12
#

--- src/usr.bin/script/script.c.orig	Sun Feb 15 13:30:13 2004
+++ src/usr.bin/script/script.c	Mon Jul  9 19:22:38 2007
@@ -50,6 +50,9 @@
 #include <sys/stat.h>
 #include <sys/ioctl.h>
 #include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/endian.h>
+#include <sys/param.h>
 
 #include <err.h>
 #include <errno.h>
@@ -63,19 +66,30 @@
 #include <termios.h>
 #include <unistd.h>
 
+#define DEF_BUF 65536
+
+struct stamp {
+	uint64_t scr_len;	/* amount of data */
+	uint64_t scr_sec;	/* time it arrived in seconds... */
+	uint32_t scr_usec;	/* ...and microseconds */
+	uint32_t scr_direction;	/* 'i', 'o', etc (also indicates endianness) */
+};
+
 FILE	*fscript;
 int	master, slave;
 int	child;
 const char *fname;
 int	qflg, ttyflg;
+int	usesleep, rawout;
 
 struct	termios tt;
 
 void	done(int) __dead2;
-void	dooutput(void);
 void	doshell(char **);
 void	fail(void);
 void	finish(void);
+void    record(FILE *, char *, size_t, int);
+void    playback(FILE *);
 static void usage(void);
 
 int
@@ -84,7 +98,7 @@
 	int cc;
 	struct termios rtt, stt;
 	struct winsize win;
-	int aflg, kflg, ch, n;
+	int aflg, kflg, pflg, ch, n;
 	struct timeval tv, *tvp;
 	time_t tvec, start;
 	char obuf[BUFSIZ];
@@ -92,18 +106,30 @@
 	fd_set rfd;
 	int flushtime = 30;
 
-	aflg = kflg = 0;
-	while ((ch = getopt(argc, argv, "aqkt:")) != -1)
+	aflg = kflg = pflg = 0;
+	usesleep = 1;
+	rawout = 0;
+ 
+	while ((ch = getopt(argc, argv, "adkpqrt:")) != -1)
 		switch(ch) {
 		case 'a':
 			aflg = 1;
 			break;
-		case 'q':
-			qflg = 1;
+		case 'd':
+			usesleep = 0;
 			break;
 		case 'k':
 			kflg = 1;
 			break;
+		case 'p':
+			pflg = 1;
+			break;
+		case 'q':
+			qflg = 1;
+			break;
+		case 'r':
+			rawout = 1;
+			break;
 		case 't':
 			flushtime = atoi(optarg);
 			if (flushtime < 0)
@@ -123,9 +149,12 @@
 	} else
 		fname = "typescript";
 
-	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL)
+	if ((fscript = fopen(fname, pflg ? "r" : aflg ? "a" : "w")) == NULL)
 		err(1, "%s", fname);
 
+	if (pflg)
+		playback(fscript);
+
 	if (ttyflg = isatty(STDIN_FILENO)) {
 		if (tcgetattr(STDIN_FILENO, &tt) == -1)
 			err(1, "tcgetattr");
@@ -138,10 +167,14 @@
 			err(1, "openpty");
 	}
 
+	if (rawout)
+		record(fscript, NULL, 0, 's');
+
 	if (!qflg) {
 		tvec = time(NULL);
 		(void)printf("Script started, output file is %s\n", fname);
-		(void)fprintf(fscript, "Script started on %s", ctime(&tvec));
+		if (!rawout)
+			(void)fprintf(fscript, "Script started on %s", ctime(&tvec));
 		fflush(fscript);
 	}
 	if (ttyflg) {
@@ -183,6 +216,8 @@
 			if (cc == 0)
 				(void)write(master, ibuf, 0);
 			if (cc > 0) {
+				if (rawout)
+					record(fscript, ibuf, cc, 'i');
 				(void)write(master, ibuf, cc);
 				if (kflg && tcgetattr(master, &stt) >= 0 &&
 				    ((stt.c_lflag & ECHO) == 0)) {
@@ -195,7 +230,10 @@
 			if (cc <= 0)
 				break;
 			(void)write(STDOUT_FILENO, obuf, cc);
-			(void)fwrite(obuf, 1, cc, fscript);
+			if (rawout)
+				record(fscript, obuf, cc, 'o');
+			else
+				(void)fwrite(obuf, 1, cc, fscript);
 		}
 		tvec = time(0);
 		if (tvec - start >= flushtime) {
@@ -274,11 +312,114 @@
 	if (ttyflg)
 		(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
 	tvec = time(NULL);
+	if (rawout)
+		record(fscript, NULL, 0, 'e');
 	if (!qflg) {
-		(void)fprintf(fscript,"\nScript done on %s", ctime(&tvec));
+		if (!rawout)
+			(void)fprintf(fscript,"\nScript done on %s", ctime(&tvec));
 		(void)printf("\nScript done, output file is %s\n", fname);
 	}
 	(void)fclose(fscript);
 	(void)close(master);
 	exit(eno);
+}
+
+
+void
+record(FILE *fscript, char *buf, size_t cc, int direction)
+{
+	struct iovec iov[2];
+	struct stamp stamp;
+	struct timeval tv;
+
+	(void)gettimeofday(&tv, NULL);
+	stamp.scr_len = cc;
+	stamp.scr_sec = tv.tv_sec;
+	stamp.scr_usec = tv.tv_usec;
+	stamp.scr_direction = direction;
+	iov[0].iov_len = sizeof(stamp);
+	iov[0].iov_base = &stamp;
+	iov[1].iov_len = cc;
+	iov[1].iov_base = buf;
+	if (writev(fileno(fscript), &iov[0], 2) == -1)
+		err(1, "writev");
+}
+
+#define swapstamp(stamp) do { \
+	if (stamp.scr_direction > 0xff) { \
+		stamp.scr_len = bswap64(stamp.scr_len); \
+		stamp.scr_sec = bswap64(stamp.scr_sec); \
+		stamp.scr_usec = bswap32(stamp.scr_usec); \
+		stamp.scr_direction = bswap32(stamp.scr_direction); \
+	} \
+} while (0/*CONSTCOND*/)
+
+void
+playback(FILE *fscript)
+{
+	struct timespec tsi, tso;
+	struct stamp stamp;
+	struct stat playback_stat;
+	char buf[DEF_BUF];
+	off_t nread, save_len;
+	size_t l;
+	time_t clock;
+
+	if (fstat(fileno(fscript), &playback_stat) == -1)
+		err(1, "fstat failed");	
+
+	for (nread = 0; nread < playback_stat.st_size; nread += save_len) {
+		if (fread(&stamp, sizeof(stamp), 1, fscript) != 1)
+			err(1, "reading playback header");
+		swapstamp(stamp);
+		save_len = sizeof(stamp);
+
+		if (stamp.scr_len >
+		    (uint64_t)(playback_stat.st_size - save_len) - nread)
+			err(1, "invalid stamp");
+
+		save_len += stamp.scr_len;
+		clock = stamp.scr_sec;
+		tso.tv_sec = stamp.scr_sec;
+		tso.tv_nsec = stamp.scr_usec * 1000;
+
+		switch (stamp.scr_direction) {
+		case 's':
+			(void)printf("Script started on %s", ctime(&clock));
+			tsi = tso;
+			fseek(fscript, stamp.scr_len, SEEK_CUR);
+			break;
+		case 'e':
+			(void)printf("\nScript done on %s", ctime(&clock));
+			fseek(fscript, stamp.scr_len, SEEK_CUR);
+			break;
+		case 'i':
+			/* throw input away */
+			fseek(fscript, stamp.scr_len, SEEK_CUR);
+			break;
+		case 'o':
+			tsi.tv_sec = tso.tv_sec - tsi.tv_sec;
+			tsi.tv_nsec = tso.tv_nsec - tsi.tv_nsec;
+			if (tsi.tv_nsec < 0) {
+				tsi.tv_sec -= 1;
+				tsi.tv_nsec += 1000000000;
+			}
+			if (usesleep)
+				(void)nanosleep(&tsi, NULL);
+			tsi = tso;
+			while (stamp.scr_len > 0) {
+				l = MIN(DEF_BUF, stamp.scr_len);
+				if (fread(buf, sizeof(char), l, fscript) != l)
+					err(1, "cannot read buffer");
+
+				(void)write(STDOUT_FILENO, buf, l);
+				stamp.scr_len -= l;
+			}
+			break;
+		default:
+			err(1, "invalid direction");
+		}
+	}
+	(void)fclose(fscript);
+	exit(0);
 }
--- src/usr.bin/script/script.1.orig	Fri Jul  2 20:24:43 2004
+++ src/usr.bin/script/script.1	Mon Jul  9 19:25:22 2007
@@ -40,7 +40,7 @@
 .Nd make typescript of terminal session
 .Sh SYNOPSIS
 .Nm
-.Op Fl akq
+.Op Fl adkpqr
 .Op Fl t Ar time
 .Op Ar file Op Ar command ...
 .Sh DESCRIPTION
@@ -76,10 +76,16 @@
 or
 .Pa typescript ,
 retaining the prior contents.
+.It Fl d
+Don't sleep between records when playing back a timestamped session.
 .It Fl k
 Log keys sent to program as well as output.
+.It Fl p
+Play back a recorded session in real time.
 .It Fl q
 Run in quiet mode, omit the start and stop status messages.
+.It Fl r
+Record a session with input, output, and timestamping.
 .It Fl t Ar time
 Specify time interval between flushing script output file.
 A value of 0
@@ -139,6 +145,13 @@
 .Nm
 command appeared in
 .Bx 3.0 .
+.Pp
+The
+.Fl d ,
+.Fl p
+and
+.Fl r
+options were imported from NetBSD.
 .Sh BUGS
 The
 .Nm


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list