[net.unix] what is going on here ???

ab@unido.UUCP (05/30/85)

If you are running 4.2, look at the following short program and
guess what it will do. Then compile and run it and find out
what it really does. 

Try to explain its behavior!! (- Is it a bug in 4.2 ??? :-) )

	Andreas

--
Andreas Bormann                 ab@unido.UUCP
University of Dortmund          N 51 29' 05"   E 07 24' 42"
West Germany

--- CUT HERE ---------- CUT HERE ---------- CUT HERE ---------- CUT HERE ----
#include <nlist.h>

#define NAMELIST "/vmunix"

struct nlist    avenrun[] = {
      { "_avenrun" },
      { "" }
};

main()
{
	register int    kmem;
	double  avg[3];

	if((kmem = open ("/dev/kmem", 0) < 0)) {
		printf("Cannot open kmem\n");
		exit(1);
	}
	nlist(NAMELIST, avenrun);
	if(avenrun[0].n_type == 0) {
		printf("Cannot find avenrun\n");
		exit(1);
	}
	lseek(kmem, (long) avenrun[0].n_value, 0);
	read(kmem, (char *) avg, 3 * sizeof (double));
	printf("Load average: %f %f %f\n", avg[0], avg[1], avg[2]);
	exit(0);
}

chris@umcp-cs.UUCP (Chris Torek) (06/20/85)

The last element of a struct nlist array as handed to nlist() should
be {0} (i.e., a null pointer in the name field), not {""} (which
is a null string in the name field, which will make nlist() do
crazy things).
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@maryland

henry@utzoo.UUCP (Henry Spencer) (06/24/85)

> The last element of a struct nlist array as handed to nlist() should
> be {0} (i.e., a null pointer in the name field), not {""} (which
> is a null string in the name field, which will make nlist() do
> crazy things).

We have here a problem that dates back to the vague and obscure wording
of the V7 nlist(3) manual page.  V7 really *does* want an empty string for
that initializer, because the array element is "char[8]", not "char *".
Later Unixes and non-Unixes have done things differently; some of them
may not have realized this, because the manual page is so vague.

Fortuitously, "{0}" is legal and correct under either interpretation,
and hence should be preferred.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

ab@unido.UUCP (06/26/85)

> If you are running 4.2, look at the following short program and
> guess what it will do. Then compile and run it and find out
> what it really does. 
> 
> Try to explain its behavior!! (- Is it a bug in 4.2 ??? :-) )

Here is the solution to the previously posted 'puzzle':

It is a nice method to terminate all running shells which share
the controlling terminal. -- And it is an example of unexpected
side-effects, caused by a relatively small and hidden bug.

The bug was produced by a typo in the statement which opens /dev/kmem.
Notice the wrong positions of the parentheses:

>       if((kmem = open ("/dev/kmem", 0) < 0)) {

kmem stays 0 and the lseek on kmem is actually done on stdin !!!

The lseek takes the read/write-pointer of the terminal-channel to
an unaccessible position and all following reads or writes of any
process from/to the terminal will fail. This will cause all shells
to terminate! (See also the source below.)

Andreas Bormann                 ab@unido.UUCP
University of Dortmund          N 51 29' 05"   E 07 24' 42"
West Germany

--- CUT HERE ---------- CUT HERE ---------- CUT HERE ---------- CUT HERE ----
#include <nlist.h>

#define NAMELIST "/vmunix"

struct nlist    avenrun[] = {
      { "_avenrun" },
      { "" }
};

main()
{
	register int    kmem;
	double  avg[3];

	if((kmem = open ("/dev/kmem", 0) < 0)) { /* HERE's THE BUG !!! */
/*
 * If the open-call succeeds, it will return a positive filedescriptor
 * and kmem will be 0 because the positive descriptor isn't less than 0.
 *
 * This is the corrected statement:
 *  
 *      if((kmem = open ("/dev/kmem", 0)) < 0) {
 */
		printf("Cannot open kmem\n");    
		exit(1);
	}
	nlist(NAMELIST, avenrun);
	if(avenrun[0].n_type == 0) {
		printf("Cannot find avenrun\n");
		exit(1);
	}
/*
 * Remember: kmem == 0 !!
 * avenrun[0].n_value is something like -2147230472,
 * so the lseek will seek the read/write-pointer ON THE STDIN to
 * an unaccessible position.
 */
	lseek(kmem, (long) avenrun[0].n_value, 0);
/*
 * All successive reads by all processes which share the terminal
 * will now return an error (-1) and the parent shells will terminate!!!
 */
	read(kmem, (char *) avg, 3 * sizeof (double));
	printf("Load average: %f %f %f\n", avg[0], avg[1], avg[2]);
	exit(0);
}