11590Srgrimes/* 2242138Sobrien * Copyright (c) 2010, 2012 David E. O'Brien 31590Srgrimes * Copyright (c) 1980, 1992, 1993 41590Srgrimes * The Regents of the University of California. All rights reserved. 51590Srgrimes * 61590Srgrimes * Redistribution and use in source and binary forms, with or without 71590Srgrimes * modification, are permitted provided that the following conditions 81590Srgrimes * are met: 91590Srgrimes * 1. Redistributions of source code must retain the above copyright 101590Srgrimes * notice, this list of conditions and the following disclaimer. 111590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer in the 131590Srgrimes * documentation and/or other materials provided with the distribution. 141590Srgrimes * 4. Neither the name of the University nor the names of its contributors 151590Srgrimes * may be used to endorse or promote products derived from this software 161590Srgrimes * without specific prior written permission. 171590Srgrimes * 181590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 191590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 221590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281590Srgrimes * SUCH DAMAGE. 291590Srgrimes */ 301590Srgrimes 31241972Sobrien#include <sys/param.h> 3287768Smarkm__FBSDID("$FreeBSD: releng/10.3/usr.bin/script/script.c 296917 2016-03-15 19:45:24Z bdrewery $"); 331590Srgrimes#ifndef lint 3427980Scharnierstatic const char copyright[] = 351590Srgrimes"@(#) Copyright (c) 1980, 1992, 1993\n\ 361590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3787768Smarkm#endif 381590Srgrimes#ifndef lint 3987768Smarkmstatic const char sccsid[] = "@(#)script.c 8.1 (Berkeley) 6/6/93"; 4027980Scharnier#endif 411590Srgrimes 421590Srgrimes#include <sys/wait.h> 431590Srgrimes#include <sys/stat.h> 441590Srgrimes#include <sys/ioctl.h> 451590Srgrimes#include <sys/time.h> 46238896Sbrian#include <sys/uio.h> 47238896Sbrian#include <sys/endian.h> 48242138Sobrien#include <dev/filemon/filemon.h> 491590Srgrimes 5027980Scharnier#include <err.h> 5134295Speter#include <errno.h> 521590Srgrimes#include <fcntl.h> 5327980Scharnier#include <libutil.h> 541590Srgrimes#include <paths.h> 551590Srgrimes#include <signal.h> 561590Srgrimes#include <stdio.h> 571590Srgrimes#include <stdlib.h> 581590Srgrimes#include <string.h> 591590Srgrimes#include <termios.h> 601590Srgrimes#include <unistd.h> 611590Srgrimes 62238896Sbrian#define DEF_BUF 65536 63238896Sbrian 64238896Sbrianstruct stamp { 65238896Sbrian uint64_t scr_len; /* amount of data */ 66238896Sbrian uint64_t scr_sec; /* time it arrived in seconds... */ 67238896Sbrian uint32_t scr_usec; /* ...and microseconds */ 68238896Sbrian uint32_t scr_direction; /* 'i', 'o', etc (also indicates endianness) */ 69238896Sbrian}; 70238896Sbrian 71211394Sedstatic FILE *fscript; 72211394Sedstatic int master, slave; 73211394Sedstatic int child; 74211394Sedstatic const char *fname; 75242138Sobrienstatic char *fmfname; 76242138Sobrienstatic int fflg, qflg, ttyflg; 77238896Sbrianstatic int usesleep, rawout; 781590Srgrimes 79211394Sedstatic struct termios tt; 801590Srgrimes 81211394Sedstatic void done(int) __dead2; 82211394Sedstatic void doshell(char **); 83211394Sedstatic void fail(void); 84211394Sedstatic void finish(void); 85238896Sbrianstatic void record(FILE *, char *, size_t, int); 86238896Sbrianstatic void consume(FILE *, off_t, char *, int); 87238896Sbrianstatic void playback(FILE *) __dead2; 8892922Simpstatic void usage(void); 891590Srgrimes 901590Srgrimesint 91102944Sdwmalonemain(int argc, char *argv[]) 921590Srgrimes{ 9387768Smarkm int cc; 9434295Speter struct termios rtt, stt; 951590Srgrimes struct winsize win; 9634295Speter struct timeval tv, *tvp; 9734295Speter time_t tvec, start; 9834295Speter char obuf[BUFSIZ]; 991590Srgrimes char ibuf[BUFSIZ]; 10034295Speter fd_set rfd; 101242138Sobrien int aflg, kflg, pflg, ch, k, n; 102242138Sobrien int flushtime, readstdin; 103242138Sobrien int fm_fd, fm_log; 1041590Srgrimes 105238896Sbrian aflg = kflg = pflg = 0; 106238896Sbrian usesleep = 1; 107238896Sbrian rawout = 0; 108242138Sobrien flushtime = 30; 109242138Sobrien fm_fd = -1; /* Shut up stupid "may be used uninitialized" GCC 110242138Sobrien warning. (not needed w/clang) */ 111238896Sbrian 112242138Sobrien while ((ch = getopt(argc, argv, "adfkpqrt:")) != -1) 1131590Srgrimes switch(ch) { 1141590Srgrimes case 'a': 1151590Srgrimes aflg = 1; 1161590Srgrimes break; 117238896Sbrian case 'd': 118238896Sbrian usesleep = 0; 11932083Speter break; 120242138Sobrien case 'f': 121242138Sobrien fflg = 1; 122242138Sobrien break; 12334295Speter case 'k': 12434295Speter kflg = 1; 12534295Speter break; 126238896Sbrian case 'p': 127238896Sbrian pflg = 1; 128238896Sbrian break; 129238896Sbrian case 'q': 130238896Sbrian qflg = 1; 131238896Sbrian break; 132238896Sbrian case 'r': 133238896Sbrian rawout = 1; 134238896Sbrian break; 13534295Speter case 't': 13634295Speter flushtime = atoi(optarg); 13734295Speter if (flushtime < 0) 13834295Speter err(1, "invalid flush time %d", flushtime); 13934295Speter break; 1401590Srgrimes case '?': 1411590Srgrimes default: 14227980Scharnier usage(); 1431590Srgrimes } 1441590Srgrimes argc -= optind; 1451590Srgrimes argv += optind; 1461590Srgrimes 14732083Speter if (argc > 0) { 1481590Srgrimes fname = argv[0]; 14932083Speter argv++; 15032083Speter argc--; 15132083Speter } else 1521590Srgrimes fname = "typescript"; 1531590Srgrimes 154238896Sbrian if ((fscript = fopen(fname, pflg ? "r" : aflg ? "a" : "w")) == NULL) 15527980Scharnier err(1, "%s", fname); 1561590Srgrimes 157242138Sobrien if (fflg) { 158242138Sobrien asprintf(&fmfname, "%s.filemon", fname); 159242138Sobrien if (!fmfname) 160242138Sobrien err(1, "%s.filemon", fname); 161242138Sobrien if ((fm_fd = open("/dev/filemon", O_RDWR)) == -1) 162242138Sobrien err(1, "open(\"/dev/filemon\", O_RDWR)"); 163242138Sobrien if ((fm_log = open(fmfname, O_WRONLY | O_CREAT | O_TRUNC, 164242138Sobrien S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) 165242138Sobrien err(1, "open(%s)", fmfname); 166242138Sobrien if (ioctl(fm_fd, FILEMON_SET_FD, &fm_log) < 0) 167242138Sobrien err(1, "Cannot set filemon log file descriptor"); 168242138Sobrien 169242138Sobrien /* Set up these two fd's to close on exec. */ 170242138Sobrien (void)fcntl(fm_fd, F_SETFD, FD_CLOEXEC); 171242138Sobrien (void)fcntl(fm_log, F_SETFD, FD_CLOEXEC); 172242138Sobrien } 173242138Sobrien 174238896Sbrian if (pflg) 175238896Sbrian playback(fscript); 176238896Sbrian 177201384Sed if ((ttyflg = isatty(STDIN_FILENO)) != 0) { 178124845Scperciva if (tcgetattr(STDIN_FILENO, &tt) == -1) 179124845Scperciva err(1, "tcgetattr"); 180124845Scperciva if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) == -1) 181124845Scperciva err(1, "ioctl"); 182124845Scperciva if (openpty(&master, &slave, NULL, &tt, &win) == -1) 183124845Scperciva err(1, "openpty"); 184124845Scperciva } else { 185124845Scperciva if (openpty(&master, &slave, NULL, NULL, NULL) == -1) 186124845Scperciva err(1, "openpty"); 187124845Scperciva } 1881590Srgrimes 189238896Sbrian if (rawout) 190238896Sbrian record(fscript, NULL, 0, 's'); 191238896Sbrian 19234295Speter if (!qflg) { 19334295Speter tvec = time(NULL); 19432083Speter (void)printf("Script started, output file is %s\n", fname); 195238896Sbrian if (!rawout) { 196238896Sbrian (void)fprintf(fscript, "Script started on %s", 197242138Sobrien ctime(&tvec)); 198238896Sbrian if (argv[0]) { 199238896Sbrian fprintf(fscript, "command: "); 200238896Sbrian for (k = 0 ; argv[k] ; ++k) 201238896Sbrian fprintf(fscript, "%s%s", k ? " " : "", 202238896Sbrian argv[k]); 203238896Sbrian fprintf(fscript, "\n"); 204238896Sbrian } 205238896Sbrian } 20634295Speter fflush(fscript); 207242138Sobrien if (fflg) { 208242138Sobrien (void)printf("Filemon started, output file is %s\n", 209242138Sobrien fmfname); 210242138Sobrien } 21134295Speter } 212124845Scperciva if (ttyflg) { 213124845Scperciva rtt = tt; 214124845Scperciva cfmakeraw(&rtt); 215124845Scperciva rtt.c_lflag &= ~ECHO; 216124845Scperciva (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt); 217124845Scperciva } 2181590Srgrimes 2191590Srgrimes child = fork(); 2201590Srgrimes if (child < 0) { 22127980Scharnier warn("fork"); 22239481Sdes done(1); 2231590Srgrimes } 224296917Sbdrewery if (child == 0) { 225296917Sbdrewery if (fflg) { 226296917Sbdrewery int pid; 227296917Sbdrewery 228296917Sbdrewery pid = getpid(); 229296917Sbdrewery if (ioctl(fm_fd, FILEMON_SET_PID, &pid) < 0) 230296917Sbdrewery err(1, "Cannot set filemon PID"); 231296917Sbdrewery } 232296917Sbdrewery 23334295Speter doshell(argv); 234296917Sbdrewery } 235205009Sed close(slave); 23634295Speter 237225809Strociny start = tvec = time(0); 238225809Strociny readstdin = 1; 23934295Speter for (;;) { 240225809Strociny FD_ZERO(&rfd); 24134295Speter FD_SET(master, &rfd); 242225809Strociny if (readstdin) 243225809Strociny FD_SET(STDIN_FILENO, &rfd); 244260924Strociny if (!readstdin && ttyflg) { 245260924Strociny tv.tv_sec = 1; 24634295Speter tv.tv_usec = 0; 247225809Strociny tvp = &tv; 248225809Strociny readstdin = 1; 249260924Strociny } else if (flushtime > 0) { 250260924Strociny tv.tv_sec = flushtime - (tvec - start); 251260924Strociny tv.tv_usec = 0; 252260924Strociny tvp = &tv; 253225809Strociny } else { 254225809Strociny tvp = NULL; 2551590Srgrimes } 25634295Speter n = select(master + 1, &rfd, 0, 0, tvp); 25734295Speter if (n < 0 && errno != EINTR) 25834295Speter break; 25934295Speter if (n > 0 && FD_ISSET(STDIN_FILENO, &rfd)) { 26034295Speter cc = read(STDIN_FILENO, ibuf, BUFSIZ); 261125848Scperciva if (cc < 0) 26234295Speter break; 263225809Strociny if (cc == 0) { 264225809Strociny if (tcgetattr(master, &stt) == 0 && 265225809Strociny (stt.c_lflag & ICANON) != 0) { 266225809Strociny (void)write(master, &stt.c_cc[VEOF], 1); 267225809Strociny } 268225809Strociny readstdin = 0; 269225809Strociny } 27034295Speter if (cc > 0) { 271238896Sbrian if (rawout) 272238896Sbrian record(fscript, ibuf, cc, 'i'); 27334295Speter (void)write(master, ibuf, cc); 27434295Speter if (kflg && tcgetattr(master, &stt) >= 0 && 27534295Speter ((stt.c_lflag & ECHO) == 0)) { 27634295Speter (void)fwrite(ibuf, 1, cc, fscript); 27734295Speter } 27834295Speter } 27934295Speter } 28034295Speter if (n > 0 && FD_ISSET(master, &rfd)) { 28134295Speter cc = read(master, obuf, sizeof (obuf)); 28234295Speter if (cc <= 0) 28334295Speter break; 28480381Ssheldonh (void)write(STDOUT_FILENO, obuf, cc); 285238896Sbrian if (rawout) 286238896Sbrian record(fscript, obuf, cc, 'o'); 287238896Sbrian else 288238896Sbrian (void)fwrite(obuf, 1, cc, fscript); 28934295Speter } 29034295Speter tvec = time(0); 29134295Speter if (tvec - start >= flushtime) { 29234295Speter fflush(fscript); 29334295Speter start = tvec; 29434295Speter } 2951590Srgrimes } 29639481Sdes finish(); 29739481Sdes done(0); 2981590Srgrimes} 2991590Srgrimes 30027980Scharnierstatic void 301102944Sdwmaloneusage(void) 30227980Scharnier{ 30334295Speter (void)fprintf(stderr, 304242138Sobrien "usage: script [-adfkpqr] [-t time] [file [command ...]]\n"); 30527980Scharnier exit(1); 30627980Scharnier} 30727980Scharnier 308211394Sedstatic void 309102944Sdwmalonefinish(void) 3101590Srgrimes{ 311207453Sed int e, status; 3121590Srgrimes 313207453Sed if (waitpid(child, &status, 0) == child) { 314207453Sed if (WIFEXITED(status)) 315207453Sed e = WEXITSTATUS(status); 316207453Sed else if (WIFSIGNALED(status)) 317207453Sed e = WTERMSIG(status); 318207453Sed else /* can't happen */ 319207453Sed e = 1; 32039481Sdes done(e); 321207453Sed } 3221590Srgrimes} 3231590Srgrimes 324211394Sedstatic void 325102944Sdwmalonedoshell(char **av) 3261590Srgrimes{ 32787296Sdwmalone const char *shell; 3281590Srgrimes 3291590Srgrimes shell = getenv("SHELL"); 3301590Srgrimes if (shell == NULL) 3311590Srgrimes shell = _PATH_BSHELL; 3321590Srgrimes 3331590Srgrimes (void)close(master); 3341590Srgrimes (void)fclose(fscript); 335242138Sobrien free(fmfname); 3361590Srgrimes login_tty(slave); 337212770Sobrien setenv("SCRIPT", fname, 1); 33839481Sdes if (av[0]) { 33932108Speter execvp(av[0], av); 34062897Skris warn("%s", av[0]); 34139481Sdes } else { 34279452Sbrian execl(shell, shell, "-i", (char *)NULL); 34362897Skris warn("%s", shell); 34439481Sdes } 3451590Srgrimes fail(); 3461590Srgrimes} 3471590Srgrimes 348211394Sedstatic void 349102944Sdwmalonefail(void) 3501590Srgrimes{ 3511590Srgrimes (void)kill(0, SIGTERM); 35239481Sdes done(1); 3531590Srgrimes} 3541590Srgrimes 355211394Sedstatic void 356102944Sdwmalonedone(int eno) 3571590Srgrimes{ 3581590Srgrimes time_t tvec; 3591590Srgrimes 360124845Scperciva if (ttyflg) 361124845Scperciva (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt); 36234295Speter tvec = time(NULL); 363238896Sbrian if (rawout) 364238896Sbrian record(fscript, NULL, 0, 'e'); 36534295Speter if (!qflg) { 366238896Sbrian if (!rawout) 367238896Sbrian (void)fprintf(fscript,"\nScript done on %s", 368242138Sobrien ctime(&tvec)); 36934295Speter (void)printf("\nScript done, output file is %s\n", fname); 370242138Sobrien if (fflg) { 371242138Sobrien (void)printf("Filemon done, output file is %s\n", 372242138Sobrien fmfname); 373242138Sobrien } 3741590Srgrimes } 37534295Speter (void)fclose(fscript); 37634295Speter (void)close(master); 37739481Sdes exit(eno); 3781590Srgrimes} 379238896Sbrian 380238896Sbrianstatic void 381238896Sbrianrecord(FILE *fp, char *buf, size_t cc, int direction) 382238896Sbrian{ 383238896Sbrian struct iovec iov[2]; 384238896Sbrian struct stamp stamp; 385238896Sbrian struct timeval tv; 386238896Sbrian 387238896Sbrian (void)gettimeofday(&tv, NULL); 388238896Sbrian stamp.scr_len = cc; 389238896Sbrian stamp.scr_sec = tv.tv_sec; 390238896Sbrian stamp.scr_usec = tv.tv_usec; 391238896Sbrian stamp.scr_direction = direction; 392238896Sbrian iov[0].iov_len = sizeof(stamp); 393238896Sbrian iov[0].iov_base = &stamp; 394238896Sbrian iov[1].iov_len = cc; 395238896Sbrian iov[1].iov_base = buf; 396238896Sbrian if (writev(fileno(fp), &iov[0], 2) == -1) 397238896Sbrian err(1, "writev"); 398238896Sbrian} 399238896Sbrian 400238896Sbrianstatic void 401238896Sbrianconsume(FILE *fp, off_t len, char *buf, int reg) 402238896Sbrian{ 403238896Sbrian size_t l; 404238896Sbrian 405238896Sbrian if (reg) { 406238896Sbrian if (fseeko(fp, len, SEEK_CUR) == -1) 407238896Sbrian err(1, NULL); 408238896Sbrian } 409238896Sbrian else { 410238896Sbrian while (len > 0) { 411238896Sbrian l = MIN(DEF_BUF, len); 412238896Sbrian if (fread(buf, sizeof(char), l, fp) != l) 413238896Sbrian err(1, "cannot read buffer"); 414238896Sbrian len -= l; 415238896Sbrian } 416238896Sbrian } 417238896Sbrian} 418238896Sbrian 419238896Sbrian#define swapstamp(stamp) do { \ 420238896Sbrian if (stamp.scr_direction > 0xff) { \ 421238896Sbrian stamp.scr_len = bswap64(stamp.scr_len); \ 422238896Sbrian stamp.scr_sec = bswap64(stamp.scr_sec); \ 423238896Sbrian stamp.scr_usec = bswap32(stamp.scr_usec); \ 424238896Sbrian stamp.scr_direction = bswap32(stamp.scr_direction); \ 425238896Sbrian } \ 426238896Sbrian} while (0/*CONSTCOND*/) 427238896Sbrian 428238896Sbrianstatic void 429238896Sbrianplayback(FILE *fp) 430238896Sbrian{ 431238896Sbrian struct timespec tsi, tso; 432238896Sbrian struct stamp stamp; 433238896Sbrian struct stat pst; 434238896Sbrian char buf[DEF_BUF]; 435238896Sbrian off_t nread, save_len; 436238896Sbrian size_t l; 437238896Sbrian time_t tclock; 438238896Sbrian int reg; 439238896Sbrian 440238896Sbrian if (fstat(fileno(fp), &pst) == -1) 441238896Sbrian err(1, "fstat failed"); 442238896Sbrian 443238896Sbrian reg = S_ISREG(pst.st_mode); 444238896Sbrian 445238896Sbrian for (nread = 0; !reg || nread < pst.st_size; nread += save_len) { 446238896Sbrian if (fread(&stamp, sizeof(stamp), 1, fp) != 1) { 447238896Sbrian if (reg) 448238896Sbrian err(1, "reading playback header"); 449238896Sbrian else 450238896Sbrian break; 451238896Sbrian } 452238896Sbrian swapstamp(stamp); 453238896Sbrian save_len = sizeof(stamp); 454238896Sbrian 455238896Sbrian if (reg && stamp.scr_len > 456238896Sbrian (uint64_t)(pst.st_size - save_len) - nread) 457238896Sbrian errx(1, "invalid stamp"); 458238896Sbrian 459238896Sbrian save_len += stamp.scr_len; 460238896Sbrian tclock = stamp.scr_sec; 461238896Sbrian tso.tv_sec = stamp.scr_sec; 462238896Sbrian tso.tv_nsec = stamp.scr_usec * 1000; 463238896Sbrian 464238896Sbrian switch (stamp.scr_direction) { 465238896Sbrian case 's': 466238896Sbrian if (!qflg) 467238896Sbrian (void)printf("Script started on %s", 468238896Sbrian ctime(&tclock)); 469238896Sbrian tsi = tso; 470238896Sbrian (void)consume(fp, stamp.scr_len, buf, reg); 471238896Sbrian break; 472238896Sbrian case 'e': 473238896Sbrian if (!qflg) 474238896Sbrian (void)printf("\nScript done on %s", 475238896Sbrian ctime(&tclock)); 476238896Sbrian (void)consume(fp, stamp.scr_len, buf, reg); 477238896Sbrian break; 478238896Sbrian case 'i': 479238896Sbrian /* throw input away */ 480238896Sbrian (void)consume(fp, stamp.scr_len, buf, reg); 481238896Sbrian break; 482238896Sbrian case 'o': 483238896Sbrian tsi.tv_sec = tso.tv_sec - tsi.tv_sec; 484238896Sbrian tsi.tv_nsec = tso.tv_nsec - tsi.tv_nsec; 485238896Sbrian if (tsi.tv_nsec < 0) { 486238896Sbrian tsi.tv_sec -= 1; 487238896Sbrian tsi.tv_nsec += 1000000000; 488238896Sbrian } 489238896Sbrian if (usesleep) 490238896Sbrian (void)nanosleep(&tsi, NULL); 491238896Sbrian tsi = tso; 492238896Sbrian while (stamp.scr_len > 0) { 493238896Sbrian l = MIN(DEF_BUF, stamp.scr_len); 494238896Sbrian if (fread(buf, sizeof(char), l, fp) != l) 495238896Sbrian err(1, "cannot read buffer"); 496238896Sbrian 497238896Sbrian (void)write(STDOUT_FILENO, buf, l); 498238896Sbrian stamp.scr_len -= l; 499238896Sbrian } 500238896Sbrian break; 501238896Sbrian default: 502238896Sbrian errx(1, "invalid direction"); 503238896Sbrian } 504238896Sbrian } 505238896Sbrian (void)fclose(fp); 506238896Sbrian exit(0); 507238896Sbrian} 508