1 static char *sccsid = "@(#)script.c 4.1 (Berkeley) 10/01/80"; 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 } 330