[net.unix-wizards] Script Hanging

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