[net.unix-wizards] brl-vgr Bug Report

dpk@BRL-VGR.ARPA (11/14/84)

Subject: Short summary of the problem
Index:	<source directory>/<source file> 4.2BSD

Description:
	Detailed description of the problem, suggestion, or complaint.
Repeat-By:
	Procedure to repeat the problem.
Fix:
	Description of how to fix the problem.
	If you don't know the fix, don't include this section or "Fix:".

---------- Remove this line and what's below it. (only for reference) ------
<source directory> ::=  bin | etc | games | ideas | lib | local | man |
			misc | sys | ucb | usr.bin | usr.lib

dpk@BRL-VGR.ARPA (11/14/84)

Subject: netstat is grossly inefficient for stupid reason
Index:	ucb/netstat/inet.c 4.2BSD Fix

Description:
	In inet.c:inetname(), netstat stupidly checks for the
	special case of INADDR_ANY (a non-host) only after a linear
	search through the entire host table.  What is worse,
	it will search the ENTIRE table for every INADDR_ANY socket,
	looking for the non-existent entry.

Repeat-By:
	Run "netstat -a" and watch how slow it is.  To be more
	graphic, start up a bunch of processes which hang an accept()
	on INTERNET sockets (say 50 or 100), and make sure your host
	table has several hundred entries (like the ARPA host table).
	Your netstat -a will take FOREVER to complete.

Fix:
	This fix is trivial and will make netstat go MUCH MUCH faster,
	over an order of magnitude faster on our system for a netstat -a.
	I have included a diff listing, although the line number will be
	off by a constant due to a header we stuck on the file (~8 lines).
	Basically, move the check for INADDR_ANY before the search
	through the host table which you KNOW will fail.
	This is a reposting since it did not make it to Berkeley or
	onto the Bug List from MT. XINU.  This fix is EASY.

diff inet.c.old inet.c
4c4
<  * $Revision: 1.2 $
---
>  * $Revision: 1.3 $
6a7,9
>  * Revision 1.3  84/07/04  01:09:36  dpk
>  * Moved check for INADDR_ANY in inetname for efficiency
>  * 
12c15
< static char RCSid[] = "@(#)$Header: inet.c,v 1.2 84/07/04 01:07:20 dpk BRL $";
---
> static char RCSid[] = "@(#)$Header: inet.c,v 1.3 84/07/04 01:09:36 dpk BRL $";
312a316,319
> 	if (in.s_addr == INADDR_ANY) {	/* A quick check for the easy case */
> 		strcpy(line, "*");
> 		return (line);
> 	}
329,331c336
< 	if (in.s_addr == INADDR_ANY)
< 		strcpy(line, "*");
< 	else if (cp)
---
> 	if (cp)

dpk@BRL-VGR.ARPA (11/24/84)

Subject: Tar is inefficient, ignores blocksize
Index:	bin/tar.c 4.2BSD 2.9BSD SysV (and others) Fix

Description:
	While technically not a bug, one can always hope that
	such unnecessary inefficiency is removed from the system.

	A letter on the net mentioning tar's behavior of always
	reading or writing blocks of 512 bytes from or to files
	prompted me to analyze it and see just how bad it was.
	It was quite bad.  It always reads or writes in blocks of 512.
	This is quite inefficient on systems with a blocksize greater
	than 512 bytes.  What's more, it spent an inordinate amount
	of time in the bcopy() routine.  First off, when extracting
	files it is totally unnecessary to bcopy() the data around,
	since you can immediately write it out to disk.  Some further
	work allowed me to actually buffer the data going to disk in
	blocks of max(diskblocksize,tapeblocksize).  For creating
	tar archives, some bcopy()s are unavoidable, but the number
	can be greatly reduced by having the writing to tape and
	reading from disk "get into sync" so that you begin reading
	and writing in tapeblocksize and avoiding all bcopy()s.
	
	I have made an analysis of the behavior before and after the
	improvements to tar were made and as you would expect the
	greatest benefit is achieved when the files involved are
	significantly larger than the tapeblocksize.  The following
	figures summarize the behavior of three version of tar.
	Ntar is the new tar, Tar is 4.2BSD tar, and Tar5 is the
	System V version of Tar under the BRL/SYSV package.

	Create an archive of large files (1 Meg):
		Ntar	0.3u	1.9s
		Tar	1.8u	4.9s
		Tar5	4.7u	4.8s

	Create an archive of small files (1 Meg):
		Ntar	4.2u	6.8s
		Tar	4.4	9.4
		Tar5	7.7	9.1

	Create an archive of /bin (4.2BSD)
		Ntar	1.4u	2.9s
		Tar	2.3	5.5
		Tar5	5.2	6.7

	Extract an archive of Large files (created above)
		Ntar	0.2u	 4.2s
		Tar	1.6	14.0

	Extract an archive of small files (created above)
		Ntar	2.6	17.1
		Tar	4.4	25.5
		Tar5	7.4	29.9

	This inefficency is also present in earlier TARs and is worse
	in most cases because the "bcopy()" routine (probably named
	something else or included in the {read/write}tape() routines)
	is not nearly as efficient as the Berkeley version.

