script.c 6.5 KB

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