| 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 | } |