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