9

Can stdout file descriptor differ from 1 (STDOUT_FILENO) assuming stdout need not be a modifiable lvalue?

For example, can freopen("/dev/null", "w", stdout) change fileno(stdout) result?

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670

1 Answers1

11

Yes.

Test program:

#include <stdio.h>

int main() {
    fclose(stdin);
    freopen("stdout.txt", "w+", stdout);
    fprintf(stderr, "%d\n", fileno(stdout));
    return 0;
}

This prints 0 on my machine (OS X 10.9.4).

File descriptors are typically reused starting from the lowest number first. By closing stdin, file descriptor 0 is freed up, and the subsequent freopen will use file descriptor 0 when opening the file.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • @J.F.Sebastian: It looks like freopen uses `dup3` on Linux, instead of `close` followed by `open`. I'm not actually sure if this is legal per POSIX standards, which suggests that the original file descriptor is to be *first* flushed and closed (implying that the open should happen later). – nneonneo Aug 26 '14 at 23:28
  • [glibc](http://fossies.org/dox/glibc-2.19/freopen64_8c_source.html) intentionally tries to preserve the old `fd`. It [doesn't close it before calling `open()`](https://sourceware.org/git/?p=glibc.git;a=blob;f=libio/freopen64.c;h=380e795ff4476159c35426342b1a96ed8455fa16;hb=HEAD#l51). It shouldn't matter unless `freopen()` happens to consume the last available fd. [bionic](https://code.google.com/p/android-source-browsing/source/browse/libc/stdio/freopen.c?repo=platform--bionic&name=jb-mr1-dev-plus-aosp#90) closes the fd if the first `open()` fails and tries to reopen to compensate. – jfs Aug 27 '14 at 11:26
  • @J.F.Sebastian The glibc behavior looks non-compliant to me. The [POSIX spec for freopen](http://pubs.opengroup.org/onlinepubs/9699919799/functions/freopen.html) makes the sequence very clear, IMO, saying the original fd is closed before the new one is opened "as if by a call to `open`". @nneonneo Reusing the lowest unused descriptor is not only typical, but [required by POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html). +1 – Nemo Oct 09 '14 at 15:35
  • @Nemo: [The sequence in the spec is not clear](http://pubs.opengroup.org/onlinepubs/9699919799/functions/freopen.html): I don't see anything that forbids calling `open()` before `close()`. I would understand if it said: *"**Then** freopen() function shall open "* but it says: *"The freopen() function shall open"* i.e., the order is not defined -- just the result. And glibc produces the same result if there are available fds (very likely): it does close the input fd (normally in dup3/dup2) eventually. – jfs Oct 09 '14 at 16:38
  • @J.F.Sebastian: "The original stream shall be closed regardless of whether the **subsequent** open succeeds." I concede they could have worded it better, but the author clearly meant for it to be sequential, in my opinion. (I would not be surprised if the glibc developers disagreed.) Also, the result is not necessarily the same, since `open` is required to return the lowest unused descriptor. The program in this answer must print 0 by my reading of POSIX. – Nemo Oct 09 '14 at 19:17
  • @Nemo: By "result" I meant that the file is closed at some point in `freopen()`. *"subsequent"* is unambiguous but it is easy to overlook like I did despite reading the description multiple times and inspecting the source for several `freopen()` implementations. Both GNU and BSD implementations do not close fd before `open()`. The only [implementation that I've seen that closes before the open is for minix](http://www.cise.ufl.edu/~cop4600/cgi-bin/lxr/http/source.cgi/lib/stdio/freopen.c). It is expected because preserving fd seems more worthy than picking low fd. Are there POSIX issue tracker? – jfs Oct 09 '14 at 19:37
  • to answer my question: here's [the open group bug tracker](http://www.opengroup.org/austin/defectform.html) – jfs Oct 09 '14 at 19:42