Correct way of using fdopen

1.4k views Asked by At

I mean to associate a file descriptor with a file pointer and use that for writing. I put together program io.cc below:

int main() {
    ssize_t nbytes;
    const int fd = 3;
    char c[100] = "Testing\n";
    nbytes = write(fd, (void *) c, strlen(c));     // Line #1
    FILE * fp = fdopen(fd, "a");
    fprintf(fp, "Writing to file descriptor %d\n", fd);
    cout << "Testing alternate writing to stdout and to another fd" << endl;
    fprintf(fp, "Writing again to file descriptor %d\n", fd);
    close(fd);     // Line #2
    return 0;
}

I can alternately comment lines 1 and/or 2, compile/run

./io 3> io_redirect.txt

and check the contents of io_redirect.txt. Whenever line 1 is not commented, it produces in io_redirect.txt the expected line Testing\n. If line 2 is commented, I get the expected lines

Writing to file descriptor 3
Writing again to file descriptor 3

in io_redirect.txt. But if it is not commented, those lines do not show up in io_redirect.txt.

  • Why is that?
  • What is the correct way of using fdopen?

NOTE. This seems to be the right approach for a (partial) answer to Smart-write to arbitrary file descriptor from C/C++ I say "partial" since I would be able to use C-style fprintf. I still would like to also use C++-style stream<<.

EDIT: I was forgetting about fclose(fp). That "closes" part of the question.

1

There are 1 answers

4
KamilCuk On BEST ANSWER

Why is that?

The opened stream ("stream" is an opened FILE*) is block buffered, so nothing gets written to the destination before the file is flushed. Exiting from an application closes all open streams, which flushes the stream.

Because you close the underlying file descriptor before flushing the stream, the behavior of your program is undefined. I would really recommend you to read posix 2.5.1 Interaction of File Descriptors and Standard I/O Streams (which is written in a horrible language, nonetheless), from which:

... if two or more handles are used, and any one of them is a stream, the application shall ensure that their actions are coordinated as described below. If this is not done, the result is undefined.

...

For the first handle, the first applicable condition below applies. ...

  • ...

  • If it is a stream which is open for writing or appending (but not also open for reading), the application shall either perform an fflush(), or the stream shall be closed.

A "handle" is a file descriptor or a stream. An "active handle" is the last handle that you did something with.

The fp stream is the active handle that is open for appending to file descriptor 3. Because fp is an active handle and is not flushed and you switch the active handle to fd with close(fd), the behavior of your program is undefined.

What is my guess and most probably happens is that your C standard library implementation calls fflush(fp) after main returns, because fd is closed, some internal write(3, ...) call returns an error and nothing is written to the output.

What is the correct way of using fdopen?

The usage you presented is the correct way of using fdopen.