From owner-freebsd-bugs@FreeBSD.ORG Mon Jul 9 23:50:04 2007 Return-Path: X-Original-To: freebsd-bugs@hub.freebsd.org Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 7B6DC16A478 for ; Mon, 9 Jul 2007 23:50:04 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [69.147.83.40]) by mx1.freebsd.org (Postfix) with ESMTP id 59CAF13C489 for ; Mon, 9 Jul 2007 23:50:04 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.13.4/8.13.4) with ESMTP id l69No4JH014413 for ; Mon, 9 Jul 2007 23:50:04 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.13.4/8.13.4/Submit) id l69No4X4014411; Mon, 9 Jul 2007 23:50:04 GMT (envelope-from gnats) Resent-Date: Mon, 9 Jul 2007 23:50:04 GMT Resent-Message-Id: <200707092350.l69No4X4014411@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Ighighi Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 098A416A400 for ; Mon, 9 Jul 2007 23:47:37 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [69.147.83.33]) by mx1.freebsd.org (Postfix) with ESMTP id EE1B413C483 for ; Mon, 9 Jul 2007 23:47:36 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.13.1/8.13.1) with ESMTP id l69NlaqX035060 for ; Mon, 9 Jul 2007 23:47:36 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.13.1/8.13.1/Submit) id l69Nla0L035059; Mon, 9 Jul 2007 23:47:36 GMT (envelope-from nobody) Message-Id: <200707092347.l69Nla0L035059@www.freebsd.org> Date: Mon, 9 Jul 2007 23:47:36 GMT From: Ighighi To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.0 Cc: Subject: bin/114465: [patch]: script(1): add really cool -d, -p & -r options from NetBSD X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 09 Jul 2007 23:50:04 -0000 >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@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 #include #include +#include +#include +#include #include #include @@ -63,19 +66,30 @@ #include #include +#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: