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