dup2 function and program behaviour
The standard I/O streams are buffered — with the possible exception of the standard error stream, which is only required to not be fully buffered by POSIX. Without a call to fflush(stdout)
to flush the output buffer for standard output (or the output of a newline sequence if it is line-buffered), the way things work with respect to the FILE
interface is not defined once you call dup2
.
Since dup2
works with file descriptors and not FILE
pointers, you have a problem: POSIX doesn't specify what to do in this case. The buffer associated with stdout
may be discarded, or it may be flushed as if with fclose
. The buffer may even remain associated and not flushed/discarded since stdout
from the perspective of the FILE
interface is still open.
So the behavior isn't necessarily deterministic without syncing the FILE
interface with the underlying file description (add an fclose(stdout)
call after dup2
). Additionally, what happens with, e.g., stderr
in addition to stdout
with dup2
associated with the file description of the file you open? Is the behavior in order of the dup2
calls as with a queue or in reverse order as with a stack or even in a seemingly random order, the latter of which suggests that a segfault may be possible? And what is the order of output if you dup2(STDERR_FILENO, STDOUT_FILENO)
, followed by dup2(fileno(fp), STDERR_FILENO)
? Do the results of writing to the standard output/error buffers appear before the fprintf results or after or mixed (or sometimes one and sometimes another)? Which appears first — the data written to stderr or the data written to stdout? Can you be certain this will always happen in that order?
The answer probably won't surprise you: No. What happens on one configuration may differ from what happens on another configuration because the interaction between file descriptors, the buffers used by the standard streams, and the FILE interface of the standard streams is left undefined.
As @twalberg commented, there is no guarantee that the file you opened is file descriptor 3, so be careful when hard-coding numbers like that. Also, you have STDOUT_FILENO
available from <unistd.h>
, which is where dup2
is actually declared, so you can avoid using a call to fileno
in place of file descriptor 1 by using it.
There are rules to follow when manipulating "handles" to open file descriptions.
Per System Interfaces Chapter 2, file descriptors and streams are "handles" to file descriptions and a file description can have multiple handles.
This chapter defines rules when working with both a file descriptor and a stream for the same file description. If they are not followed, then the results are "undefined".
Calling dup2()
to replace the stdout
file descriptor after calling printf()
(on the STDOUT
stream) without a call to fflush(STDOUT)
in between is a violation of those rules (simplified), hence the undefined behavior.