Commit | Line | Data |
---|---|---|
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" | |
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 | /* used for version 7 with environments - gets your environment shell */ | |
41 | #define STDSHELL "/bin/sh" | |
42 | #define NEWSHELL "/bin/csh" | |
43 | char *shell; /* initialized in the code */ | |
44 | # include <sys/types.h> | |
45 | # include <sys/stat.h> | |
46 | # define MODE st_mode | |
47 | # define STAT stat | |
48 | char *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 | ||
57 | struct 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 | ||
81 | char *tty; /* name of users tty so can turn off writes */ | |
82 | char *ttyname(); /* std subroutine */ | |
83 | int mode = 0622; /* old permission bits for users tty */ | |
84 | int outpipe[2]; /* pipe from shell to output */ | |
85 | int fd; /* file descriptor of typescript file */ | |
86 | int inpipe[2]; /* pipe from input to shell */ | |
87 | long tvec; /* current time */ | |
88 | char buffer[256]; /* for block I/O's */ | |
89 | int n; /* number of chars read */ | |
90 | int status; /* dummy for wait sys call */ | |
91 | char *fname; /* name of typescript file */ | |
92 | int forkval, ttn; /* temps for error checking */ | |
93 | int qflg; /* true if -q (quiet) flag */ | |
94 | int aflg; /* true if -q (append) flag */ | |
95 | struct STAT sbuf; | |
96 | int flsh(); | |
97 | ||
98 | main(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 | ||
183 | doinput() { | |
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 | ||
207 | dooutput() { | |
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 | ||
236 | doshell() { | |
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 | ||
258 | fixtty() | |
259 | { | |
260 | ||
261 | fstat(2, &sbuf); | |
262 | mode = sbuf.MODE&0777; | |
263 | chmod(tty, 0600); | |
264 | } | |
265 | ||
266 | flsh() { | |
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 | ||
272 | fail() { | |
273 | unlink(fname); | |
274 | kill(0,15); /* shut off other script processes */ | |
275 | done(); | |
276 | } | |
277 | ||
278 | done() { | |
279 | chmod(tty,mode); | |
280 | chmod(fname,0664); | |
281 | exit(); | |
282 | } | |
283 | ||
284 | #ifndef V7ENV | |
285 | #ifndef CC | |
286 | char *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 | ||
296 | check(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 | } |