keith@fstohp.crd.ge.com (Keith D Gregory) (06/17/89)
In short, if one attempts to fclose() files, exec() another program, and freopen() the same files, the freopen() appears to fail. In the specific case, we ran the following programs on an HP-9000 (HP-UX 6.2) and a Mac-2 (A/UX 1.1 beta). The second program failed on the second call to fprintf() (the first completed), leaving beind a core dump. The _iob for StdErr appeared to be in pretty bad shape (as I recall, the character pointers were trashed). As a guess, the startup code in P2 is initalizing its _iob array, and the call to fclose() (as part of freopen()) is thinking that the structure refers to a valid stream. Is this what is happening? Is there any reason (documented) that we should not close a file before exec()ing a program? The obvious workaround (leave them open) is in place, but this application is to run in the background (after a foreground launch), and I don't particularly want any ill-behave programs to start sending data to an unclosed StdFile. Please mail any responses; you will need to explicitly use CRDGW1 as a gateway. Thanks Keith D Gregory keith%fstohp@crdgw1.ge.com # Program 1 Start, Cut here ################################################### #include <stdio.h> void main() { fclose( stdin ); fclose( stdout ); fclose( stderr ); execl( "p2", "p2", NULL ); } # Program 2 Start, Cut here ################################################### #include <stdio.h> void main() { if (freopen( "/dev/tty", "w", stderr) == NULL) exit( -1 ); else fprintf( stderr, "Reopened StdErr, Flags = %d, FileNo = %d\n", stderr->_flag, stderr->_file ); fflush( stderr ); if (freopen( "/etc/passwd", "r", stdin) == NULL) perror( "Unable to freopen StdIn" ); else fprintf( stderr, "Reopened StdIn, Flags = %d, FileNo = %d\n", stdin->_flag, stdin->_file ); fflush( stderr ); if (freopen( "/tmp/ix", "w", stdout) == NULL) perror( "Unable to freopen StdOut" ); else fprintf( stderr, "Reopened StdOut, Flags = %d, FileNo = %d\n", stdout->_flag, stdout->_file ); fflush( stderr ); } # Program 2 End, Cut here #####################################################
john@prcrs.UUCP (John C. Schettino Jr.) (06/19/89)
In article <20032@adm.BRL.MIL>, keith@fstohp.crd.ge.com (Keith D Gregory) writes: > > In short, if one attempts to fclose() files, exec() another program, > and freopen() the same files, the freopen() appears to fail. In the .... > #include <stdio.h> > > void main() > { > fclose( stdin ); > fclose( stdout ); > fclose( stderr ); > > execl( "p2", "p2", NULL ); > } When you exec a program, that program takes its stdin, stdout, and stderr from the current executable image (this is howe the shell supports redirection.) So in the example above, "p2" is started with stdin, stdout, and stderr closed, ie there are no valid file pointers or file descriptors available. > #include <stdio.h> > > void main() > { > if (freopen( "/dev/tty", "w", stderr) == NULL) > exit( -1 ); > else > fprintf( stderr, "Reopened StdErr, Flags = %d, FileNo = %d\n", > stderr->_flag, stderr->_file ); > > fflush( stderr ); > > < ADDITIONAL EXAMPLES REMOVED> > } Ok, so program p2 trys to freopen a closed file pointer. The first thing it does is close a file descriptor which is NOT open. This is "not good", and your behavior is undefined. Your "work around" of leaving the files open is actually correct. Remember that an exec() call starts up an executable which takes many things from the current executable: - open file descriptors (without the close-on-exec flag set, see fcntl) - COPY of the environment - file pointers for stdin, stdout, stderr (and in PC-DOS, stdprn) - current userid, groupid, etc. Hope this helps... --------------------- John Schettino : uunet!prcrs!john [no snappy saying or disclaimer]
maart@cs.vu.nl (Maarten Litmaath) (07/22/89)
keith@fstohp.crd.ge.com (Keith D Gregory) writes:
\...
\As a guess, the startup code in P2 is initalizing its _iob array,
\and the call to fclose() (as part of freopen()) is thinking that
\the structure refers to a valid stream.
\
\Is this what is happening?
Yes. See below.
\Is there any reason (documented) that
\we should not close a file before exec()ing a program?
Yes. See below.
\...
\# Program 1 Start, Cut here ###################################################
\#include <stdio.h>
\
\void main()
\{
\ fclose( stdin );
\ fclose( stdout );
\ fclose( stderr );
\
\ execl( "p2", "p2", NULL );
\}
Now file descriptors 0, 1 and 2 are all unused! Read on.
\# Program 2 Start, Cut here ###################################################
\#include <stdio.h>
\
\void main()
\{
\ if (freopen( "/dev/tty", "w", stderr) == NULL)
Which file descriptor will freopen() get for stderr?
Right: number 0 (== the first unused).
\ exit( -1 );
\ else
\ fprintf( stderr, "Reopened StdErr, Flags = %d, FileNo = %d\n",
\ stderr->_flag, stderr->_file );
\
\ fflush( stderr );
\
\ if (freopen( "/etc/passwd", "r", stdin) == NULL)
\...
Guess what stdin->_file is initialized too?
Right! ZERO.
Guess what the freopen() does first?
Right: a close(stdin->_file).
Guess whose file descriptor we've just managed to destroy?
Right again! stderr->_file is now closed too... :-(
It will be opened again READONLY, due to the "r" argument of freopen().
Hence perror() etc. fail.
One might consider the static initialization of stdin/stdout/stderr a bad
feature in the stdio implementation.
On the other hand, you do some pretty strange things, so you could
expect strange results.
One should provide every program will valid file descriptors 0, 1 and 2.
If you don't want the program to do anything with them, just connect them
to /dev/null.
If the program isn't interested in the pre-connected files, it could close()
the descriptors itself, e.g. implicitly by freopen().