Commit | Line | Data |
---|---|---|
3481eb52 BJ |
1 | static 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" | |
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 | } |