Repeat-By:
	Time tar for extraction and creation.  The time is significant.
	Gprof or prof the tar program and analyze the results.  You
	will find that it is spending a large portion of its time
	in bcopy() and the number of calls to the read/write system
	call for files on disk is also unreasonable.

Fix:
	There were serveral basic changes:
	  - readtape replaced with a routine readtbuf which is passed
	    a pointer to a pointer and a maximum number of bytes to be
	    accepted, returns a pointer to the new data, and the amount
	    there.  
	  - small readtape() routine that emulates old behavior.
	  - writetbuf that take a pointer to data and a count of blocks.
	    It is smart enough to detect that the buffers contain at least
	    tapeblock characters and issues a write without bcopying.
	    This routine also returns the number blocks left to fill
	    the tape block buffer.  This is used to "get into sync".
	  - dynamically allocated big buffers and assured they were page
	    aligned.
	  - changed the putfile and extract functions to use these changes
	    to advantage.

	A "diff -e" editor script follows based on the original 4.2BSD
	tar and a regular diff follows that for other sites.  The changes
	should also be 	almost directly applicable to other versions of TAR.
	ARPA sites for whom BRL has copies of your ATT source license,
	can obtain a copy by sending a message to "~dpk@vgr".

####################################
diff -e tar.c.old tar.c
1109c
		
	while (n-- > 0) {
		bcopy(buffer, (char *)&tbuf[recno++], TBLOCK);
		buffer += TBLOCK;
		if (recno >= nblock) {
			if (write(mt, tbuf, TBLOCK*nblock) < 0) {
				fprintf(stderr, "tar: tape write error\n");
				done(2);
			}
			recno = 0;
		}
	}

	/* Tell the user how much to write to get in sync */
	return (nblock - recno);
.
1107c
		n -= nblock;
		buffer += (nblock * TBLOCK);
