redirect stdin when using execl

Chris Torek chris at mimsy.UUCP
Wed Jun 14 06:09:13 AEST 1989


[Astounding, an article which actually belongs in both a C newsgroup
and a Unix newsgroup.]

In article <2489 at mit-caf.MIT.EDU> vlcek at mit-caf.MIT.EDU (Jim Vlcek) writes:
>... [someone gave] a bit of code showing how to fork(), dup2(), and then
>execl() to get the desired effect.
>
>I think a much better way, under BSD, is to use freopen() to attach
>stdin to the redirected file.  Such redirection is, in fact, precisely
>the intended usage of this function call.  Is freopen() a Berklism, or
>do you have that in Sys V as well?

freopen() is a standard <stdio.h> function and appears in the pANS;
most existing implementations should already provide it:

	FILE *freopen(const char *name, const char *type, FILE *stream)

replaces the current instance of `stream' (whatever it may be connected
to) with one connected to the given file `name', opened according to
`type'.  The type argument has the same format as for fopen ("r", "w",
"a", "rb", "wb", "r+", and so forth).  If the named file cannot be
connected to the given stream, the current connection is closed and
freopen() returns (FILE *)NULL; otherwise, the return value is equal
to `stream'.

[End C-specific topic; on to Unix-specific topic.]

This is not really a better way to redirect input after a fork(),
because freopen() makes no promises as to how it goes about its job.
In particular, it does *not* guarantee that fileno(stream) will be
the same after the operation as it was before.  There are, however,
a number of Unix utility programs whose source assume that it will,
so it probably will; but relying on this is a bad idea.

I found out about the various naughty programs when I changed freopen()
to open the new file before closing the old.  In particular, this is
necessary to make

	freopen("/dev/stdin", "r", stdin)

work.  I had to add code to dup2() the new descriptor over the old one
(after my first change, the above line caused fileno(stdin) to be 3).
This does, however, change the behaviour from the original, in which

	f1 = fopen("something", "r");
	... error checking deleted ...
	f2 = fopen("somethingelse", "r");
	...
	fclose(f1);
	freopen("yetanother", "r", f2);

caused fileno(f2) to decrease, e.g., from 4 to 3.  (This is not as
bad as a few programs which have code like

	fileno(fp) = 2;

Yow!)

Anyway, freopen() will probably work, but makes no guarantees, and
does more work than necessary.  In addition, if you use vfork() rather
than fork(), freopen() will clobber data in the parent process.
Stick with dup2().  But use it correctly (note in particular the
line marked with an arrow):

	/* initial error checking occurs in parent process */
	newfd = open(newfile, 0);
	if (newfd < 0)
		... error ...

	switch (pid = fork()) {

	case -1:
		... error ...

	case 0:
		/* child */
->		if (newfd != 0) {
			if (dup2(newfd, 0) < 0)
				... error ...
			if (close(newfd))
				... error ...
		}
		/* else it was already fd 0 */
		execl(pathname, argv0, argv1, argv2, ..., (char *)NULL);
		... error ...
	}
	/* parent */
	(void) close(newfd); /* if it fails, what would we do differently? */
	while ((w = wait(&status)) != pid)
		if (w == -1 && errno != EINTR)
			... error ...
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris at mimsy.umd.edu	Path:	uunet!mimsy!chris



More information about the Comp.unix.wizards mailing list