[comp.unix.wizards] Of csh & filepointers & fopen calls ...

bobm@rtech.UUCP (02/04/87)

I ran across this while trying to figure out how to tell following an
fopen() call that the reason it failed was due to too many open file
pointers.

Take the following program which simply opens a file over and over again
until it fails and prints the number of the failed fopen(), and the last
error number (the obvious declarations omitted for brevity):

	++argv;
	for (count = 1; 1; ++count)
	{
		if (fopen(*argv,"r") == NULL)
		{
			printf("file %d, errno %d\n",count,errno);
			exit(1);
		}
	}

If you just run this thing, you get:

file 62, errno 0

This is on an ULTRIX system which allows 64 open files.  Fine.  Subtract
3 for stdin / stdout / stderr, realize that fopen() doesn't leave errno
set.  That's what you ought to get.  "exec" this thing under either ksh
or sh and you get the same result.  NOW, "exec" it under csh, and you get:

file 58, errno 24

24 is the error number for too many open files.  Two questions I'm curious
about:

WHAT is csh doing with those 4 extra file pointers it apparently has open?

WHAT does csh set up differently which causes the errno 24 to be hanging
around after the fopen call (I assume coming from the underlying open())?

You get this same result (4 fewer files / meaningful error number) on
a couple other systems (Pyramids and CCIs) except for the actual number
of files opened, which differs from system to system.  You also get this
result if you assure that you are opening different files, not that that
should make any difference.
-- 

Bob McQueer
{amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm

brett@wjvax.UUCP (02/11/87)

In article <638@rtech.UUCP> bobm@rtech.UUCP (Bob Mcqueer) writes:
>I ran across this while trying to figure out how to tell following an
>fopen() call that the reason it failed was due to too many open file
>pointers.
>WHAT is csh doing with those 4 extra file pointers it apparently has open?
>WHAT does csh set up differently which causes the errno 24 to be hanging
>around after the fopen call (I assume coming from the underlying open())?

I don't have source, but I will hazard a guess.  The stdio package allocates
a static list of NOFILE entries, where NOFILE is the number of file descriptors
which can be open; stdin, stdout, and stderr are set to be already open.
When you fopen(), the stdio package first must find an entry in this list; then
it must open() the underlying file.  Apparently, csh leaves some other
file descriptors open across the exec(), which the stdio package can't
know about.  Thus, the stdio package still has entries available when
it runs out of file descriptors.  However, if the shell only leaves
stdin, stdout, and stderr open, the stdio package runs out of FILE entries
and file descriptors at the same time.  However, it must check for a
FILE entry first; otherwise the open might fail and yet leave the underlying
file open. Thus, fopen() returns error because there are no FILE entries
left, and the stdio package is too stupid to simulate an EMFILE error in
that case.

-- 
-------------
Brett Galloway
{pesnta,twg,ios,qubix,turtlevax,tymix,vecpyr,certes,isi}!wjvax!brett

ken@rochester.UUCP (02/13/87)

I seem to remember csh used its own file descriptors for error messages
to the terminal. That would account for one descriptor used up. Anybody
got the full story?

	Ken

chris@mimsy.UUCP (02/13/87)

In article <176@quacky.mips.UUCP> dce@mips.UUCP (David Elliott) writes:
>I believe that what csh does with the extra file descriptors is to use
>them to save the values of the standard file descriptors when redirection
>and pipes are done. Without this, you have to fork each time you do
>redirection or pipes, which can cause problems when it involves control
>structures (if, while, foreach, etc.).

Indeed, csh salts away copies of its stdin, stdout, and stderr in
`high numbered' file descriptors.  In 4.1BSD these were 17, 18,
and 19, as NOFILE was 20, making [0..20) legal file descriptor
values.  These were arranged to be closed on exec() by, as I recall,
FIOCLEX ioctls on the *old* descriptors.  Dup2 originally carried
this attribute over to the new descriptors.  This behaviour was
deemed bogus, and dropped in 4.2.  It seems, however, that the 4.2
csh was converted by a process best described as `tweak it until
it works'.  No one noticed that the extra descriptors were left
behind.

I thought all this had been straightened out in 4.3BSD, but it is
possible it was overlooked.  Csh is one large series of bugs, and
desperately needs a complete overhaul.  But I do not promise to do
it.

Incidentally, it really is not necessary to move stdin &c out of
the way for redirection of internal commands.  A possible data
structure to demonstrate:

	struct cnode {	/* command node */
		int	cn_type;	/* type of this node */
		char	**cn_cmd;	/* argv */
		struct	redir *cn_redir;/* I/O redirections */
		caddr_t	cn_data;	/* type-dependent data (if any) */
	};

	/*
	 * There may be many command nodes pointing at some particular
	 * redirection node.  In particular, a redirected loop construct
	 * will have all its subcommands pointing to the same redir
	 * (unless those subcommands have also been redirected).
	 */
	struct redir {	/* redirection node */
		int	r_refcnt;	/* reference count */
		struct	redir *r_next;	/* linked list */
		int	r_type;		/* type: TIE<, TIE>; <, <<, >, >>; | */
		union {
			char	*r_fname;	/* file name for < > >> */
			int	r_hereis;	/* hereis file for << */
			/*
			 * A `tied' redirect lists an actual fd
			 * (owned by this process) and a desired
			 * or `pretend' fd, so that >'d shell
			 * constructs will list an acutal fd of
			 * whatever the shell opened, with a
			 * desired fd of 1, e.g.
			 *
			 * Tied fd's are also used as back links in
			 * pipes.
			 */
			struct {
				int	rt_realfd;	/* actual fd */
				int	rt_desfd;	/* desired fd */
			} r_tie;
			struct	cnode *r_pipe;	/* link in pipe */
		} r_redir;
	};

(How is that for convoluted? :-)  It could well be wrong, too; I
just now made it up.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
UUCP:	seismo!mimsy!chris	ARPA/CSNet:	chris@mimsy.umd.edu

gwyn@brl-smoke.UUCP (02/14/87)

In article <176@quacky.mips.UUCP> dce@quacky.UUCP (David Elliott) writes:
>... Unless the folks at AT&T rewrote a major portion
>of csh, the System V.3 csh works much the same way.

?? Does SVR3 really have csh?  It's not in my copy of the User's
Reference Manual.  (Actually I hope they don't have it.)

I hear rumors that ksh may become part of the standard release of
UNIX System V.  Anyone know for sure?

guy@gorodish.UUCP (02/14/87)

>Unless the folks at AT&T rewrote a major portion of csh, the System V.3
>csh works much the same way.

The System V.3 *what*?

karl@osu-eddie.UUCP (02/15/87)

dce@mips writes:
>>The problem with the guess is that csh doesn't use stdio. It even uses
>>a local version of printf(), and in 4.xBSD, you have to come up with
>>a new version when you port csh to a new machine because the code is
>>in assembly language. Unless the folks at AT&T rewrote a major portion
>>of csh, the System V.3 csh works much the same way.

guy%gorodish@Sun.COM writes:
>   The System V.3 *what*?

No kidding, that could make several people's stomach's do gymnastics.
If there's a V.3 Csh, I sure wish someone would tell *me* about it,
and I'd give up my own version.

No such thing exists except as local hacks like mine, and I doubt
anyone wants to make it part of official SysV.anything.  Too bad in
some ways, very good in others.

Besides, it's absolutely no problem at all to get the printf()
incompatibilities out of the way.  Just go to any ex/vi source area
and pick up the portable printf (in C) - it drops right into csh in
place of the standard (ha - assembly code as standard) printf.c and
doprnt.c.
-- 
Karl