.
1101,1103c

	/*
	 *  Special case:  We have an empty tape buffer, and the
	 *  users data size is >= the tape block size:  Avoid
	 *  the bcopy and dma direct to tape.  BIG WIN.  Add the
	 *  residual to the tape buffer.
	 */
	while (recno == 0 && n >= nblock) {
		if (write(mt, buffer, TBLOCK*nblock) < 0) {
.
1090,1091c
writetbuf(buffer, n)
	register char *buffer;
	register int n;
.
1086,1087c
	if (size > ((nblock-recno)*TBLOCK))
		size = (nblock-recno)*TBLOCK;
	*bufpp = (char *)&tbuf[recno];
	recno += (size/TBLOCK);
	return (size);
.
1064a
	char *bufp;
	int nread;

	readtbuf (&bufp, TBLOCK);
	bcopy(bufp, buffer, TBLOCK);
	return(TBLOCK);
}

readtbuf(bufpp, size)
	char **bufpp;
	int size;
{
.
1062c
readtape (buffer)
.
710a
			bytes -= nread;
			blocks -= (((nread-1)/TBLOCK)+1);
.
703,705c
			} else if (write(ofile, bufp, (int) bytes) < 0) {
.
694,697c
		for (; blocks > 0;) {
			register int nread;
			char	*bufp;
			register int nwant;
			
			nwant = NBLOCK*TBLOCK;
			if (nwant > (blocks*TBLOCK))
				nwant = (blocks*TBLOCK);
			nread = readtbuf(&bufp, nwant);
			if (bytes > nread) {
				if (write(ofile, bufp, nread) < 0) {
.
609d
590a
		if (bigbuf != buf)
#ifndef vax
			free(bigbuf);
#else
			free(origbuf);
#endif
.
589a
#endif vax

		while ((i = read(infile, bigbuf, min((hint*TBLOCK), maxread))) > 0
		  && blocks > 0) {
		  	register int nblks;

			nblks = ((i-1)/TBLOCK)+1;
		  	if (nblks > blocks)
		  		nblks = blocks;
			hint = writetbuf(bigbuf, nblks);
			blocks -= nblks;
		}
.
586,588c
			if ((origbuf = malloc(maxread+pagesize)) == 0) {
				maxread = TBLOCK;
				bigbuf = buf;
			} else {
				bigbuf = (char *)(((int)origbuf+pagesize)&~(pagesize-1));
			}
.
584c
		hint = writetape((char *)&dblock);
		maxread = max(stbuf.st_blksize, (nblock * TBLOCK));
#ifndef vax
		if ((bigbuf = malloc(maxread)) == 0) {
			maxread = TBLOCK;
			bigbuf = buf;
		}
#else
		/*
		 *  The following is for 4.2BSD and related systems to force
		 *  the buffer to be page aligned.  The kernel will avoid
		 *  bcopy()'s on disk IO this way by manipulating the page tables.
		 */
		{
			int pagesize = getpagesize();
.
534a
			close(infile);
.
416a
	int	maxread;
	int	hint;		/* amount to write to get "in sync" */
.
410a
#ifdef vax
	char *origbuf;
#endif
	char *bigbuf;
.
400c
		readtbuf(&bufp, TBLOCK);
.
391c
	char *bufp;
.
222a
#else
	/*
	 *  The following is for 4.2BSD and related systems to force
	 *  the buffer to be page aligned.  The kernel will avoid
	 *  bcopy()'s on disk IO this way by manipulating the page tables.
	 */
	{
		int pagesize = getpagesize();

		tbuf = (union hblock *)malloc((nblock*TBLOCK)+pagesize);
		tbuf = (union hblock *)(((int)tbuf+pagesize)&~(pagesize-1));
	}
#endif vax
.
221a
#ifndef vax
.
21a
#define	writetape(b)	writetbuf(b, 1)
#define	min(a,b)  ((a) < (b) ? (a) : (b))
#define	max(a,b)  ((a) > (b) ? (a) : (b))

.
1a
static char RCSid[] = "@(#)$Header: tar.c,v 1.4 84/11/14 00:08:15 root Exp $";
#endif

#ifndef lint
.
0a
/*
 *			T A R . C 
 *
 * $Revision: 1.4 $
 *
 * $Log:	tar.c,v $
 * Revision 1.4  84/11/14  00:08:15  root
 * New more efficient version.  Minimizes the number of bcopys
 * and maximizes block buffering.  Page aligns block buffers.
 * 
 * Revision 1.3  84/02/23  20:24:42  dpk
 * Added missing close(infile) to prevent running out of fd's
 * 
 * Revision 1.2  84/02/23  20:17:02  dpk
 * Added distinctive RCS header
 * 
 */
.
###################### cut here #########################
diff tar.c.old tar.c
0a1,17
> /*
>  *			T A R . C 
>  *
>  * $Revision: 1.4 $
>  *
>  * $Log:	tar.c,v $
>  * Revision 1.4  84/11/14  00:08:15  root
>  * New more efficient version.  Minimizes the number of bcopys
>  * and maximizes block buffering.  Page aligns block buffers.
>  * 
>  * Revision 1.3  84/02/23  20:24:42  dpk
>  * Added missing close(infile) to prevent running out of fd's
>  * 
>  * Revision 1.2  84/02/23  20:17:02  dpk
>  * Added distinctive RCS header
>  * 
>  */
1a19,22
> static char RCSid[] = "@(#)$Header: tar.c,v 1.4 84/11/14 00:08:15 root Exp $";
> #endif
> 
> #ifndef lint
21a43,46
> #define	writetape(b)	writetbuf(b, 1)
> #define	min(a,b)  ((a) < (b) ? (a) : (b))
> #define	max(a,b)  ((a) > (b) ? (a) : (b))
> 
221a247
> #ifndef vax
222a249,261
> #else
> 	/*
> 	 *  The following is for 4.2BSD and related systems to force
> 	 *  the buffer to be page aligned.  The kernel will avoid
> 	 *  bcopy()'s on disk IO this way by manipulating the page tables.
> 	 */
> 	{
> 		int pagesize = getpagesize();
> 
> 		tbuf = (union hblock *)malloc((nblock*TBLOCK)+pagesize);
> 		tbuf = (union hblock *)(((int)tbuf+pagesize)&~(pagesize-1));
> 	}
> #endif vax
391c430
< 	char buf[TBLOCK];
---
> 	char *bufp;
400c439
< 		readtape(buf);
---
> 		readtbuf(&bufp, TBLOCK);
410a450,453
> #ifdef vax
> 	char *origbuf;
> #endif
> 	char *bigbuf;
416a460,461
> 	int	maxread;
> 	int	hint;		/* amount to write to get "in sync" */
534a580
> 			close(infile);
584c630,644
< 		writetape((char *)&dblock);
---
> 		hint = writetape((char *)&dblock);
> 		maxread = max(stbuf.st_blksize, (nblock * TBLOCK));
> #ifndef vax
> 		if ((bigbuf = malloc(maxread)) == 0) {
> 			maxread = TBLOCK;
> 			bigbuf = buf;
> 		}
> #else
> 		/*
> 		 *  The following is for 4.2BSD and related systems to force
> 		 *  the buffer to be page aligned.  The kernel will avoid
> 		 *  bcopy()'s on disk IO this way by manipulating the page tables.
> 		 */
> 		{
> 			int pagesize = getpagesize();
586,588c646,651
< 		while ((i = read(infile, buf, TBLOCK)) > 0 && blocks > 0) {
< 			writetape(buf);
< 			blocks--;
---
> 			if ((origbuf = malloc(maxread+pagesize)) == 0) {
> 				maxread = TBLOCK;
> 				bigbuf = buf;
> 			} else {
> 				bigbuf = (char *)(((int)origbuf+pagesize)&~(pagesize-1));
> 			}
589a653,664
> #endif vax
> 
> 		while ((i = read(infile, bigbuf, min((hint*TBLOCK), maxread))) > 0
> 		  && blocks > 0) {
> 		  	register int nblks;
> 
> 			nblks = ((i-1)/TBLOCK)+1;
> 		  	if (nblks > blocks)
> 		  		nblks = blocks;
> 			hint = writetbuf(bigbuf, nblks);
> 			blocks -= nblks;
> 		}
590a666,671
> 		if (bigbuf != buf)
> #ifndef vax
> 			free(bigbuf);
> #else
> 			free(origbuf);
> #endif
609d689
< 	char buf[TBLOCK];
694,697c774,784
< 		for (; blocks-- > 0; bytes -= TBLOCK) {
< 			readtape(buf);
< 			if (bytes > TBLOCK) {
< 				if (write(ofile, buf, TBLOCK) < 0) {
---
> 		for (; blocks > 0;) {
> 			register int nread;
> 			char	*bufp;
> 			register int nwant;
> 			
> 			nwant = NBLOCK*TBLOCK;
> 			if (nwant > (blocks*TBLOCK))
> 				nwant = (blocks*TBLOCK);
> 			nread = readtbuf(&bufp, nwant);
> 			if (bytes > nread) {
> 				if (write(ofile, bufp, nread) < 0) {
703,705c790
< 				continue;
< 			}
< 			if (write(ofile, buf, (int) bytes) < 0) {
---
> 			} else if (write(ofile, bufp, (int) bytes) < 0) {
710a796,797
> 			bytes -= nread;
> 			blocks -= (((nread-1)/TBLOCK)+1);
1062c1149
< readtape(buffer)
---
> readtape (buffer)
1064a1152,1163
> 	char *bufp;
> 	int nread;
> 
> 	readtbuf (&bufp, TBLOCK);
> 	bcopy(bufp, buffer, TBLOCK);
> 	return(TBLOCK);
> }
> 
> readtbuf(bufpp, size)
> 	char **bufpp;
> 	int size;
> {
1086,1087c1185,1189
< 	bcopy((char *)&tbuf[recno++], buffer, TBLOCK);
< 	return (TBLOCK);
---
> 	if (size > ((nblock-recno)*TBLOCK))
> 		size = (nblock-recno)*TBLOCK;
> 	*bufpp = (char *)&tbuf[recno];
> 	recno += (size/TBLOCK);
> 	return (size);
1090,1091c1192,1194
< writetape(buffer)
< 	char *buffer;
---
> writetbuf(buffer, n)
> 	register char *buffer;
> 	register int n;
1101,1103c1204,1212
< 	bcopy(buffer, (char *)&tbuf[recno++], TBLOCK);
< 	if (recno >= nblock) {
< 		if (write(mt, tbuf, TBLOCK*nblock) < 0) {
---
> 
> 	/*
> 	 *  Special case:  We have an empty tape buffer, and the
> 	 *  users data size is >= the tape block size:  Avoid
> 	 *  the bcopy and dma direct to tape.  BIG WIN.  Add the
> 	 *  residual to the tape buffer.
> 	 */
> 	while (recno == 0 && n >= nblock) {
> 		if (write(mt, buffer, TBLOCK*nblock) < 0) {
1107c1216,1217
< 		recno = 0;
---
> 		n -= nblock;
> 		buffer += (nblock * TBLOCK);
1109c1219,1233
< 	return (TBLOCK);
---
> 		
> 	while (n-- > 0) {
> 		bcopy(buffer, (char *)&tbuf[recno++], TBLOCK);
> 		buffer += TBLOCK;
> 		if (recno >= nblock) {
> 			if (write(mt, tbuf, TBLOCK*nblock) < 0) {
> 				fprintf(stderr, "tar: tape write error\n");
> 				done(2);
> 			}
> 			recno = 0;
> 		}
> 	}
> 
> 	/* Tell the user how much to write to get in sync */
> 	return (nblock - recno);
################### End of Bug Report ####################

brad@bradley.UUCP (11/30/84)

Also watch out for tar in doing updates.  My system is 2.9 11/44
and if I run 'tar cv files' then later run 'tar uv morefiles' tar
writes in blocksize of 20(default). Well on my tape drive, you can
only update tar tapes that use /dev/mt0 and are blocked at 512. tar
uses /dev/rmt0 default with 20. The result is that the 'tar uv' is
stuck out on the end with an EOF bewtween the files.  And sometimes
you can not read the second file becuse (at least on my machine)
the EOF seems to be 512 bytes, and the tar gets directory checksum
error.  You have to fudge to get the second file.

I haven't yet fixed it (I think it is a bug), but will shortly.
Am I the only one with a tape drive that can't update large block
tar files?  Tape drive is a Cipher 900X.

Bradley Smith			UUCP: {cepu,ihnp4,noao,uiucdcs}!bradley!brad
Text Processing			ARPA: cepu!bradley!brad@UCLA-LOCUS
Bradley University		PH: (309) 676-7611 Ext. 446
Peoria, IL 61625

dpk@BRL-VGR.ARPA (03/08/85)

Subject: RSH can hang in rcmd()
Index:	ucb/rsh.c 4.2BSD Fix
	lib/libc/net/rcmd.c 4.2BSD Fix

Description:
	Rsh can hang indefinitly in rcmd for two reasons:

	1.  The accept for the second TCP connection can fail to complete
	    do to remote failure,

	2.  If the remote end fails at the right time, the local end
	    will never discover the failure if it has no data to send
	    (which results in the send side being shutdown).

Repeat-By:
	1.  Wait for rsh to connect to the remote end and send the name
	of the local reserved socket, and then have the daemon exit.
	The rcmd() subroutine will have gone into an accept waiting
	for the remote side to issue a connect() which it never will.

	2.  Difficult, but happened regularly with our rsh'ing of batch
	and unbatch for news.  Create an rsh connection where the local
	end only reads from the connection.  Kill the remote side.
	The rsh will not discover the fact.  The "right time" is when
	the local side has acked all the data received so far, and has
	yet to receive more.  In this state he is content forever unless
	he is doing keepalives.
Fix:
	1.  Add a sanity time in the startup code of rsh to about the program
	with an error indication if the rcmd function takes too long.  While
	I was modifing rsh.c, I removed an extraneous external definition for
	"error()" which does not exist.

	2.  In the rcmd() function, set keepalive on both of the sockets
	to the remote host to allow detection of a downed host.

---------------------------------------------

Context diffs follow for ucb/rsh.c and lib/libc/net/rcmd.c

RCS file: RCS/rsh.c,v
retrieving revision 1.2
diff -c -r1.2 rsh.c
*** /tmp/,RCSt1010538	Thu Mar  7 15:54:48 1985
--- rsh.c	Thu Mar  7 15:44:14 1985
***************
*** 19,25
   * rsh - remote shell
   */
  /* VARARGS */
- int	error();
  char	*index(), *rindex(), *malloc(), *getpass(), *sprintf(), *strcpy();
  
  struct	passwd *getpwuid();

--- 19,24 -----
   * rsh - remote shell
   */
  /* VARARGS */
  char	*index(), *rindex(), *malloc(), *getpass(), *sprintf(), *strcpy();
  
  struct	passwd *getpwuid();
***************
*** 28,33
  int	options;
  int	rfd2;
  int	sendsig();
  
  #define	mask(s)	(1 << ((s) - 1))
  

--- 27,33 -----
  int	options;
  int	rfd2;
  int	sendsig();
+ int	timeout();
  
  #define	mask(s)	(1 << ((s) - 1))
  
***************
*** 115,120
  		fprintf(stderr, "rsh: shell/tcp: unknown service\n");
  		exit(1);
  	}
          rem = rcmd(&host, sp->s_port, pwd->pw_name,
  	    user ? user : pwd->pw_name, args, &rfd2);
          if (rem < 0)

--- 115,122 -----
  		fprintf(stderr, "rsh: shell/tcp: unknown service\n");
  		exit(1);
  	}
+ 	signal (SIGALRM, timeout);
+ 	alarm(120);		/* Sanity timer */
          rem = rcmd(&host, sp->s_port, pwd->pw_name,
  	    user ? user : pwd->pw_name, args, &rfd2);
  	alarm(0);
***************
*** 117,122
  	}
          rem = rcmd(&host, sp->s_port, pwd->pw_name,
  	    user ? user : pwd->pw_name, args, &rfd2);
          if (rem < 0)
                  exit(1);
  	if (rfd2 < 0) {

--- 119,125 -----
  	alarm(120);		/* Sanity timer */
          rem = rcmd(&host, sp->s_port, pwd->pw_name,
  	    user ? user : pwd->pw_name, args, &rfd2);
+ 	alarm(0);
          if (rem < 0)
                  exit(1);
  	if (rfd2 < 0) {
***************
*** 218,221
  {
  
  	(void) write(rfd2, (char *)&signo, 1);
  }

--- 221,230 -----
  {
  
  	(void) write(rfd2, (char *)&signo, 1);
+ }
+ 
+ timeout()
+ {
+ 	fputs("rsh: rcmd: timeout\n", stderr);
+ 	exit(14);
  }

-------------------------------------------------------------------


RCS file: RCS/rcmd.c,v
retrieving revision 1.1
diff -c -r1.1 rcmd.c
*** /tmp/,RCSt1010907	Thu Mar  7 16:14:34 1985
--- rcmd.c	Fri Mar  1 01:00:01 1985
***************
*** 25,30
  	char c;
  	int lport = IPPORT_RESERVED - 1;
  	struct hostent *hp;
  
  	hp = gethostbyname(*ahost);
  	if (hp == 0) {

--- 25,31 -----
  	char c;
  	int lport = IPPORT_RESERVED - 1;
  	struct hostent *hp;
+ 	int on = 1;
  
  	hp = gethostbyname(*ahost);
  	if (hp == 0) {
***************
*** 54,59
  		perror(hp->h_name);
  		return (-1);
  	}
  	lport--;
  	if (fd2p == 0) {
  		write(s, "", 1);

--- 54,62 -----
  		perror(hp->h_name);
  		return (-1);
  	}
+ 	if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof on) < 0)
+ 		perror("setsockopt (SO_KEEPALIVE)");
+ 
  	lport--;
  	if (fd2p == 0) {
  		write(s, "", 1);
***************
*** 82,87
  			goto bad;
  		  }
  		}
  		*fd2p = s3;
  		from.sin_port = ntohs((u_short)from.sin_port);
  		if (from.sin_family != AF_INET ||

--- 82,89 -----
  			lport = 0;
  			goto bad;
  		}
+ 		if (setsockopt(s3, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof on) < 0)
+ 			perror("setsockopt (SO_KEEPALIVE)");
  		*fd2p = s3;
  		from.sin_port = ntohs((u_short)from.sin_port);
  		if (from.sin_family != AF_INET ||

ron@BRL.ARPA (09/27/85)

Subject: Short summary of the problem
Index:	games/scene 4.3BSD

Description:
	Gibert and Sullivan quote attributed to wrong work.

Repeat-By:
	Run fortune until you see this:

		I'm very good at integral and differential calculus,
		I know the scientific names of beings animalculous;
		In short, in matters vegetable, animal, and mineral,
		I am the very model of a modern Major-General.
				-- Gilbert & Sullivan, "H.M.S. Pinafore"
Fix:
	Change the last line to read:
			-- Gilbert & Sullivan, "The Pirates of Penzance"

ron@BRL.ARPA (10/14/86)

Subject: Short summary of the problem
Index:	lib/libc/gen/getusershell.c 4.3BSD

Description:
	Setusershell and endusershell are ineffective and
	sometimes dump core.

Repeat-By:
	Do a few getusershell calls with an interspersed setusershell
	and/or endusershell.
Fix:
	The problem is that getusershell.c is horrible.  Endusershell
	may free things that have not been malloced.  There are two
	separate instances of the shells pointer one local to getusershell.
	Setusershell sets a different one than the one getusershell uses.

	A fixed up version follows:
/*
 * Copyright (c) 1985 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)getusershell.c	5.2 (Berkeley) 3/9/86";
#endif LIBC_SCCS and not lint

#include <sys/param.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdio.h>

#define SHELLS "/etc/shells"

/*
 * Do not add local shells here.  They should be added in /etc/shells
 */
static char *okshells[] =
    { "/bin/sh", "/bin/csh", 0 };

static int inprogress;
static char **shells, *strings;
extern char **initshells();

/*
 * Get a list of shells from SHELLS, if it exists.
 */
char *
getusershell()
{
	char *ret;
	static char **shp;

	if (!inprogress)
		shp = initshells();
	ret = *shp;
	if (*shp != NULL)
		shp++;
	return (ret);
}

endusershell()
{
	
	if (shells != NULL)
		free((char *)shells);
	shells = NULL;
	if (strings != NULL)
		free(strings);
	strings = NULL;
	inprogress = 0;
}

setusershell()
{
	endusershell();
}

static char **
initshells()
{
	register char **sp, *cp;
	register FILE *fp;
	struct stat statb;
	extern char *malloc(), *calloc();

	inprogress = 1;
	if (shells != NULL)
		free((char *)shells);
	shells = NULL;
	if (strings != NULL)
		free(strings);
	strings = NULL;
	if ((fp = fopen(SHELLS, "r")) == (FILE *)0)
		return(okshells);
	if (fstat(fileno(fp), &statb) == -1) {
		(void)fclose(fp);
		return(okshells);
	}
	if ((strings = malloc((unsigned)statb.st_size)) == NULL) {
		(void)fclose(fp);
		return(okshells);
	}
	shells = (char **)calloc((unsigned)statb.st_size / 3, sizeof (char *));
	if (shells == NULL) {
		(void)fclose(fp);
		free(strings);
		strings = NULL;
		return(okshells);
	}
	sp = shells;
	cp = strings;
	while (fgets(cp, MAXPATHLEN + 1, fp) != NULL) {
		while (!isspace(*cp) != '/' && *cp != '\0')
			cp++;
		if (*cp == '#' || *cp == '\0')
			continue;
		*sp++ = cp;
		while (!isspace(*cp) && *cp != '#' && *cp != '\0')
			cp++;
		*cp++ = '\0';
	}
	*sp = (char *)0;
	(void)fclose(fp);
	return (shells);
}

ron@BRL.ARPA (10/14/86)

Subject: chsh won't change shell back
Index:	bin/passwd.c 4.3BSD

Description:
	Chsh won't change shell to one that is earlier in /etc/shells
	file than the one you are using.
Repeat-By:
	Chsh to the a later shell in the file (say tcsh) and then
	try to switch back to csh.
Fix:
	Chsh verifies that the shell you are switching from is in
	/etc/shells by calling getusershell until it matches.  It
	then verifies the new shell by calling getusershell, but it
	does not rewind the file by calling set/endusershell.

	Add endusershell call prior to the second lookup
	/*
	 * Allow user to give shell name w/o preceding pathname.
	 */
	if (u == 0) {
		valid = newshell;
	} else {
!!!		endusershell();
		for (valid = getusershell(); valid; valid = getusershell()) {

ron@BRL.ARPA (10/14/86)

Subject: FTP daemon doesn't like Bourne shell users
Index:	etc/ftpd/ftpd.c 4.3BSD

Description:
	The FTP daemon denies access to users who have the default
	login shell (/bin/sh).
Repeat-By:
	chsh to /bin/sh and then try to FTP to your account.
Fix:
	Ftpd attempts to verify the user's shell against those in
	/etc/shells to prevent accounts like finger from being FTP
	targets.  However, getpwent returns a string with a null
	in it if the shell field is blank and Ftpd tries to compare
	this against /bin/sh and misses.

	This fix avoids the check if the user is using the default system
	shell.  Add the if statement around the getusershell lookup.

	if(*pw->pw_shell != 0) {
		while ((cp = getusershell()) != NULL)
			if (strcmp(cp, pw->pw_shell) == 0)
				break;
		endusershell();
		if (cp == NULL)
			return (0);
	}

ron@BRL.ARPA (10/14/86)

Subject:  Warning to those who would change proc.h
Index:	sys/h/proc.h 4.3BSD

Description:
	Changing the length of the proc structure so that it is not
	double word aligned anymore will cause the system to crash
	in bizarre ways.
Repeat-By:
	Add a word to the proc structure and then put a load on the
	system.  Soon things like ps will stop working and then the
	whole machine will experience a strange trap.
Fix:
	1.  Don't change the proc structure.
	2.  If you do, pad it out to the next double word.

ron@BRL.ARPA (10/14/86)

Subject: Unable to set vv address on subnet
Index:	sys/vaxif/if_vv.c 4.3BSD

Description:
	Ifconfig returns an error when you try to set the address on
	the proteon when using subneting.
Repeat-By:
	ifconfig vv0 128.63.4.3 netmask 255.255.255.0
Fix:
	The vv driver checks to see if the local net part of
	address corresponds to the hardware address of the interface
	installed in your system.  The subnet mask can not be set
	before the address because the mask has to be tied to a
	particular internet address.

	The fix is to make the vv driver mask off all but the lowest
	eight bits of the address before making the validity check.
	This is OK since the device can only deal with eight local
	address bits.  In vvioctl:

                /*
                 * Attempt to check agreement of protocol address
                 * and board address.
                 */
		switch (ifa->ifa_addr.sa_family) {
                case AF_INET:
			if ((in_lnaof(IA_SIN(ifa)->sin_addr) & 0xFF) !=
			    vv_softc[ifp->if_unit].vs_host)
				error = EADDRNOTAVAIL;
			break;
		}
		break;

ron@BRL.ARPA (10/23/86)

Subject: RCP clobbers files.
Index:	bin/rcp.c 4.3BSD

Description:
	Rcp will silently make a file zero lenght if it is specified
	as both the source and destination of a copy.  It is difficult
	to predict when this will happen as there is no sure way to
	verify that two CPU's are in fact using the same filesystem.
	In addiiton, when rcp is called with one argument it just silently
	exit without saying anything.

Repeat-By:
	1.	create a file with non-zero lenght called foo on machine host
		issue command on host:  rcp host:foo foo
		the file will now be zero length.
	2.	type "rcp foo"
Fix:
	Make rcp non-destructive in it's copies.  Rather than doing an
	initial creat, just open the file. Then copy all the blocks.
	When the files are the same, each block will be read and written
	back into the same place it was read.
	Then do an ftruncate to fix up the file length.

	Add usage message when too few arguments.

*** 118,123 ****
--- 114,125 ----
  		}
  	}
  	rem = -1;
+ 
+ 	if(argc <= 1){
+ 		fprintf(stderr,"Usage: rcp [-p] f1 f2; or: rcp [-rp] f1...fn d2\n");
+ 		exit(1);
+ 	}
+ 	
  	if (argc > 2)
  		targetshouldbedirectory = 1;
  	(void) sprintf(cmd, "rcp%s%s%s",
***************
*** 605,611 ****
  			}
  			continue;
  		}
! 		if ((of = creat(nambuf, mode)) < 0) {
  	bad:
  			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
  			continue;
--- 607,613 ----
  			}
  			continue;
  		}
! 		if ((of = open(nambuf, O_WRONLY|O_CREAT, mode)) < 0) {
  	bad:
  			error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
  			continue;
***************
*** 649,654 ****
--- 651,657 ----
  		if (count != 0 && wrerr == 0 &&
  		    write(of, bp->buf, count) != count)
  			wrerr++;
+ 		ftruncate(of, size);
  		(void) close(of);
  		(void) response();
  		if (setimes) {