steve@umd-cs.UUCP (Steven M. Miller) (04/29/86)
We're having a fairly serious problem on our vax used for Instructional computing: Problem: Script hangs upon exit with control-d It only does so when a process is running in the background on the same pty that script was using. OS: Ultrix 1.1 Machine: Vax 11/750 Anyone know what causes this??? -steve -- Spoken: Steven M. Miller UUCP: umd-cs!steve CSNET: steve%umn-duluth USNail: Computer Science Dept, University of Minnesota at Duluth 10 University Drive, Duluth, MN 55812
schaefer@bgsuvax.UUCP (Stephen Schaefer) (05/03/86)
I've fixed about three different pieces of script. When I last offered to post them, I got one response, so I mailed instead. Is there new interest? -- Stephen P. Schaefer Systems Programmer schaefer@bgsu ...!cbosgd!osu-eddie!bgsuvax!schaefer
graham@brueer.UUCP (Graham Carpenter) (05/08/86)
> We're having a fairly serious problem on our vax used for Instructional > computing: > > Problem: Script hangs upon exit with control-d > It only does so when a process is running in the background > on the same pty that script was using. > > OS: Ultrix 1.1 > Machine: Vax 11/750 > > Anyone know what causes this??? > > > -steve We had a similar problem on a VAX running 4.2BSD. It occurred when a user tried to run script from a terminal which had been left in an odd state by some other process - eg someone exiting emacs by killing it. Those characteristics of the pty which script isn't bothered about are set to be the same as the user's tty. If any of these characteristics are at all weird then CTRL-D will kill the child process which opened the slave pty without closing the pty properly. Then you are hung! Sending a SIGCHLD to the parent process doesn't work because the parent only tests for the closure of the pty. I don't know why SIGCHLD is not a valid way of telling the parent that the child is done, but I'm no Unix expert (Comments from a more informed source will be gratefully received!). Anyway - my fix was to modify the source to script to look for a SIGCHLD <<OR>> the closure of the pty. I've included the context diffs to script.c (in shar format to stop the satellites munging them as they cross the pond!) - hope they are of some help. I've been using this version for about 6 weeks now with no problems at all. Graham /------------------------------------------------------------------------------\ | Graham J Carpenter. | VOICE: +44 895 74000 Ext 2849 | | Dept of Electrical Engineering | | | and Electronics | ARPA: graham%ee.brunel.ac.uk@ucl-cs.arpa | | Brunel University | | | Uxbridge | UUCP: ...!mcvax!ukc!ee.brunel.ac.uk!graham | | UB8 3PH | | | UNITED KINGDOM | JANET: graham@uk.ac.brunel.ee | \------------------------------------------------------------------------------/ #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # scriptdiff # This archive created: Thu May 8 10:07:30 1986 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'scriptdiff'" '(4304 characters)' if test -f 'scriptdiff' then echo shar: "will not over-write existing file 'scriptdiff'" else sed 's/^ X//' << \SHAR_EOF > 'scriptdiff' X*** script.c Thu May 8 09:56:26 1986 X--- script.c.dist Thu May 8 09:57:33 1986 X*************** X*** 4,19 X X /* X * script X- * X- * Modified by Graham Carpenter - Brunel University 9/4/86 X- * X- * Make exit condition of child dependent upon EITHER SIGCHLD from grandchild X- * OR closure of channel to psuedo-teletype. X- * X- * Original script.c only checks closure of pty. This may not always close X- * if pty inherits strange attributes from /dev/tty, leaving shell exited X- * but script not closed. System than hangs. Script cannot then be killed X- * by signals without losing buffered output to script file. X */ X X #include <stdio.h> X X--- 4,9 ----- X X /* X * script X */ X #include <stdio.h> X #include <signal.h> X*************** X*** 15,21 X * but script not closed. System than hangs. Script cannot then be killed X * by signals without losing buffered output to script file. X */ X- X #include <stdio.h> X #include <signal.h> X #include <sys/types.h> X X--- 5,10 ----- X /* X * script X */ X #include <stdio.h> X #include <signal.h> X #include <sys/types.h> X*************** X*** 31,38 X int master; X int slave; X int child; X- int grandchild; X- int sigfrom; X char *fname = "typescript"; X int finish(); X time_t tvec; X X--- 20,25 ----- X int master; X int slave; X int child; X char *fname = "typescript"; X int finish(); X X*************** X*** 35,41 X int sigfrom; X char *fname = "typescript"; X int finish(); X! time_t tvec; X struct sgttyb b; X struct tchars tc; X struct ltchars lc; X X--- 22,28 ----- X int child; X char *fname = "typescript"; X int finish(); X! X struct sgttyb b; X struct tchars tc; X struct ltchars lc; X*************** X*** 48,53 X int argc; X char *argv[]; X { X X shell = getenv("SHELL"); X if (shell == 0) X X--- 35,41 ----- X int argc; X char *argv[]; X { X+ int f; X X shell = getenv("SHELL"); X if (shell == 0) X*************** X*** 84,91 X fail(); X } X if (child == 0) { X! grandchild = fork(); X! if (grandchild < 0) { X perror("fork"); X fail(); X } X X--- 72,79 ----- X fail(); X } X if (child == 0) { X! f = fork(); X! if (f < 0) { X perror("fork"); X fail(); X } X*************** X*** 89,95 X perror("fork"); X fail(); X } X! if (grandchild) X dooutput(); X else X doshell(); X X--- 77,83 ----- X perror("fork"); X fail(); X } X! if (f) X dooutput(); X else X doshell(); X*************** X*** 110,116 X X #include <sys/wait.h> X X- X finish() X { X union wait status; X X--- 98,103 ----- X X #include <sys/wait.h> X X finish() X { X union wait status; X*************** X*** 115,125 X { X union wait status; X X! if ((sigfrom = wait3(&status, WNOHANG, 0)) == child) X! done(); X! else if (sigfrom == grandchild) X! donechild(); X! else X return; X } X X X--- 102,108 ----- X { X union wait status; X X! if (wait3(&status, WNOHANG, 0) != child) X return; X done(); X } X*************** X*** 121,126 X donechild(); X else X return; X } X X dooutput() X X--- 104,110 ----- X X if (wait3(&status, WNOHANG, 0) != child) X return; X+ done(); X } X X dooutput() X*************** X*** 125,130 X X dooutput() X { X char obuf[BUFSIZ]; X int cc; X X X--- 109,115 ----- X X dooutput() X { X+ time_t tvec; X char obuf[BUFSIZ]; X int cc; X X*************** X*** 138,144 X (void) write(1, obuf, cc); X (void) fwrite(obuf, 1, cc, fscript); X } X! donechild(); X } X X doshell() X X--- 123,133 ----- X (void) write(1, obuf, cc); X (void) fwrite(obuf, 1, cc, fscript); X } X! tvec = time((time_t *)0); X! fprintf(fscript,"\nscript done on %s", ctime(&tvec)); X! (void) fclose(fscript); X! (void) close(master); X! exit(0); X } X X doshell() X*************** X*** 183,206 X { X X ioctl(0, TIOCSETP, (char *)&b); X! printf("\nScript done, file is %s\n", fname); X! exit(0); X! } X! X! /* X! * Donechild closes output file and exits child - this is called when X! * EITHER child receives SIGCHLD from grandchild (shell) OR if psuedo- X! * teletype closes. X! */ X! X! X! donechild() X! { X! X! tvec = time((time_t *)0); X! fprintf(fscript,"\nscript done on %s", ctime(&tvec)); X! (void) fclose(fscript); X! (void) close(master); X exit(0); X } X X X--- 172,178 ----- X { X X ioctl(0, TIOCSETP, (char *)&b); X! printf("Script done, file is %s\n", fname); X exit(0); X } X SHAR_EOF if test 4304 -ne "`wc -c < 'scriptdiff'`" then echo shar: "error transmitting 'scriptdiff'" '(should have been 4304 characters)' fi fi exit 0 # End of shell archive
schaefer@bgsuvax.UUCP (Stephen Schaefer) (05/13/86)
I suppose I should be quicker with my "f" key. Here are fixes to three different problems with script, building from the code distributed with 4.2BSD: % rlog script.c RCS file: RCS/script.c,v; Working file: script.c head: 1.5 locks: root: 1.5; strict access list: symbolic names: comment leader: " * " total revisions: 5; selected revisions: 5 description: As received from Berkeley. ---------------------------- revision 1.5 locked by: root; date: 86/05/13 15:07:58; author: root; state: Exp; lines added/del: 3/0 Disable the following, which CBREAK was not passing through as desired: t_flushc (toggles throwing away output), t_lnextc (quotes characters - I hope nothing needs this now), and ^M (which was being translated to ^J on input, to ^M^J on output). I hope I haven't missed anything... ---------------------------- revision 1.4 date: 86/01/28 16:53:17; author: root; state: Exp; lines added/del: 13/1 Changed real terminal mode to CBREAK while running, setting the real terminals interrupt generating characters undefined. All this to handle control-s/control-q. ---------------------------- revision 1.3 date: 85/12/10 17:17:28; author: root; state: Exp; lines added/del: 13/5 Problem: script would open /dev/ptyXX for which it did not have suitable permissions on the corresponding /dev/ttyXX. Fix: in getmaster(), try the next line if /dev/ttyXX does not have suitable permissions, as returned by stat(2). ---------------------------- revision 1.2 date: 85/12/05 14:02:48; author: root; state: Exp; lines added/del: 15/6 Problem: script would occasionally hang when exiting its subshell. Caused by a race condition when the script child performed a "read" system call, which either would or would not return <=0 when the shell child died. Fix: changed the routing which handles the SIGCHLD signal in the script child to perform proper cleanup - named "finish2". ---------------------------- revision 1.1 date: 85/12/05 13:55:54; author: root; state: Exp; Initial revision ============================================================================= % rcsdiff -r1.1 script.c RCS file: RCS/script.c,v retrieving revision 1.1 diff -r1.1 script.c 15a16,18 > #define READ_PERM 4 > #define WRITE_PERM 2 > 24a28 > int finish2(); 75c79,80 < f = fork(); --- > (void) signal(SIGCHLD, finish2); > f = fork(); 109a115,125 > finish2() > { > time_t tvec; > > tvec = time((time_t *)0); > fprintf(fscript,"\nscript done on %s", ctime(&tvec)); > (void) fclose(fscript); > (void) close(master); > exit(0); > } > 126,130c142 < tvec = time((time_t *)0); < fprintf(fscript,"\nscript done on %s", ctime(&tvec)); < (void) fclose(fscript); < (void) close(master); < exit(0); --- > finish2(); 156a169,170 > struct tchars tbuf; > struct ltchars lbuf; 159c173 < sbuf.sg_flags |= RAW; --- > sbuf.sg_flags |= CBREAK; 160a175 > sbuf.sg_flags &= ~CRMOD; 161a177,186 > tbuf = tc; > tbuf.t_intrc = -1; > tbuf.t_quitc = -1; > ioctl(0, TIOCSETC, (char *)&tbuf); > lbuf = lc; > lbuf.t_suspc = -1; > lbuf.t_dsuspc = -1; > lbuf.t_flushc = -1; > lbuf.t_lnextc = -1; > ioctl(0, TIOCSLTC, (char *)&lbuf); 174a200,201 > ioctl(0, TIOCSETC, (char *)&tc); > ioctl(0, TIOCSLTC, (char *)&lc); 184a212 > line[strlen("/dev/")] = 't'; 186,189c214 < line[strlen("/dev/pty")] = c; < line[strlen("/dev/ptyp")] = '0'; < if (stat(line, &stb) < 0) < break; --- > line[strlen("/dev/tty")] = c; 191c216,222 < line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; --- > line[strlen("/dev/ttyp")] = "0123456789abcdef"[i]; > if (stat(line, &stb) < 0) /* does line exist? */ > continue; > if (((READ_PERM | WRITE_PERM) & (stb.st_mode)) != > (READ_PERM | WRITE_PERM)) /* protected? */ > continue; > line[strlen("/dev/")] = 'p'; 200a232 > line[strlen("/dev/")] = 't'; -- Stephen P. Schaefer Systems Programmer schaefer@bgsu ...!cbosgd!osu-eddie!bgsuvax!schaefer