xref: /csrg-svn/usr.bin/script/script.c (revision 1089)
1*1089Sbill static char *sccsid = "@(#)script.c	4.1 (Berkeley) 10/01/80";
2*1089Sbill  /*
3*1089Sbill   * script - makes copy of terminal conversation. usage:
4*1089Sbill   *
5*1089Sbill   * script [ -n ] [ -s ] [ -q ] [ -a ] [ -S shell ] [ file ]
6*1089Sbill   * conversation saved in file. default is DFNAME
7*1089Sbill   */
8*1089Sbill 
9*1089Sbill #define DFNAME "typescript"
10*1089Sbill 
11*1089Sbill #ifdef HOUXP
12*1089Sbill #define STDSHELL "/bin/sh"
13*1089Sbill #define NEWSHELL "/p4/3723mrh/bin/csh"
14*1089Sbill char *shell = NEWSHELL;
15*1089Sbill #endif
16*1089Sbill 
17*1089Sbill #ifdef HOUXT
18*1089Sbill #define STDSHELL "/bin/sh"
19*1089Sbill #define NEWSHELL "/t1/bruce/ucb/bin/csh"
20*1089Sbill char *shell = NEWSHELL;
21*1089Sbill #endif
22*1089Sbill 
23*1089Sbill #ifdef CORY
24*1089Sbill #define STDSHELL "/bin/sh"
25*1089Sbill #define NEWSHELL "/bin/csh"
26*1089Sbill char *shell = NEWSHELL;
27*1089Sbill #endif
28*1089Sbill 
29*1089Sbill #ifdef CC
30*1089Sbill #define STDSHELL "/bin/sh"
31*1089Sbill #define NEWSHELL "/bin/csh"
32*1089Sbill char *shell = NEWSHELL;
33*1089Sbill #endif
34*1089Sbill 
35*1089Sbill #ifndef STDSHELL
36*1089Sbill # define V7ENV
37*1089Sbill #endif
38*1089Sbill 
39*1089Sbill #ifdef V7ENV
40*1089Sbill #include <signal.h>
41*1089Sbill /* used for version 7 with environments - gets your environment shell */
42*1089Sbill #define STDSHELL "/bin/sh"
43*1089Sbill #define NEWSHELL "/bin/csh"
44*1089Sbill char *shell;	/* initialized in the code */
45*1089Sbill # include <sys/types.h>
46*1089Sbill # include <sys/stat.h>
47*1089Sbill # define MODE st_mode
48*1089Sbill # define STAT stat
49*1089Sbill char *getenv();
50*1089Sbill 
51*1089Sbill #else
52*1089Sbill 
53*1089Sbill /*
54*1089Sbill  * The following is the structure of the block returned by
55*1089Sbill  * the stat and fstat system calls.
56*1089Sbill  */
57*1089Sbill 
58*1089Sbill struct inode {
59*1089Sbill 	char	i_minor;	/* +0: minor device of i-node */
60*1089Sbill 	char	i_major;	/* +1: major device */
61*1089Sbill 	int	i_number;	/* +2 */
62*1089Sbill 	int	i_flags;	/* +4: see below */
63*1089Sbill 	char	i_nlinks;	/* +6: number of links to file */
64*1089Sbill 	char	i_uid;		/* +7: user ID of owner */
65*1089Sbill 	char	i_gid;		/* +8: group ID of owner */
66*1089Sbill 	char	i_size0;	/* +9: high byte of 24-bit size */
67*1089Sbill 	int	i_size1;	/* +10: low word of 24-bit size */
68*1089Sbill 	int	i_addr[8];	/* +12: block numbers or device number */
69*1089Sbill 	int	i_actime[2];	/* +28: time of last access */
70*1089Sbill 	int	i_modtime[2];	/* +32: time of last modification */
71*1089Sbill };
72*1089Sbill 
73*1089Sbill #define	IALLOC	0100000
74*1089Sbill #define	IFMT	060000
75*1089Sbill #define		IFDIR	040000
76*1089Sbill #define		IFCHR	020000
77*1089Sbill #define		IFBLK	060000
78*1089Sbill #define MODE i_flags
79*1089Sbill #define STAT inode
80*1089Sbill #endif
81*1089Sbill 
82*1089Sbill char	*tty;		/* name of users tty so can turn off writes */
83*1089Sbill char	*ttyname();	/* std subroutine */
84*1089Sbill int	mode = 0622;	/* old permission bits for users tty */
85*1089Sbill int	outpipe[2];	/* pipe from shell to output */
86*1089Sbill int	fd;		/* file descriptor of typescript file */
87*1089Sbill int	inpipe[2];	/* pipe from input to shell */
88*1089Sbill long	tvec;		/* current time */
89*1089Sbill char	buffer[256];	/* for block I/O's */
90*1089Sbill int	n;		/* number of chars read */
91*1089Sbill int	status;		/* dummy for wait sys call */
92*1089Sbill char	*fname;		/* name of typescript file */
93*1089Sbill int	forkval, ttn;	/* temps for error checking */
94*1089Sbill int	qflg;		/* true if -q (quiet) flag */
95*1089Sbill int	aflg;		/* true if -q (append) flag */
96*1089Sbill struct STAT sbuf;
97*1089Sbill int	flsh();
98*1089Sbill 
99*1089Sbill main(argc,argv) int argc; char **argv; {
100*1089Sbill 
101*1089Sbill 	if ((tty = ttyname(2)) < 0) {
102*1089Sbill 		printf("Nested script not allowed.\n");
103*1089Sbill 		fail();
104*1089Sbill 	}
105*1089Sbill 
106*1089Sbill #ifdef V7ENV
107*1089Sbill 	shell = getenv("SHELL");
108*1089Sbill #endif
109*1089Sbill 
110*1089Sbill 	while ( argc > 1 && argv[1][0] == '-') {
111*1089Sbill 		switch(argv[1][1]) {
112*1089Sbill 			case 'n':
113*1089Sbill 				shell = NEWSHELL;
114*1089Sbill 				break;
115*1089Sbill 			case 's':
116*1089Sbill 				shell = STDSHELL;
117*1089Sbill 				break;
118*1089Sbill 			case 'S':
119*1089Sbill 				shell = argv[2];
120*1089Sbill 				argc--; argv++;
121*1089Sbill 				break;
122*1089Sbill 			case 'q':
123*1089Sbill 				qflg++;
124*1089Sbill 				break;
125*1089Sbill 			case 'a':
126*1089Sbill 				aflg++;
127*1089Sbill 				break;
128*1089Sbill 			default:
129*1089Sbill 				printf("Bad flag %s - ignored\n",argv[1]);
130*1089Sbill 		}
131*1089Sbill 		argc--; argv++;
132*1089Sbill 	}
133*1089Sbill 
134*1089Sbill 	if (argc > 1) {
135*1089Sbill 		fname = argv[1];
136*1089Sbill 		if (!aflg && stat(fname,&sbuf) >= 0) {
137*1089Sbill 			printf("File %s already exists.\n",fname);
138*1089Sbill 			done();
139*1089Sbill 		}
140*1089Sbill 	} else	fname = DFNAME;
141*1089Sbill 	if (!aflg) {
142*1089Sbill 		fd = creat(fname,0);	/* so can't cat/lpr typescript from inside */
143*1089Sbill 	} else {
144*1089Sbill 		/* try to append to existing file first */
145*1089Sbill 		fd = open(fname,1);
146*1089Sbill 		if (fd >= 0) lseek(fd,0l,2);
147*1089Sbill 		    else     fd = creat(fname,0);
148*1089Sbill 	}
149*1089Sbill 	if (fd<0) {
150*1089Sbill 		printf("Can't create %s\n",fname);
151*1089Sbill 		if (unlink(fname)==0) {
152*1089Sbill 			printf("because of previous typescript bomb - try again\n");
153*1089Sbill 		}
154*1089Sbill 		fail();
155*1089Sbill 	}
156*1089Sbill 
157*1089Sbill 	chmod(fname,0);	/* in case it already exists */
158*1089Sbill 	fixtty();
159*1089Sbill 	if (!qflg) {
160*1089Sbill 		printf("Script started, file is %s\n",fname);
161*1089Sbill 		check(write(fd,"Script started on ",18));
162*1089Sbill 		time(&tvec);
163*1089Sbill 		check(write(fd,ctime(&tvec),25));
164*1089Sbill 	}
165*1089Sbill 	pipe(inpipe);
166*1089Sbill 	pipe(outpipe);
167*1089Sbill 
168*1089Sbill 	forkval = fork();
169*1089Sbill 	if (forkval < 0)
170*1089Sbill 		goto ffail;
171*1089Sbill 	if (forkval == 0) {
172*1089Sbill 		forkval = fork();
173*1089Sbill 		if (forkval < 0)
174*1089Sbill 			goto ffail;
175*1089Sbill 		if (forkval == 0)
176*1089Sbill 			dooutput();
177*1089Sbill 		forkval = fork();
178*1089Sbill 		if (forkval < 0)
179*1089Sbill 			goto ffail;
180*1089Sbill 		if (forkval == 0)
181*1089Sbill 			doinput();
182*1089Sbill 		doshell();
183*1089Sbill 	}
184*1089Sbill 	close(inpipe[0]); close(inpipe[1]);
185*1089Sbill 	close(outpipe[0]); close(outpipe[1]);
186*1089Sbill 	signal(SIGINT, SIG_IGN);
187*1089Sbill 	signal(SIGQUIT, done);
188*1089Sbill 	wait(&status);
189*1089Sbill 	done();
190*1089Sbill 	/*NOTREACHED*/
191*1089Sbill 
192*1089Sbill ffail:
193*1089Sbill 	printf("Fork failed. Try again.\n");
194*1089Sbill 	fail();
195*1089Sbill }
196*1089Sbill 
197*1089Sbill /* input process - copy tty to pipe and file */
198*1089Sbill doinput()
199*1089Sbill {
200*1089Sbill 
201*1089Sbill 	signal(SIGINT, SIG_IGN);
202*1089Sbill 	signal(SIGQUIT, SIG_IGN);
203*1089Sbill 	signal(SIGTSTP, SIG_IGN);
204*1089Sbill 
205*1089Sbill 	close(inpipe[0]);
206*1089Sbill 	close(outpipe[0]);
207*1089Sbill 	close(outpipe[1]);
208*1089Sbill 
209*1089Sbill 	/* main input loop - copy until end of file (ctrl D) */
210*1089Sbill 	while ((n=read(0,buffer,256)) > 0) {
211*1089Sbill 		check(write(fd,buffer,n));
212*1089Sbill 		write(inpipe[1],buffer,n);
213*1089Sbill 	}
214*1089Sbill 
215*1089Sbill 	/* end of script - close files and exit */
216*1089Sbill 	close(inpipe[1]);
217*1089Sbill 	close(fd);
218*1089Sbill 	done();
219*1089Sbill }
220*1089Sbill 
221*1089Sbill /* do output process - copy to tty & file */
222*1089Sbill dooutput()
223*1089Sbill {
224*1089Sbill 
225*1089Sbill 	signal(SIGINT, flsh);
226*1089Sbill 	signal(SIGQUIT, SIG_IGN);
227*1089Sbill 	signal(SIGTSTP, SIG_IGN);
228*1089Sbill 	close(0);
229*1089Sbill 	close(inpipe[0]);
230*1089Sbill 	close(inpipe[1]);
231*1089Sbill 	close(outpipe[1]);
232*1089Sbill 
233*1089Sbill 	/* main output proc loop */
234*1089Sbill 	while (n=read(outpipe[0],buffer,256)) {
235*1089Sbill 		if (n > 0) { /* -1 means trap to flsh just happened */
236*1089Sbill 			write(1,buffer,n);
237*1089Sbill 			check(write(fd,buffer,n));
238*1089Sbill 		}
239*1089Sbill 	}
240*1089Sbill 
241*1089Sbill 	/* output sees eof - close files and exit */
242*1089Sbill 	if (!qflg) {
243*1089Sbill 		printf("Script done, file is %s\n",fname);
244*1089Sbill 		check(write(fd,"\nscript done on ",16));
245*1089Sbill 		time(&tvec);
246*1089Sbill 		check(write(fd,ctime(&tvec),25));
247*1089Sbill 	}
248*1089Sbill 	close(fd);
249*1089Sbill 	exit(0);
250*1089Sbill }
251*1089Sbill 
252*1089Sbill /* exec shell, after diverting std input & output */
253*1089Sbill doshell()
254*1089Sbill {
255*1089Sbill 
256*1089Sbill 	close(0);
257*1089Sbill 	dup(inpipe[0]);
258*1089Sbill 	close(1);
259*1089Sbill 	dup(outpipe[1]);
260*1089Sbill 	close(2);
261*1089Sbill 	dup(outpipe[1]);
262*1089Sbill 
263*1089Sbill 	/* close useless files */
264*1089Sbill 	close(inpipe[0]);
265*1089Sbill 	close(inpipe[1]);
266*1089Sbill 	close(outpipe[0]);
267*1089Sbill 	close(outpipe[1]);
268*1089Sbill 	execl(shell, "sh", "-i", 0);
269*1089Sbill 	execl(STDSHELL, "sh", "-i", 0);
270*1089Sbill 	execl(NEWSHELL, "sh", "-i", 0);
271*1089Sbill 	printf("Can't execute shell\n");
272*1089Sbill 	fail();
273*1089Sbill }
274*1089Sbill 
275*1089Sbill fixtty()
276*1089Sbill {
277*1089Sbill 
278*1089Sbill 	fstat(2, &sbuf);
279*1089Sbill 	mode = sbuf.MODE&0777;
280*1089Sbill 	chmod(tty, 0600);
281*1089Sbill }
282*1089Sbill 
283*1089Sbill /* come here on rubout to flush output - this doesn't work */
284*1089Sbill flsh()
285*1089Sbill {
286*1089Sbill 
287*1089Sbill 	signal(SIGINT, flsh);
288*1089Sbill 	/* lseek(outpipe[0],0l,2);	/* seeks on pipes don't work !"$"$!! */
289*1089Sbill }
290*1089Sbill 
291*1089Sbill fail()
292*1089Sbill {
293*1089Sbill 
294*1089Sbill 	unlink(fname);
295*1089Sbill 	kill(0, 15);	/* shut off other script processes */
296*1089Sbill 	done();
297*1089Sbill }
298*1089Sbill 
299*1089Sbill done()
300*1089Sbill {
301*1089Sbill 
302*1089Sbill 	chmod(tty, mode);
303*1089Sbill 	chmod(fname, 0664);
304*1089Sbill 	exit();
305*1089Sbill }
306*1089Sbill 
307*1089Sbill #ifndef V7ENV
308*1089Sbill #ifndef CC
309*1089Sbill char *ttyname(i) int i; {
310*1089Sbill 	char *string;
311*1089Sbill 	string = "/dev/ttyx";
312*1089Sbill 	string[8] = ttyn(fd);
313*1089Sbill 	if (string[8] == 'x') return((char *) (-1));
314*1089Sbill 		else return(string);
315*1089Sbill }
316*1089Sbill #endif
317*1089Sbill #endif
318*1089Sbill 
319*1089Sbill check(n)
320*1089Sbill int n;
321*1089Sbill {
322*1089Sbill 	/* checks the result of a write call, if neg
323*1089Sbill 	   assume ran out of disk space & die */
324*1089Sbill 	if (n < 0) {
325*1089Sbill 		write(1,"Disk quota exceeded - script quits\n",35);
326*1089Sbill 		kill(0,15);
327*1089Sbill 		done();
328*1089Sbill 	}
329*1089Sbill }
330