#ifndef lint static char *sccsid = "@(#)script.c 4.1 (Berkeley) 10/1/80"; #endif /* * script - makes copy of terminal conversation. usage: * * script [ -n ] [ -s ] [ -q ] [ -a ] [ -S shell ] [ file ] * conversation saved in file. default is DFNAME */ #define DFNAME "typescript" #ifdef HOUXP #define STDSHELL "/bin/sh" #define NEWSHELL "/p4/3723mrh/bin/csh" char *shell = NEWSHELL; #endif #ifdef HOUXT #define STDSHELL "/bin/sh" #define NEWSHELL "/t1/bruce/ucb/bin/csh" char *shell = NEWSHELL; #endif #ifdef CORY #define STDSHELL "/bin/sh" #define NEWSHELL "/bin/csh" char *shell = NEWSHELL; #endif #ifdef CC #define STDSHELL "/bin/sh" #define NEWSHELL "/bin/csh" char *shell = NEWSHELL; #endif #ifndef STDSHELL # define V7ENV #endif #ifdef V7ENV #include #include /* used for version 7 with environments - gets your environment shell */ #define STDSHELL "/bin/sh" #define NEWSHELL "/bin/csh" char *shell; /* initialized in the code */ # include # include # define MODE st_mode # define STAT stat char *getenv(); char *ctime(); #else /* * The following is the structure of the block returned by * the stat and fstat system calls. */ struct inode { char i_minor; /* +0: minor device of i-node */ char i_major; /* +1: major device */ int i_number; /* +2 */ int i_flags; /* +4: see below */ char i_nlinks; /* +6: number of links to file */ char i_uid; /* +7: user ID of owner */ char i_gid; /* +8: group ID of owner */ char i_size0; /* +9: high byte of 24-bit size */ int i_size1; /* +10: low word of 24-bit size */ int i_addr[8]; /* +12: block numbers or device number */ int i_actime[2]; /* +28: time of last access */ int i_modtime[2]; /* +32: time of last modification */ }; #define IALLOC 0100000 #define IFMT 060000 #define IFDIR 040000 #define IFCHR 020000 #define IFBLK 060000 #define MODE i_flags #define STAT inode #endif char *tty; /* name of users tty so can turn off writes */ char *ttyname(); /* std subroutine */ int mode = 0622; /* old permission bits for users tty */ int outpipe[2]; /* pipe from shell to output */ int fd; /* file descriptor of typescript file */ int inpipe[2]; /* pipe from input to shell */ long tvec; /* current time */ char buffer[256]; /* for block I/O's */ int n; /* number of chars read */ int status; /* dummy for wait sys call */ char *fname; /* name of typescript file */ int forkval; /* temp for error checking */ int qflg; /* true if -q (quiet) flag */ int aflg; /* true if -q (append) flag */ struct STAT sbuf; int flsh(); main(argc,argv) int argc; char **argv; { int done(); if ((tty = ttyname(2)) < 0) { printf("Nested script not allowed.\n"); fail(); } #ifdef V7ENV shell = getenv("SHELL"); #endif while ( argc > 1 && argv[1][0] == '-') { switch(argv[1][1]) { case 'n': shell = NEWSHELL; break; case 's': shell = STDSHELL; break; case 'S': shell = argv[2]; argc--; argv++; break; case 'q': qflg++; break; case 'a': aflg++; break; default: printf("Bad flag %s - ignored\n",argv[1]); } argc--; argv++; } if (argc > 1) { fname = argv[1]; if (!aflg && stat(fname,&sbuf) >= 0) { printf("File %s already exists.\n",fname); done(); } } else fname = DFNAME; if (!aflg) { fd = creat(fname,0); /* so can't cat/lpr typescript from inside */ } else { /* try to append to existing file first */ fd = open(fname,1); if (fd >= 0) lseek(fd,0l,2); else fd = creat(fname,0); } if (fd<0) { printf("Can't create %s\n",fname); if (unlink(fname)==0) { printf("because of previous typescript bomb - try again\n"); } fail(); } chmod(fname,0); /* in case it already exists */ fixtty(); if (!qflg) { printf("Script started, file is %s\n",fname); check(write(fd,"Script started on ",18)); time(&tvec); check(write(fd,ctime(&tvec),25)); } pipe(inpipe); pipe(outpipe); forkval = fork(); if (forkval < 0) goto ffail; if (forkval == 0) { forkval = fork(); if (forkval < 0) goto ffail; if (forkval == 0) dooutput(); forkval = fork(); if (forkval < 0) goto ffail; if (forkval == 0) doinput(); doshell(); } close(inpipe[0]); close(inpipe[1]); close(outpipe[0]); close(outpipe[1]); signal(SIGINT, SIG_IGN); signal(SIGQUIT, done); wait(&status); done(); /*NOTREACHED*/ ffail: printf("Fork failed. Try again.\n"); fail(); } /* input process - copy tty to pipe and file */ doinput() { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); #ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); #endif close(inpipe[0]); close(outpipe[0]); close(outpipe[1]); /* main input loop - copy until end of file (ctrl D) */ while ((n=read(0,buffer,256)) > 0) { check(write(fd,buffer,n)); write(inpipe[1],buffer,n); } /* end of script - close files and exit */ close(inpipe[1]); close(fd); done(); } /* do output process - copy to tty & file */ dooutput() { signal(SIGINT, flsh); signal(SIGQUIT, SIG_IGN); #ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); #endif close(0); close(inpipe[0]); close(inpipe[1]); close(outpipe[1]); /* main output proc loop */ while (n=read(outpipe[0],buffer,256)) { if (n > 0) { /* -1 means trap to flsh just happened */ write(1,buffer,n); check(write(fd,buffer,n)); } } /* output sees eof - close files and exit */ if (!qflg) { printf("Script done, file is %s\n",fname); check(write(fd,"\nscript done on ",16)); time(&tvec); check(write(fd,ctime(&tvec),25)); } close(fd); exit(0); } /* exec shell, after diverting std input & output */ doshell() { close(0); dup(inpipe[0]); close(1); dup(outpipe[1]); close(2); dup(outpipe[1]); /* close useless files */ close(inpipe[0]); close(inpipe[1]); close(outpipe[0]); close(outpipe[1]); execl(shell, "sh", "-i", 0); execl(STDSHELL, "sh", "-i", 0); execl(NEWSHELL, "sh", "-i", 0); printf("Can't execute shell\n"); fail(); } fixtty() { fstat(2, &sbuf); mode = sbuf.MODE&0777; chmod(tty, 0600); } /* come here on rubout to flush output - this doesn't work */ flsh() { signal(SIGINT, flsh); /* lseek(outpipe[0],0l,2); /* seeks on pipes don't work !"$"$!! */ } fail() { unlink(fname); kill(0, 15); /* shut off other script processes */ done(); } done() { chmod(tty, mode); chmod(fname, 0664); exit(0); } #ifndef V7ENV #ifndef CC char *ttyname(i) int i; { char *string; string = "/dev/ttyx"; string[8] = ttyn(fd); if (string[8] == 'x') return((char *) (-1)); else return(string); } #endif #endif check(nwritten) int nwritten; { /* checks the result of a write call, if neg assume ran out of disk space & die */ if (nwritten < 0) { write(1,"Disk quota exceeded - script quits\n",35); kill(0,15); done(); } }