BSD 4 release
[unix-history] / usr / src / cmd / script.c
static char *sccsid = "@(#)script.c 4.1 (Berkeley) 10/1/80";
/*
* 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 <signal.h>
/* 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 <sys/types.h>
# include <sys/stat.h>
# define MODE st_mode
# define STAT stat
char *getenv();
#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, ttn; /* temps 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; {
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);
signal(SIGTSTP, SIG_IGN);
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);
signal(SIGTSTP, SIG_IGN);
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();
}
#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(n)
int n;
{
/* checks the result of a write call, if neg
assume ran out of disk space & die */
if (n < 0) {
write(1,"Disk quota exceeded - script quits\n",35);
kill(0,15);
done();
}
}