date and time created 80/10/01 17:29:40 by bill
[unix-history] / usr / src / usr.bin / script / script.c
CommitLineData
3481eb52
BJ
1static char *sccsid = "@(#)script.c 4.1 (Berkeley) %G%";
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"
14char *shell = NEWSHELL;
15#endif
16
17#ifdef HOUXT
18#define STDSHELL "/bin/sh"
19#define NEWSHELL "/t1/bruce/ucb/bin/csh"
20char *shell = NEWSHELL;
21#endif
22
23#ifdef CORY
24#define STDSHELL "/bin/sh"
25#define NEWSHELL "/bin/csh"
26char *shell = NEWSHELL;
27#endif
28
29#ifdef CC
30#define STDSHELL "/bin/sh"
31#define NEWSHELL "/bin/csh"
32char *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"
44char *shell; /* initialized in the code */
45# include <sys/types.h>
46# include <sys/stat.h>
47# define MODE st_mode
48# define STAT stat
49char *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
58struct 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
82char *tty; /* name of users tty so can turn off writes */
83char *ttyname(); /* std subroutine */
84int mode = 0622; /* old permission bits for users tty */
85int outpipe[2]; /* pipe from shell to output */
86int fd; /* file descriptor of typescript file */
87int inpipe[2]; /* pipe from input to shell */
88long tvec; /* current time */
89char buffer[256]; /* for block I/O's */
90int n; /* number of chars read */
91int status; /* dummy for wait sys call */
92char *fname; /* name of typescript file */
93int forkval, ttn; /* temps for error checking */
94int qflg; /* true if -q (quiet) flag */
95int aflg; /* true if -q (append) flag */
96struct STAT sbuf;
97int flsh();
98
99main(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
192ffail:
193 printf("Fork failed. Try again.\n");
194 fail();
195}
196
197/* input process - copy tty to pipe and file */
198doinput()
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 */
222dooutput()
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 */
253doshell()
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
275fixtty()
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 */
284flsh()
285{
286
287 signal(SIGINT, flsh);
288 /* lseek(outpipe[0],0l,2); /* seeks on pipes don't work !"$"$!! */
289}
290
291fail()
292{
293
294 unlink(fname);
295 kill(0, 15); /* shut off other script processes */
296 done();
297}
298
299done()
300{
301
302 chmod(tty, mode);
303 chmod(fname, 0664);
304 exit();
305}
306
307#ifndef V7ENV
308#ifndef CC
309char *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
319check(n)
320int 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}